Monday, September 1, 2008

Rails: simple_localization hates observers

One of the most helpful and simple-to-use Rails plugins is simple_localization. It makes the job of localizing a Rails application a lot easy and straight forward. It implements localization through a set of features. One of the most commonly used features is localized ActiveRecord error messages. Its use is for localizing the standard error messages generated upon ActiveRecord validations ("can't be blank", "is too short", ..etc).

Working on a Rails project, after localizing the whole application, everything was working just fine in the development environment. However, when trying it in production environment, the locaized-error-messages feature didn't seem to work properly. Error messages for some (not all) of the models were not translated. For my surprise, Those models were exactly those being observed by declared observers.

Now, what seems to be the problem? The problem is that the implementation of localized-error-messages feature is just as simple as follows; it just overrides ActiveRecord::Errors.default_error_messages with the translated version. (simple_localization/lib/features/localized_error_messages.rb - line 26):

ActiveRecord::Errors.default_error_messages = ArkanisDevelopment::SimpleLocalization::LangSectionProxy.new :sections => [:active_record_messages],
:orginal_receiver => ActiveRecord::Errors.default_error_messages do |localized, orginal|
orginal.merge localized.symbolize_keys
end


The problem is that, during Rails initialization, observers declared in the standard way in the environment's configuration (and their associated models) are loaded before plugins. (config/environment.rb):
Rails::Initializer.run do |config|
config.active_record.observers = :user_observer
end

Using this line, we are not starting the observers; we are just telling Rails what observers to load when initializing. We don't control its loading order with respect to the loading plugins. By default, observers load first, loading their models accordingly. Now those models are completely loaded, including their default_error_messages way before the plugin is allowed to override them. This problem occures only in production environment, becuse in development environment, models are reloaded with each request, inheriting the overridden default_error_messages.

To work around this problem, we need to deliberately postpone loading the observers until initialization is done. This can be done by explicitly setting the observers in the after_initialize block instead of the above standard scheme:
Rails::Initializer.run do |config|
config.after_initialize do do
ActiveRecord::Base.observers = :user_observer
end
end

Like this, observers will load after initialization is finished, and all models will inherit the overridden default_error_messages.


1 comment:

Thiago Silva said...

hi Mohammad
congratulation!!
i had the same problem
tnks