Handling an e-commerce order form I wrote this form object to handle an e-commerce order form. My areas of concern are the verbosity of the populate method, and the repetition of the validations. I omitted some address fields and validations for brevity.

class OrderForm
  include ActiveModel::Model

  def persisted?
    false
  end

  attr_accessor :params

  delegate :email, :bill_to_shipping_address, to: :order
  delegate :name, :street, :city, :state, :post_code, to: :shipping_address, prefix: :shipping
  delegate :name, :street, :city, :state, :post_code, to: :billing_address,  prefix: :billing

  validates :email, length: { maximum: 60 }, email_format: true

  validates :shipping_name, :shipping_street, :shipping_city, presence: true
  validates :shipping_post_code, numericality: { only_integer: true }

  validates :billing_name, :billing_street, :shipping_city, presence: true, unless: -&gt; { bill_to_shipping_address }
  validates :billing_post_code, numericality: { only_integer: true }, unless: -&gt; { bill_to_shipping_address }

  def initialize(item, params = nil, customer = nil)
    @item, @params, @customer = item, params, customer
  end

  def submit
    populate
    if valid?
      order.save!
      true
    else
      false
    end
  end

  def order
    @order ||= @item.build_order do |order|
      order.customer = @customer if @customer
    end
  end

  private

  def shipping_address
    @shipping_address ||= order.build_shipping_address
  end

  def billing_address
    @billing_address ||= order.build_billing_address
  end

  def populate
    order.email = params[:email]
    order.bill_to_shipping_address = params[:bill_to_shipping_address]

    shipping_address.name          = params[:shipping_name]
    shipping_address.street        = params[:shipping_street]
    shipping_address.city          = params[:shipping_city]
    shipping_address.state         = params[:shipping_state]
    shipping_address.post_code     = params[:shipping_post_code]

    if order.bill_to_shipping_address?
      billing_address.name          = params[:shipping_name]
      billing_address.street        = params[:shipping_street]
      billing_address.city          = params[:shipping_city]
      billing_address.state         = params[:shipping_state]
      billing_address.post_code     = params[:shipping_post_code]
    else
      billing_address.name          = params[:billing_name]
      billing_address.street        = params[:billing_street]
      billing_address.city          = params[:billing_city]
      billing_address.state         = params[:billing_state]
      billing_address.post_code     = params[:billing_post_code]
    end
  end
end


The Order object has billing_address and one shipping_address objects. They both inherit from an Address.

Here's the controller:

  def new
    @order_form = OrderForm.new(@item)
  end

  def create
    @order_form = OrderForm.new(@item, params[:order], current_user)
    if @order_form.submit
      # process payment
    else
      render 'new'
    end
  end
I was about to propose some changes to your code to reduce the line-count (and get rid of those @cached objects, bad idea IMHO), but I think it's the big picture what it's failing here. Some notes:


You are creating an OrderForm abstraction when in fact Rails gives all the infrastructure you need to write this in Order in a simple and very concise way.
All those validations belong to each model, validating them from an outside class is not a good practice.
Use accepts_nested_attributes_for (in models) + fields_for (in views) so the associations are automatically (and transparently) handled by Rails.


[EDIT] Regarding the non-cached approach, a glimpse of what I'd write:

order = @item.order || build_order
order.customer = @customer
shipping_address = order.shipping_address || order.build_shipping_address
shipping_address.attributes = params.slice(:shipping_name, :shipping_street,...)