1

私の Rails 4 アプリには、Stripe Payments Processor との通信を処理する Service オブジェクトがあります。複数のコントローラ/モデルがその中のメソッドを利用できるように、サービス オブジェクトとして使用したいと考えています。

ただし、エラーを特定のオブジェクトに割り当てる必要があるため、Stripe API と通信するときにエラーをトラップできるようにする必要もあります。

これが私のStripeCommunicator.rbクラスのメソッドです:

def create_customer(token,object)
  customer = Stripe::Customer.create(:description => 'Accommodation', :email => object.email, :card => token)
  return customer

rescue Stripe::CardError => e
  @account.errors.add :base, e.message
  false
end

ご覧のとおり、エラーは @account オブジェクトに追加されています。別のオブジェクトを参照してエラーを表示する View を持つ別のコントローラーからこのメソッドを使用したい場合、本質的に役に立たなくなります。

何か案は?

4

1 に答える 1

3

@account最も簡単なのは、インスタンスを別の引数として渡すことです。エラーはどのモデル インスタンスにも発生します。

def create_customer(token,object,model_instance)
  Stripe::Customer.create(description: 'Accommodation', email: object.email, card: token)
  # return customer <- don't need this. whatever is last evaluated will be returned
rescue Stripe::CardError => e
  model_instance.errors.add :base, e.message
  false
end

サービス オブジェクトの代わりにコントローラーでエラー処理を行っていた場合rescue_from、コントローラーや ApplicationController などで、アクション メソッドから発生する例外を処理できるものを利用できます。次のようにします。

rescue_from Stripe::CardError, with: :add_error_message_to_base

def add_error_message_to_base(e)
  # this assumes that you set @instance in the controller's action method.
  @instance.errors.add :base, e.message
  respond_with @instance
end

またはより一般的に:

rescue_from Stripe::CardError, with: :add_error_message_to_base

def add_error_message_to_base(e)
  model_class_name = self.class.name.chomp('Controller').split('::').last.singularize
  instance_value = instance_variable_get("@#{model_class_name}")
  instance_value.errors.add :base, e.message if instance_value
  respond_with instance_value
end

rescue_fromまたは、懸念事項として、上記のいずれかを実行して、含まれているブロックにを入れることができます。

module StripeErrorHandling
  extend ::ActiveSupport::Concern

  included do
    rescue_from Stripe::CardError, with: :add_error_message_to_base
  end

  def add_error_message_to_base(e)
    # see comment above...
    @instance.errors.add :base, e.message
    respond_with @instance
  end
end

José Valim がここでconfig.exceptions_app説明しているように、ラック レベルでエラーを処理するために使用できます。

メソッドを継承するのではなく、個別のサービス クラスを使用するか、懸念/モジュールを使用することもできます。フックを介して行うこともできます。たとえば、次のようになります。

# not exactly what you were doing but just for example.
# could put in app/controller/concerns among other places.
module ActionsCreateStripeCustomer
  extend ::ActiveSupport::Concern

  included do
    around_action :create_stripe_customer
  end

  def create_stripe_customer
    # this (indirectly) calls the action method, and you will
    # set @instance in your action method for this example.
    yield
    customer = Stripe::Customer.find_or_create_by(description: 'Accommodation', email: object.email, card: token)
    # could set customer on @instance here and save if needed, etc.
  rescue Stripe::CardError => e
    if @instance
      @instance.errors.add :base, e.message
      respond_with @instance
    else
      logger.warn("Expected @instance to be set by #{self.class.name}##{params[:action]}")
      raise e
    end
  end
end

次に、コントローラーで:

include ActionsCreateStripeCustomer

before_action、などもafter_actionあります。また、モジュールをインクルードすることもできます。インスタンス メソッドが呼び出されると、インクルード クラス インスタンスが最初に呼び出され、次に最初にインクルードされたモジュールが呼び出され、次に 2 番目のモジュールがsuper if defined?(super)呼び出されます。前のメソッドを呼び出す場合は、すべての引数とブロックを自動的に挿入します。

また、インスタンスではなくモデル クラス名を取得することであれば、それも簡単です。呼び出し元のクラスが AccountStripeCommunicator で@model_classあるとすると、次の後に Account になります。

qualified_class_name = self.class.name.chomp('StripeCommunictor')
@model_class = qualified_class_name.split('::').last.singularize.constantize

あらゆる種類の可能性。

于 2013-10-08T22:03:01.147 に答える