一般情况 Controller 里都不会有太多的 before_action, 一旦需要使用多个的时候往往伴随着各个方法的顺序问题, 以下 DEMO :

before_action 就是根据顺序一个一个来, 队列模型;

append_before_action 根据定义顺序, 向 before_action 的队列中插入 callback ;

prepend_before_action 会插队执行, 多个方法的话会依次插入队列头(越晚声明的 callback 越先执行);

skip_before_action 从队列中剔除某 callback .

class UsersController < ApplicationController
  append_before_action :m1
  before_action :m2
  append_before_action :m3
  before_action :m4, :m5
  before_action :m6
  append_before_action :m7
  prepend_before_action :m10, :m11
  before_action :m8, :m9
  skip_before_action :m8

  def index
    render json: User.all
  end

  private

  15.times do |i|
    define_method("m#{i}") do
      p __method__
    end
  end
end

Started GET "/users" for ::1 at 
   (0.1ms)  SELECT sqlite_version(*)
Processing by UsersController#index as HTML
:m11
:m10
:m1
:m2
:m3
:m4
:m5
:m6
:m7
:m9
  User Load (0.1ms)  SELECT "users".* FROM "users"
  ↳ app/controllers/users_controller.rb:13:in `index'
Completed 200 OK in 6ms (Views: 4.4ms | ActiveRecord: 0.9ms | Allocations: 4236)

包含模块的情况

include 的模块跟类公用一个队列 (其实关键在 ActiveSupport::Concernincluded 的实现).

根据代码的执行顺序, 依照之前的规则推定就不会有错啦, 以下 DEMO :

module ConcernDemo
  extend ActiveSupport::Concern

  included do
    append_before_action :module_m1
    before_action :module_m2
    append_before_action :module_m3
    before_action :module_m4, :module_m5
    include ConcernDemo
    before_action :module_m6
    append_before_action :module_m7
    prepend_before_action :module_m10, :module_m11
    before_action :module_m8, :module_m9
    skip_before_action :module_m8
  end

  private

  15.times do |i|
    define_method("module_m#{i}") do
      p __method__
    end
  end
end

class UsersController < ApplicationController
  append_before_action :m1
  before_action :m2
  append_before_action :m3
  before_action :m4, :m5
  
  include ConcernDemo
  
  before_action :m6
  append_before_action :m7
  prepend_before_action :m10, :m11
  before_action :m8, :m9
  skip_before_action :m8

  def index
    render json: User.all
  end

  private

  15.times do |i|
    define_method("m#{i}") do
      p __method__
    end
  end
end

output:

Started GET "/users" for 127.0.0.1 at 
   (0.0ms)  SELECT sqlite_version(*)
Processing by UsersController#index as HTML
:m11
:m10
:module_m11
:module_m10
:m1
:m2
:m3
:m4
:m5
:module_m1
:module_m2
:module_m3
:module_m4
:module_m5
:module_m6
:module_m7
:module_m9
:m6
:m7
:m9
  User Load (0.2ms)  SELECT "users".* FROM "users"
  ↳ app/controllers/users_controller.rb:16:in `index'
Completed 200 OK in 13ms (Views: 10.6ms | ActiveRecord: 0.8ms | Allocations: 4457)

prepend_before_action 替换顺序之后:

class UsersController < ApplicationController
  append_before_action :m1
  before_action :m2
  append_before_action :m3
  before_action :m4, :m5
  before_action :m6
  append_before_action :m7
  prepend_before_action :m10, :m11

  include ConcernDemo

  before_action :m8, :m9
  skip_before_action :m8

  def index
    render json: User.all
  end

  private

  15.times do |i|
    define_method("m#{i}") do
      p __method__
    end
  end
end

output:

Started GET "/users" for 127.0.0.1 at 
   (0.1ms)  SELECT sqlite_version(*)
Processing by UsersController#index as HTML
:module_m11
:module_m10
:m11
:m10
:m1
:m2
:m3
:m4
:m5
:m6
:m7
:module_m1
:module_m2
:module_m3
:module_m4
:module_m5
:module_m6
:module_m7
:module_m9
:m9
  User Load (0.2ms)  SELECT "users".* FROM "users"
  ↳ app/controllers/users_controller.rb:16:in `index'
Completed 200 OK in 7ms (Views: 4.5ms | ActiveRecord: 0.8ms | Allocations: 4395)