Ruby On Rails - Using concerns in controllers
If you include modules (extended by ActiveSupport::Concern
or not), the methods of that module become instance methods of the including class/module.
Your Controller method should hence read
def show
somethingfoo
render plain: "Yeah, I'm shown!"
end
Where to store 'concerns' in a Ruby on Rails project? (Rails 5.2+)
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-extendinclude
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)
How to properly set up, make or include concern in Rails
Related explanation has been written in official guide.
All right, Rails has a collection of directories similar to $LOAD_PATH in which to look up post.rb. That collection is called autoload_paths and by default it contains:
Any existing second level directories called app/*/concerns in the application and engines.
https://guides.rubyonrails.org/autoloading_and_reloading_constants.html
The reason why app/controllers/admin/concerns
isn't loaded is it's not a second level directory.
Since files in the second level concerns directory are automatically loaded, in this case you sould put the test.rb file in the app/controllers/concerns/admin
.
Or adding app/controllers/admin/concerns
to autoload path, but it's not highly recommended because this is out of rails design pattern.
Rails concerns, how to include a concern inside an api controller
Since rails expects your module naming to follow your file structure, your concern should be named:
module Api::Concerns::ErrorSerializer
Since you're including it in Api::TemplatesController
, I would do:
class Api::TemplatesController < ApiController
include Api::Concerns::ErrorSerializer
...
end
To help rails out with the constant lookup.
Rails: including concern in controller results in action 'index' could not be found
It should be include CurrentUserConcern
instead of include current_user_concern
class EntriesController < ApplicationController
include CurrentUserConcern
end
Rails 5: Abstracting actions out of controllers
You have completly misunderstood pretty much everything about ActiveSupport::Concern
and how module mixins work.
Lets start by using composition to seperate concerns. For example:
module LocalStorage
extend ActiveSupport::Concern
class_methods do
# use a memoized helper instead of a constant
# as its easier to stub for testing
def local_storage
@local_storage ||= ::PerceptSys::LocalStore
end
end
end
This makes sense to extract to a separate concern since its a reusable behaviour.
We can then compose an Uploadable
concern:
module Uploadable
# we compose modules by extending
extend ActiveSupport::Concern
extend LocalStorage
# put instance methods in the module body
# GET /<model>/upload(.:format)
def webupload
# ...
end
#
# POST /<model>/upload(.:format)
#
def upload
# ...
end
# don't abuse use a callback for this - just use a straight
# method that returns a value and preferably does not have side effects
def upload_params
# ...
end
# ...
# use "included" to hook in the class definition
# self here is the singleton class instance
# so this is where you put callbacks, attr_accessor etc
# which would normally go in the class defintion
included do
before_action(:set_title_uploading,
:only => [
:upload,
:webupload,
])
before_action(:set_file_upload,
:only => [
:upload,
])
end
# just use class_methods for actual class_methods!
class_methods do
# for example to derive the name of a model from the controller name
def resource_class_name
controller_name.singularize
end
def resource_class
@resource_class ||= resource_class_name.classify.constantize
end
end
end
Concerns vs Services
Concerns and services are very different abstraction patterns used for entirely different purposes.
- Services are operations/functions turned into classes.
- Concerns are mixins.
Say I want to authenticate a user. I could easily just write a authenticate(username, password)
function, but it might be useful to encapsulate the logic for that function in a class instead:
class AuthenticationService
def initialize(username, password); end
def run(); end
end
Aside from helping me encapsulate all of the logic for authentication in a single class, this allows me to create other authentication classes.
class GoogleAuthenticationService
def initialize(username, password); end
def run(); end
end
class FacebookAuthenticationService
def initialize(username, password); end
def run(); end
end
A concern, on the other hand, mixes functionality from a module into another class. Say you have a Rails controller that was concerned with authorizing a user. You might write a concern that implements a before_action
which checks to see if the user is authorized. Then you could mix that concern into any controller where you wanted to enforce that authorization check.
how to include concerns module with only actions in rails controllers
AFAIK this is impossible out of the box. I use the following approach:
PLAN_FINDING_USE = [:show]
include Concerns::V1::PlanFinding
and
included do |base|
actions = base.const_defined?('PLAN_FINDING_USE') &&
base.const_get('PLAN_FINDING_USE')
if actions.is_a?(Array)
before_action :set_plan, only: actions
else
before_action :set_plan
end
end
Related Topics
Simple Neural Network Can't Learn Xor
How to Run a Simple File on Heroku
How to See the Ruby Code in a Proc
Accessing Nested Model Attributes Inside a Fields_For Without Using Formbuilder
How to Get Generators Call Other Generators in Rails 3
Mail Gem - How to Clean Up the Body String
Memory Usage Increase with Ruby 2.1 Versus Ruby 2.0 or 1.9
Custom Rails Configuration Section
Does Multibyte Character Interfere with End-Line Character Within a Regex
Ruby Time Object Converted from Float Doesn't Equal to Orignial Time Object
Dynamically Generate Scopes in Rails Models
How to Escape a Single Quote in Ruby
Why Does Rails Titlecase Add a Space to a Name
Require Lib in Rspec with Ruby 1.9.2 Brings "No Such File to Load"