solnic / virtus

[DISCONTINUED ] Attributes on Steroids for Plain Old Ruby Objects
MIT License
3.77k stars 229 forks source link

extend ClassMethods Pattern Re-Defines Initializer in Virtus Class #161

Closed sbellware closed 10 years ago

sbellware commented 11 years ago

This works:

module Foo
  include Virtus

  attribute :name
end

class Bar
  include Foo
end

b = Bar.new :name => 'some name'
puts b.name #=> some name

But if I use the extend ClassMethods pattern in the Foo module, the initializer of the Bar class now has no initializer arguments:

module Foo
  include Virtus

  attribute :name

  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
  end

end

class Bar
  include Foo
end

b = Bar.new :name => 'some name'
# => 'initialize': wrong number of arguments(1 for 0) (ArgumentError)

However, if the ClassMethods pattern is moved to another module, and included in the Foo module, everything works fine:

module Baz
  def self.included(base)
    base.extend ClassMethods
  end

  module ClassMethods
    def build(attributes={})
      instance = new({ :id => SimpleUUID::UUID.new.to_guid }.merge(attributes))
    end
  end
end

module Foo
  include Virtus
  include Baz

  attribute :name
end

class Bar
  include Foo
end

b = Bar.new :name => 'some name'
puts b.name #=> some name

Note also that Baz can be included before Virtus and it still works.

Is there a constraint on using this pattern with Virtus objects?

Thanks! Scott

solnic commented 11 years ago

That's a bug. I'll be fixing it for 1.0.0 release. Thanks for the report.

solnic commented 10 years ago

@sbellware this is now fixed however there are also two issues in your code example. You need to pass in type of the attribute (now it will raise) + you need to call super in the included hook. Then it'll work just fine.