Upgrade to Pro — share decks privately, control downloads, hide ads and more …

POROs To The Rescue - Tropical Ruby 2015

POROs To The Rescue - Tropical Ruby 2015

Being tasked with rescuing an ancient codebase and turning it into something workable and manageable is a predicament most of us have already found ourselves in. In the 10 years since we began using Ruby to write Web applications, a lot has changed concerning architectures and design patterns. How do you take your code in a time travel from the olden days to more current practices such as POROs, lean models, presenters and decorators? This talk will walk you through some easy practices that can make that trip less bumpy and allow you to survive to tell the story.

Marcelo De Polli

March 06, 2015
Tweet

Other Decks in Programming

Transcript

  1. class User attr_accessor :first_name, :last_name, :email def initialize(first_name, last_name, email)

    @first_name = first_name @last_name = last_name @email = email end def full_name "#{first_name} #{last_name}" end end
  2. class User attr_accessor :first_name, :last_name, :email def initialize(first_name, last_name, email)

    @first_name = first_name @last_name = last_name @email = email end def full_name "#{first_name} #{last_name}" end end
  3. 1

  4. class CsvImport < ActiveRecord::Base def order_data(csv_line) order_params = {} order_params[:account_id]

    = account.id order_params[:user_id] = account.user.id order_params[:retailer_id] = retailer.id order_params[:status_id] = 1 order_confirmed = true category_supported = true case retailer.name when "Amazon" # Get order data from csv line else "eBay" # Get order data from csv line end order = Order.new(order_params) order_shipped = order_params[:shipped_at].present? shipped_days_ago = if order_shipped (user.timezone.now - user.timezone.parse(order_params[:shipped_at]))/1.day end if category_supported && order_confirmed && order_shipped && shipped_days_ago < 30 order.save end end end
  5. when "Amazon" category_name = line[8].gsub('Category_', '') if order_category = Category.find_by(name:

    category_name) if line[18].strip == 'Confirmed' order_params[:shipped_at] = account.user.timezone.parse(line[18]) order_params[:shipping_name] = line[49] order_params[:shipping_company] = line[50] order_params[:shipping_street] = line[51] order_params[:shipping_city] = line[52] order_params[:shipping_state] = line[53] order_params[:shipping_zip] = line[54] order_params[:shipping_country] = line[55] order_params[:shipping_phone] = line[17] order_params[:billing_name] = line[4] order_params[:billing_company] = line[5] order_params[:billing_street] = line[6] order_params[:billing_city] = line[7] order_params[:billing_state] = line[8] order_params[:billing_zip] = line[9] order_params[:billing_country] = line[15] order_params[:billing_phone] = line[17] else order_confirmed = false end else category_supported = false end else "eBay" # ...
  6. # ... else "eBay" category_name = line[2].gsub('eBay ', '').strip if

    order_category = Category.find_by(name: category_name) if line[20].strip == 'CF' shipped_date = Date.parse(line[5]) shipped_time = line[6] order_params[:shipped_at] = account.user.timezone.parse("#{shipped_date} #{shipped_time}") order_params[:shipping_name] = line[8] order_params[:shipping_company] = line[9] order_params[:shipping_street] = line[10] order_params[:shipping_city] = line[11] order_params[:shipping_state] = line[12] order_params[:shipping_zip] = line[13] order_params[:shipping_country] = line[14] order_params[:shipping_phone] = line[15] order_params[:billing_name] = line[24] order_params[:billing_company] = line[25] order_params[:billing_street] = line[26] order_params[:billing_city] = line[27] order_params[:billing_state] = line[28] order_params[:billing_zip] = line[29] order_params[:billing_country] = line[30] order_params[:billing_phone] = line[31] else order_confirmed = false end else category_supported = false end end
  7. class CsvImport < ActiveRecord::Base def order_data(csv_line) order_params = {} order_params[:account_id]

    = account.id order_params[:user_id] = account.user.id order_params[:retailer_id] = retailer.id order_params[:status_id] = 1 order_confirmed = true category_supported = true case retailer.name when "Amazon" category_name = line[8].gsub('Category_', '') if order_category = Category.find_by(name: category_name) if line[18].strip == 'Confirmed' order_params[:shipped_at] = account.user.timezone.parse(line[18]) order_params[:shipping_name] = line[49] order_params[:shipping_company] = line[50] order_params[:shipping_street] = line[51] order_params[:shipping_city] = line[52] order_params[:shipping_state] = line[53] order_params[:shipping_zip] = line[54] order_params[:shipping_country] = line[55] order_params[:shipping_phone] = line[17] order_params[:billing_name] = line[4] order_params[:billing_company] = line[5] order_params[:billing_street] = line[6] order_params[:billing_city] = line[7] order_params[:billing_state] = line[8] order_params[:billing_zip] = line[9] order_params[:billing_country] = line[15] order_params[:billing_phone] = line[17] else order_confirmed = false end else category_supported = false end else "eBay" category_name = line[2].gsub('eBay ', '').strip if order_category = Category.find_by(name: category_name) if line[20].strip == 'CF' shipped_date = Date.parse(line[5]) shipped_time = line[6] order_params[:shipped_at] = account.user.timezone.parse("#{shipped_date} #{shipped_time}") order_params[:shipping_name] = line[8] order_params[:shipping_company] = line[9] order_params[:shipping_street] = line[10] order_params[:shipping_city] = line[11] order_params[:shipping_state] = line[12] order_params[:shipping_zip] = line[13] order_params[:shipping_country] = line[14] order_params[:shipping_phone] = line[15] order_params[:billing_name] = line[24] order_params[:billing_company] = line[25] order_params[:billing_street] = line[26] order_params[:billing_city] = line[27] order_params[:billing_state] = line[28] order_params[:billing_zip] = line[29] order_params[:billing_country] = line[30] order_params[:billing_phone] = line[31] else order_confirmed = false end else category_supported = false end end order = Order.new(order_params) order_shipped = order_params[:shipped_at].present? shipped_days_ago = if order_shipped (user.timezone.now - user.timezone.parse(order_params[:shipped_at]))/1.day end if category_supported && order_confirmed && order_shipped && shipped_days_ago < 30 order.save end end end
  8. class CsvImport < ActiveRecord::Base def order_data(csv_line) order_params = {} order_params[:account_id]

    = account.id order_params[:user_id] = account.user.id order_params[:retailer_id] = retailer.id order_params[:status_id] = 1 order_confirmed = true category_supported = true case retailer.name when "Amazon" category_name = line[8].gsub('Category_', '') if order_category = Category.find_by(name: category_name) if line[18].strip == 'Confirmed' order_params[:shipped_at] = account.user.timezone.parse(line[18]) order_params[:shipping_name] = line[49] order_params[:shipping_company] = line[50] order_params[:shipping_street] = line[51] order_params[:shipping_city] = line[52] order_params[:shipping_state] = line[53] order_params[:shipping_zip] = line[54] order_params[:shipping_country] = line[55] order_params[:shipping_phone] = line[17] order_params[:billing_name] = line[4] order_params[:billing_company] = line[5] order_params[:billing_street] = line[6] order_params[:billing_city] = line[7] order_params[:billing_state] = line[8] order_params[:billing_zip] = line[9] order_params[:billing_country] = line[15] order_params[:billing_phone] = line[17] else order_confirmed = false end else category_supported = false end else "eBay" category_name = line[2].gsub('eBay ', '').strip if order_category = Category.find_by(name: category_name) if line[20].strip == 'CF' shipped_date = Date.parse(line[5]) shipped_time = line[6] order_params[:shipped_at] = account.user.timezone.parse("#{shipped_date} #{shipped_time}") order_params[:shipping_name] = line[8] order_params[:shipping_company] = line[9] order_params[:shipping_street] = line[10] order_params[:shipping_city] = line[11] order_params[:shipping_state] = line[12] order_params[:shipping_zip] = line[13] order_params[:shipping_country] = line[14] order_params[:shipping_phone] = line[15] order_params[:billing_name] = line[24] order_params[:billing_company] = line[25] order_params[:billing_street] = line[26] order_params[:billing_city] = line[27] order_params[:billing_state] = line[28] order_params[:billing_zip] = line[29] order_params[:billing_country] = line[30] order_params[:billing_phone] = line[31] else order_confirmed = false end else category_supported = false end end order = Order.new(order_params) order_shipped = order_params[:shipped_at].present? shipped_days_ago = if order_shipped (user.timezone.now - user.timezone.parse(order_params[:shipped_at]))/1.day end if category_supported && order_confirmed && order_shipped && shipped_days_ago < 30 order.save end end end Atribuição de valores
  9. class CsvImport < ActiveRecord::Base def order_data(csv_line) order_params = {} order_params[:account_id]

    = account.id order_params[:user_id] = account.user.id order_params[:retailer_id] = retailer.id order_params[:status_id] = 1 order_confirmed = true category_supported = true case retailer.name when "Amazon" category_name = line[8].gsub('Category_', '') if order_category = Category.find_by(name: category_name) if line[18].strip == 'Confirmed' order_params[:shipped_at] = account.user.timezone.parse(line[18]) order_params[:shipping_name] = line[49] order_params[:shipping_company] = line[50] order_params[:shipping_street] = line[51] order_params[:shipping_city] = line[52] order_params[:shipping_state] = line[53] order_params[:shipping_zip] = line[54] order_params[:shipping_country] = line[55] order_params[:shipping_phone] = line[17] order_params[:billing_name] = line[4] order_params[:billing_company] = line[5] order_params[:billing_street] = line[6] order_params[:billing_city] = line[7] order_params[:billing_state] = line[8] order_params[:billing_zip] = line[9] order_params[:billing_country] = line[15] order_params[:billing_phone] = line[17] else order_confirmed = false end else category_supported = false end else "eBay" category_name = line[2].gsub('eBay ', '').strip if order_category = Category.find_by(name: category_name) if line[20].strip == 'CF' shipped_date = Date.parse(line[5]) shipped_time = line[6] order_params[:shipped_at] = account.user.timezone.parse("#{shipped_date} #{shipped_time}") order_params[:shipping_name] = line[8] order_params[:shipping_company] = line[9] order_params[:shipping_street] = line[10] order_params[:shipping_city] = line[11] order_params[:shipping_state] = line[12] order_params[:shipping_zip] = line[13] order_params[:shipping_country] = line[14] order_params[:shipping_phone] = line[15] order_params[:billing_name] = line[24] order_params[:billing_company] = line[25] order_params[:billing_street] = line[26] order_params[:billing_city] = line[27] order_params[:billing_state] = line[28] order_params[:billing_zip] = line[29] order_params[:billing_country] = line[30] order_params[:billing_phone] = line[31] else order_confirmed = false end else category_supported = false end end order = Order.new(order_params) order_shipped = order_params[:shipped_at].present? shipped_days_ago = if order_shipped (user.timezone.now - user.timezone.parse(order_params[:shipped_at]))/1.day end if category_supported && order_confirmed && order_shipped && shipped_days_ago < 30 order.save end end end Atribuição de valores Guarda (critério de aceitação)
  10. module AmazonParser private def category_name data[8].gsub('Category_', '') end def order_confirmed?

    data[18].strip == 'Confirmed' end def shipped_at timezone.parse(data[18]) end def number data[2].strip end def shipping_name data[49] end def shipping_company data[50] end # ... end
  11. class RetailerParser attr_accessor :data, :import, :timezone def initialize(data, import) self.data

    = data self.import = import self.timezone = import.timezone include_retailer_methods end ALLOWED_ATTRIBUTES_FROM_DATA = %i( shipped_at number shipping_name shipping_company shipping_street shipping_city shipping_state shipping_zip shipping_country shipping_phone billing_name billing_company billing_street billing_city billing_state billing_zip billing_country billing_phone ) def order import.orders.build(order_attributes_from_data) end private def order_attributes_from_data ALLOWED_ATTRIBUTES_FROM_DATA.each_with_object({}) { |a, e| a[e] = send(e) } end def include_retailer_methods include Object.const_get("#{import.retailer.name}Parser") end end Value Object
  12. class Order < ActiveRecord::Base SHIPPING_LIMIT_IN_DAYS = 30 validates :shipped_at, presence:

    true validate :shipping_must_be_recent def shipping_must_be_recent days_ago = (Time.zone.now - shipped_at.in_time_zone) / 1.day return unless days_ago <= SHIPPING_LIMIT_IN_DAYS errors.add(:shipped_at, "cannot be older than #{SHIPPING_LIMIT_IN_DAYS} days") end end
  13. 2

  14. class CsvImport < ActiveRecord::Base def import_data(data) order_params = {} order_params[:account_id]

    = account.id order_params[:user_id] = user.id order_params[:retailer_id] = retailer.id case retailer.name when "Amazon" # Get order data from csv data line else "eBay" # Get order data from csv data line end order = Order.new(order_params) order_shipped = order_params[:shipped_at].present? shipped_days_ago = if order_shipped (user.timezone.now - user.timezone.parse(order_params[:shipped_at])) / 1.day end if order_shipped && shipped_days_ago < 30 order.save end end end
  15. class CsvImport < ActiveRecord::Base # ... def validate_address(data) # ...

    end def shipping_date_in_user_timezone(data) # ... end def order_delivered?(data) # ... end def order_confirmed?(data) # ... end def order_shipped?(data) # ... end def order_shipped_days_ago(data) # ... end def shipping_address_from_data(data) # ... end def billing_address_from_data(data) # ... end end
  16. class CsvImport < ActiveRecord::Base # ... def validate_address(data) # ...

    end def shipping_date_in_user_timezone(data) # ... end def order_delivered?(data) # ... end def order_confirmed?(data) # ... end def order_shipped?(data) # ... end def order_shipped_days_ago(data) # ... end def shipping_address_from_data(data) # ... end def billing_address_from_data(data) # ... end end Compartilhamento de estado + Desacoplamento natural
  17. class OrderDecorator < SimpleDelegator attr_accessor :data def initialize(order, data) super(order)

    self.data = data end def address_valid? # ... end def shipping_date_in_user_timezone # ... end def order_delivered? # ... end def order_confirmed? # ... end def order_shipped? # ... end def shipping_address_from_data # ... end end Decorator
  18. 3

  19. class OrderHelper < ApplicationHelper def validate_address(data) # ... end def

    shipping_date_in_user_timezone(data) # ... end def order_delivered?(data) # ... end def order_confirmed?(data) # ... end def order_shipped_days_ago(data) # ... end def shipping_address_from_data(data) # ... end end
  20. class OrderPresenter < SimpleDelegator attr_accessor :data, :order def initialize(template, order,

    data) super(template) self.order = order self.data = data end def address_valid? # ... end def shipping_date_in_user_timezone # ... end def order_delivered? # ... end def order_confirmed? # ... end def order_shipped? # ... end def shipping_address_from_data # ... end end Presenter
  21. <% @order_presenter = OrderPresenter.new(self, @order, @data) %> <p> <b>Shipping date:</b>

    <%= @order_presenter.shipping_date_in_user_timezone %> </p>