0
votes

I am fairly new to RoR. I have spent the afternoon reading about modules (used as concerns). I have yet to find a good article which describes the file path that using include or extend methods looks up (if include and extend are methods?).

The most specific example I have found was here: Ruby On Rails - Using concerns in controllers. This makes me feel that if I wanted to include the 'Bar' module in my Foo model I would create a concerns/ directory in my models/ directory, and create a 'Bar' module file in this folder.

# in models/concerns/bar.rb
modlue Bar
  # do I need this???
  extend ActiveSupport::Concern

  def speak_bar
    puts "model module bar!"
  end
end

# in models/foo.rb
class Foo < ApplicationRecord
  include Bar
end

# I could then perform:
Foo.new.speak_bar
=> "model module bar!"

And if I wanted to include a Bar module in my Foo controller I would do:

# in controllers/concerns/bar.rb
modlue Bar
  # Again, do I need this???
  extend ActiveSupport::Concern

  def speak_bar
    return "controller module bar!"
  end
end

# in controllers/foo.rb
class FoosController < ApplicationController
  include Bar

  def make_bar
    @bar = speak_bar
  end
end

# I could then use @bar in my views (anywhere else?) as <%= @bar %> and get it to output 
=> "controller module bar!"

Summary of questions:

Is this understanding set out above correct in terms of the file paths?

And do I need to use the extend ActiveSupport::Concern line in order to use this path system?

Are include and extend methods?

Thank you for your help.

3

3 Answers

2
votes

the file path that using include or extend Rails does some magic when starting to autoload a lot of things so you don't have to worry later when you call "Bar". This talk is really helpfull to understand WHY you can just do include Bar inside a rails model without much thinking https://www.youtube.com/watch?v=I0a5zv7uBHw

Usually, you want model related concerns inside /app/models/concerns and controller related concerns inside /app/controllers/concerns, but that's just for organization purposes, rails will autoload them even if you use /app/whatever/concerns, so be carefull about name collisions.

You DO need to extend ActiveSupport::Concern if you want to use the syntax sugar that Concerns provide, but at the end they are just modules that can be included. https://api.rubyonrails.org/classes/ActiveSupport/Concern.html check this examples, concerns are just a way to write modules to share behaviour with a more friendly syntax for common rails patterns.

extend is a method of Object https://docs.ruby-lang.org/en/2.6.0/Object.html#method-i-extend include is a method of Module https://docs.ruby-lang.org/en/2.6.0/Module.html#method-i-include (and Module inherits extend from Object)

1
votes

You should always extend your concerns module with the supplied concerns base from Rails.

Pathing is usually app/models/concerns/file.rb for model concerns and app/controllers/file.rb for controllers and so on.

If you specifically have logic that crosses the controller and models separation, consider placing that in lib, and adding lib to your autoload path.

include and extend are methods. Most things (almost all) are objects in ruby. So almost all operations are methods on objects.

0
votes

concerns are auto-loaded by rails by default starting from rails v4+. You can read the article written by DHH to get a fair idea of what concern does and what does it try to solve.

However, it gets pretty complicated in determining which scope you are in and what self is in the method. Check out this video by Ryan Bates regarding the problems with concerns.

To solve some parts of the problem, I generally nest the concern inside a folder and refer it by giving a class. For example

# app/models/concerns/user/authentication.rb

class User
  module Authentication
    extend ActiveSupport::Concern
    # stuff
  end
end

and include in the model like

# app/models/user.rb

include Authentication

In my opinion, the separation of concerns helps in isolating your methods. For example, you can create a Filterable concern in a similar way, and isolate it from your other models.