Read more

Ruby GetText will eval scripts containing ActiveRecord classes

Arne Hartherz
August 12, 2011Software engineer at makandra GmbH

When the Ruby parser module of Ruby-GetText Show archive.org snapshot comes across a file in one of its search directories (e.g. lib/scripts/) and finds out that you are defining ActiveRecord classes inside it, it evaluates the whole file. Here is how to avoid that.

What's happening?

Illustration UI/UX Design

UI/UX Design by makandra brand

We make sure that your target audience has the best possible experience with your digital product. You get:

  • Design tailored to your audience
  • Proven processes customized to your needs
  • An expert team of experienced designers
Read more Show archive.org snapshot

Let's say you have the following script which is only run once, manually, via script/runner:

# lib/scripts/doomsday.rb
class User < ActiveRecord::Base; end
User.destroy_all

In that case we would define a User model solely for the purpose to have access to the users table via ActiveRecord, without being affected by the application's real User class.

Unfortunately GetText::ActiveRecordParser.target? tries to be smart when looking for model attributes to put into po files. If it encounters any declaration like ...

class User < ActiveRecord::Base

... it will claim that it can handle the class. Then, its parse method will evaluating the whole file to do so:

# from the Gem's lib/gettext_activerecord/parser.rb:
eval(open(file).read, TOPLEVEL_BINDING)

This means that the script will be executed every time you run the rake task to rebuild your po files.

Holy cow!

Fix 1: Cheat

Use the following hack to define classes inside your scripts and trick the gem:

eval <<RUBY
  class User < Active#{}Record::Base; end
RUBY
User.destroy_all

Ugly as hell, but works.

Fix 2: Blacklist

Another solution would be to exclude lib/scripts explicitly in your GetText rakefile:

paths = Dir.glob("{app,lib,assets}/**/*.{rb,erb,rjs}").reject do |filename|
  filename =~ %r{^lib/scripts}
end
GetText.update_pofiles('my_project', paths.sort, 'my fancy project')

Note that this means it will break for you again once files are added in a non-excluded path.

Posted by Arne Hartherz to makandra dev (2011-08-12 15:09)