Skip to content

Instantly share code, notes, and snippets.

@gmmoreira
Last active March 1, 2019 02:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmmoreira/5b8fbcb2b52c689c4f63f48bcc90e531 to your computer and use it in GitHub Desktop.
Save gmmoreira/5b8fbcb2b52c689c4f63f48bcc90e531 to your computer and use it in GitHub Desktop.
class Payment
attr_reader :authorization_number, :amount, :invoice, :order, :payment_method, :paid_at
def initialize(attributes = {})
@authorization_number, @amount = attributes.values_at(:authorization_number, :amount)
@invoice, @order = attributes.values_at(:invoice, :order)
@payment_method = attributes.values_at(:payment_method)
end
def pay(paid_at = Time.now)
@amount = order.total_amount
@authorization_number = Time.now.to_i
@invoice = Invoice.new(billing_address: order.address, shipping_address: order.address, order: order)
@paid_at = paid_at
order.close(@paid_at)
end
def paid?
!paid_at.nil?
end
end
class Invoice
attr_reader :billing_address, :shipping_address, :order
def initialize(attributes = {})
@billing_address = attributes.values_at(:billing_address)
@shipping_address = attributes.values_at(:shipping_address)
@order = attributes.values_at(:order)
end
end
class Order
attr_reader :customer, :items, :address, :closed_at
attr_accessor :payment
def initialize(customer, overrides = {})
@customer = customer
@items = []
@order_item_class = overrides.fetch(:item_class) { OrderItem }
@address = overrides.fetch(:address) { Address.new(zipcode: '45678-979') }
end
def add_product(product)
@items << @order_item_class.new(order: self, product: product)
end
def total_amount
@items.map(&:total).inject(:+)
end
def close(closed_at = Time.now)
@closed_at = closed_at
end
end
class OrderItem
attr_reader :order, :product
def initialize(order:, product:)
@order = order
@product = product
end
def total
10
end
end
class Product
# use type to distinguish each kind of product: physical, book, digital, membership, etc.
attr_reader :name, :type
def initialize(name:, type:)
@name, @type = name, type
end
end
class Address
attr_reader :zipcode
def initialize(zipcode:)
@zipcode = zipcode
end
end
class CreditCard
def self.fetch_by_hashed(code)
CreditCard.new
end
end
class Customer
# you can customize this class by yourself
end
class Membership
# you can customize this class by yourself
end
# Book Example (build new payments if you need to properly test it)
foolano = Customer.new
book = Product.new(name: 'Awesome book', type: :book)
book_order = Order.new(foolano)
book_order.add_product(book)
payment_book = Payment.new(order: book_order, payment_method: CreditCard.fetch_by_hashed('43567890-987654367'))
order.payment = payment_book
payment_book.pay
p payment_book.paid? # < true
p payment_book.order.items.first.product.type
# now, how to deal with shipping rules then?
ProcessPaidOrder.new(payment).call
class ProcessPaidOrder
attr_reader :payment
def initialize(payment)
@payment = payment
end
def call
payment.order.items.each do |item|
item_processor_for(item.product).call
end
end
private
def item_processor_for(item)
case item.product.type
when :physical then PhysicalProductProcessor.new(item, shipping_label_printer_class: ShippingLabelPrinter)
when :book then BookProductProcessor.new(item, shipping_label_printer_class: BookShippingLabelPrinter)
when :membership then MembershipProductProcessor.new(item)
when :digital then DigitalProductProcessor.new(item)
else
raise ArgumentError, "Unsupported type #{item.product.type}"
end
end
end
class PhysicalProductProcessor
attr_reader :order_item
def initialize(order_item, shipping_label_printer_class: ShippingLabelPrinter)
@order_item
end
def call
puts shipping_label_printer.call
end
def shipping_label_printer
shipping_label_printer_class.new(order_item)
end
end
class ShippingLabelPrinter
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
"Envio do produto #{product.name}\n" +
"Endereço: #{order_item.order.address}"
end
end
class BookShippingLabelPrinter < ShippingLabelPrinter
def call
"#{super}\n" +
"Produto isento de imposto conforme disposto na Constituição Art 150, Vl, d."
end
end
class MembershipProductProcessor
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
membership_activator.call
email_sender.call
end
private
def membership_activator
MembershipActivator.new(order_item).call
end
def email_sender
MembershipEmailSender.new(order_item).call
end
end
class MembershipActivator
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
puts "Ativando a assinatura de #{order_item.product.name}..."
end
end
class MembershipEmailSender
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
puts "Sua assinatura de #{order_item.product.name} foi ativada com sucesso!"
end
end
class DigitalProductProcessor
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
email_sender.call
voucher_creator.call
end
def email_sender
DigitalEmailSender.new(order_item).call
end
def voucher_creator
VoucherCreator.new(amount: BigDecimal(10), customer: order_item.order.customer)
end
end
class DigitalEmailSender
attr_reader :order_item
def initialize(order_item)
@order_item
end
def call
puts "Parabéns, você adquiriu um #{order_item.product.name}!"
end
end
class VoucherCreator
attr_reader :amount, :customer
def initialize(amount:, customer:)
@amount = amount
@customer = customer
end
def call
customer.vouchers << Voucher.new(amount)
end
end
class Voucher
attr_reader :amount
def initialize(amount)
@amount = amount
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment