Module: ConvenientService::Dependencies::Extractions::ActiveSupportConcern::Concern
- Defined in:
- lib/convenient_service/dependencies/extractions/active_support_concern/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.
-
#instance_methods(include_private = false, &instance_methods_module_definition) ⇒ Object
NOTE: Customization compared to original ‘Concern` implementation.
-
#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.
-
#singleton_class_methods(&singleton_class_methods_module_definition) ⇒ Object
Define singleton class methods from given block.
Class Method Details
.extended(base) ⇒ Object
:nodoc:
156 157 158 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 156 def self.extended(base) # :nodoc: base.instance_variable_set(:@_dependencies, []) end |
Instance Method Details
#append_features(base) ⇒ Object
:nodoc:
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 160 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 eval_included_block(base) ## # NOTE: Customization compared to original `Concern` implementation. # TODO: Why original `Concern` implementation uses `const_defined?(:ClassMethods)`, not `const_defined?(:ClassMethods, false)`? # NOTE: Changed order to have a way to control when to include `InstanceMethods` and `ClassMethods` from `included` block. # base.include const_get(:InstanceMethods) if const_defined?(:InstanceMethods) base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.singleton_class.extend const_get(:SingletonClassMethods) if const_defined?(:SingletonClassMethods) 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)
276 277 278 279 280 281 282 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 276 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.
209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 209 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 |
#instance_methods(include_private = false, &instance_methods_module_definition) ⇒ Object
NOTE: Customization compared to original ‘Concern` implementation.
243 244 245 246 247 248 249 250 251 252 253 254 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 243 def instance_methods(include_private = false, &instance_methods_module_definition) ## # NOTE: This `if` is propably the reason why Rails team decided to create only `class_methods` in the original Concern implementation. # return super(include_private) unless instance_methods_module_definition mod = const_defined?(:InstanceMethods, false) ? const_get(:InstanceMethods) : const_set(:InstanceMethods, Module.new) mod.module_eval(&instance_methods_module_definition) end |
#prepend_features(base) ⇒ Object
:nodoc:
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 184 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 eval_prepended_block(base) ## # NOTE: Customization compared to original `Concern` implementation. # TODO: Why original `Concern` implementation uses `const_defined?(:ClassMethods)`, not `const_defined?(:ClassMethods, false)`? # NOTE: Changed order to have a way to control when to include `InstanceMethods` and `ClassMethods` from `included` block. # base.prepend const_get(:InstanceMethods) if const_defined?(:InstanceMethods) base.singleton_class.prepend const_get(:ClassMethods) if const_defined?(:ClassMethods) 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.
226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 226 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 |
#singleton_class_methods(&singleton_class_methods_module_definition) ⇒ Object
Define singleton class methods from given block. You can define private class methods as well.
module Example
extend ActiveSupport::Concern
singleton_class_methods do
def foo; puts 'foo'; end
private
def ; puts 'bar'; end
end
end
class Buzz
include Example
end
Buzz.singleton_class.foo # => "foo"
Buzz.singleton_class. # => private method 'bar' called for Buzz:Class(NoMethodError)
304 305 306 307 308 309 310 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 304 def singleton_class_methods(&singleton_class_methods_module_definition) mod = const_defined?(:SingletonClassMethods, false) ? const_get(:SingletonClassMethods) : const_set(:SingletonClassMethods, Module.new) mod.module_eval(&singleton_class_methods_module_definition) end |