Skip to content

Instantly share code, notes, and snippets.

@plusplus
Last active December 26, 2015 17:48
Show Gist options
  • Save plusplus/7189147 to your computer and use it in GitHub Desktop.
Save plusplus/7189147 to your computer and use it in GitHub Desktop.
Using ActiveModel::Validations in service objects.
# 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