Tony
2010-Aug-13 21:16 UTC
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 :-)
================== 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
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To unsubscribe from this group, send email to
rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en.
Tony
2010-Aug-16 18:03 UTC
Re: Rails3: Should nested ActiveModel be treated like nested ActiveRecord
(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" On Aug 13, 5:16 pm, Tony <tony.primer...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> 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 :-) > > ================== 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-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en.