read_inheritable_attribute在生产中意外修改

时间:2011-08-10 14:07:47

标签: ruby-on-rails metaprogramming

这种情况发生在Rails 3.0.7和3.0.9,WEBrick和Apache。

我有一个模块Reportable,它有一个写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已加载到config/initializers中,一个类使用它:

class User < ActiveRecord::Base
  include Reportable
  add_report :report1
  add_report :report2
end

在生产模式下,首次在服务器启动后加载页面时,属性正确加载:

User.read_inheritable_attribute(:reportable_report_names)
# => {:report1 => {:dates => true, :details => 'something'},
      :report2 => {:dates => true, :details => 'something'}}

但在第二页加载:

User.read_inheritable_attribute(:reportable_report_names)
# => {:report1 => {:dates => true},
      :report2 => {:dates => true}}

它在开发过程中按预期工作,在生产模式下在控制台中工作。该问题仅出现在生产模式下Web服务器上的POST请求中。是什么给了什么?

2 个答案:

答案 0 :(得分:1)

刚试过,它可以在所有环境中正常运行。还有其他细节吗?


否则:

  • 为什么你使用instance_eval,因为你正在处理模型本身?

  • 你应该使用splat来传递参数

这将导致:

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

在模特中:

include Reportable
add_reports :report1, :report2

答案 1 :(得分:1)

经过许多令人头疼的事后,原因是read_inheritable_attribute返回了引用而不是值。

将错误重定向到另一个操作的操作,这是问题的真正原因。该模块有另一种方法:

def available_reports
  read_inheritable_attribute(:reportable_report_names)
end

另一个动作是这样做的:

# @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

由于available_reports返回了引用,dup没有做任何事情,keep_if从类变量中删除了我宝贵的值。

修复是:

properties = properties.dup.keep_if...

感谢那些花时间检查这个问题的人,即使没有足够的信息可以正确回答。