Rails3: Should nested ActiveModel be treated like nested ActiveRecord

I have an User model (ActiveRecord). It has many Addresses
(ActiveRecord) and one CreditCard (ActiveModel)

When I submit my nested form the CreditCard validations did not fire.

So… in my User model I added this so the credit card validations
would get called.

def before_validation
credit_card.valid? unless credit_card.nil?
return true
end

This worked great. the missing credit card fields were highlighted
and everything… except

@user.errors.full_messages only contained errors for the user and
addresses.

So I updated my before_validation call again

def before_validation
credit_card.valid? unless credit_card.nil?
credit_card.errors.each{|k,v|
errors[“credit_card.#{k}”] = v
}
return true
end

Now everything is cool except the ugly error messages that nested
forms give you.

I chop off the credit card and addresses prefix when displaying them

  <% @user.errors.full_messages.each do |msg| %>
    <%
    message =    msg.gsub(/Credit card /, '').capitalize
    message =  message.gsub(/Addresses /, '').capitalize
  %>
    <li><%= message %></li>
  <% end %>

This seems like a lot of work and I suspect there is a 1 liner I’m
missing that will make this all automatic.

Something like this in my user model.

has_one_virtual :credit_card

Does such a thing exist? I hope so :slight_smile:

================== Code snippets ====================

==== User model ====

class User < ActiveRecord::Base
acts_as_authentic
has_many :addresses
accepts_nested_attributes_for :addresses
validates :credit_card, :presence => true
attr_accessor :email_confirmation, :credit_card

validates_confirmation_of :email, :message => “should match
confirmation”

def credit_card_attributes=(attributes)
self.credit_card = CreditCard.new(attributes)
end

def before_validation
credit_card.valid? unless credit_card.nil?
credit_card.errors.each{|k,v|
errors[“credit_card.#{k}”] = v
}
return true
end
end

==credit_card=================
class CreditCard

include ActiveModel::Validations
include ActiveModel::AttributeMethods
include ActiveModel::Callbacks
include ActiveModel::Conversion
extend ActiveModel::Naming

belongs_to :user

attr_accessor :card_type, :card_number, :card_verification,
:card_expires_on, :agree
validates :card_type, :presence => true
validates :card_number, :presence => true
validates :card_verification, :presence => true
validates :card_expires_on, :presence => true
validates :agree, :presence => true

def initialize(attributes = {})
expire_date = {}
attributes.each do |name, value|
if name.include?(‘card_expires_on’)
expire_date[name] = value
else
send("#{name}=", value)
end
end
# yeah, this is a total mess. Can ActiveModel handle this?
ymd = expire_date.map(&:last).map(&:to_i)
begin
send(“card_expires_on=”, Time.zone.local(ymd[2], ymd[1],1))
unless ymd[1].blank? || ymd[2].blank?
rescue
end
end

def persisted?
false
end
end

(I’m still looking for some feedback on an equivalent way to do
accepts_nested_attributes for nested resources that are
ActiveModels… instead of the hack I have below).

For the bad error message formatting…instead of stripping the
prefixes from the error messages I could use l18n

  <% @user.errors.full_messages.each do |msg| %>
    <%
    message =    msg.gsub(/Credit card /, '').capitalize
    message =  message.gsub(/Addresses /, '').capitalize
  %>
    <li><%= message %></li>
  <% end %>

after some trial and error I figured out how to to make l18n with
nested resources.

en:
activerecord:
attributes:
user:
credit_card:
agree: “Agreement”
card_verification: “Card Verification Value”
addresses:
address1: “Street Address”

I’ll probably stick to using gsub for most cases (until I start
localizing all my text). Unless of course there is an automatic way
to make “Credit card card verification can’t be blank” display “Card
verification can’t be blank”

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs