read_inheritable_attribute unexpectedly modified in production
This is happening in Rails 3.0.7 and 3.0.9, WEBrick and Apache.
I have a module Reportable
that has a method that writes an inheritable_attribute
:
module Reportable
module ClassMethods
def add_report(report_name)
instance_eval do
write_inheritable_hash(:reportable_report_names,
{report_name => {:dates => true, :details => 'something'})
end
end
end
end
def self.included(base)
base.extend(ClassMethods)
end
end
Reportable
is loaded in config/initializers
and a class uses it:
class User < ActiveRecord::Base
include Reportable
add_report :report1
add_report :report2
end
In production mode, the first time I load a page after the server starts, the attribute loads correctly:
User.read_inheritable_attribute(:reportable_report_names)
# => {:report1 => {:dates => true, :details => 'something'},
:report2 => {:dates => true, :details => 'something'}}
But on the second page load:
User.read_inheritable_attribute(:reportable_report_names)
# => {:report1 => {:dates => true},
:report2 => {:dates => true}}
It works as expected in development, and in the console in production mode. The problem only appears in a POST request on the web server in production mod开发者_如何学运维e. What gives?
Just tried, it works in all environments without problem. Any other details?
Otherwise:
why do you use
instance_eval
since you're working on the model itself?you should use a splat to pass arguments
This would lead to:
module ClassMethods
def add_reports(*report_names)
report_names.each do |report_name|
write_inheritable_hash(:loggable_report_names,
{report_name => {:dates => true, :details => 'something'})
end
end
end
end
And in model:
include Reportable
add_reports :report1, :report2
After a lot of headaches, the cause was that read_inheritable_attribute
returned a reference rather than value.
The action throwing the error redirected to another action, which was the true cause of the problem. The module had another method:
def available_reports
read_inheritable_attribute(:reportable_report_names)
end
And the other action did this:
# @model_reports = a hash of 'model_class_name'.to_sym => model.available_reports.dup
@model_reports.each do |model, model_reports|
model_reports.each do |name, properties|
properties = properties.keep_if... # remove per the view's requirements
end
end
Because available_reports
returned a reference, the dup
did nothing and the keep_if
deleted my precious values from the class variable.
The fix was:
properties = properties.dup.keep_if...
Thanks to those who took the time to check this question out even though there wasn't enough information in it to be answered properly.
精彩评论