Проверка на сложной модели для многостраничной формы

Я пытаюсь написать регистрацию, используя devise и active merchant. Форма сложна в том смысле, что мой пользовательский объект выглядит следующим образом:

class User < 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 => :delete
  accepts_nested_attributes_for :payment_profile

Теперь у класса PaymentProfile есть свои дочерние элементы, два элемента от активного продавца:

require 'active_merchant'

class PaymentProfile < 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     => last_name,
      :address1 => address1,
      :city     => city,
      :state    => state,
      :zip      => zipcode,
      :country  => country,
      :phone    => 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               => card_type,
      :number             => card_number,
      :verification_value => verification_code,
      :first_name         => first_name,
      :last_name          => 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

Теперь я переопределил RegistrationsController из Devise для обработки многостраничной формы используя решение из многостраничного скринкаста Райана Бейтса ( http: //railscasts.com/episodes/217-multistep-forms ). Мне пришлось немного подправить его, чтобы он работал с Devise, но я добился успеха. Теперь, поскольку многостраничная форма Райана просто запрашивала разные поля из одной и той же модели на разных страницах, он смог переопределить свой метод valid? , добавив блок: if в свой метод проверки a la:

validates_presence_of :username, :if => lambda { |o| o.current_step == "account" }

Но в моем случае я запрашиваю все поля в первой форме из моей родительской модели (User), а затем запрашиваю все поля из моих двух моделей внуков (User: PaymentProfile: Address, User: PaymentProfile: Credit_Card) на второй странице.

Проблема, с которой я столкнулся, заключается в том, что хотя PaymentProfile.valid? возвращает ошибки на основе логики ActiveMerchant, сама форма не отображает и даже не отображает эти ошибки. Код просмотра для платежной страницы выглядит так:

Payment Details

<%= semantic_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> <%= f.semantic_fields_for :payment_profile do |p| %> <%= p.semantic_fields_for :address do |a| %> <%= a.inputs "Billing Information", :id => "billing" do %> <%= a.input :type, :label => "Credit Card", :as => :select, :collection => get_creditcards %> <%= a.input :number, :label => "Card Number", :as => :numeric %> <%= a.input :card_expire_on, :as => :date, :discard_day => true, :start_year => Date.today.year, :end_year => (Date.today.year+10), :add_month_numbers => true %> <%= a.input :first_name %> <%= a.input :last_name %> <%= a.input :verification_code, :label => "CVV Code" %> <% end %> <% end %> <%= f.semantic_fields_for :credit_card do |c| %> <%= c.inputs "Billing Address", :id => "address" do %> <%= c.input :address1, :label => "Address" %> <%= c.input :city %> <%= c.input :state, :as => :select, :collection => Carmen::states %> <%= c.input :country, :as => :select, :collection => Carmen::countries, :selected => 'US' %> <%= c.input :zipcode, :label => "Postal Code" %> <%= c.input :phone, :as => :phone %> <% end %> <% end %> <% end %> <%= f.commit_button :label => "Continue" %> <% unless @user.first_step? %> <%= f.commit_button :label => "Back", :button_html => { :name => "back_button" } %> <% end %> <% end %>

Я добавил сообщение устанавливает ошибки в свой код сразу после действительного? команда, и она отображается следующим образом:

{:base=>[["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=>[["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"]]]}

Теперь структура этого вывода не совпадает с выводом стандартного вывода ошибок, построенного на основе однослойного хэша, такого как:

{:username=>["can't be blank"]}

Итак, после демонстрации всего этого, мои вопросы такие: а) как мне получить вывод ошибок, чтобы они отображались правильно, чтобы форма фактически выплевывала их? б) как мне предотвратить parent.valid? от также проверки grandchildren.valid? когда я не на той странице? Я не могу использовать решение: if => lambda ... для дочерних моделей, потому что они не знают, что такое current_step.

Приношу свои извинения за такой длинный пост, я просто хотел включить как можно больше информации . Я борюсь с этим уже неделю, и, кажется, не могу пройти через это. Любые советы были бы очень полезны. Заранее спасибо.

6
задан R. Yanchuleff 28 March 2011 в 04:57
поделиться