module ActiveSupport::Concern
A typical module looks like this:
module M def self.included(base) base.extend ClassMethods base.class_eval do scope :disabled, -> { where(disabled: true) } end 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 included do scope :disabled, -> { where(disabled: true) } end class_methods do ... end end
Moreover, it gracefully handles module dependencies. Given a
Foo
module and a Bar
module which depends on the
former, we would typically write the following:
module Foo def self.included(base) base.class_eval do def self.method_injected_by_foo ... end end end end module Bar def self.included(base) base.method_injected_by_foo end end class Host include Foo # We need to include this dependency for Bar include Bar # Bar is the module that Host really needs end
But why should Host
care about Bar
's
dependencies, namely Foo
? We could try to hide these from
Host
directly including Foo
in Bar
:
module Bar include Foo def self.included(base) base.method_injected_by_foo end end class Host include Bar end
Unfortunately this won't work, since when Foo
is included,
its base
is the Bar
module, not the
Host
class. With ActiveSupport::Concern
, module
dependencies are properly resolved:
require 'active_support/concern' module Foo extend ActiveSupport::Concern included do def self.method_injected_by_foo ... end end end module Bar extend ActiveSupport::Concern include Foo included do self.method_injected_by_foo end end class Host include Bar # It works, now Bar takes care of its dependencies end
Public Instance Methods
Define class methods from given block. You can define private class methods as well.
module Example extend ActiveSupport::Concern class_methods do def foo; puts 'foo'; end private def bar; puts 'bar'; end end end class Buzz include Example end Buzz.foo # => "foo" Buzz.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
# File lib/active_support/concern.rb, line 162 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
Evaluate given block in context of base class, so that you can write class
macros here. When you define more than one included
block, it
raises an exception.
# File lib/active_support/concern.rb, line 128 def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block) if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end