Module: ActiveSupport::Concern
- Included in:
- ActionableError, Callbacks, Configurable, LoggerSilence, LoggerThreadSafeLevel, Rescuable, Testing::ConstantLookup, Testing::FileFixtures
- Defined in:
- lib/active_support/concern.rb
Overview
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
Prepending concerns
Just like include
, concerns also support prepend
with a corresponding prepended do
callback. module ClassMethods
or class_methods do
are prepended as well.
prepend
is also used for any dependencies.
Defined Under Namespace
Classes: MultipleIncludedBlocks, MultiplePrependBlocks
Class Method Summary collapse
-
.extended(base) ⇒ Object
:nodoc:.
Instance Method Summary collapse
-
#append_features(base) ⇒ Object
:nodoc:.
-
#class_methods(&class_methods_module_definition) ⇒ Object
Define class methods from given block.
-
#included(base = nil, &block) ⇒ Object
Evaluate given block in context of base class, so that you can write class macros here.
-
#prepend_features(base) ⇒ Object
:nodoc:.
-
#prepended(base = nil, &block) ⇒ Object
Evaluate given block in context of base class, so that you can write class macros here.
Class Method Details
.extended(base) ⇒ Object
:nodoc:
123 124 125 |
# File 'lib/active_support/concern.rb', line 123 def self.extended(base) #:nodoc: base.instance_variable_set(:@_dependencies, []) end |
Instance Method Details
#append_features(base) ⇒ Object
:nodoc:
127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/active_support/concern.rb', line 127 def append_features(base) #:nodoc: if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false else return false if base < self @_dependencies.each { |dep| base.include(dep) } super base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end |
#class_methods(&class_methods_module_definition) ⇒ Object
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 ; puts 'bar'; end
end
end
class Buzz
include Example
end
Buzz.foo # => "foo"
Buzz. # => private method 'bar' called for Buzz:Class(NoMethodError)
207 208 209 210 211 212 213 |
# File 'lib/active_support/concern.rb', line 207 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 |
#included(base = nil, &block) ⇒ Object
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.
156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/active_support/concern.rb', line 156 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 |
#prepend_features(base) ⇒ Object
:nodoc:
140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/active_support/concern.rb', line 140 def prepend_features(base) #:nodoc: if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies).unshift self false else return false if base < self @_dependencies.each { |dep| base.prepend(dep) } super base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block) end end |
#prepended(base = nil, &block) ⇒ Object
Evaluate given block in context of base class, so that you can write class macros here. When you define more than one prepended
block, it raises an exception.
173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/active_support/concern.rb', line 173 def prepended(base = nil, &block) if base.nil? if instance_variable_defined?(:@_prepended_block) if @_prepended_block.source_location != block.source_location raise MultiplePrependBlocks end else @_prepended_block = block end else super end end |