Estoy tratando de escribir un registro utilizando el inventor y el comerciante activo. La forma es compleja en la que mi objeto de usuario se ve así:Validación en un modelo complejo para un formulario de varias páginas
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
Ahora la clase PaymentProfile tiene sus propios hijos, dos artículos de comerciante activo:
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
Ahora he overrided la RegistrationsController de Diseñe para manejar el formulario de varias páginas usando la solución del screencast de varias páginas de Ryan Bates (http://railscasts.com/episodes/217-multistep-forms). Tuve que modificarlo un poco para que funcionara con Devise, pero tuve éxito. Ahora, debido a la forma de varias páginas de Ryan se limitó a preguntar para diferentes campos de la misma modelo en diferentes páginas, fue capaz de anular su método valid?
añadiendo una: si el bloque a su método de validación a la:
validates_presence_of :username, :if => lambda { |o| o.current_step == "account" }
Pero en mi caso, estoy solicitando todos los campos en el primer formulario de mi modelo padre (Usuario), y luego solicitando todos los campos de mis dos modelos de nieto (Usuario: Perfil de pago: Dirección, Usuario: Perfil de pago: Tarjeta de crédito) en la segunda página.
El problema al que me enfrento es que, aunque PaymentProfile.valid? devuelve errores basados en la lógica de ActiveMerchant, el formulario en sí no representa o incluso muestra esos errores. El código de la vista de la página de facturación es el siguiente:
<h2>Payment Details</h2>
<%= 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 %>
he añadido un mensaje puts errors
en mi código justo después de la válida? comando y se nota como sigue:
{: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"]]]}
Ahora la estructura de esta salida no coincide con la salida de una salida de error estándar que se construye de una sola capa de hash tales como:
{:username=>["can't be blank"]}
Así Después de mostrarle todo eso, mis preguntas son las siguientes: a) ¿Cómo obtengo que la salida del error se muestre correctamente para que la forma realmente las escupe? b) ¿cómo evito parent.valid? también validando nietos.valido? cuando no estoy en esa página? No puedo usar la solución: if => lambda ... en modelos secundarios porque no saben qué es el actual_step.
Mis disculpas por una publicación tan larga, solo quería incluir la mayor cantidad de información posible. He estado luchando con esto durante una semana y parece que no puedo superarlo. Cualquier consejo sería de gran ayuda. Gracias por adelantado.
La validación del lado del cliente es un buen extra, pero absolutamente ningún reemplazo para la validación del lado del servidor. La validación del lado del cliente es fácilmente socavada por scripts maliciosos que no usan un navegador habilitado para JS. Para garantizar que sus datos sean de alta integridad, la validación del lado del servidor es el único mecanismo confiable. –