I never understood why one has to use ActiveSupport::Concern is used for mixins instead of a plain module. Is there a simple answer to what ActiveSupport::Concern provides (at least in Rails 5) that a simple module without using ActiveSupport::Concern will do?
From https://api.rubyonrails.org/classes/ActiveSupport/Concern.html:
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
One reason to use ActiveSupport::Concern over a regular mixin is that it lazy loads the includes. Therefore, if you are referencing code in a model, controller, etc., it will resolve. Whereas you may get undefined errors when a mixin is loaded.
© 2022 - 2024 — McMap. All rights reserved.