Project

General

Profile

Feature #11923

Put Struct accessors into separate module to allow redefining them in Struct.new's block

Added by prijutme4ty (Ilya Vorontsov) over 3 years ago. Updated over 3 years ago.

Status:
Open
Priority:
Normal
Assignee:
-
Target version:
-
[ruby-core:72591]

Description

Struct.new(*args, &block) creates a subclass of Struct. Some methods like accessors are defined right in newly created class. Others like #initialize, #values and so on are declared in a superclass, i.e. in Struct.
The fact that #initialize is defined in a superclass allows redefining constructor as was proposed in #11708. But it is not that simple to redefine accessors (and documentation gives no clue which methods are to be redefined and which aren't):

Point=Struct.new(:x,:y) do
  def x=(val)
    puts "set x = #{val}"
    super
  end
end

pt = Point.new(1,2)
pt.x = 3 # => NoMethodError: super: no superclass method `x=' for #<struct Point x=1, y=2>

As accessors are defined just inside a class Point, we can't use super. To correctly implement desired logging behavior of #x= one need to create a module with #x= method and to prepend it in front of class.

I propose instead one of two solutions which overcome this issue:

  1. (worse) Struct.new when takes a block should not evaluate it in a context of created class. Instead it should create an anonymous module, evaluate block in its context (so that module gets all defined methods) and prepend that module in front of created class. But this possibly can conflict with reopening a class.
  2. (better) Struct.new should create an anonymous module, define struct accessors in it and then include a module into created class. Then block should be evaluated in context of the class as it's done now. So super invocation in #x= definition from a block supplied to Struct.new will hit method in included module instead of NoMethodError.

There is one possible drawbacks of implementing this (I could overlook some other problems): cutting perfomance due to additional module in ancestors chain.

History

Updated by prijutme4ty (Ilya Vorontsov) over 3 years ago

  • Assignee changed from marcandre (Marc-Andre Lafortune) to ruby-core

Sorry. Don't how to choose appropriate assignee.

#2

Updated by naruse (Yui NARUSE) over 3 years ago

  • Assignee deleted (ruby-core)

Also available in: Atom PDF