Odd ActiveRecord model dynamic initialization bug in production

Posted by qfinder on Stack Overflow See other posts from Stack Overflow or by qfinder
Published on 2010-04-29T18:35:19Z Indexed on 2010/04/29 18:37 UTC
Read the original article Hit count: 395

I've got an ActiveRecord (2.3.5) model that occasionally exhibits incorrect behavior that appears to be related to a problem in its dynamic initialization. Here's the code:

class Widget < ActiveRecord::Base
  extend ActiveSupport::Memoizable

  serialize :settings
  VALID_SETTINGS = %w(show_on_sale show_upcoming show_current show_past)

  VALID_SETTINGS.each do |setting|
    class_eval %{
      def #{setting}=(val); self.settings[:#{setting}] = (val == "1"); end
      def #{setting}; self.settings[:#{setting}]; end
    }
  end

  def initialize_settings
    self.settings ||= { :show_on_sale => true, 
                        :show_upcoming => true }
  end
  after_initialize :initialize_settings

  # All the other stuff the model does

end

The idea was to use a single record field (settings) to persist a bunch of configuration data for this object, but allow all the settings to seamlessly work with form helpers and the like. (Why this approach makes sense here is a little out of scope, but let's assume that it does.) Net-net, Widget should end up with instance methods (eg #show_on_sale= #show_on_sale) for all the entires in the VALID_SETTINGS array. Any default values should be specified in initialize_settings.

And indeed this works, mostly. In dev and staging, no problems at all. But in production, the app sometimes ends up in a state where a) any writes to the dynamically generated setters fail and b) none of the default values appear to be set - although my leading theory is that the dynamically generated reader methods are just broken. The code, db, and environment is otherwise identical between the three.

A typical error message / backtrace on the fail looks like:

IndexError: index 141145 out of string

(eval):2:in []='
(eval):2:in
show_on_sale='
[GEM_ROOT]/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in send'
[GEM_ROOT]/gems/activerecord-2.3.5/lib/active_record/base.rb:2746:in
attributes='
[GEM_ROOT]/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in each'
[GEM_ROOT]/gems/activerecord-2.3.5/lib/active_record/base.rb:2742:in
attributes='
[GEM_ROOT]/gems/activerecord-2.3.5/lib/active_record/base.rb:2634:in `update_attributes!'
...(then controller and all the way down)

Ideas or theories as to what might be going on?

My leading theory is that something is going wrong in instance initialization wherein the class instance variable settings is ending up as a string rather than a hash. This explains both the above setter failure (:show_on_sale is being used to index into the string) and the fact that getters don't work (an out of bounds [] call on a string just returns nil). But then how and why might settings occasionally end up as a string rather than hash?

© Stack Overflow or respective owner

Related posts about rails

Related posts about activerecord