Posts tagged ‘programming’
This is the second post in the toolbox series. FancyLog is basically a proxy for Ruby’s Logger that solves some problems that occurred in my development and enhances slightly the logger API. All original methods should work as expected, so you can use FancyLog as a Logger replacement without any changes to your code.
But lets start from the beginning. In my first project I faced the problem that I did not see errors on the console when they occurred. They were nicely written to the log file or the console, but the log could get really large and I felt not to comfortable searching a large log for possible errors. I wanted an easy way to see the errors on my console, when I was developing, and to log them to the log file when in production mode. Replacing the calls to the logger with
puts seemed a bad idea, managing two logger instances, too. So I decided to factor out the logger to a separate gem and that was the birth of FancyLog.
FancyLog uses the singleton pattern, so you always have only one instance flying around your application, agnostic to where and when it was first invoked. At the first call you can pass options to it, to let it behave the way you want it to, but you can change most of them later. The only thing you can not change at run time are the log devices. Maybe this will be included in a future version.
Want an example? Here it is:
log = FancyLog.instance log.error('An undefined error occurred') log.just_an_information('I like FancyLog!!!') log.information_to_err('This is an information')
What happens here?
- In line 1, we get an instance of FancyLog. Because we do not specify any options, all informational log messages (debug, info, warn) go to STDOUT and all errors (error, fatal) go to STDERR. This is FancyLog’s default behavior, that can be overridden.
- Line 2 sends an error to the logger, that is printed to STDERR – just like Ruby’s Logger would.
- Line 3 gets interesting, because this is no more the standard Logger API. FancyLog uses Ruby’s method_missing to determine what you wanted it to do. It scans the provided method for debug, info, warn, error and fatal and calls the appropriate Logger method – in this case
info– and passes the arguments to it. If it can not find a match it defaults to the default_level specified in the options.
- Line 4 sends – as it explicitly says – an information to the error log. How this is done, you ask? You can always tell FancyLog to log messages to err or out with the appropriate ending of your method (
As you can see, FancyLog enables you to specify log methods that tell you as a programmer what really happens, regardless of the message printed to the user.
If you want to switch from development to production mode, it is just one option, you have to specify in the setup of your logger –
log = FancyLog(:errs_to_err => false).instance and you are done. Of course, FancyLog does not perform as well as the original Ruby Logger, but I do not expect logging to be a performance critical task.
To summarize: FancyLog lets you easily define were your log messages are logged to and it lets you name the log method in a way that your code makes more sense and is better readable.
- Ruby’s Logger
- Singleton pattern
The beginning of a gem factory
Emerald is a good starting point as it is a RubyGem that creates a bare directory structure for another gem. The default structure, it generates, is:
[gemname] - bin [gemname] - lib [gemname].rb - tests tc_[gemname].rb rakefile README [gemname].gemspec
If you do not want to generate all directories you simply specify the directories you want from the command line, e.g.
emerald gemname lib tests to create only the lib and tests directories.
- The bin directory contains the executable (named after the gem) which requires the class in the lib directory.
- The main class lies in the lib directory and contains a bare structure – the class definition and an initialize method
- The test for the main class is in the tests directory. It is named after the main class and sets a require statement for the main class, so it can be used in the tests. It contains a basic (setup, _tc_method and _teardown_) method structure. All you have to do, is write your tests.
Lets do an example an see what happens:
When we type
emerald FancyTest in the console. Emerald generated the following structure for us:
- gem directory
- binaries directory, used to invoke your gems from the console
- this is our executable after installing the gem
- this is a bare gemspec, where you can define dependencies, tests, documentation, …
- the lib directory is the applications main directory where all classes and modules are found
- the main class of the application
- with the rakefile you can perform tasks that are described in the next section in detail
- the readme is used in the rdoc and is a starting point for users of your application
- directory for all your tests
- this is the test case for our application that contains a bare unit test case layout
The real magic of Emerald is in the Rakefile. It has predefined tasks to
- create new classes + associated tests
- create the gem from the gemspec
- generate rdoc documentation for development or release
- run your tests.
The latest gem is stored in the pkg directory, all previous gems are deleted. It is assumed that old gems are no longer needed, because you have them stored in your SCM (don’t you?) and would otherwise just litter up this directory. All this saves a developer a lot of typing and leads to a standard gem development, where you can easily find your way through a gem.
Here is a complete listing of all rake tasks:
Tasks: rake class n=Klass # generate class with test GEMS rake clear_packages # clear packages rake clobber_package # Remove package products rake clobber_rdoc # Remove rdoc products rake clobber_rdoc_dev # Remove rdoc products rake gem # create gem package / Build the gem file rake package # Build all the packages DOCS rake rdoc # Build the rdoc HTML Files rake rdoc_dev # Build the rdoc_dev HTML Files rake repackage # Force a rebuild of the package files rake rerdoc # Force a rebuild of the RDOC files rake rerdoc_dev # Force a rebuild of the RDOC files TESTS rake test # run tests normally rake test TEST=just_one_file.rb # run just one test file. rake test TESTOPTS="-v" # run in verbose mode
Emerald is designed to keep repetive tasks away from the developer. I do not say that the structure it generates is perfect for everyone. It is just the way I like to write my gems. It does encourage you to write tests and not to leave them out of your programs, because each class you generate has an associated test case.
In my case this gem design pattern has led to a cleaner programming style.