Вот как я реализую часть метапрограммирования, которая создает методы доступа и устанавливает их как attr_accessibles.
Я использую YAML intead 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