We have a legacy codebase where rubocop reports some of these errors which I never could wrap my head around:
Don't extend an instance initialized by
Struct.new
. Extending it introduces a superfluous class level and may also introduce weird errors if the file is required multiple times.
What exactly is meant by "a superfluous class level" and what kind of "weird errors" may be introduced?
(Asking because obviously we didn't have any such problems over the last years.)
Struct.new
creates an anonymous class that happens to be a subclass of Struct
:
s = Struct.new(:foo)
#=> #<Class:0x00007fdbc21a0270>
s.ancestors
#=> [#<Class:0x00007fdbc21a0270>, Struct, Enumerable, Object, Kernel, BasicObject]
You can assign that anonymous class to a constant in order to name it:
Foo = Struct.new(:foo)
#=> Foo
Foo.ancestors
#=> [Foo, Struct, Enumerable, Object, Kernel, BasicObject]
That's the regular way to create a Struct
subclass.
Your legacy code on the other hand seems to contain something like this:
class Foo < Struct.new(:foo)
end
Struct.new
creates an anonymous class (it's not assigned to a constant) and Foo
subclasses it, which results in:
Foo.ancestors
#=> [Foo, #<Class:0x00007fee94191f38>, Struct, Enumerable, Object, Kernel, BasicObject]
Apparently, the anonymous class doesn't serve any purpose.
It's like:
class Bar
end
class Foo < Bar # or Foo = Class.new(Bar)
end
Foo.ancestors
#=> [Foo, Bar, Object, Kernel, BasicObject]
as opposed to:
class Bar
end
class Foo < Class.new(Bar)
end
Foo.ancestors
#=> [Foo, #<Class:0x00007fdb870e7198>, Bar, Object, Kernel, BasicObject]
The anonymous class returned by Class.new(Bar)
in the latter example is not assigned to a constant and therefore neither used nor needed.