2017kamb
2017kamb

Reputation: 242

ActiveSupport::Concern should be included or extended

I know include is used for access module methods as instance methods while extend is used to access module methods as class methods.

For ActiveSupport::Concern somewhere I see written as,

module Test
  include ActiveSupport::Concern
end

while at some places written as,

module Test
  extend ActiveSupport::Concern
end

Here my confusion is, ActiveSupport::Concern should be used with include or with extend?

Upvotes: 5

Views: 2842

Answers (2)

max
max

Reputation: 101811

You need to extend the module with ActiveSupport::Concern in order for the ActiveSupport::Concern#included and #class_methods methods to work properly.

These two methods are after all pretty much the only reason for its existance.

module A
  extend ActiveSupport::Concern
  # raises ArgumentError (wrong number of arguments (given 0, expected 1))
  included do
    puts "Hello World"
  end
end

module B
  extend ActiveSupport::Concern
  included do
    puts "Hello World"
  end
end

class C
  include B
end
# Outputs Hello World

Check out what happens if we inspect the included method:

module AB
  include ActiveSupport::Concern
  puts method(:included).source_location # nil
end
module ABC
  extend ActiveSupport::Concern
  puts method(:included).source_location # .../ruby/gems/2.7.0/gems/activesupport-6.0.2.1/lib/active_support/concern.rb
end

When we extend the module with ActiveSupport::Concern we are putting it on the ancestors chain of ABC, thus the methods of ActiveSupport::Concern are available as module methods of ABC. This does not happen when you use include and the included method called is actually Module#included from the Ruby core.

Upvotes: 2

3limin4t0r
3limin4t0r

Reputation: 21110

You should use extend ActiveSupport::Concern, like shown in the examples in the documentation.

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

The reason for using extend is to make the methods defined in ActiveSupport::Concern available in module context. This allows you to use the methods included and class_methods within the module.

When using include those methods would not be available within the module and instead be available on instances of a class that includes M.

If you want to know the difference between the two I suggest taking a look at What is the difference between include and extend in Ruby?

Upvotes: 3

Related Questions