Inheritance and Class Methods

Let's define a setter class method in a class.

class A
  def self.foo=(value)
    p value
  end
end

Let's define a subclass B that extends from A.

class B < A
  foo = true
end

It tries to call the setter with true as the value. When you run this, it prints nothing. Why did not print the value true?

class B < A
  foo = true

  p local_variables
end

If you print the local variables inside the subclass, it prints [:foo]. Ruby thinks you have defined a local variable foo instead of calling the class method defined in the super class. We can fix it by explicitly using the self keyword to make it clear we mean the class method foo setter.

class B < A
  self.foo = true
end

It now prints true. What is the point of all these toy examples? Can we see a realistic example?

ActiveResource

If you subclass ActiveResource::Base and do something like this:

class MyActiveResoureClient < ActiveResource::Base
  include_root_in_json = true
end

This will not work. The reason is the same as the toy example we saw earlier. Where is the include_root_in_json method defined? Searching the github for https://github.com/rails/activeresource/search?utf8=%E2%9C%93&q=include_root_in_json%3D&type=Code does not tell us where this method is defined. Let's dig further into this in the rails console:

ActiveResource::Base.singleton_methods.grep(:include_root_in_json=)
=> [:include_root_in_json=]

The setter is include_root_in_json= is a class method defined in ActiveResource::Base. Let's get the line number where we can find this method.

[4] pry(main)> ActiveResource::Base.method(:include_root_in_json=)
=> #<Method: ActiveResource::Base.include_root_in_json=>
[5] pry(main)> ActiveResource::Base.method(:include_root_in_json=).source_location
=> ["/Users/bparanj/.rvm/gems/ruby-2.3.0@blog/gems/activesupport-4.2.6/lib/active_support/core_ext/class/attribute.rb", 83]

I had to use this method because, searching for include_root_in_json= in the github does not show where it is defined. The reason is that meta-programming is used to define this method. Here is the code where this is defined:

define_singleton_method("#{name}=") do |val|
  singleton_class.class_eval do
    remove_possible_method(name)
    define_method(name) { val }
  end

  if singleton_class?
    class_eval do
      remove_possible_method(name)
      define_method(name) do
        if instance_variable_defined? ivar
          instance_variable_get ivar
        else
          singleton_class.send name
        end
      end
    end
  end
  val
end

This is found inside the class_attribute(*attrs) method. ActiveResource opens the Ruby Class class to define this method. From the docs:

Declare a class-level attribute whose value is inheritable by subclasses.
Subclasses can change their own value and it will not impact parent class.

class Base
  class_attribute :setting
end

class Subclass < Base
end

Base.setting = true
Subclass.setting            # => true
Subclass.setting = false
Subclass.setting            # => false
Base.setting                # => true

Why do we need the class-level attribute that can be inherited by subclasses? The answer deserves to be explained in another article.

Summary

In this article, you learned how to call the class method setters defined in the superclass from a subclass. You also learned how you can find out answers to your questions by using Ruby's introspection abilities


Related Articles


Ace the Technical Interview

  • Easily find the gaps in your knowledge
  • Get customized lessons based on where you are
  • Take consistent action everyday
  • Builtin accountability to keep you on track
  • You will solve bigger problems over time
  • Get the job of your dreams

Take the 30 Day Coding Skills Challenge

Gain confidence to attend the interview

No spam ever. Unsubscribe anytime.