在ApplicationController

时间:2016-05-10 20:32:44

标签: ruby-on-rails ruby callback activesupport actioncontroller

我需要对控制器中的回调顺序进行更细粒度的控制。目前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;

1 个答案:

答案 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