我可以将方法用作lambda吗?

时间:2012-08-27 20:59:07

标签: ruby class methods lambda extending

我有一个界面定义了一组条件。它是几个这样的接口之一,将与其他模型一起使用。

这些条件将由消息队列处理程序调用,以确定警报的完整性。所有的警报调用都是一样的,因此我通过将条件抽象到他们自己的方法中来寻求干扰排队调用(我怀疑方法是否是正确的技术)。我认为通过这样做,我将能够测试这些条件。

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成为intervalsend_limitexcluding的来源,它会在调用时传递给块/ proc它。


有人向我(离线)建议范围是lambda,因此可能是更合适的解决方案 - 根据@BorisStitnicky的推断,钳子可以用作锤子,但是不应该。我也愿意接受这条线的答案。

2 个答案:

答案 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中的范围/条件:

  • 按方法
  • 鸭打字