Module: Module::Concerning
| Relationships & Source Files | |
| Extension / Inclusion / Inheritance Descendants | |
| Included In: 
        ActiveModel::Type::Helpers::AcceptsMultiparameterTime,
           ::ActiveModel::Validations::AcceptanceValidator::LazilyDefineAttributes,
          ActiveRecord::AttributeMethods::GeneratedAttributeMethods,::Module | |
| Defined in: | activesupport/lib/active_support/core_ext/module/concerning.rb | 
Overview
Bite-sized separation of concerns
We often find ourselves with a medium-sized chunk of behavior that we’d like to extract, but only mix in to a single class.
Extracting a plain old Ruby object to encapsulate it and collaborate or delegate to the original object is often a good choice, but when there’s no additional state to encapsulate or we’re making DSL-style declarations about the parent class, introducing new collaborators can obfuscate rather than simplify.
The typical route is to just dump everything in a monolithic class, perhaps with a comment, as a least-bad alternative. Using modules in separate files means tedious sifting to get a big-picture view.
Dissatisfying ways to separate small concerns
Using comments:
class Todo < ApplicationRecord
  # Other todo implementation
  # ...
  ## Event tracking
  has_many :events
  before_create :track_creation
  private
    def track_creation
      # ...
    end
endWith an inline module:
Noisy syntax.
class Todo < ApplicationRecord
  # Other todo implementation
  # ...
  module EventTracking
    extend ActiveSupport::Concern
    included do
      has_many :events
      before_create :track_creation
    end
    private
      def track_creation
        # ...
      end
  end
  include EventTracking
endMix-in noise exiled to its own file:
Once our chunk of behavior starts pushing the scroll-to-understand-it boundary, we give in and move it to a separate file. At this size, the increased overhead can be a reasonable tradeoff even if it reduces our at-a-glance perception of how things work.
class Todo < ApplicationRecord
  # Other todo implementation
  # ...
  include TodoEventTracking
endIntroducing Module#concerning
By quieting the mix-in noise, we arrive at a natural, low-ceremony way to separate bite-sized concerns.
class Todo < ApplicationRecord
  # Other todo implementation
  # ...
  concerning :EventTracking do
    included do
      has_many :events
      before_create :track_creation
    end
    private
      def track_creation
        # ...
      end
  end
end
Todo.ancestors
# => [Todo, Todo::EventTracking, ApplicationRecord, Object]This small step has some wonderful ripple effects. We can
- 
grok the behavior of our class in one glance, 
- 
clean up monolithic junk-drawer classes by separating their concerns, and 
- 
stop leaning on protected/private for crude “this is internal stuff” modularity. 
Instance Method Summary
- 
    
      #concern(topic, &module_definition)  
    
    A low-cruft shortcut to define a concern. 
- 
    
      #concerning(topic, &block)  
    
    Define a new concern and mix it in. 
Instance Method Details
#concern(topic, &module_definition)
A low-cruft shortcut to define a concern.
concern :EventTracking do
  #...
endis equivalent to
module EventTracking
  extend ActiveSupport::Concern
  #...
end# File 'activesupport/lib/active_support/core_ext/module/concerning.rb', line 126
def concern(topic, &module_definition) const_set topic, Module.new { extend ::ActiveSupport::Concern module_eval(&module_definition) } end
#concerning(topic, &block)
Define a new concern and mix it in.
# File 'activesupport/lib/active_support/core_ext/module/concerning.rb', line 109
def concerning(topic, &block) include concern(topic, &block) end