Feature #11708
closedSpecify a way to override Struct-subclass constructor
Description
It's common to create simple data-object with some constraints. One can either implement custom class or use Struct
. Struct is generally simpler and helps to avoid some mistakes as non-defined #hash
and #eql?
. But at the same time it's more difficult to make validation for Struct
subclass.
Point = Struct.new(:x, :y)
NonnegativePoint = Struct.new(:x,:y) do
def initialize(*args, &block)
super
raise 'Negative coordinates are not allowed' if x < 0 || y < 0
end
end
Above written code solves the problem but has one flaw. Struct.new
creates a subclass of Struct
and defines some methods as #x
, #x=
. And there are no guarantees that NonnegativePoint#initialize
wasn't redefined too.
We can check that Point.new
without explicitly defined #initialize
actually hits Struct#initialize
and Point#initialize
not defined:
Point.instance_method(:initialize)
# => #<UnboundMethod: Point(Struct)#initialize>
NonnegativePoint.instance_method(:initialize)
# => #<UnboundMethod: NonnegativePoint#initialize>
But nothing in Struct
documentation or test suite states that this behavior can't be changed in newer ruby versions.
I propose either to declare in docs and test that initialize method can be safely overriden because #initialize
is not defined in Struct
subclasses.
In you assume that one day current behavior can change (e.g. for perfomance reasons), then it's reasonable to create an extension point like '#after_initialize' which is called from Struct
's subclass #initialize
method.