我有一些模型代表用户可以获得的各种积分:
class User
has_many :totals
end
class Total
belongs_to :user
belongs_to :total_type
end
class TotalType
has_many :totals
end
还有points
和goals
等命名类型。我在Total:
method_missing
def method_missing(method, *args, &block)
if TotalType.find_by_name(method)
joins(:total_type).where(:total_type => { :name => method }).sum(total)
else
super
end
end
这让我做了一些甜蜜动态的东西,比如:
user = User.first
user.totals.points #=> 100
user.totals.goals #=> 10
现在困难的部分。我希望能够直接在User上调用相同的points
和goals
方法。如果我使用delegate
,那么我需要提前知道方法的名称:
class User
# ...
delegate :points, :totals, :to => :totals
end
但当然我更喜欢那也是动态的。我尝试在用户上创建method_missing
,但当我尝试将send
某些内容发送到totals
时,我得到NoMethodError: undefined method 'points' for #<Array:0x007fd2016e1510>
所以显然关联代理已将结果集转换为数组(遗漏了super
以保持示例简单):
class User
#...
def method_missing(method, *args, &block)
totals.send(method, *args, &block)
end
end
user = User.first
user.totals.points #=> 100
user.points #=> NoMethodError: undefined method 'points' for #<Array:0x007fd2016e1510>
如何将此调用转发到totals
,而无需对这些动态查找器名称进行硬编码?任何帮助将不胜感激,谢谢!
答案 0 :(得分:1)
是的,使用named_scope(或类方法)是最好的选择。
例如,如果这是totals.rb:
class Total < ActiveRecord::Base
belongs_to :user
belongs_to :total_type
def self.points
joins(:total_type).where(:total_types => {:name => 'points'}).sum(:total)
end
end
这是users.rb:
class User < ActiveRecord::Base
has_many :totals
def method_missing(name, *args, &blk)
if totals.respond_to? name
totals.send(name, *args, &blk)
else
super
end
end
end
然后User.first.points#=&gt; 100
你可以让它变得更加动态......但代码更难以遵循,如果你不缓存一些东西,那么就会执行大量不必要的SQL查询。例如,total.rb可能是这样的:
class Total < ActiveRecord::Base
belongs_to :user
belongs_to :total_type
def self.method_missing(name, *args, &blk)
if TotalType.find_by_name(name.to_s)
joins(:total_type).where(:total_types => {:name => name.to_s}).sum(:total)
else
super
end
end
def self.respond_to_missing?(name, priv)
if TotalType.find_by_name(name.to_s)
true
else
super
end
end
end
因此,当调用User.first.points时,首先调用User#method_missing,看看user.totals.respond_to? :点。它起初并没有,所以调用Total :: respond_to_missing,看看是否有一个名为&#39; points&#39;的total_type。有...所以它被调用然后调用Total :: method_missing,它会返回该用户的点数之和。
当然,您可以轻松地缓存结果,因此每次在method_missing和respond_to_missing方法中调用user.points时都不会执行不必要的SQL查询,但它只是变得过于复杂而且方法开销不是很高。值得。我之前提供的不那么动态的解决方案是你最好的选择,现在你的模型如何布局。
希望这有帮助,
路加福音
答案 1 :(得分:0)
如果关注动态,像by_total_type
这样的命名范围可能会解决问题。
class Total
scope :by_total_type, lambda { |ttype| joins(:total_type).where(:type => ttype) }
end
然后你可以说:
Total.by_total_type('point')
或者
user.totals.by_total_type('goal')