3

我正在使用 active_admin 并将 meta_search 引入我的项目。(我不想用于其他任何事情)。

它似乎在我所有的模型上定义了搜索方法,这意味着当我包含轮胎时,我不能使用它的搜索方法。

它如何定义方法似乎也有些奇怪 - method_defined?说搜索方法没有定义,但是当我调用它时,我得到了 meta_search 一个。即使我在类中定义了自己的搜索方法,当我调用 Document.search 时,我仍然会得到 meta_search。

编辑:我会对处理这类事情的一般方法感兴趣 - 我已经通过使用 Model.tire.search 解决了这个特殊问题(因为轮胎也可以通过这种方式访问​​),但我仍然讨厌我的宝石我什至不使用都可以迫使我在项目的其余部分中使用解决方法。

编辑:我不知道在答案的答案中包含代码块的好方法,所以我会把它放在这里。

# Meta_search loaded, tire is not
1.9.3p125 :001 > require "tire"   #=> true
1.9.3p125 :002 > Document.send(:include, Tire::Model::Search)
=> Document(...)
1.9.3p125 :003 > Document.search
  Document Load (2.1ms)  SELECT "documents".* FROM "documents" 
  # I get meta_search, as I should


# Tire loaded (and the include Tire::Model::Search is inside the class definition), meta_search is not loaded
1.9.3p125 :001 > Document.search
# I get tire, as I should
1.9.3p125 :002 > require "meta_search"   #=> true
1.9.3p125 :003 > Document.search
# I still get tire, all is well

# Tire loaded, meta_search is not loaded
1.9.3p125 :001 > require "meta_search"   #=> true
1.9.3p125 :002 > Document.search
  Document Load (1.8ms)  SELECT "documents".* FROM "documents" 
# I get meta_search, even though Document.search was already defined!

# Tire loaded, meta_search is not loaded, RAILS_ENV="production"
Loading production environment (Rails 3.2.2)
1.9.3p125 :001 > require "meta_search"
=> true 
1.9.3p125 :002 > Document.search
# I get tire!

我对此的解释是,meta_search 如何在类尚未实际加载时检测是否已定义搜索存在错误。万岁!

4

2 回答 2

6

相关的2行:

https://github.com/ernie/meta_search/blob/master/lib/meta_search.rb#L55

https://github.com/ernie/meta_search/blob/master/lib/meta_search/searches/active_record.rb#L46

我不认为这是一个错误,事情就是这样。

在场景 3 中,在开发环境中,您不预加载模型。当您需要 'meta_search' 时,它将在ActiveRecord::Base. 然后你说Document,加载模型,将首先继承定义的搜索方法,所以当它包含轮胎搜索模块时,它会将搜索别名为元搜索。

在生产模式(场景 4)和场景 2 中,您在元搜索之前预加载文档模型,因此 Tire 将定义搜索。现在要求元搜索只会对新加载的类产生影响。

您可以看到定义 gem 的顺序不计算在内。但是您可以在 gem 要求之后取消定义搜索方法。

# application.rb
# ...
Bundler.require(:default, Rails.env) if defined?(Bundler)
# now move search out of the way
ActiveRecord::Base.instance_eval { undef :search }

所以以后我们加载一个模型类并包含轮胎,搜索将正确地转到轮胎,无论是在开发还是生产中。

这并不理想,因为非轮胎模型的搜索方法不会委托给元搜索,实际上它不会被定义。所以可能第二种解决方案是最好的:在这里你用一个在运行时检查轮胎的方法覆盖搜索方法:

class ActiveRecord::Base
  def self.search(*args, &block)
    if respond_to?(:tire)
       tire.search(*args, &block)
    else
       metasearch(*args, &block)
    end
  end
end

这有帮助吗?

于 2012-05-30T20:09:34.587 回答
1

简短但不令人满意的答案:将轮胎移到 Gemfile 中的 active_admin 上方。如果方法已经定义, meta_search 和轮胎都避免定义search方法,因此确保首先加载轮胎应该这样做。

或者,轮胎和 meta_search 都被加载后(例如在 Rails 初始化程序中),你可以重新定义 ActiveRecord::Base.search 来做tire.search

method_defined?检查是否定义了实例方法,而search在这种情况下是类方法。查看是否定义了类方法的唯一真正方法是Document.methods.include?(:search). 同样,当您重新定义 时search,您是否确保将其设为类方法(例如def self.search)?

不幸的是,“我的依赖项的依赖项是猴子修补我的代码”问题是 Ruby 中的一个主要烦恼,并且在某种程度上是不可避免的。图书馆作者有很大的灵活性来做他们想做的事,但他们经常滥用权力以“让事情更容易使用”。

于 2012-05-30T06:03:24.487 回答