Showing posts with label Ruby. Show all posts
Showing posts with label Ruby. Show all posts

Thursday, October 01, 2009

Podcast with Željko Filipin

Recently I recorded a podcast with Željko Filipin. We talked on FireWatir history, Watir & Firewatir merge. Replacing JSSh with Firedriver and other stuff. Follow the link to listen to the podcast
http://watirpodcast.com/27-angrez-singh-on-firewatir/

Wednesday, December 27, 2006

Log4r - Usage and Examples

Log4r is a library used for logging in Ruby programs. It can be used for logging to any kind of destination and with varying degrees of importance (levels). Log4r supports custom level names (i.e. you can have as many levels as you can instead of using built-in levels), logger inheritance, multiple output destination, custom formatting of messages, XML and YAML configuration etc.

In this article we'll see how to use Log4r in Ruby programs.

1. Simple logging using Log4r.

The below code shows how to log simply to the Standard output using Log4r. It doesn't use any formatting or any custom levels.

require 'log4r'

include Log4r

# create a logger named 'mylog' that logs to stdout
mylog = Logger.new 'mylog'

# You can use any Outputter here.
mylog.outputters = Outputter.stdout

# log level order is DEBUG < INFO < WARN < ERROR < FATAL
mylog.level = Log4r::INFO

# Now we can log.
def do_log(log)
    log.debug "This is a message with level DEBUG"
    log.info "This is a message with level INFO"
    log.warn "This is a message with level WARN"
    log.error "This is a message with level ERROR"
    log.fatal "This is a message with level FATAL"
end

do_log(mylog)


The "do_log" method logs the messages at different levels. For logging you don't have call to a function that takes 'log level' and message as argument. Instead in Log4r each level is a method itself which takes message as argument and then logs the message depending upon the level that is set for the logger.

You can also check the logging level before logging a message using "query methods" like debug?, info? etc. You can use them like this:

if(log.debug?)
    log.debug "debug message"
end


The above code will first check that if current logger can log the messages at debug level or not. The method with level name and question mark is called "query method". It will return true or false depending upon the level set for the logger.

2. Logging using Formatter and Outputter:

Above example used Standard output as destination for the logging. It also didn't use any Formatter for formatting the message. In this section we'll see what all destinations we can use for logging (with example of one of them) and what all formatting we can use (with example of one of them).

Log4r provides the following destination for logging:
1. IOOutputter -> For logging to any IO object
2. StdoutOutputter -> For logging to standard output IO i.e. $stdout
3. StderrOutputter -> For logging to standard error IO i.e. $stderr
4. FileOutputter -> For logging to file
5. RollingFileOutputter -> For logging to file and split the file as it grows
6. SyslogOutputter -> For logging to system log
7. EmailOutputter -> For emailing the logs
8. RemoteOutputter -> For remote logging

You can attach any number of outputter to a logger using 'outputters' property or by using 'add' method of logger object. Suppose you have created four outputters out1 to out4, you can add them to logger 'mylog' as follows:

mylog.outputters = out1, out2
mylog.add(out3, out4)


Log4r provides several formatters for formatting the message before logging it. It also provides a custom formatter called 'PatternFormatter' which is very flexible and powerful.

Log4r provides the following formatters:
1. BasicFormatter -> This is default formatter. Logs the message as it is.
2. PatternFormatter -> Most powerful and flexible formatter for custom formatting.
3. SimpleFormatter -> Adds log level and logger name to the message. Used only for strings. Doesn't inspect objects.
4. ObjectFormatter -> Inspect the objects and logs them as IRB(Interactive Ruby) does. Doesn't inspect strings.
5. NullFormatter -> Does nothing :)

Following code sample uses 'FileOutputter' alongwith 'PatternFormatter' for logging the message to log file.

require 'log4r'
include Log4r

# create a logger named 'mylog' that logs to stdout
mylog = Logger.new 'mylog'

# You can use any Outputter here.
mylog.outputters = Outputter.stdout

# Open a new file logger and ask him not to truncate the file before opening.
# FileOutputter.new(nameofoutputter, Hash containing(filename, trunc))
file = FileOutputter.new('fileOutputter', :filename => 'log',:trunc => false)

# You can add as many outputters you want. You can add them using reference
# or by name specified while creating
mylog.add(file)
# or mylog.add(fileOutputter) : name we have given.

# As I have set my logging level to ERROR. only messages greater than or
# equal to this level will show. Order is
# DEBUG < INFO < WARN < ERROR < FATAL
mylog.level = Log4r::INFO

# specify the format for the message.
format = PatternFormatter.new(:pattern => "[%l] %d :: %m")

# Add formatter to outputter not to logger.
# So its like this : you add outputter to logger, and add formattters to outputters.
# As we haven't added this formatter to outputter we created to log messages at
# STDOUT. Log messages at stdout will be simple
# but the log messages in file will be formatted
file.formatter = format

# Now we can log.
def do_log(log)
    log.debug "This is a message with level DEBUG"
    log.info "This is a message with level INFO"
    log.warn "This is a message with level WARN"
    log.error "This is a message with level ERROR"
    log.fatal "This is a message with level FATAL"
end

do_log(mylog)


FileOuputter.new creates new instance of FileOutputter. First argument is name of the outputter, second is hash containing name of the file and whether to trunc the file while opening the file or not. For more information on outputters refer to this link.

PatternFormatter.new creates a new pattern formatter. It takes hash as input specifying the format for the message. If you are using date in the message you can optionally give date_pattern also. If no date_pattern is specified default date time patter will be chosen, which will log the date like this:
2006-12-21 13:15:50

To specify custom date formatting use the following code:

format = PatternFormatter.new(:pattern => "[%l] %d :: %m",
     :date_pattern => "%a %d %b %H:%M %p %Y")


For more information of "date_pattern" directive (%a, %d etc) refer to this link. For more information about "pattern" directives (%l, %m) refer to this link.

Wednesday, November 22, 2006

Executing ruby scripts without Ruby installation

RubyScript2Exe is an interesting project that helps you to run ruby scripts on a machine that doesn't have Ruby installed on it. This projects creates standalone applications for Windows, Mac and Linux which can than be executed.

It collects all the files that are required to run the script on other machine: the Ruby script, Ruby interpreter and the Ruby run time library. Because these files are gathered from the local installation RubyScript2Exe creates an executable for the platform it run's on. No cross compile.

For more information checkout the RubyScript2Exe project.

Friday, November 10, 2006

Using reflection in Ruby

Reflection in Ruby is a great way of doing the things at runtime. Sometimes you need to create an instance of class depending upon the parameter passed to a function. This parameter could be the name of the class to be created.

One way to do this is to write conditional loops and create the object. But if there are too many classes then this would become messy. Here comes reflection to rescue.

In Ruby using reflection you can get the following information:
1, What all class currently exists?
2. Their methods information
3. Their class hierarchy and lot more

Let consider the above problem and try to find out the solution using Reflection.

Ruby provide a module called "ObjectSpace" that lets you to use reflection and see all the above mentioned information.

so if you say

ObjectSpace.each_object { |x| puts x }


It will print all living, nonimmediate objects in Ruby process.
If you specify the type of objects that you want then you can specify it as option to each_object method. So,

ObjectSpace.each_object(Class) { |x| puts x}


will print all the classes that are there in the Ruby process.

So now the above problem becomes simple.
Iterate over all the classes compare their name. If name matches then create object and execute whichever function you like.

So, the code looks like

class ClassFromString
@@counter = 0
def initialize
@@counter += 1
end
def getCounterValue
puts @@counter
end
end


def createClassFromString(classname)
ObjectSpace.each_object(Class) do |x|
if x.name == classname
object = x.new
object.getCounterValue
object = x.new
object.getCounterValue
end
end
end
createClassFromString("ClassFromString")


Once you get the object you can use any method of that object. For e.g. you can use superclass method to get the name of the parent class and so on and can build complete hierarchy dynamically.
You can get the information about methods of a given class using methods like private_methods(), protected_methods() which are defined in Object class which is base class for each object.

Reflection is great thing but there is also some performance hit when you use Reflection.

Happy programming !!!