Subclass options available to Class#inherited

Hello rubyists,

I'm pretty sure what I want to do isn't possible, but I thought I'd ask
here before moving on to other approaches. I have a base class that does
some things to subclasses using Class#inherited, and it would be ideal if
the code could react to optional settings that can be configured in the
subclass declaration.

Some illustrative sample code, adapted from the Class#inherited
documentation:

class Foo
  class << self
    attr_accessor :opt

    def inherited(subclass)
      puts "New subclass: #{subclass}"
      puts "The cow goes #{opt}"
    end
  end

  self.opt = :foo
end

class Bar < Foo
  self.opt = :bar
end

class Baz < Foo
end

Output:

New subclass: Bar
The cow goes foo
New subclass: Baz
The cow goes foo

Desired output:

New subclass: Bar
The cow goes bar
New subclass: Baz
The cow goes foo

I'm not too worried what the syntax looks like; the attr_accessor could be
replaced with a method that subclasses can override, or some sort of DSL. I
suspect however that Class#inherited is called before any code in the
subclass declaration is executed, which I guess means I'm out of luck. Is
there any documentation or source code describing the inheritance process
that I could read to give me closure?

Or maybe there's another way of doing what I want? Another illustrative
example that's a bit closer to my actual code.

What I'd like to do:

def self.inherited(subclass)
  if subclass.thing_enabled?
    define_method(inferred_name) { do_the_thing }
  else
    define_method(inferred_name) { do_something_else }
  end
end

What I'd like to avoid:

def self.inherited(subclass)
  define_method(inferred_name) do
    if thing_enabled?
      do_the_thing
    else
      do_something_else
    end
  end
end

The class itself is passed on to another gem and never instantiated, so any
clever tricks during object initialization probably aren't applicable.

Ideas? Any input would be greatly appreciated.

Thanks,
Doug

I'm pretty sure what I want to do isn't possible, but I thought I'd ask here

"Isn't possible" does not exist in Ruby. :wink:

I'm not too worried what the syntax looks like; the attr_accessor could be
replaced with a method that subclasses can override, or some sort of DSL.

I suspect however that Class#inherited is called before any code in the
subclass declaration is executed, which I guess means I'm out of luck. Is
there any documentation or source code describing the inheritance process
that I could read to give me closure?

You can easily check that:

irb(main):001:0> class X; def self.inherited(x) printf "inherited
%p\n", x end end
=> :inherited
irb(main):002:0> class Y < X; puts "now" end
inherited Y
now
=> nil

If you think about it for a moment this is the only way it can be,
because classes can be opened any time. So if Class#inherited was to
be called at the end of the sub class definition, when would that be?

Or maybe there's another way of doing what I want? Another illustrative
example that's a bit closer to my actual code.

What I'd like to do:

def self.inherited(subclass)
  if subclass.thing_enabled?
    define_method(inferred_name) { do_the_thing }
  else
    define_method(inferred_name) { do_something_else }
  end
end

What I'd like to avoid:

def self.inherited(subclass)
  define_method(inferred_name) do
    if thing_enabled?
      do_the_thing
    else
      do_something_else
    end
  end
end

The class itself is passed on to another gem and never instantiated, so any
clever tricks during object initialization probably aren't applicable.

Ideas? Any input would be greatly appreciated.

If I understand correctly what you want to do, you want a method
defined in one of several (two?) different ways based on a flag
defined on the sub class level. So, the sub class writer has to do
something anyway, so why not make it explicit? You could define a
method "prepare_class" in Foo and make that define the method. Or you
just implement the attribute setter to do that as a side effect. That
would probably be closest to your current syntax.

class Foo
  class << self
    attr_reader :opt

    def opt=(flag)
      if flag == :foo
        define_method(:foo)
          puts "true"
        end
      else
        define_method(:foo)
          puts "false"
        end
      end
    end
  end

  self.opt = :foo
end

Of course the conditions etc. must be adjusted to your needs.

Maybe we can come up with more ideas if you disclose more details of
your real use case.

Kind regards

robert

···

On Mon, Dec 19, 2016 at 2:48 PM, Doug Lake-Hammond <d.lakehammond@gmail.com> wrote:

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/

For years I've wanted class_opened/closed hooks in order to manage some things cleaner and easier. inherited is nice, but it is severely lacking.

···

On Dec 20, 2016, at 05:48, Robert Klemme <shortcutter@googlemail.com> wrote:

If you think about it for a moment this is the only way it can be,
because classes can be opened any time. So if Class#inherited was to
be called at the end of the sub class definition, when would that be?

Hi Robert, thanks very much for the reply!

If you think about it for a moment this is the only way it can be,

because classes can be opened any time. So if Class#inherited was to
be called at the end of the sub class definition, when would that be?

That makes perfect sense. I can see how trying to do it any other way would
quickly turn into a mess. Thanks for clearing that up.

So, the sub class writer has to do
something anyway, so why not make it explicit? You could define a
method "prepare_class" in Foo and make that define the method.

That's definitely an option.

Maybe we can come up with more ideas if you disclose more details of
your real use case.

Sure! In fact the code is open (if not quite yet ready for primetime), it's
maybe a lot of context to digest but if anyone is would like to take a look
we'd be glad to have your input:

We have an Grape application, backed by ActiveRecord, and we're aiming to
migrate certain APIs to JSON API (jsonapi.org) format in making use of the
excellent jsonapi-resources gem. So grape-jr is a bridge; where
jsonapi-resources provides ResourceController (a substitute for
ActionController::Base), grape-jr provides Grape::JSONAPI::API (a
substitute for Grape::API).

So where a minimal, working ResourceController subclass is simply:

class PeopleController < JSONAPI::ResourceController
end

A minimal working Grape::JSONAPI::API implementation is:

class People < Grape::JSONAPI::API
end

We use the class name to infer the model class, the related
JSONAPI::Resource class and to define all of the resources and endpoints
required for a JSON API-conformant implementation. So where I used
define_method in my earlier examples, we're actually talking about the
get/post/patch/delete DSL methods provided by Grape::API.

Grape::JSONAPI::API is more or less working, but so far completely missing
any sort of configuration options. High on our MVP list would be a setting
to disable the endpoints for updating relationships.

Related specification:
http://jsonapi.org/format/#crud-updating-relationships
Related code:

So, given an API definition such as:

class People < Grape::JSONAPI::API
  # Disable updating of all relationships
  immutable_relationships

  # Or for only certain relationships
  immutable_relationships :posts, comments
end

The endpoint definitions would change from:

patch { process_relationship_request(:update) }

To something like:

patch { respond_with_409_error }

As I mentioned in the previous post, it would certainly be possible using
an if statement inside the method definition. But it's not going to be the
only configuration option we'll need and I'd prefer to avoid going through
a flow control tree that arrives at the same conclusion for every incoming
request if it's at all possible to just set up the endpoints correctly in
the first place.

You've certainly got me thinking already. Maybe it's possible with the DSL
described above, or maybe with a bit of refactoring or a slightly different
design. I have one or two ideas to explore but in the mean time any further
suggestions or input would be much appreciated.

Thanks to all for your time :slight_smile:
Doug

I have never felt the need for that. Now I am curios what use cases
that might be. Can you share some more details? Since you say "it is
severely lacking" you must have gone through quite some hoops and
loops to get done what you wanted to.

Kind regards

robert

···

On Wed, Dec 21, 2016 at 12:26 AM, Ryan Davis <ryand-ruby@zenspider.com> wrote:

On Dec 20, 2016, at 05:48, Robert Klemme <shortcutter@googlemail.com> wrote:

If you think about it for a moment this is the only way it can be,
because classes can be opened any time. So if Class#inherited was to
be called at the end of the sub class definition, when would that be?

For years I've wanted class_opened/closed hooks in order to manage some things cleaner and easier. inherited is nice, but it is severely lacking.

--
[guy, jim, charlie].each {|him| remember.him do |as, often| as.you_can
- without end}
http://blog.rubybestpractices.com/