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.