-
Parsing Quoted Strings in Ruby April 28th, 2008
Python has a nice module in the standard library called shlex that parses strings as a Unix shell would. Here’s a Python interpreter session demonstrating its usage:
>>> import shlex >>> shlex.split('foo "bar baz" qux') ['foo', 'bar baz', 'qux']
It’s useful for creating your own mini-languages or external DSLs that need to parse quoted strings like the one shown above.
We recently built an inventory tracking application in Ruby that has a user interface for selecting search filters. To expose the same search capabilities as a web service, we created a simple query language. I was looking for an
shlexequivalent in Ruby.It turns out that the Ruby Standard Library has a module called Shellwords:
>> require 'shellwords' => true >> Shellwords::shellwords('foo "bar baz" qux') => ["foo", "bar baz", "qux"]
Shellwordsis a little less capable thanshlexbut handles the most common use case just fine. It’s a convenient solution for a problem that comes up too often. -
Fail Early March 25th, 2008
I’m pleased to have been able to contribute a recipe to Mike Clark’s new book, Advanced Rails Recipes. The concept presented in my recipe, “Fail Early”, is that you can use initializers to prevent your application from starting up under certain conditions.
Rails applications typically run under persistent application server processes, like
mongrelorthin. When a Rails application starts, it goes through a startup procedure that is performed only once. The startup includes reading the environment configuration files and running any initializers that have been set up. This can also be used as an opportunity to detect potentially dangerous situations and bail out.“Fail Early” uses the case of pending migrations to demonstrate. If the application is started while there are pending migrations for the production database, the results can wreck production data. Instead, an initializer detects this condition and exits by calling Ruby’s
Kernel.abort.Here’s another case where this idea is useful. It’s well-known that the Ruby-based MySQL driver included with Rails isn’t suitable for use in production. In fact, Rails will produce this warning in the log if it is in use:
WARNING: You’re using the Ruby-based MySQL library that ships with Rails. This library is not suited for production. Please install the C-based MySQL library instead (gem install mysql).
This can go unnoticed in the log. Instead, we can write a short initializer that detects this condition and aborts the application start if the production server is misconfigured.
config/initializers/check_mysql_driver.rb:
if RAILS_ENV == 'production' config = ActiveRecord::Base.configurations['production'] if config['adapter'] == 'mysql' ActiveRecord::Base.require_mysql if Mysql::VERSION.to_s.include?('-ruby') abort "Ruby-based MySQL driver is not suitable for production" end end end
When the initializer above is run in a production environment that has the Ruby-based MySQL driver instead of the C-based one, startup will be aborted.
$ mongrel_rails start -e production ** Starting Mongrel listening at 0.0.0.0:3000 ** Starting Rails with production environment... Ruby-based MySQL driver is not suitable for production $
You can run many other safety checks like this at startup. Since they will be run only once and not per-request, your application incurs no performance penalty by doing so.
-
New Rails for PHP Developers Website February 18th, 2008
Rails for PHP Developers is a new website that’s a companion to the new book by the same name. Like the book, it’s aimed towards PHP developers who have an interest in Rails and Ruby. The website features articles that alternate between Ruby and PHP focus, so PHP developers that aren’t interested in Ruby should still find it useful.
-
Rails Hackfest Winner December 5th, 2007

I was pleased to learn today that I am a winner in the Rails Hackfest.
The Hackfest is a contest where your ranking is primarily determined by how many of your patches get accepted into Rails core during the contest month.
-
Keep the Flash and Test it, too. September 8th, 2007
The
flashin Ruby on Rails is a special hash that is stored in the session. Its contents are available to the next request and cleared out immediately after.This makes is very useful one-time events, such as an item being added to a shopping cart:
flash[:success] = "#{cart.items.size} items added to your cart."
The
flash[:success]will be available for the next request only.However, Ajax requests will also clear the flash. This can lead to the sometimes mysterious problem of the flash contents disappearing before their intended request because an intermediate Ajax request caused them to be cleared.
The solution is to explicitly preserve the flash contents in actions that could unintentionally clear it. This is done using
flash.keep:flash.keepThe current contents of the flash will then be preserved for the next request. You can also pass a specific key such as
flash.keep(:foo).On one application I work on, we make Ajax requests periodically on a timer. Putting
flash.keepin these Ajax actions was a simple way to make sure they didn’t unexpectedly gobble the flash contents.Of course, once adding
flash.keep, we also want to add a functional test for it. You might try doing this:def test_something_keeps_flash @request.flash[:foo] = 'bar' xhr :get, :something assert_response :success assert_equal 'bar', @response.flash[:foo] end
You’d be close but wrong. That would seem like a natural way to do it but unfortunately, the flash can’t be populated that way due to how
TestRequestwas built. It doesn’t have aflashattribute and the session doesn’t have aFlashHash. However, that won’t stop us from writing our test.We could try monkeypatching but I try to avoid clever things like that. This problem is easily remedied with a helper in
test_helper.rb:def make_flash_hash(with_contents = {}) returning ActionController::Flash::FlashHash.new do |flash_hash| flash_hash.update with_contents end end
Our test case now can now use the simple
make_flash_hashhelper to populate the request’s session with flash:def test_something_keeps_flash @request.session['flash'] = make_flash_hash(:foo => 'bar') xhr :get, :something assert_response :success assert_equal 'bar', @response.flash[:foo] end
In addition to using
@request.sessionas shown above, you could also populate the session in thexhrmethod directly. I like the former for readability.Whichever way you choose to do it, writing the functional test will ensure that you’ll continue to preserve the flash when the application changes later.
-
New RubyOSA Website April 12th, 2007
RubyOSA now has a new website! It has a fresh look thanks to Derek DeVries and the Maintainable design team:
The new site features updated information from Laurent and I, including a new guide. Over the coming weeks, we’ll continue to expand and improve its content.
Please let us know what you think about it!
Update: This post was featured on Laurent Sansonetti’s diary.
-
OSA::ObjectSpecifierList#every March 26th, 2007
For those of you who aren’t yet on the RubyOSA list (you should be), Laurent just committed a nice new feature to
OSA::ObjectSpecifierList. It’s actually been there since last week, in the form of somemethod_missinghackery, but we finally decided to take that out and call it#every.Sometimes you need to collect an attribute from every object in the specifier list. Normally, you’d do something like this:
names = OSA.app('iCal').calendars.collect { |c| c.name }
Symbol#to_proc fans use the convenient form:
names = OSA.app('iCal').calendars.collect(&:name)
Now,
OSA::ObjectSpecifierListalso has the#everymethod:names = OSA.app('iCal').calendars.every(:name)
The difference is subtle but
#everywill fetch all of the attributes in a single Apple Event, something not possible when iterating over each item in the collection. For most purposes this is not important but it is a nice feature that could make a difference on larger collections.This feature is currently in the RubyOSA trunk and will appear in the next stable release (coming soon).
Update: RubyOSA 4.0 has been released and includes this feature!
-
DRY up testing in Rails with Autotest March 23rd, 2007
As Rails developers, we’ve been trained hard to test early and test often. We are also acutely aware of the DRY principle (Don’t Repeat Yourself). However, these ideas don’t quite agree in Rails because in our test-code-test cycle, we’re constantly typing
rakeevery time we need to run our tests.Autotest will DRY up your testing by running your tests automatically whenever your files change. In this article, we’ll explore Autotest:
- Installation
- Starting Autotest
- Stopping Autotest
- Autotest Plugins
- Coloring with RedGreen
- Notifications
- Next Steps
Installation
Autotest is a smart little program included in the ZenTest bundle of goodies. To install it, you’ll just need to install the gem for ZenTest.
gem install ZenTest
Depending on how your system is set up, you might need to run this as the root user or through
sudo.Starting Autotest
Running Autotest is as simple as running rake. First, change to the root directory of your Rails project. This is the directory that has
Rakefile,app/,config/, etc. Next, run theautotestcommand:$ autotest
Autotest will discover that it is running inside a Rails project and your tests will run just as they do with
rake(or the wordierrake test).After your tests run, Autotest will not exit back to the shell prompt. It will then sit and poll your files. When it notices files that change, it will run the tests for only the files that you’ve changed! It will do this continuously until you stop it.
Stopping Autotest
Pressing
Control-Conce will run your entire test suite again.Pressing
Control-Ctwice in quick succession will exit Autotest back to the shell prompt.Autotest Plugins
Autotest includes a plugin mechanism that allows plugins to monitor different aspects of the testing lifecycle. Autotest includes a number of useful plugins out of the box.
In the next sections, we’ll see how to activate the plugins and what functionality they provide.
Coloring with RedGreen
One of the problems of testing under
rakeandautotestis that a lot of output can be generated and when looking at the results, you sometimes have to filter out the normal output to see the failures.RedGreen is a simple Autotest plugin that solves this problem by coloring the summary lines of the test output either red or green to indicate whether the tests passed or failed:

Autotest automatically looks for a dotfile (
.autotest) when it is started. This file may be in your Rails project directory or in your home directory where it will be used by all projects.To install RedGreen or any other plugin, create the
.autotestfile with a simplerequireto load the plugin:# .autotest require 'autotest/redgreen'
That’s it! When you run autotest again, the plugin will be automatically loaded and your test output colored.
Notifications
Autotest also comes with the plugins
growl,snarl, andkdenotify. Each are installed the same way as shown above, simply add the require line to your.autotestfile. These allow Autotest to communicate each respective notification system.
Using one of these can be useful when running autotest in the background or in a minimized window. The screenshot above shows a Growl pop-up notification from Autotest under Mac OS X.
Note that for Autotest to send notifications to Growl, the
growlnotifyutility must be installed. This comes in theExtras/directory of the Growl disk image.Next Steps
Autotest isn’t limited to plugins shown here. There are a number of other useful plugins you can explore and more are added all the time. The plugins can be found in
/path/to/your/gems/ZenTest-x.x.x/lib/autotest.While Autotest can be an invaluable tool when testing Rails applications, it isn’t limited to Rails at all. Autotest can be used with an Ruby project that follows some simple conventions.
Visit the Autotest section of the ZenTest RDoc to learn about this, writing plugins, and more.
Update: This article was featured on Ryan Davis’ blog.
-
Rails Logging Tips February 24th, 2007
In this article, we’ll take a look at how to use the logging facilities built into Rails and then share a few tips:
- Accessing the Rails Logger
- Log Levels
- Filtering Sensitive Parameters
- Creating Audit Logs
- ActiveRecord Logging to the Console
- ActionController Logging to the Console
- Firebug Console
Accessing the Rails Logger
Rails automatically sets up logging to a file in the
log/directory using Logger from the Ruby Standard Library. Do not confuse this with Log4r, a completely different library. The logfile will be named corresponding to your environment, e.g.log/development.log.To log a message from either a controller or a model, access the Rails logger instance with the
loggermethod:class HomeController < ActionController::Base def index logger.info 'informational message' end end
From outside a controller or model, you can pass the logger instance or access it with the constant
RAILS_DEFAULT_LOGGER.Log Levels
In the snippet above, a message is logged at the INFO level by calling a method of the same name (
info).The levels available on the logger are (in ascending order):
debug,info,warn,error, andfatal.Ruby’s Logger supports masking levels so the types of messages recorded in the log can be controlled. By default, Rails will log all levels (
debugand higher) in every environment except production. In the production environment, it will only loginfoand higher. This behavior can be changed in the configuration for each environment.Filtering Sensitive Parameters
When Rails receives a request, ActionController logs the request parameters. This is very handy for debugging but sometimes it’s not desirable to have certain parameters, such as passwords, stored as plain text in the log.
Rails 1.2 introduced the
filter_parameter_loggingclass method to remedy this:class ApplicationController < ActionController::Base filter_parameter_logging :password end
The above will cause any parameter name matching
/password/ito have its value replaced with[FILTERED]in the log. To filter multiple parameters, simply add them as extra arguments tofilter_parameter_loggingby separating them with commas. For other uses offilter_parameter_logging, see the ActionController documentation.Note: it’s important to remember that
filter_parameter_loggingwill only filter ActionController request information. The parameters could still appear in any SQL queries being logged by ActiveRecord. However, SQL queries are not logged in the production environment by default.Creating Audit Logs
Sometimes logging is required but putting the messages in the Rails log isn’t desirable. One such case is when keeping a separate logfile for auditing is a business requirement.
To create an audit log, simply create a new instance of
Loggerand pass it aFileinstance for your own logfile.One possible source of confusion is the formatting of the log message due to a patch Rails makes to
Logger. This can be seen when usingirbas opposed toscript/console:$ irb irb(main):001:0> require 'logger' => true irb(main):002:0> Logger.new(STDOUT).info('message') I, [2007-02-24T09:45:51.236763 #557] INFO -- : message$ script/console Loading development environment. >> Logger.new(STDOUT).info('message') messageAs you can see, the message formatting is lost when run in the Rails environment. To format a log message when using Rails, create your own Logger subclass and implement the
format_messagemethod:class AuditLogger < Logger def format_message(severity, timestamp, progname, msg) "#{timestamp.to_formatted_s(:db)} #{severity} #{msg}\n" end end
To use the new
AuditLogger, instantiate it with aFileinstance:logfile = File.open('/path/to/audit.log', 'a') audit_log = AuditLogger.new(logfile)
Your new log is now ready to use by calling methods on it like
audit_log.info 'message'.One important point to remember is that the
logfileobject does not implicitly flush to the file by default. This means that your code must calllogfile.flushfor the data to be written out. Alternatively, you can setlogfile.sync = trueto turn on implicit flushing.ActiveRecord Logging on the Console
When debugging your applications with
script/console, it can be very useful to see the SQL output of your ActiveRecord queries. One way to do this is to usetail -fon your logfile.However, this isn’t very convenient and shows all other log information as well. An easier way that can be done directly from script/console is to enter this line:
ActiveRecord::Base.logger = Logger.new(STDOUT)
This will cause the queries to be displayed immediately on the console as you interact with your ActiveRecord objects with method calls like
Article.find :all.ActionController Logging on the Console
Just like with ActiveRecord above, you can also redirect ActionController’s log output to the console when using
script/console:ActionController::Base.logger = Logger.new(STDOUT)
You can then observe ActionController activities when using using using the app object on the console, such as app.get ‘/’. For a brief tutorial on using app, see this post on Mike Clark’s weblog.
Firebug Console
Firebug is a popular extension for the Firefox browser that provides a number of useful capabilities, with a Javascript console being among them. The console is accessed by
console.log('message')in Javascript.This means that any Rails view emitting this little Javascript call between
<script>tags can write to the Firebug console.It also means that any RJS template can write to the Firebug console for debugging:
page.call 'console.log', 'informational message'
This can be very useful for logging debug information during AJAX requests where a popup from
alert()isn’t desirable. -
New Javascript Goodies February 24th, 2007
On the Maintainable site today are two new articles on Javascript. Minifying Your Rails Javascript shows how to cut down your Javascript footprint with JSMin and Rake. Testing Javascript in Rails introduces the Test.Unit component of Scriptaculous.
We also released Firebug RJS Errors, a tiny plugin that patches RJS to log errors to the Firebug console instead of its default
alert()box.



