How to use concerns in Rails 4

后端 未结 6 1807
甜味超标
甜味超标 2020-11-22 06:57

The default Rails 4 project generator now creates the directory \"concerns\" under controllers and models. I have found some explanations about how to use routing concerns,

6条回答
  •  北海茫月
    2020-11-22 07:20

    I felt most of the examples here demonstrated the power of module rather than how ActiveSupport::Concern adds value to module.

    Example 1: More readable modules.

    So without concerns this how a typical module will be.

    module M
      def self.included(base)
        base.extend ClassMethods
        base.class_eval do
          scope :disabled, -> { where(disabled: true) }
        end
      end
    
      def instance_method
        ...
      end
    
      module ClassMethods
        ...
      end
    end
    

    After refactoring with ActiveSupport::Concern.

    require 'active_support/concern'
    
    module M
      extend ActiveSupport::Concern
    
      included do
        scope :disabled, -> { where(disabled: true) }
      end
    
      class_methods do
        ...
      end
    
      def instance_method
        ...
      end
    end
    

    You see instance methods, class methods and included block are less messy. Concerns will inject them appropriately for you. That's one advantage of using ActiveSupport::Concern.


    Example 2: Handle module dependencies gracefully.

    module Foo
      def self.included(base)
        base.class_eval do
          def self.method_injected_by_foo_to_host_klass
            ...
          end
        end
      end
    end
    
    module Bar
      def self.included(base)
        base.method_injected_by_foo_to_host_klass
      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
    

    In this example Bar is the module that Host really needs. But since Bar has dependency with Foo the Host class have to include Foo (but wait why does Host want to know about Foo? Can it be avoided?).

    So Bar adds dependency everywhere it goes. And order of inclusion also matters here. This adds lot of complexity/dependency to huge code base.

    After refactoring with ActiveSupport::Concern

    require 'active_support/concern'
    
    module Foo
      extend ActiveSupport::Concern
      included do
        def self.method_injected_by_foo_to_host_klass
          ...
        end
      end
    end
    
    module Bar
      extend ActiveSupport::Concern
      include Foo
    
      included do
        self.method_injected_by_foo_to_host_klass
      end
    end
    
    class Host
      include Bar # It works, now Bar takes care of its dependencies
    end
    

    Now it looks simple.

    If you are thinking why can't we add Foo dependency in Bar module itself? That won't work since method_injected_by_foo_to_host_klass have to be injected in a class that's including Bar not on Bar module itself.

    Source: Rails ActiveSupport::Concern

提交回复
热议问题