How I can skip callback in action - ruby-on-rails

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.

Related

Use the same `before_action` filter in multiple controllers

In a Rails app, I have a before_action filter that sends a webhook message. Since I have a few controllers that I want the before_action to act on, is there a good way to make it a module and prepend it?
My current logic is:
# first_controller.rb:
before_action :do_something
def do_something
#same logic
end
# second_controller.rb:
before_action :do_something
def do_something
#same logic
end
# third_controller.rb:
before_action :do_something
def do_something
#same logic
end
If your controllers inherit from ApplicationController, You can do the following:
class ApplicationController < ActionController::Base
def do_something
#same logic
end
end
class FirstController < ApplicationController
before_action :do_something
end
class SecondController < ApplicationController
before_action :do_something
end
class ThirdController < ApplicationController
before_action :do_something
end
Or you can make your own parent controller, eg. DoSomethingController
class DoSomethingController < ApplicationController
before_action do_something
def do_something
#same logic
end
end
class FirstController < DoSomethingController
end
class SecondController < DoSomethingController
end
class ThirdController < DoSomethingController
end
Or you can use #code_aks's answer https://stackoverflow.com/a/59846330/8554172 to make a module and include it.
Yes, it is good to use DRY here. If your controllers do have same parent class you can place that method in there. If not it is good practice to move this method to the module and include it with reusing.
You can try below code write the methods in a controller concern. For example:
# app/controllers/concerns/example_concern.rb
module ExampleConcern
extend ActiveSupport::Concern
protected
def before_filter_1
puts "from first before_filter_method"
end
def before_filter_2
puts "from second before_filter_method"
end
end
Now in the controller, include the module in the concern and call the methods using before_action as required. For example:
# app/controllers/examples_controller.rb
class ExamplesController < ApplicationController
include ExampleConcern
before_action :before_filter_1, only: [:action_a, :action_b, :action_c]
before_action :before_filter_2, only: [:action_d, :action_e]
def action_a
end
def action_b
end
def action_c
end
def action_d
end
def action_e
end
end
Hope this will help you. :)

What is the difference between before_action and prepend_before_action in Rails?

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.

Explicitly allow Doorkeeper scope in controller

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!

before_action for specific controller

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

Controller not inheriting before_filter

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

Resources