I have an Order object that belongs_to a BillingAddress and a ShippingAddress. I want to present my user with only ShippingAddress fields and a checked checkbox indicating that the billing address matches the shipping address. If the user unchecks the box, the BillingAddress fields appear.
This design proved painful to implement, and my Order object has a lot of callbacks I would rather not have.
class Order < ActiveRecord::Base
attr_accessor :bill_to_shipping_address
belongs_to :billing_address, class_name: 'Address'
belongs_to :shipping_address, class_name: 'Address'
accepts_nested_attributes_for :billing_address, :shipping_address
after_initialize :set_billing_to_shipping_address
before_validation :set_billing_address
after_validation :clear_billing_address_errors
# Init the object with option checked
def set_billing_to_shipping_address
self.bill_to_shipping_address ||= '1'
end
# Copy shipping address attrs to billing address
def set_billing_address
self.billing_address = self.shipping_address if bill_to_shipping_address?
end
def bill_to_shipping_address?
bill_to_shipping_address == '1'
end
# If shipping address matches billing, we copy the attrs, and thus duplicate errors too.
# We only need to show the user one set of errors if addresses are the same, so remove them for billing address.
def clear_billing_address_errors
if bill_to_shipping_address?
self.errors.messages.each { |k,v| self.errors.messages.delete(k) if k.to_s.split('.')[0] == 'billing_address' }
end
end
end
This feels smelly, and I have four methods along with three registered callbacks to satisfy this need. I have no logic in the controller and the form is relatively simple.
= form_for @order do |f|
# ...
= f.label :bill_to_shipping_address, class: 'checkbox' do
#{f.check_box :bill_to_shipping_address} Use my shipping address as my billing address.
This doesn't feel right. I'm also hacking around the error messages.
Questions:
How can I improve my design?
Would switching the relationships help?
Order has_one :billing_addressandhas_one :shipping_addressinstead ofbelongs_to. Nested forms will feel more natural; in that, a parent creates children, not the other way around.
I'm reading a fair bit of refactoring books, but I can never map their examples to my own object design. I'm not that experienced I guess.