title: Validation on a Complex Model for a multi-page form
body: <p>I'm trying to write a registration using devise and active merchant.  The form is complex in that my user object looks like this:</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    include ActiveMerchant::Utils
  
    # Include default devise modules. Others available are:
    # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
    devise :database_authenticatable, :registerable, 
           :recoverable, :rememberable, :trackable, :validatable, :omniauthable
  
    # Setup accessible (or protected) attributes
    attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :first_name, 
                    :subscription_attributes, :last_name, :zipcode, 
                    :payment_profile_attributes, :customer_cim_id, :payment_profile_id
  
  ...
  
    # Track multi-page registration
    attr_writer :current_step
  
  ...
  
    # Setup Payment Profile element (authorize.net billing profile)
    has_one :payment_profile, :dependent =&gt; :delete
    accepts_nested_attributes_for :payment_profile
  </code></pre>
  
  <p>Now the PaymentProfile class has its own children, two items from active merchant:</p>
  
  <pre><code>require 'active_merchant'
  
  class PaymentProfile &lt; ActiveRecord::Base
    include ActiveMerchant::Billing
    include ActiveMerchant::Utils
  
    validate_on_create :validate_card, :validate_address
  
    attr_accessor :credit_card, :address
  
    belongs_to :user
  
    validates_presence_of :address, :credit_card
  
    def validate_address
      unless address.valid?
        address.errors.each do |error|
          errors.add( :base, error )
        end
      end
    end
  
    def address
      @address ||= ActiveMerchant::Billing::Address.new(
        :name     =&gt; last_name,
        :address1 =&gt; address1,
        :city     =&gt; city,
        :state    =&gt; state,
        :zip      =&gt; zipcode,
        :country  =&gt; country,
        :phone    =&gt; phone
      )
    end
  
    def validate_card
      unless credit_card.valid?
        credit_card.errors.full_messages.each do |message|
          errors.add( :base, message )
        end
      end
    end
  
    def credit_card
      @credit_card ||= ActiveMerchant::Billing::CreditCard.new(
        :type               =&gt; card_type,
        :number             =&gt; card_number,
        :verification_value =&gt; verification_code,
        :first_name         =&gt; first_name,
        :last_name          =&gt; last_name
      )
      @credit_card.month ||= card_expire_on.month unless card_expire_on.nil?
      @credit_card.year  ||= card_expire_on.year unless card_expire_on.nil?
      return @credit_card
    end
  </code></pre>
  
  <p>Now I've overrided the RegistrationsController from Devise to handle the multi-page form using the solution from Ryan Bates multi-page form screencast (<a href="http://railscasts.com/episodes/217-multistep-forms" rel="noreferrer">http://railscasts.com/episodes/217-multistep-forms</a>).  I had to tweak it a bit to get it working with Devise, but I was successful.  Now because Ryan's multi-page form simply asked for different fields from the same model on different pages, he was able to override his <code>valid?</code> method by adding an :if block to his validate method a la:</p>
  
  <pre><code>validates_presence_of :username, :if =&gt; lambda { |o| o.current_step == "account" }
  </code></pre>
  
  <p>But in my case, I'm asking for all the fields on the first form from my parent model (User), and then asking for the all the fields from my two grandchild models (User:PaymentProfile:Address, User:PaymentProfile:Credit_Card) on teh second page.</p>
  
  <p>The problem I'm facing is that although PaymentProfile.valid? returns errors based on ActiveMerchant's logic, the form itself doesn't render or even display those errors.  The view code for the billing page looks like this:</p>
  
  <pre><code>&lt;h2&gt;Payment Details&lt;/h2&gt;
  
  &lt;%= semantic_form_for(resource, :as =&gt; resource_name, :url =&gt; registration_path(resource_name)) do |f| %&gt;
      &lt;%= devise_error_messages! %&gt;
  
      &lt;%= f.semantic_fields_for :payment_profile do |p| %&gt;
          &lt;%= p.semantic_fields_for :address do |a| %&gt;
              &lt;%= a.inputs "Billing Information", :id =&gt; "billing" do %&gt;
                  &lt;%= a.input :type,    :label =&gt; "Credit Card", :as =&gt; :select, :collection =&gt; get_creditcards %&gt;
                  &lt;%= a.input :number,     :label =&gt; "Card Number", :as =&gt; :numeric %&gt;
                  &lt;%= a.input :card_expire_on, :as =&gt; :date, :discard_day =&gt; true, :start_year =&gt; Date.today.year, :end_year =&gt; (Date.today.year+10), :add_month_numbers =&gt; true %&gt;
                  &lt;%= a.input :first_name %&gt;      
                  &lt;%= a.input :last_name %&gt;
                  &lt;%= a.input :verification_code, :label =&gt; "CVV Code" %&gt;
              &lt;% end %&gt;
          &lt;% end %&gt;
  
          &lt;%= f.semantic_fields_for :credit_card do |c| %&gt;
              &lt;%= c.inputs "Billing Address", :id =&gt; "address" do %&gt;
                  &lt;%= c.input :address1, :label =&gt; "Address" %&gt;
                  &lt;%= c.input :city %&gt;
                  &lt;%= c.input :state,   :as =&gt; :select, :collection =&gt; Carmen::states %&gt;
                  &lt;%= c.input :country, :as =&gt; :select, :collection =&gt; Carmen::countries, :selected =&gt; 'US' %&gt;
                  &lt;%= c.input :zipcode, :label =&gt; "Postal Code" %&gt;
                  &lt;%= c.input :phone,   :as =&gt; :phone %&gt;
              &lt;% end %&gt;
          &lt;% end %&gt;
      &lt;% end %&gt;
  
      &lt;%= f.commit_button :label =&gt; "Continue" %&gt;
      &lt;% unless @user.first_step? %&gt;
      &lt;%= f.commit_button :label =&gt; "Back", :button_html =&gt; { :name =&gt; "back_button" } %&gt;
      &lt;% end %&gt;
  &lt;% end %&gt;
  </code></pre>
  
  <p>I added a <code>puts errors</code> message in my code right after the valid? command and it shows as follows:</p>
  
  <pre><code>{:base=&gt;[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]}
  {:base=&gt;[["first_name", ["cannot be empty"]], ["last_name", ["cannot be empty"]], ["year", ["expired", "is not a valid year"]], ["type", ["is required", "is invalid"]], ["number", ["is not a valid credit card number"]], ["verification_value", ["is required"]], ["address1", ["is required"]], ["city", ["is required"]], ["state", ["is required"]], ["zip", ["is required", "must be a five digit number"]], ["phone", ["is required", "must be in the format of 333-333-3333"]]]}
  </code></pre>
  
  <p>Now the structure of this output doesn't match the output of a standard error output which is built off a single layer hash such as:</p>
  
  <pre><code>{:username=&gt;["can't be blank"]}
  </code></pre>
  
  <p>So after showing you all of that, my questions are these:
  a) how do I get the error output to show properly so that the form actually spits them out?
  b) how do I prevent the parent.valid? from also validating the grandchildren.valid? when I'm not on that page?  I can't use the :if => lambda... solution on child models because they don't know what the current_step is.</p>
  
  <p>My apologies for such a long post, I just wanted to include as much information as possible.  I've been wrestling with this for a week now and I can't seem to get past it.  Any advice would be hugely helpful.  Thanks in advance.</p>