我有一个相当独特的类,允许其子类声明虚拟字段。子进程可以通过调用父类的方法声明存储为XML的虚拟字段,如下所示:
class Child1 < Parent
create_xml_field ["readings", "usage"]
end
我已经设法通过讨厌的工作让它工作。 create_xml_field 方法将字段名称存储在Class变量中(参见下文)。从 after_initialize 方法内部调用 init_xml_fields 方法。
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以允许在子类中进行批量分配的经验?提前谢谢!
为了清晰起见,编辑了这篇文章!
答案 0 :(得分:1)
以下是我如何实现创建存取方法并将它们设置为attr_accessibles的元编程部分。
我正在使用YAML intead of 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属性将是:
答案 1 :(得分:0)
@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