匿名子类元编程导致不必要的行为(Virtus)

时间:2011-10-11 02:02:05

标签: ruby metaprogramming

我正在使用Virtus(基本上是来自DataMapper的Property API)来构建一个远程API,但是我不认为 Virtus是这里的问题,我认为这是我对Ruby正在做什么。

我想通过使用语法:

来允许强制使用给定类型的属性
Node[AnotherClass]

它只是简单地生成Node的子类并返回该新类。那部分是有效的。但由于某种原因,它具有不良副作用。从Virtus::Attribute::Object下降的所有其他对象实际上也是Node子类本身。我无法解释它,但我认为它必须是与继承模型相关的预期ruby行为。任何人都可以指出我正确的方向吗?

(请注意,如果gem install virtus),则以下代码无需修改即可运行。

require "virtus"

class JsonModel
  include Virtus
end

class Node < Virtus::Attribute::Object
  attr_reader :type

  class << self
    def [](type)
      raise ArgumentError, "Child nodes may only be other JsonModel classes" unless type <= JsonModel

      @generated_class_map       ||= {}
      @generated_class_map[type] ||= Class.new(self) do
        default lambda { |m, a| type.new }

        define_method :type do
          type
        end

        define_method :coerce do |value|
          value.kind_of?(Hash) ? type.new(value) : value
        end
      end
    end
  end
end

class ChildModel < JsonModel
end

class ParentModel < JsonModel
  attribute :child,  Node[ChildModel]
  attribute :string, String
end

# This should be String, but it's a descendant of Node??
puts ParentModel.attributes[:string].class.ancestors.inspect

1 个答案:

答案 0 :(得分:1)

我已将代码缩减为以下内容,但行为仍然相同。

Class.new(Node)的存在是导致:string属性在其祖先中拥有Node的原因。

require "virtus"

class JsonModel
  include Virtus
end

class Node < Virtus::Attribute::Object
end

# this does it...
Class.new(Node)

class ParentModel < JsonModel
  attribute :string, String
end

# This should be String, but it's a descendant of Node??
puts ParentModel.attributes[:string].class.ancestors.inspect

我不熟悉Virtus,但我猜这与Virtus实现属性类型的方式有关。