ruby-on-railsrubyformsreform

Reform gem: use one model for multiple forms


I am using Reform gem to make a form object for checkout in my web store. I have the Checkout form which contains properties for Order model, which has associations with Address model.

Problem is the Order model has two association with the same Address model:

class Order < ActiveRecord::Base
  #...
  belongs_to :billing_address, :class_name => 'Address'
  belongs_to :shipping_address, :class_name => 'Address'
  #...
end

So I need to set up my Checkout form to use Address model twice. The temporary approach I used to apply was trivial. However it was working fine:

class Checkout < Reform::Form
  extend ::ActiveModel::Callbacks
  #...
  property :billing_address, populate_if_empty: Address do
    property :firstname
    property :lastname
    property :address1
    property :address2
    property :phone
    property :city
    property :zipcode
    property :country_id
    property :billing_address_for_id
    property :shipping_address_for_id

    validates :firstname,
              :lastname,
              :address1,
              :phone,
              :city,
              :zipcode,
              :country_id,
              presence: true

    # provided by phony_rails gem
    # validates phone number to be correct and plausible 
    # without country accordance
    validates :phone, phony_plausible: { ignore_record_country_code: true }

    # provided by validates_zipcode gem
    # validates zipcode to be correct due to country alpha2 code
    validates :zipcode, zipcode: { country_code: :country_code }
  end

  property :shipping_address, populate_if_empty: Address do
    property :firstname
    property :lastname
    property :address1
    property :address2
    property :phone
    property :city
    property :zipcode
    property :country_id
    property :billing_address_for_id
    property :shipping_address_for_id

    validates :firstname,
              :lastname,
              :address1,
              :phone,
              :city,
              :zipcode,
              :country_id,
              presence: true

    # provided by phony_rails gem
    # validates phone number to be correct and plausible 
    # without country accordance
    validates :phone, phony_plausible: { ignore_record_country_code: true }

    # provided by validates_zipcode gem
    # validates zipcode to be correct due to country alpha2 code
    validates :zipcode, zipcode: { country_code: :country_code }
  #...
  end

But it's obvious that duplicated code must be refactored. And there I found that I can't come up with any working solution. My last attempt was like following:

class Checkout < Reform::Form
  extend ::ActiveModel::Callbacks
  #...
  property :billing_address, populate_if_empty: Address, form: BillingAddress 
  property :shipping_address, populate_if_empty: Address, form: ShippingAddress 
  #...
end

class BillingAddress < Reform::Form
  extend ::ActiveModel::Callbacks
  include Address
end

class ShippingAddress < Reform::Form
  extend ::ActiveModel::Callbacks
  include Address
end

module Address
  include Reform::Form::Module

  property :firstname
  property :lastname
  property :address1
  property :address2
  property :phone
  property :city
  property :zipcode
  property :country_id
  property :billing_address_for_id
  property :shipping_address_for_id

  validates :firstname,
            :lastname,
            :address1,
            :phone,
            :city,
            :zipcode,
            :country_id,
            presence: true

  # provided by phony_rails gem
  # validates phone number to be correct and plausible 
  # without country accordance
  validates :phone, phony_plausible: { ignore_record_country_code: true }

  # provided by validates_zipcode gem
  # validates zipcode to be correct due to country alpha2 code
  validates :zipcode, zipcode: { country_code: :country_code }
end

And with that setup

@checkout = Checkout.new(@order)

was giving me the error

undefined method `active_record' for #Declarative::Heritage:0x007ff6ea6fb038

So is there any standard approach in Reform to make things above work? Or can anybody tell what I do wrong? Thanks!


Solution

  • Accidentally I've found an answer. The actual problem was my module name was Address what is an exactly name of one of my models. And it made some conflict obviously. So I've changed Address to AddressModule.

    And my working setup will be:

    class Checkout < Reform::Form
      extend ::ActiveModel::Callbacks
      #...
      property :billing_address, populate_if_empty: Address, form: BillingAddress 
      property :shipping_address, populate_if_empty: Address, form: ShippingAddress 
      #...
    end
    
    class BillingAddress < Reform::Form
      extend ::ActiveModel::Callbacks
      include AddressModule
    end
    
    class ShippingAddress < Reform::Form
      extend ::ActiveModel::Callbacks
      include AddressModule
    end
    
    module AddressModule
      include Reform::Form::Module
    
      property :firstname
      property :lastname
      property :address1
      property :address2
      property :phone
      property :city
      property :zipcode
      property :country_id
      property :billing_address_for_id
      property :shipping_address_for_id
    
      validates :firstname,
                :lastname,
                :address1,
                :phone,
                :city,
                :zipcode,
                :country_id,
                presence: true
    
      # provided by phony_rails gem
      # validates phone number to be correct and plausible 
      # without country accordance
      validates :phone, phony_plausible: { ignore_record_country_code: true }
    
      # provided by validates_zipcode gem
      # validates zipcode to be correct due to country alpha2 code
      validates :zipcode, zipcode: { country_code: :country_code }
    end