Last active
December 26, 2015 17:48
-
-
Save plusplus/7189147 to your computer and use it in GitHub Desktop.
Using ActiveModel::Validations in service objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Thoughts on using active model validations for service objects... | |
# | |
# NOTE: I edited this code in the GIST - there are probably errors. I even converted | |
# to old hash syntax for Pat and Warren. | |
# | |
# So this is a service or command object, something straight out of a method object refactoring. | |
# You create it, then execute it (call `call`). I've used `ActiveModel::Validations` to define validations | |
# that need to be met before the command is run. | |
# | |
# It would be perfectly reasonable to define call so it calls valid first, and returns true or false | |
# based on whether the command was run or not in a fashion more closely mimicking active record `save`. | |
# I didn't do it in this instance because I wanted to get the created payment out of the call, but | |
# there might be other ways to do that (pay it forward etc). | |
# | |
# Example use (say in a controller) | |
# | |
# charge_balance = ChargeOrderBalance.new(order: order, payment_method: payment_method) | |
# if charge_balance.valid? | |
# @payment = charge_balance.() | |
# else | |
# # do something with charge_balance.errors | |
# end | |
# | |
# Performs a token based payment for the nominated payment method | |
# on the nominated order, for the nominated amount (or the balance) | |
# | |
class ChargeOrderBalance | |
include ActiveModel::Validations | |
validates_presence_of :authorisation_for_gateway | |
validates_numericality_of :amount_cents, greater_than: 0 | |
validate :validate_payment_method_shop | |
validate :validate_payment_method_is_active | |
attr_accessor :order, :amount_cents, :payment_method, :user | |
def initialize(attrs) | |
@order = attrs.fetch(:order) | |
@payment_method = attrs.fetch(:payment_method) | |
@amount_cents = attrs[:amount_cents] || order.balance | |
@user = attrs[:user] | |
end | |
def call | |
create_payment.tap {|payment| payment.invoke_and_update} | |
end | |
private | |
def create_payment | |
OnlinePayment.create!(payment_attributes) | |
end | |
def payment_attributes | |
{ | |
:order => order, | |
:shop => order.shop, | |
:amount_cents => amount_cents, | |
:payment_method => payment_method, | |
:gateway_token_to_use => authorisation_for_gateway, | |
:invoice_reference => order.best_order_number, | |
:approved_by => user | |
} | |
end | |
def authorisation_for_gateway | |
order.authorisation_for(gateway_name) | |
end | |
def gateway_name; payment_method.gateway_name end | |
def validate_payment_method_shop | |
if order.shop != payment_method.shop | |
errors[:payment_method] = "doesn't belong to same shop as order!" | |
end | |
end | |
def validate_payment_method_is_active | |
errors[:payment_method] = "is deactivated" if payment_method.deactivated? | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment