私は数年間、大規模なRuby on Railsアプリケーションに取り組んできました。それは貧しい状態で受け継がれましたが、生産バグのほとんどは時間とともに解決されました。支払い処理コードなど、変更されていないセクションがあります。このコードはほとんどの部分で機能しますが、支払い処理業者によって請求が拒否された場合は常に、ユーザーに役立つメッセージではなく500エラーが表示されます。保守を容易にするためにコードをリファクタリングしたいと思います。それがどのように機能するかの簡単な概要を提供します。
次のスニペットからすべてのエラー処理コードを削除しました。
迷路はコントローラーで始まります:
def submit_credit_card
...
@credit_card = CreditCard.new(params[:credit_card].merge(:user => @user))
@credit_card.save
...
@submission.do_initial_charge(@user)
...
end
次に、Submission
モデルで:
def do_initial_charge(user)
...
self.initial_charge = self.charges.create(:charge_type => ChargeType.find(1), :user => user)
self.initial_charge.process!
self.initial_charge.settled?
end
ではCharge
モデル:
aasm column: 'state' do
...
event :process do
transitions :from => [:created, :failed], :to => :settled, :guard => :transaction_successful?
end
...
end
def initialize(*params)
super(*params)
...
self.amount = self.charge_type.amount
end
def transaction_successful?
user.reload
credit_card = CreditCard.where(user_id: user_id).last
cct = self.cc_transactions.build(:user => user, :credit_card => credit_card, :cc_last_four => credit_card.num_last_four, :amount => amount, :charge_id => id)
cct.process!
if self.last_cc_transaction.success
self.update_attribute(:processed, Time.now)
return true
else
self.fail!
return false
end
end
保存したものを渡すのではなく、をリロードしuser
て最後を見つけるなど、疑問の余地が多くありますCreditCard
。また、このコードはChargeType
、ハードコードされたIDを使用してデータベースからロードされたものに依存します。
ではCcTransaction
、私たちトレイルを下り続けます。
def do_process
response = credit_card.process_transaction(self)
self.authorization = response.authorization
self.avs_result = response.avs_result[:message]
self.cvv_result = response.cvv_result[:message]
self.message = response.message
self.params = response.params.inspect
self.fraud_review = response.fraud_review?
self.success = response.success?
self.test = response.test
self.response = response.inspect
self.save!
self.success
end
このように見えるのは、cc_transactions
データベーステーブルにレコードを保存することだけです。実際の支払い処理はCreditCard
モデルで実行されます。そのクラスの詳細については退屈しません。実際の作業はによって行われActiveMerchant::Billing::AuthorizeNetCimGateway
ます。
我々は少なくとも5つのモデルが関与持っているので(Submission
、Charge
、ChargeType
、CcTransaction
、およびCreditCard
)。これを最初から行う場合は、1つのPayment
モデルのみを使用します。課金タイプは2つしかないため、これらの値をクラス変数としてハードコーディングします。クレジットカードの詳細は保存されないため、そのモデルは不要です。トランザクション情報はpayments
テーブルに保存できます。失敗した支払いを保存する必要はありません。
運用サーバーでは何も問題が発生しないという要件を除いて、このリファクタリングをかなり簡単に実行できます。冗長な各クラスには、コードベースのどこからでも呼び出すことができる多くのメソッドがあります。一連の統合テストがありますが、カバレッジは100%ではありません。
壊れないようにしながら、これをリファクタリングするにはどうすればよいですか?5つの支払いクラスを通過し、grep
すべてのメソッドを呼び出して、それらが呼び出される場所を見つけた場合、何かを見落とす可能性が高くなります。クライアントは現在のコードの実行方法にすでに慣れているため、新しいバグの導入は受け入れられません。テストカバレッジを100%に増やすこととは別に、何も壊れないという確信を持ってこれをリファクタリングする方法はありますか?
AASM::InvalidTransition: Event 'process' cannot transition from 'failed'
、失敗したトランザクションである実際のエラーをマスクする例外が原因です。間接性が非常に高いため、ユーザーに応答を返して再送信を許可するのは困難です。それは可能だと確信していますが、リファクタリングと同じくらい難しいようです。