Rails 装饰器方法未在生产中调用,在开发中有效

Rails decorator method not being called in production, works in development

我为从 gem (Spree) 继承的控制器添加以下覆盖:

module Spree
  module Admin
    UsersController.class_eval do
      def index
        if params[:role].present?
          included_users = Spree::User.joins(:role_users).
              where( spree_role_users: { role_id: params[:role] } ).map(&:id)
          flash[:notice] = "Filtered in #{included_users.count} users"
          @users = @users.where(id: included_users)
        end
      end
    end
  end
end

基本上,它通过 Admin::UsersController 控制器上的附加参数进行过滤。 gem 中那个控制器的源实际上并没有定义 index 方法,所以我的只是被调用。

现在,这在开发中非常有效。但是,在生产中,此方法永远不会被调用。

有什么关于 class_eval 的事情我没有说到这里吗?这样的事情不应该在生产中和在开发中基本一样吗?

感谢您的帮助。

装饰器是包装另一个对象的对象。例如,它们通常用于包装具有表示逻辑的模型。

class UserDecorator < SimpleDelegator
  def full_name
    "#{first_name} #{last_name}"
  end
end

> @user = UserDecorator.new(User.new(first_name: 'John', last_name: 'Doe'))
> @user.full_name
=> "John Doe"

这不是装饰器方法 - 您只是重新打开 class 并添加一个方法。这被称为 monkey-patching.

在这种情况下使用 class_eval 与使用 class 关键字完全相同:

module Spree
  module Admin
    class UsersController
      def index
        if params[:role].present?
          included_users = Spree::User.joins(:role_users).
              where( spree_role_users: { role_id: params[:role] } ).map(&:id)
          flash[:notice] = "Filtered in #{included_users.count} users"
          @users = @users.where(id: included_users)
        end
      end
    end
  end
end

使用 monkey-patches 的关键是确保您的 class 重新定义被读取。我猜 dev 和 production 之间的区别是由于 class catching 阻止 class 从 /app 读取,如果它已经被 Spree gem 定义. config/application.rb 使用捆绑器在应用程序启动时要求所有 gem。

您可以通过将猴子补丁放在 config/initializers 中来确保加载它,因为该目录中的所有文件都在启动时加载。

但是 monkeypatching 的一个更好的替代方法可能是代替 class 供应商控制器和 route 它:

class MyUsersController < ::Spree::Admin::UsersController
   def index
     if params[:role].present?
       included_users = Spree::User.joins(:role_users).
         where( spree_role_users: { role_id: params[:role] } ).map(&:id)
       flash[:notice] = "Filtered in #{included_users.count} users"
       @users = @users.where(id: included_users)
    end
  end
end

另见: