Inheriting Class Methods from Modules/Mixins in Ruby

Inheriting class methods from modules / mixins in Ruby

A common idiom is to use included hook and inject class methods from there.

module Foo
def self.included base
base.send :include, InstanceMethods
base.extend ClassMethods
end

module InstanceMethods
def bar1
'bar1'
end
end

module ClassMethods
def bar2
'bar2'
end
end
end

class Test
include Foo
end

Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"

Can a ruby class method inherit from another class?

class Users::PasswordsController < Devise::PasswordsController
...
end

In the above code, Users is the module and PasswordsController is the class inside Users module. Similarly Devise is the module and PasswordsController is the class inside Devise module.

so when you run

Users::PasswordsController.class
#=> Class
Users.class
#=>Module

Ruby module inclusion with class methods and inheritance

When you do include M1 in M2, the instance methods of M1 as well as the class methods coming from ClassMethods are incorporated into M2 because of the self.included definition in M1.

But when you do include M2 in Foo, you only include the instance methods of M2. The class methods of M2 are not incorporated into Foo.

ruby inheritance vs mixins

I just read about this topic in The Well-Grounded Rubyist (great book, by the way). The author does a better job of explaining than I would so I'll quote him:


No single rule or formula always results in the right design. But it’s useful to keep a
couple of considerations in mind when you’re making class-versus-module decisions:

  • Modules don’t have instances. It follows that entities or things are generally best
    modeled in classes, and characteristics or properties of entities or things are
    best encapsulated in modules. Correspondingly, as noted in section 4.1.1, class
    names tend to be nouns, whereas module names are often adjectives (Stack
    versus Stacklike).

  • A class can have only one superclass, but it can mix in as many modules as it wants. If
    you’re using inheritance, give priority to creating a sensible superclass/subclass
    relationship. Don’t use up a class’s one and only superclass relationship to
    endow the class with what might turn out to be just one of several sets of characteristics.

Summing up these rules in one example, here is what you should not do:

module Vehicle 
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...

Rather, you should do this:

module SelfPropelling 
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...

The second version models the entities and properties much more neatly. Truck
descends from Vehicle (which makes sense), whereas SelfPropelling is a characteristic of vehicles (at least, all those we care about in this model of the world)—a characteristic that is passed on to trucks by virtue of Truck being a descendant, or specialized
form, of Vehicle.

To break up Ruby class into separate files by mixins or plain definitions?

Are your support methods general enough that they might be useful for other totally unrelated classes? If so, a mixin is the proper way to do this, since it lets you easily reuse the support code.

However if your support methods are very specific to ReallyBigClass and are unlikely to work if included somewhere else, reopening the class is the way to go. Using a mixin there could give the appearance that the methods are more general than they really are, when really they should only be used with instances of a specific class.

That being said, I think your question indicates a larger design problem. If you are in the former case (general methods), you should be designing more general modules in the first place to avoid tight coupling. A module called ReallyBigClassFileB gives off some strong code smell. In the latter case (very specific methods) if your class is so big that its file is unmanageably large you probably need to refactor something. Maybe your class is responsible for too much? Maybe it could use some subclasses (which make sense in separate files)?

Inheriting from class Module

Module is indeed a Ruby class. An instance of the class Module is a Ruby module. To illustrate, these two ways of defining a module are equivalent:

module MyModule
# ...
end

# is equivalent to

MyModule = Module.new do
# ...
end

If an instance of Module is a Ruby module, that means that an instance of any subclass of Module is also a Ruby module, including Shrine::Attachment. This makes sense, because we know that we can include only modules, so an instance of Shrine::Attachment has to be a module.

Because of Shrine's plugin system design, this:

class Attachment < Module
@shrine_class = ::Shrine
end

isn't the whole implementation of Shrine::Attachment; the actual implementation is defined in the Shrine::Plugins::Base::AttachmentMethods module, which gets included into Shrine::Attachment.

If we look at the implementation of Shrine::Attachment.new, we can see that it dynamically defines methods on itself based on the given attribute name. For example, Shrine::Attachment.new(:image) will generate a module with the following methods defined: #image_attacher, #image=, #image, and #image_url. These methods will then get added to the model that includes that Shrine::Attachment instance.


Why didn't I just have a method that creates a new module via Module.new (like Refile does), instead of creating a whole subclass of Module? Well, two main reasons:

First, this gives better introspection, because instead of seeing #<Module:0x007f8183d27ab0> in your model's ancestors list, you now see an actual Shrine::Attachment instance that points to its definition. You could still manually override #to_s and #inspect, but this is better.

Second, since Shrine::Attachment is now a class, other Shrine plugins can extend it with more behaviour. So remote_url plugin adds the #<attachment>_remote_url accessor, data_uri plugin adds the #<attachment>_data_uri accessor etc.

Ruby mixin class methods

I'd rewrite the module using instance methods and instance variables, i.e.:

module Mixin
def set_n(n)
@n = n
end

def get_n
@n ||= 0
end
end

The above can then be added to your class via extend:

class Klass
extend Mixin

set_n 100
end

Klass.get_n #=> 100

Note that get_ and set_ prefixes are quite unidiomatic in Ruby. You typically use the attribute's name as the getter and append a = for the setter.

How to mixin some class methods and some instance methods from a module in Ruby?

This pattern is very common in Ruby. So common, in fact, that ActiveSupport::Concern abstracts it a bit.

Your typical implementation looks like this:

module Foo
def self.included(other_mod)
other_mod.extend ClassMethods
end

def instance_method
end

module ClassMethods
def class_method
end
end
end

class Bar
include Foo
end

You can't accomplish this easily as you describe without somehow splitting the included module into multiple pieces, though, unfortunately.



Related Topics



Leave a reply



Submit