With most of my code becoming more and more stateless and functional, I haven’t given up leveraging Ruby’s excellent way of defining declarative APIs and DSL. Ruby is just too good at that.
However, I am missing a crucial tool in Ruby: being able to pass dynamic arguments to the inheritance, exactly the way I can pass arguments to the object constructor. In short, here’s what I would love to have.
class Memo::Render < Render, engine: Render::JSON property :title property :body end
The Render class could then use those additional arguments in its inherited hook, acting as an initialize on the class level.
class Render
def self.inherited(subclass, options)
super
subclass.engine = options[:engine]
end
# def self.property(name); end
# def self.engine=(name); end
end
This would also work for anonymous classes, of course.
Memo::Render = Class.new( Render, engine: Render::JSON)
Looks very Ruby-esque, doesn’t it?
The Problem with inherited
Currently, you need to set class variables in the inherited class to configure it.
class Memo::Render < Render
def self.engine
Render::JSON
end
end
The massive drawback here (a design flaw in Ruby?) is that this override is evaluated too late, after the inherited hook is called. Check out the exemplary inherited method in the superclass Render and how it behaves.
class Render
def self.engine; end
def self.inherited(subclass)
puts subclass #=> Memo::Render
puts subclass.engine #=> nil !!!
end
end
As you can see, even though Memo::Render implements engine, it isn’t evaluated at inherited time, making this hook more or less useless if you want to cleanly initialize and setup a subclass at compile-time.
One solution could be the proposed extension of the hook, however, the complexity of this change is completely unknown to me due to my lack of Ruby core participation.

May you use a constant from subclass instead of calling its method?
LikeLike
Cool idea, but I guess this will be the same problem: The class body is evaluated _after_ its inherited hook was called. Have you tested it?
LikeLike
Looks like yes, it has the same problem:
$ irb
irb(main):001:0> class A
irb(main):002:1> def self.inherited(cls)
irb(main):003:2> puts cls::CONSTANT
irb(main):004:2> end
irb(main):005:1> end
=> :inherited
irb(main):006:0>
irb(main):007:0* class B CONSTANT = “Here’s the constant”
irb(main):009:1> end
NameError: uninitialized constant B::CONSTANT
from (irb):3:in `inherited’
from (irb):7
from .rbenv/versions/2.4.1/bin/irb:11:in `’
LikeLike
Ruby simply evaluates the text file, and A<B will literally trigger the inherited hook.
LikeLike
I think this might do what you want: https://gist.github.com/bkudria/82e3721d4ec2b62a2a3d95050e964d84
LikeLike
Hey Ben, thanks for your snippet.
The “problem” here is that it basically does the same we do here: https://apotonick.wordpress.com/2018/01/18/dear-ruby-2-the-functional-inherited-hook/ in the “Inherit-then-tune” section. As described, I don’t want the third anonymous class in the middle, which is unavoidable in your solution. Thanks anyway, and also thanks for `define_singleton_method`, new one for me! :beers:
LikeLike
I’d like to translate the articles https://apotonick.wordpress.com/2018/01/17/dear-ruby-1-what-about-arguments-when-inheriting/ and https://apotonick.wordpress.com/2018/01/18/dear-ruby-2-the-functional-inherited-hook/ into Japanese and publish on our tech blog https://techracho.bpsinc.jp/ for sharing it. Is it OK for you?
I make sure to indicate the link to original, title, author name in the case.
Best regards,
LikeLike
I would be honored! Arrigato!!!
LikeLike