【问题标题】:How do you call attr_accessible dynamically in Rails?你如何在 Rails 中动态调用 attr_accessible?
【发布时间】:2009-05-02 10:10:46
【问题描述】:

我有一个相当独特的类,它允许其子类声明虚拟字段。子类可以通过调用父类的方法来声明存储为 XML 的虚拟字段,如下所示:

class Child1 < Parent
  create_xml_field ["readings", "usage"]
end

我已经设法通过一个令人讨厌的工作来让它工作。 create_xml_field 方法将字段名称存储在 Class 变量中(见下文)。 init_xml_fields 方法是从 after_initialize 方法内部调用的。

class Parent < ActiveRecord::Base

  def self.create_xml_field(fields)
    @@xml_fields[self.name] = fields
  end

  def init_xml_fields(xml_fields)
    xml_fields.each do |f|
      f=f.to_sym
      self.class_eval do
        define_method(f) { ... } # define getter
        define_method(f) { ... } # define setter
        attr_accessible(f)       # add to mass assign OK list, does not seem to work!
      end
    end
  end

  protected
    def after_initialize
      init_xml_fields
    end
end

够恶心吧?我并不自豪,但我很难让它发挥作用。此外,该解决方法不适用于表单参数的批量分配。

有没有人有过动态调用 attr_acessible 以允许在子类中进行批量分配的经验?提前谢谢!

为清楚起见,对这篇文章进行了编辑!

【问题讨论】:

  • 这一切都非常令人困惑,但在我看来,您正在调用实例方法来修改类,这将为该特定实例创建一个特征类。如果您在实际课程中发布了一个最小示例,这将有所帮助。您尝试做的事情非常简单,但您似乎从一个稍微错误的角度接近它。
  • 同意,这令人困惑。对不起,我无法更好地解释它!也感谢您的回答,我将发布更多代码以更好地描述场景。
  • 好吧,我一直在琢磨你的例子。很明显,我在方孔中有一些圆钉。以您的示例为起点,我如何声明将 add_yaml_fields :foo, :bar 声明放置在子类而不是父类中?另外,我的父母有多个孩子,那么 write_inheritable_array(:yaml_fields, ...) 声明会导致 Child1、Child2 等...之间出现问题吗?在此先感谢,是的,YAML 很可爱。
  • 我已经更新了我的答案,让我知道是否可以解决问题。

标签: ruby-on-rails ruby metaprogramming


【解决方案1】:

以下是我如何实现创建访问器方法并将它们设置为 attr_accessibles 的元编程部分。

我使用 YAML 而不是 XML,只是作为个人的圣战。我什至继续实施了不需要的序列化部分,只是为了将您推向 YAML。

require 'yaml'
require 'rubygems'
require 'active_support'
require 'active_record'

module Yamlable
  def self.included m
    m.extend ClassMethods
  end

  module ClassMethods
    def add_yaml_fields *args
      write_inheritable_array(:yaml_fields, args)
      attr_accessor(*args)
      attr_accessible(*args)
      before_save :serialize_yaml_fields
    end
  end

  def serialize_yaml_fields
    self.yamlable_column = read_inheritable_attribute(:yaml_fields)\
      .inject({}) { |h, a| h[a] = send(a); h }.to_yaml
  end

  def initialize(*args)
    super
    YAML::load(yamlable_column).each { |k, v| send("#{k}=", v) }
  end
end

class ParentModel < ActiveRecord::Base
  include Yamlable
  add_yaml_fields :foo, :bar
end

class ChildModel < ParentModel
end

# look, they're there:
y ChildModel.read_inheritable_attribute(:yaml_fields)

现在,如果您想知道为什么您的特定代码不起作用,您必须发布更多内容。


我可能应该对类的可继承属性进行一些扩展。它们有点像类变量,有点像类实例变量。

如果你在一个类中定义了一个可继承的属性,它的所有子类都会共享它。但是如果你在一个子类中更新这个属性,这个子类会复制原始属性并更新它,所以更新是它独有的,不会影响继承链中它周围的其他类。

使用普通的write_inheritable_attribute 方法,在子类上设置它只会覆盖父类的值。使用可继承的数组和哈希,write_inheritable_* 将连接/合并到父类的值。


所以,在实践中,我的add_yaml_fields 是这样工作的:

class Parent
  add_yaml_attributes :foo

class Child1 < Parent
  add_yaml_attributes :bar

class Child2 < Parent
  add_yaml_attributes :baz

这样,每个类的 yaml 属性将是:

  • 父级:foo
  • Child1:foo,bar
  • Child2:foo,baz

【讨论】:

  • 这似乎是在正确的轨道上,但我不确定如何将它集成到我的父类中,并且您的示例从父类而不是子类调用“add_yaml_fields”。感谢您提供任何额外的建议。
  • Yamlable 模型可以包含在任何类中,它会为它及其所有后代提供 add_yaml_fields。因此,要在 ChildModel 上调用它,只需在那里调用它而不是在父模型中调用它。无需任何技巧。
  • 非常感谢您再次尝试!在将“yamlable_column”更改为特定于我的数据库表的“内容”后,我让它工作了。干得好,我给了你正确的答案(而且竞争非常激烈!)干杯。
【解决方案2】:

@kch 是正确的,但是我使用 initialize(*args) 发现了一个问题。 ActiveRecord 并不总是使用 new() 实例化模型对象,因此并不总是调用 initialize() 方法。

改为使用 after_initialize(*args),如下所示。

def self.included m
    m.extend ClassMethods
  end

  module ClassMethods
    def add_yaml_fields *args
      write_inheritable_array(:yaml_fields, args)
      attr_accessor(*args)
      attr_accessible(*args)
      before_save :serialize_yaml_fields
    end
  end

  def serialize_yaml_fields
    self.yamlable_column = read_inheritable_attribute(:yaml_fields)\
      .inject({}) { |h, a| h[a] = send(a); h }.to_yaml
  end

  def after_initialize(*args)
    super
    YAML::load(yamlable_column).each { |k, v| send("#{k}=", v) }
  end
end

class ParentModel < ActiveRecord::Base
  include Yamlable
  add_yaml_fields :foo, :bar
end

class ChildModel < ParentModel
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-26
    • 2011-11-03
    • 2012-07-14
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多