class ApplicationController < ActionController::Base
before_action :test, only: [:index]
def test
ap 'test'
end
end
The above is run before every single index action, be it dogs#index or cats#index or rabbits#index. How should I get it to execute just before cats#index and rabbits#index?
I want to test to be exectuted before actions in many controllers.
You can skip this method:
class ApplicationController < ActionController::Base
before_action :test, only: [:index]
def test
p 'test'
end
end
class DogsController < ApplicationController
skip_before_action :test
end
Just move your call into the controller you want it to run in.
class ApplicationController < ActionController::Base
# nothing here!
def test
# ...
end
end
class CatsController < ApplicationController
before_action :test, only: [:index]
end
class RabbitsController < ApplicationController
before_action :test, only: [:index]
end
It is pretty simple actually
Create before_action in application_controller and check if: that incoming request is for concerned_controller.
class ApplicationController < ActionController::Base
before_create :assign_setting, only: :create, if: :registration_controller?
def registration_controller?
params["controller"] == "registrations"
end
def assign_settings
# your code
puts "settings applied"
end
end
Related
My codebase contains callbacks like
prepend_before_action :authenticate_api_user! and
before_action :authenticate_api_v1_user!
What is the difference between these two?
Generally before_action runs before every action to a method and
prepend_before_action does what it says. It just add your definition at index zero.
Here is a great use case to prove the same:
class ConfuseUsersController < ApplicationController
prepend_before_action :find_user, only: [:update]
prepend_before_action :new_user, only: [:create]
before_action :save_and_render
def update
end
def create
end
private
def find_user
#user = User.find(params[:id])
end
def new_user
#user = User.new
end
def save_and_render
persited = #user.persited?
#user.assign_attributes(user_params)
if #user.save
redirect users_path(#user)
else
render (persited ? :edit : :new)
end
end
end
before_action :save_and_render this makes save_and_render to get called before every action.
prepend_before_action :find_user, only: [:update] This prepends find_user function to get called before save_and_render
Another example:
We have an ApplicationController where...
class ApplicationController < ActionController::Base
before_action :one
before_action :three
end
Now in any controller if we want to execute any other method for e.g. two before three you can use prepend_before_action like
prepend_before_action :three, :two
class AdminsController < ApplicationController
prepend_before_action :three, :two
end
Now before three gets executed two will get execute and then three for this specific method.
In my current application I have two doorkeeper scopes, user and admin. In the doorkeeper documentation for setting scopes in an API it shows
class Api::V1::ProductsController < Api::V1::ApiController
before_action -> { doorkeeper_authorize! :public }, only: :index
before_action only: [:create, :update, :destroy] do
doorkeeper_authorize! :admin, :write
end
...
end
I don't want to call doorkeeper in every controller, so in my ApplicationController I have
module API
module V1
class ApplicationController < ActionController::API
before_action { doorkeeper_authorize! :user, :project }
...
end
end
end
but I don't want to give :project access to every controller. Is there a way for me to allow user in our application controller before_action { doorkeeper_authorize! :user } and on a per-controller basis allow project? ie:
module API
module V1
class SomeController < ApplicationController
before_action only: [:index, :show] { doorkeeper_authorize! :project }
...
end
end
end
Use a conditional with controller_name - smith like this:
before_action { doorkeeper_authorize! :project },
if: -> { controller_name == 'some' }
Check if maybe you should pass a param to lambda like:
if: ->(instance) { instance.controller_name == 'some' }
I was able to solve this by doing the following in my API::V1::ApplicationController
module API
module V1
class ApplicationController < ActionController::API
WHITELISTED_PROJECT_CONTROLLERS = %w( projects pre_task_plans
job_hazard_analyses ).freeze
before_action :authorize!
def authorize!
if project_scope?
if !WHITELISTED_PROJECT_CONTROLLERS.include?(controller_name)
return user_not_authorized
end
end
doorkeeper_authorize! :user, :project
end
def project_scope?
doorkeeper_token&.scopes&.any? { |s| s == 'project' }
end
...
end
end
end
maybe creating your own filter might be an option
before_action :doorkeeper_user_authorize!, only: [:create, :update, :destroy]
protected
def doorkeeper_user_authorize!
doorkeeper_authorize!( :user )
end
def doorkeeper_project_authorize!
doorkeeper_authorize!( :user, :project )
end
then in controller where project should be allowed
skip_before_action :doorkeeper_user_authorize!
before_action :doorkeeper_project_authorize!
I have an application controller where I'm handling some authentication
class ApplicationController < ActionController::Base
before_action :prep_data
def prep_data
# code...
# authenticate
end
end
and I have a controller that inherits from this one
class OtherController < ApplicationController
def custom_action_method
end
end
can I skip the before_action hook for the OtherController for a custom action method custom_action_method
I found the answer, this is the syntax
class OtherController < ApplicationController
skip_before_action :prep_data, only: [:custom_action_method]
def custom_action_method
end
end
I have controller looks like
class BarsController < ApplicationController
after_action :some_method, only: [:index]
def index
get_cache = $redis.get('some_key')
if get_cache.present?
# i want to skip after_action callback in here
else
# other stuff
end
end
end
How can I skip after_action :some_method if get_cache is present? I know I can do this with conditional like this
class BarsController < ApplicationController
after_action :some_method, only: [:index], unless: :check_redis
def index
get_cache = $redis.get('some_key')
if get_cache.present?
# i want to skip after_action callback in here
else
# other stuff
end
end
private
def check_redis
$redis.get('some_key')
end
end
But I think that is redundant, because should multiple get to redis.
This should work:
class BarsController < ApplicationController
after_action :some_method, only: [:index], unless: :skip_action?
def index
get_cache = $redis.get('some_key')
if get_cache.present?
#skip_action = true
# i want to skip after_action callback in here
else
# other stuff
end
end
private
def skip_action?
#skip_action
end
end
You can also use attr_accessor :skip_action instead of private method because controller is just object.
So I have ApplicationController.rb:
class ApplicationController < ActionController::Base
protect_from_forgery
def decode_email
params[:email] = URI::decode(params[:email])
end
end
and then UsersController.rb:
class UsersController < ApplicationController
before_filter :decode_email, only: [:show]
def show
#blah blah
end
end
Now hitting the show action results in:
undefined local variable or method 'decode_email' for #<UsersController:0x007fb5f216a710>
Why isn't that method being inherited so it can be properly used as a before_filter?
class ApplicationController < ActionController::Base
protect_from_forgery
private
def decode_email
params[:email] = URI::decode(params[:email])
end
end
is working for me