What does class_methods do in concerns?
class_methods
is used to add class methods to the model used by the concern.
A typical module looks like this:
module M
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
...
end
end
By using ActiveSupport::Concern
the above module could instead be written as:
require 'active_support/concern'
module M
extend ActiveSupport::Concern
class_methods do
...
end
end
As Oleg Antonyan pointed out, from the source code, we know that it's going to use ClassMethods
module under the hood.
Reference: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
It's just for convenience. module ClassMethods
is pure Ruby, but class_methods
is defined in ActiveSupport::Concern
for convenience. If you look at a source code you'll find that class_methods
does exactly the same thing
# activesupport/lib/concern.rb
def class_methods(&class_methods_module_definition)
mod = const_defined?(:ClassMethods, false) ?
const_get(:ClassMethods) :
const_set(:ClassMethods, Module.new)
mod.module_eval(&class_methods_module_definition)
end
ActiveSupport::Concern
provides syntactic sugar for common Ruby patterns for module mixins.
When you are using modules as mixins you can't just use self
to declare class methods like you would from a class:
module Foo
def self.bar
"Hello World"
end
def instance_method
"Hello World"
end
end
class Baz
include Foo
end
irb(main):010:0> Baz.bar
NoMethodError: undefined method `bar' for Baz:Class
from (irb):10
irb(main):011:0> Foo.bar
=> "Hello World"
irb(main):012:0>
As you can see from the example that actually creates a module method - thats because self
is the module. You can use extend instead:
module Foo
def a_class_method
"Hello World"
end
end
class Bar
extend Foo
end
irb(main):049:0> Bar.a_class_method
=> "Hello World"
But that does not let you declare instance methods in the module. Which is not really that useful.
So the solution is to create an inner module which is commonly named ClassMethods
and extend the class when the module is included:
module Foo
# this is a method thats called when you include the module in a class.
def self.included(base)
base.extend ClassMethods
end
def an_instance_method
end
# the name ClassMethods is just convention.
module ClassMethods
def a_class_method
"Hello World"
end
end
end
class Bar
include Foo
end
irb(main):071:0> Bar.a_class_method
=> "Hello World"
This boilerplate code is found in almost every ruby gem/library.
By extending your module with ActiveSupport::Concern
you can shorten this to just:
module Foo
extend ActiveSupport::Concern
class_methods do
def a_class_method
"Hello World"
end
end
end
Under the hood ActiveSupport::Concern
creates a ClassMethods
module and evaluates the block in the context of the module. Dig into the source if you curious about how it actually does this.