2

条件のグループを定義するインターフェースがあります。これは、他のモデルと共存するいくつかのそのようなインターフェイスの 1 つです。

これらの条件は、アラートの完全性を判断するためにメッセージ キュー ハンドラーによって呼び出されます。すべてのアラート呼び出しは同じであるため、条件を独自のメソッドに抽象化することで、エンキュー呼び出しを少し乾かそうとします (メソッドが正しい手法であるかどうかは疑問です)。これを行うことで、これらの各条件をテストできるようになると思います。

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


スコープはラムダであることが(オフラインで)提案されたため、より適切な解決策になる可能性があります-@BorisStitnickyの推論によると、ペンチはハンマーとして使用できますが、使用しないでください。私もこの線に沿った答えを受け入れています。

4

2 に答える 2

2

これはあなたが求めている答えではないかもしれませんが、最善を尽くします。コメントをドロップするよりも、このように回答する方が適切です。あなたのコードでは、いくつかの非常に珍しいことを行っています。まず、クラス内でモジュールを定義しています。私はこれを行ったことも見たこともなかったので、irb を試してみました。次に、ラムダを返すメソッドを定義しています。それは、私が Ruby を学んでいた頃に何をしてきたかを思い出させてくれます。ラムダには非常に特殊なアプリケーションがあり、通常、この形式では可能な限り避ける必要があります。このようにラムダを使用したい場合は、少なくともそれらを変数に割り当てるか、定数に割り当ててください。

STUDENT_AWAITING_COSIGNER = lambda { |interval, send_limit, excluding|
  # do your SQL magic
}

私はあなたの語彙を理解するのに苦労しています: 特に、あなたが「スコープ」と呼んでいるものは Ruby スコープなのか、それとも他の種類のスコープなのかわかりません。

しかし、個人的には、ラムダを使用するべきではないと思います。私は、あなたのコードには、少し DRY するだけでなく、それ以上のものが必要であるとあえて言います。クラスにサブ名前空間を設定するべきではないと思います。たとえば、使用しないのはなぜですか。インスタンス変数?また、パブリック クラス メソッドは、ほんの一部です。最初にそれらなしで問題を解決してから、それらを追加してインターフェイスをより便利にすることができます。全体として、私は単にこれらの行に沿って何かをするだけです:

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でこの種の問題をシンプルに解決する必要があると私が謙虚に考える方法の概要にすぎません。特定の複雑なケースでそれを機能させるには、独自の努力をする必要があります。

于 2012-09-05T22:06:02.427 に答える
1

これを処理する1つの方法は、ドメインの他のモデル/部分によって期待される(または明らかにされる)名前空間(モジュール内)を使用することです。

この場合AlertHandler、ブロックを渡す必要はありません。代わりに、名前空間の存在を知ることができます(代わりに、より実用的LifecycleScopeEnqueues読むことができます)。したがって、内部で起こっていることは何でも:Lifecycle_EnqueuingScopesAlertHandler.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

これらすべてのスコープのアラートは、1つのリフレクティブメソッドを介して「エンキュー」することができます(質問のコードと追加混合)

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

  • メソッドごと
  • ダックタイピング
于 2012-08-29T13:29:44.537 に答える