Rails tip #3: Filter BLOBs from ActiveRecord logging

Posted on April 23, 2008

We threw attachment_fu into our app the other day, starting off with the default :db_file setting to store images in the database. (I’ve been bitten by this approach before, but this time around I’m intrigued by page caching. We’ll see.)

In the meantime, we’ve got blob after blob flying by in the script/server output, and it’s messing up our Terminal fonts. No good.

Here’s a quick fix, config/initializers/filter_db_files_logging.rb:

class ActiveRecord::ConnectionAdapters::AbstractAdapter
  def format_log_entry_with_db_files_filtering(message, dump = nil)
    dump = 'INSERT INTO db_files' if dump.to_s =~ /^INSERT INTO db_files/
    dump = 'UPDATE db_files'      if dump.to_s =~ /^UPDATE db_files/
    format_log_entry_without_db_files_filtering(message, dump)
  end

  alias_method_chain :format_log_entry, :db_files_filtering
end

Note that this isn’t yet a general solution (other tables’ blobs will still be logged), but it’s good enough for our needs, and we’ve got a known place we can come back to should we need to filter more blobs in the future.

Also, looking at AbstractAdapter#log_info just now, we notice that could have written our own Logger and saved the monkeypatch. Maybe we’ll look into doing that some day.

5 Rails tips

Each day this week, Joachim and I will post something we’ve learned in our time programming together. It’s fun to do, and we might just win something as well.

So far, we’ve written:

  1. Reloadable custom FormBuilder
  2. Faking DATA in tests
  3. Filter BLOBs from ActiveRecord logging

Rails tip #2: Faking DATA in tests

Posted on April 22, 2008

Ruby gives you access to the raw text after an __END__ statement in the currently running file through the DATA IO object.

Sometimes you’d like to use DATA in a test. You’ll find it works when you run the test individually but then fails when you run the whole suite. (Remember DATA comes from $0, the currently running file, only.)

We’ve found a nice way to fake it in the rubyforge gem: read __FILE__ instead, then split the results.

Here’s an example of the technique in use, functionally testing a wiki parser we’ve been tinkering with. It can often be painful to work with metaprogrammed tests like this, but on balance, we like the results here:

require 'test_helper'

class TestParser < Test::Unit::TestCase
  EXAMPLES = /={80}\n/m
  PARTS    = /-{80}\n/m

  # Faking data = DATA.read
  data = File.read(__FILE__).split('__END__').last

  data.split(EXAMPLES).map { |example| example.split(PARTS) }.each do |comment, markup, html|
    define_method "test_#{comment.strip.downcase.gsub(/\W/, '_')}" do
      assert_equal html, Parser.new.parse(markup).result.to_html
    end
  end
end

__END__
A single wiki word should be linked and wrapped in a paragraph.
--------------------------------------------------------------------------------
WikiWord
--------------------------------------------------------------------------------
<p><a href="WikiWord">WikiWord</a></p>
================================================================================
Two wiki words on separate lines should become two paragraphs
--------------------------------------------------------------------------------
WikiWord
WikiWord
--------------------------------------------------------------------------------
<p><a href="WikiWord">WikiWord</a></p>
<p><a href="WikiWord">WikiWord</a></p>

5 Rails tips

Each day this week, Joachim and I will post something we’ve learned in our time programming together. It’s fun to do, and we might just win something as well.

So far, we’ve written:

  1. Reloadable custom FormBuilder
  2. Faking DATA in tests

Rails tip #1: Reloadable custom FormBuilder

Posted on April 21, 2008

August Lilleaas has a nice writeup on making your own FormBuilder. Towards the end, he describes wiring it up as the default:

Create a new file called config/initializers/setup.rb. Or, if you’re on pre-2.0, add it to the bottom of config/environment.rb:

ActionView::Base.default_form_builder = LabellingFormBuilder

Though “proper,” this approach requires restarting the server to see changes in LabellingFormBuilder.

You can avoid restarting the server by doing this instead:

class ActionView::Base
  def self.default_form_builder
    LabellingFormBuilder
  end
end

It’s a subtle difference, but it provides a nice window into understanding the Dependencies mechanism:

In August’s version, const_missing loads the LabellingFormBuilder class only once, when the initializer is run.

In our version, we’ve tricked const_missing into running on each request by not holding onto the class it loads. That is, instead of caching the class lookup in @@default_form_builder, we’re allowing the lookup to be performed on demand.

Sweet.

5 Rails tips

Each day this week, Joachim and I will post something we’ve learned in our time programming together. It’s fun to do, and we might just win something as well.