【问题标题】:Rails3: HOWTO Override/Reopen a class within a Gem and the Rails initialization processRails3:如何在 Gem 和 Rails 初始化过程中覆盖/重新打开一个类
【发布时间】:2010-11-05 16:22:11
【问题描述】:

我的问题与这个问题非常相似:How do I add a method to a ruby gem without editing the gem source?。但是,这个问题已经有将近一年的历史了,并且选择的解决方案并不是最干净的,至少对我来说不是。

提供答案的人提供了 3 条建议。第一个建议被选为答案,但我真的很想弄清楚如何做第二种方法。

我需要重写一个由 Gem 定义的类的实例方法。更具体地说,它是SessionSerializer class in 1.1.2 Devise。问题是 Devise 不尊重非标准的主键名称。它总是使用id。你可以看到在warden_compat.rb on Line 30,它使用下面的方法通过它的ID来查找模型:

klass.constantize.find(:first, :conditions => { :id => id })

在我的例子中,我的id 列的名称是application_user_id,所以很明显这不起作用。 Devise 已在 1.1.3 中修复了此问题,但是我无法使用 1.1.3,因为 Devise LDAP Authenticatable 插件不支持 1.1.3。

所以这就是我所做的。我首先应该提到的是,我通过直接编辑 Gem 源代码来测试此修复,所以现在我只想将它移到我的项目中。

  1. lib/warden/(即lib/warden/session_serializer.rb)中创建session_serializer.rb文件,重新打开Warden::SessionSerializer类,并重新定义deserialize方法。
  2. 修改了application.rb 以在config.autoload_paths 中包含lib/

    config.autoload_paths += ["#{config.root}/lib"]
    

但是,这似乎并不能解决问题。它仍在使用 Gem 源代码中定义的相同代码。所以我有几个问题希望能得到解答:

问题

  1. 我在这里做错了什么?
  2. Rails 是在 Gems 之前加载 config.autoload_paths 中定义的路径的文件,还是相反?

提前感谢您的帮助!

lib/warden/session_serializer.rb

module Warden

  class SessionSerializer
    def deserialize(keys)
      klass, id = keys

      if klass.is_a?(Class)
        raise "Devise changed how it stores objects in session. If you are seeing this message, " <<
          "you can fix it by changing one character in your cookie secret, forcing all previous " <<
          "cookies to expire, or cleaning up your database sessions if you are using a db store."
      end
      # NOTE: Original line code. Notice that it uses an :id symbol. It doesn't respect the primary key that explicity defined in the model
      # klass.constantize.find(:first, :conditions => { :id => id })
      # NOTE: THIS IS THE FIX
      klass.constantize.find(:first, :conditions => { :application_user_id => id })
    rescue NameError => e
      if e.message =~ /uninitialized constant/
        Rails.logger.debug "Trying to deserialize invalid class #{klass}"
        nil
      else
        raise
      end
    end
  end

end

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-3 overriding


    【解决方案1】:

    我会在initializers 目录中创建一个名为warden.rb 的文件,并将猴子补丁代码放入该文件中。我经常在我的项目中使用这种技术来修补 gem。

    要将补丁放在 lib 目录下,请执行以下操作:

    config.autoload_paths += ["#{config.root}/lib/warden"]
    

    PS:我知道你已经尝试过了,但看起来你的路径不正确。

    PPS 要了解 Rails 2.3 加载顺序,请参阅 this code

    【讨论】:

    • 感谢神田!这是我的后腰解决方案。我知道这会奏效——它在我链接的原始问题中——但当涉及到这些事情时,我有点坚持。如果事实证明我正在尝试做的事情无法完成,我会给你解决方案。 :)
    • 更新了我的答案看看。
    • 糟糕,这肯定是我的 autoload_paths 中的错字!就像你上面所说的那样。让我更新一下。
    • 对不起,这是我的错误。只是lib。我应该解释为什么我只指定lib 而不是lib/warden 背后的原因。我实际上在这里stackoverflow.com/questions/4018757/… 询问了一个关于Rails 自动加载机制的问题。基本上,只要我的模块/类定义正确,我就不必指定lib/warden。所以也许他们不是?
    • config.autload_paths 中缺少一个 o
    【解决方案2】:

    【讨论】:

    • 感谢您的帮助!我确实读过这个,但它没有关于config.autoload_paths 的任何内容。我承认我关于初始化的问题是模棱两可的,但更多的是为后续问题奠定基础:Gems 在lib 之前加载吗?这意味着:config.autoload_paths 中的文件何时加载?如果我应用一些逻辑,似乎 lib 在 Gems 之前加载,因为我没有看到我的补丁生效。也可能是我的 config.autoload_path 错误,或者我的 lib 布局和模块/类定义不符合 Rails 启用自动加载的约定。
    • 感谢您的帮助。我将更新我的初始化问题以使其更清晰。
    • 是的,但您似乎已选择autoload_paths 作为解决方案,现在正努力实现这一目标。我希望传达的是 Rails 初始化的有序记录方式。如果你愿意的话,你在这个过程中有一些点可以保证一定程度的“安全”。如果您想知道什么时候加载了什么,只需在其中一个 gem 和一个初始化程序中执行 put,这样您就可以看到它们何时加载。
    猜你喜欢
    • 1970-01-01
    • 2011-05-26
    • 1970-01-01
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 2012-10-22
    相关资源
    最近更新 更多