我正在开发一个项目,需要在ActiveRecord::Relation
对象上调用非常具体的方法。这些方法无法扩展ActiveRecord::Relation
,因为Class
拥有自己的initialize
方法来确定是否应该收集对象。我已经尝试了十几种方法来处理这个问题,但由于AR
中的方法链接,我无法完成此任务。目前我使用一种方法修改了ActiveRecord::Relation
,并将其转换为:
module ActiveRecord
class Relation
def to_claim_set
exec_queries unless loaded?
ClaimSet.new(@records)
end
end
end
首先,我确信这是处理它的一种不正确的方法。其次,这导致我必须在整个应用程序中不断调用#to_claim_set
。
我希望有人可以在所有方法链接完成后协助将此作为默认返回。
我希望的是
Claim.policy_number('913006')
#=> ClaimSetObjectHere
但是我需要它支持像AR
这样的链接,以便像
Claim.policy_number('913006').by_program('Base')
#=> ClaimSetObjectHere
我还尝试在#where
内修补Claim
方法,除非我使用scope
或I链式方法,否则它会起作用ClaimSet
定义default_scoped?
。
非常感谢任何见解。至于"为什么你要这样做" 就像我说我在整个应用程序中不断调用这个方法而我需要在ClaimSet
中定义的方法功能正常。
注意:这是在rails
答案 0 :(得分:0)
好的,我最终做的是为ActiveRecord::Relation
强加一个包装器,如下所示:(为简洁起见,删除了特定的业务逻辑)
class ClaimSet
def initialize(object)
process_target(object)
# ...
end
# ...
def respond_to_missing?(method_name,include_private=false)
@target.respond_to?(method_name)
end
def method_missing(method_name, *args, &block)
if @target.respond_to?(method_name)
ClaimSet.new(@target.send(method_name,*args,&block))
else
super
end
end
private
def process_target(object)
@target = object if object.is_a?(ActiveRecord::Relation)
@target = object.target if object.is_a?(ClaimSet)
end
end
然后在Claim
班。
class Claim < ActiveRecord::Base
class << self
def where(*args)
ClaimSet.new(super(*args))
end
def localized_scope(name,proc)
scope_proc = lambda do |*args|
ClaimSet.new(proc.call(*args))
end
singleton_class.send(:define_method,name,scope_proc)
end
end
end
然后我将所有范围定义为localized_scope
,例如
localized_scope :policy_number, ->(policy_number){where(policy_number: policy_number)}
现在它总是返回ClaimSet
代替ActiveRecord::Relation
和#where
的{{1}},并支持通过#localized_scope
进行方法链接。它还删除了#method_missing
上的猴子补丁。
如果您有任何其他建议,请告诉我,因为我很乐意接受其他想法,但这暂时适用。