4

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?

2017kamb
  • 192
  • 3
  • 8
  • 27
  • Could you further specify *"some places"*? Do you mean randomly on the internet or in the official documentation? – 3limin4t0r Jun 03 '20 at 09:40
  • randomly on the net like one here, https://medium.com/@carlescliment/about-rails-concerns-a6b2f1776d7d – 2017kamb Jun 03 '20 at 09:45
  • 1
    Seems like the author of that article doesn't understand what `ActiveSupport::Concern` is for. The `include ActiveSupport::Concern` effectively does nothing in his examples and can be removed without the code breaking. – 3limin4t0r Jun 03 '20 at 09:54

2 Answers2

3

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?

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
  • I know the include vs extend diff, my confusion is with ActiveSupport::Concern. I checked this API doc also but it's not clear. Can you elaborate why extend and not include for ActiveSupport::Concern – 2017kamb Jun 03 '20 at 09:43
  • @RajKumarSharma I've updated the answer with a small explanation. – 3limin4t0r Jun 03 '20 at 10:01
2

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.

max
  • 96,212
  • 14
  • 104
  • 165
  • nice example thanks, so we always have to extend right? – 2017kamb Jun 03 '20 at 10:00
  • Yes. Since the whole point of ActiveSupport::Concern is to use its methods as module methods. It does not provide any instance methods of use to a class. – max Jun 03 '20 at 10:03
  • There might be some fringe scenarios where you might want to `include` if you're planning on doing some meta programming yourself. However 99.99% of the time `extend` is the way to go. – 3limin4t0r Jun 03 '20 at 10:04
  • @3limin4t0r If you're doing additional meta programming I would'nt use ActiveSupport::Concern in the first place as its functionality is actually pretty simple to emalute in whatever crazy shenanigans you're up to with less added complixity. But thats just my 2c. – max Jun 03 '20 at 10:07