我需要对控制器中的回调顺序进行更细粒度的控制。目前Rails只允许你使用append|prepend_before|after_action
,但如果你想添加一个带有专用回调的模块,那就非常糟糕了。
我试图理解AbstractController :: Callbacks是如何工作的,我试图注册一种新类型的回调,它将在特定时刻执行,利用Rail的控制器语法添加回调(仅限/除+动作列表等)。
您可以将其视为自定义访问控制功能,但此问题并非关于访问控制,请不要使用像Cancan这样的宝石进行回答。
class ApplicationController
include xxx
include MyModuleWithCallbacks
include yyy
...
end
class MyController < ApplicationController
prepend_before_action :something_before_my_callbacks
my_callback_list :custom_callback, only: [:index, :show]
before_action :something_after_my_callbacks
# Goal : the order of above callbacks should NOT matter, my_callback does not depend on ActionController process_action callback list
end
module MyModuleWithCallbacks
extend ActiveSupport::Concern
extend AbstractController::Callbacks
included do
around_action :perform_if_condition
def perform_if_condition
run_callbacks :my_callback_list do
if my_callbacks_went_good?
yield # And run the controller's before_callbacks
else
# log, render error page, etc.
end
end
end
# This is the hard part register the callback, I tried
class_methods do
define_method :my_callback_list do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:my_callback_list, :before, name, options)
end
end
end
目前的错误是
未定义的方法`_my_callbacks_list_callbacks&#39; for PublicController:Class
我从 AbstractController::Callbacks的源代码中汲取灵感,但我不确定我是否理解那里发生的事情^^&#34;
答案 0 :(得分:0)
我看到了一些赞成票,所以这是我目前的解决方案:
以非常轻量级的访问控制方法为例,my_callback
的原始名称为access_control
# controllers/concerns/access_control.rb
module AccessControl
extend ActiveSupport::Concern
class_methods do
define_method :my_callback do |*names, &blk|
_insert_callbacks(names, blk) do |name, options|
set_callback(:my_callback, :before, name, options)
end
end
end
included do
define_callbacks :my_callback
def perform_if_access_granted
run_callbacks :my_callback do
if @access_denied and not @access_authorized and not god_mode?
@request_authentication = true unless user_signed_in?
render(
file: File.join(Rails.root, 'app/views/errors/403.html'),
status: 403,
layout: 'error')
else
yield
end
end
end
然后在包含模块的其他控制器中(再次使用Access控件示例)
# controllers/your_controller.rb
class YourController < SeekerController
your_callback do
set_entity
allow_access_to_entity(@entity)
end