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:
146 147 148 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 146 def self.extended(base) # :nodoc: base.instance_variable_set(:@_dependencies, []) end |
Instance Method Details
#append_features(base) ⇒ Object
:nodoc:
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 150 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.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) ## # 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.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
266 267 268 269 270 271 272 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 266 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.
199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 199 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.
233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 233 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:
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 174 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.class_eval(&@_prepended_block) if instance_variable_defined?(:@_prepended_block) ## # 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.
216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 216 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.bar # => private method 'bar' called for Buzz:Class(NoMethodError)
294 295 296 297 298 299 300 |
# File 'lib/convenient_service/dependencies/extractions/active_support_concern/concern.rb', line 294 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 |