【问题标题】:How can I stop meta_search from defining search on my models?如何阻止 meta_search 在我的模型上定义搜索?
【发布时间】:2012-05-26 14:18:08
【问题描述】:

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

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

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

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

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

# 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 如何在类尚未实际加载时检测是否已定义搜索存在错误。万岁!

【问题讨论】:

  • meta_search 是什么版本的?
  • 这是 meta_search (1.1.3)

标签: ruby-on-rails meta-search tire


【解决方案1】:

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

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

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

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

【讨论】:

  • 你对定义搜索的定义是对的——当我自己定义它时,我忘了把它变成一个类方法。
  • 虽然我已经添加到问题中 - 不幸的是,meta_search 对搜索是否已经定义的检测似乎以某种方式被破坏了
【解决方案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 中,您在元搜索之前预加载文档模型,以便 Tyre 定义搜索。现在要求元搜索只会对新加载的类产生影响。

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

# 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

这有帮助吗?

【讨论】:

  • 感谢您的回答并解释发生了什么:) - 我尝试实施第一个建议,因为我不希望搜索被别名为元搜索(只有使用它是 active_admin) - 但我收到一个错误:application.rb:11:in remove_method': method search' not defined in Class (NameError). 如果我删除它并在控制台中尝试:1.9.3p125 :004 > ActiveRecord::Base.methods.include? :search #=> true 1.9.3p125 :005 > ActiveRecord::Base.send(:remove_method, :search) NameError: method search' not defined in ActiveRecord::Base`
  • 您正在尝试删除实例方法,我们需要类方法。在响应中查看我的
  • 你可以试试 ActiveRecord::Base.instance_eval { undef :search }
  • 好的 - 抱歉,这可能会有点混乱!起初我确实有你的建议,其中 application.rb:13: in 'remove_method': method search' 未在 Class (NameError)` 中定义的原因。但是,您对 instance_eval 的建议效果很好 - 我把它放在我的 application.rb 中并且它正在工作。 Ruby 有时奇怪 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多