Ruby Modules - Included Do End Block

Ruby modules - included do end block

Here's a practical example.

class MyClass
include MyModule
end

When you will include the module in a class, the included hook will be called. Therefore, second_class_method will be called within the scope of Class.

What happens here is

  1. first_method and second_method are included as instance-methods of MyClass.

    instance = MyClass.new
    instance.first_method
    # => whatever returned value of first_method is
  2. The methods of ClassMethods are automatically mixed as class methods of MyClass. This is a common Ruby pattern, that ActiveSupport::Concern encapsulates. The non-Rails Ruby code is

    module MyModule
    def self.included(base)
    base.extend ClassMethods
    end

    module ClassMethods
    def this_is_a_class_method
    end
    end
    end

    Which results in

    MyClass.this_is_a_class_method

    or in your case

    MyClass.first_class_method
  3. included is a hook that is effectively to the following code

    # non-Rails version
    module MyModule
    def self.included(base)
    base.class_eval do
    # somecode
    end
    end
    end

    # Rails version with ActiveSupport::Concerns
    module MyModule
    included do
    # somecode
    end
    end

    It's mostly "syntactic sugar" for common patterns. What happens in practice, is that when you mix the module, that code is executed in the context of the mixer class.

self.included – including class methods from a module in Ruby


What's the difference between the two examples?

The first code block adds the class methods in ClassMethods to the including class and calls the scope method on it as well. The second one does neither of these things and will result in a NoMethodError because the module has no scope class method. self.some_class_method will not be available on the including class once the module is included.

For the full story on how module inclusion works in Ruby, read my answer here:

Inheriting class methods from modules / mixins in Ruby

What's the point of self.included if a module's sole purpose is to be included?

Inclusion is not the only purpose of modules. They are also used for other things such as namespacing or simply storing various class methods that are then callable on the module itself.

Why doesn't Ruby include class methods automatically?

Theoretically Ruby could automatically add all class methods defined in a module to the including class, but in practice that would be a bad idea, because you would not get to choose anymore whether you want to include class methods — all class methods would be included every time, whether or not they are intended to be included. Consider this example:

module M
def self.class_method
"foo"
end

def self.configure_module
# add configuration for this module
end
end

class C
include M
end

Here, the configure_module method is obviously not supposed to be added to C, as its purpose is to set the configuration for the module object. Yet, if we had auto-inclusion for class methods, you would not be able to prevent it from being included.

But all instance methods are already included! How is that okay then?

Instance methods in a module are only really useful if they are included into a class, since modules cannot have instances, only classes can. So in a module every instance method is expected to be included somewhere to work.

A "class" method on a module is different, because it can be called on the module itself, so it can be used just fine regardless of whether it's also added to the including class. That is why it is better that you have a choice there.

Ruby Blocks inside module or class

Yes, you can use a block in a method call at the class/module level. The reason you're getting the error isn't because of the block but because you're calling say_hi in the context of the class, so it's looking for methods of the class itself, not for methods of instances of the class. You defined say_hi as an instance method, so it's unavailable at the class level. If you change it to def self.say_hi( name ), it works fine.

ActiveSupport::Concern code placed inside the included block or in the module body

When the concern is included in a class, the include and class_methods allows for the receiving class to inherit those methods.

included adds instance methods while class_methods adds class methods.

Source: Rails Concerns Docs

On the other hand, if your question is the difference between the placement of include Magic, there is no difference in how the class would function.

included do ..... end (wrong no of arguments) error

oh.... got it fixed...

Stupid typo mistake

extend ActiveSupport::Concerns should be Concern

Difference between redefining self.included and passing block to included

Both snippets produce the same result. However, there is a small but important difference.

The first code is pure Ruby. It means it will work without any dependency.

The second piece of code depends on ActiveSupport that is an external dependency. If you want to use it you need to include the gem in your project. In a Rails application, there is almost no overhead because the application already depends on ActiveSupport. But in a non-Rails application, it may not be convenient.

Moreover, ActiveSupport::Concern does a lot more than simply adding some syntactic sugar for the Ruby included hook. In fact, the primary scope of such module was to manage multiple dependencies between modules.

In the Rails codebase it's very common to define small piece of features into separate modules. A good example is ActiveSupport itself. However, you may have that the feature A may require some feature defined in the module B, so if you include B in your code, then the module should make sure to require and mix also A, otherwise your code will crash.

It also implements a very common pattern based on the included hook: class-level extensions. The following Ruby code

module A
def self.included(base)
base.extend ClassMethods
end

module ClassMethods
def bar
"bar"
end
end
end

class Foo
include A
end

Foo.bar
# => "bar"

becomes

module A
extend ActiveSupport::Concern

module ClassMethods
def bar
"bar"
end
end
end

class Foo
include A
end

Foo.bar
# => "bar"

My personal advice is to avoid using the ActiveSupport::Concern if

  • You don't need to use its advanced features
  • The code you are writing, can be easily written with a few code with no dependency on Concern
  • The app does not already include ActiveSupport as dependency


Related Topics



Leave a reply



Submit