我有一个界面定义了一组条件。它是几个这样的接口之一,将与其他模型一起使用。
这些条件将由消息队列处理程序调用,以确定警报的完整性。所有的警报调用都是一样的,因此我通过将条件抽象到他们自己的方法中来寻求干扰排队调用(我怀疑方法是否是正确的技术)。我认为通过这样做,我将能够测试这些条件。
class Loan
module AlertTriggers
def self.included(base)
base.extend LifecycleScopeEnqueues
# this isn't right
Loan::AlertTriggers::LifecycleScopeEnqueues.instance_method.each do |cond|
class << self
def self.cond
::AlertHandler.enqueue_alerts(
{:trigger => Loan.new},
cond
)
end
end
end
end
end
module LifecycleScopeEnqueues
def student_awaiting_cosigner
lambda { |interval, send_limit, excluding|
excluding ||= ''
Loan.awaiting_cosigner.
where('loans.id not in (?)', excluding.map(&:id) ).
joins(:petitions).
where('petitions.updated_at > ?', interval.days.ago).
where('petitions.updated_at <= ?', send_limit.days.ago)
}
end
end
我已经考虑了替代方案,其中每种方法都像一个范围。在这条路上,我不确定如何让AlertHandler
成为interval
,send_limit
和excluding
的来源,它会在调用时传递给块/ proc它。
有人向我(离线)建议范围是lambda,因此可能是更合适的解决方案 - 根据@BorisStitnicky的推断,钳子可以用作锤子,但是不应该。我也愿意接受这条线的答案。
答案 0 :(得分:2)
你知道,这可能不是你寻求的答案,但我会尽我所能。这样回答比删除评论更好。在你的代码中,你正在做一些非常不寻常的事情。首先,您要在类中定义一个模块。我以前从来没有做过或看过这个,所以我打了irb来尝试一下。其次,您正在定义一个返回lambda的方法。当我刚刚学习Ruby时,这让我想起了很多我正在做的事情。 Lambdas具有非常特定的应用程序,应尽可能以这种形式避免使用。如果你想使用这样的lambdas,至少将它们分配给一个变量,或者更好,一个常量:
STUDENT_AWAITING_COSIGNER = lambda { |interval, send_limit, excluding|
# do your SQL magic
}
我在理解你的词汇时遇到了麻烦:特别是,我不确定你所谓的“范围”是一个Ruby范围,还是其他一些范围。
但就个人而言,我认为你根本不应该使用lambdas。我敢说,你的代码需要的不仅仅是干一点。我认为你不应该在类中设置子名称空间。你为什么不用eg。一个实例变量?此外,公共类方法只是馅饼上的一个樱桃。首先解决没有它们的问题,然后您可以决定添加它们以使您的界面更方便。总而言之,我只是沿着这些方向做点什么:
class Loan
attr_reader :alerts
def initialize( whatever_options )
@alerts = Array( whatever_options[ :alerts ] )
end
def check_alerts
@alerts.each &:test
end
end
# Then I would set up an alert class:
class Alert
def test( interval, send_limit, excluding = '' )
Loan.awaiting_cosigner.
where('loans.id not in (?)', excluding.map(&:id) ).
joins(:petitions).
where('petitions.updated_at > ?', interval.days.ago).
where('petitions.updated_at <= ?', send_limit.days.ago)
end
end
# I used your prescription statically, if you have different kind
# of alerts, you would have to make the class sufficiently flexible
# to handle them all.
# and then I would eg. supply alerts to a Loan upon instantiation
# (this can be also done later, if you make it so)
my_little_loan = Loan.new( alerts: Alert.new )
# and when the time comes to check whether the alerts alert or not:
my_little_loan.check_alerts
显然,这只是我如何谦虚地认为应该在Ruby中简单地解决这些问题的概述。在特定复杂的情况下,你需要自己努力使它适合你。
答案 1 :(得分:1)
处理此问题的一种方法是使用域中其他模型/部分所期望的(或显示的)命名空间(<模块内)。
在这种情况下,AlertHandler
不需要传递一个块。相反,它可以知道命名空间LifecycleScopeEnqueues
的存在(相反,它可能会更多地将可操作地读作Lifecycle_EnqueuingScopes
)。因此,无论AlertHandler.enqueue_alerts
内发生了什么:
class AlertHandler
def enqueue_alerts(options, condition)
trigger = options[:trigger]
handler = options[:trigger_handler].capitalize
interval, send_limit, excluding = handler_metrics(handler, condition)
range = "#{trigger.class.name}".constantize.send(condition, [interval, send_limit, excluding])
# do other things
end
end
所有这些范围的提醒仍然可以排队&#39;通过一种反思方法(与问题中的代码混合添加)
class Loan
module AlertTriggers
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def enqueue_lifecycle_reminders
Loan::AlertTriggers::LifecycleScopeEnqueues.instance_method.each do |cond|
::AlertHandler.enqueue_alerts(
{:trigger => Loan.new},
cond
)
end
end
end
end
end
此方法还允许通过以下方式测试Loan::AlertTriggers::LifecycleScopeEnqueues
中的范围/条件: