如何阻止meta_search在我的模型上定义搜索?

时间:2012-05-19 04:15:29

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

我正在使用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如何检测搜索是否已定义。万岁!

2 个答案:

答案 0 :(得分: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需要之后取消定义搜索方法。

# 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

这有帮助吗?

答案 1 :(得分:1)

简短但不满意的答案:在Gemfile中将轮胎移到active_admin上方。如果已经定义了search方法,meta_search和轮胎都会避免定义tire.search方法,因此首先要确保轮胎已加载。

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

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

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