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
first_method
andsecond_method
are included as instance-methods ofMyClass.
instance = MyClass.new
instance.first_method
# => whatever returned value of first_method isThe methods of
ClassMethods
are automatically mixed as class methods ofMyClass
. This is a common Ruby pattern, thatActiveSupport::Concern
encapsulates. The non-Rails Ruby code ismodule MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def this_is_a_class_method
end
end
endWhich results in
MyClass.this_is_a_class_method
or in your case
MyClass.first_class_method
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
endIt'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
Which Ruby Version am I Really Running
Jekyll Serve Dependency Error - Could Not Open 'Lib Curl'
In Ruby Why Does Nil.Id Return 4
How to Test 'Rand()' with Rspec
How to Get Past "Http://Gems.Rubyforge.Org/ Does Not Appear to Be a Repository" Error Message
Ruby Gets/Puts Only for Strings
How to Have Rspec Test for My Default Scope
How to Find Each Instance of a Class in Ruby
Creating Permutations from a Multi-Dimensional Array in Ruby
Converting from Xml Name-Values into Simple Hash
Optional Arguments with Default Value in Ruby
Ruby Array to JSON and Rails JSON Rendering