Rails 3, Action Mailer, attachments and validations of email contents - ruby-on-rails-3

I've made a simple form for sending emails with attachments. The problem is that I don't know how to make it work in the mailer. All tutorials that I've found so far are covering the scenario when the file for the attachment is already somewhere one the server and I wasn't able to find anything about validating email contents.
So, I've got 2 questions for you:
How can I let users send emails with attachments uploaded by them?
How can I validate user's inputs and extensions of attachments?
My email form...
<div id="form_wrapper">
<%= form_for(:kontakt, :url => {:action => 'kontakt'}, :html => { :multipart => true }, :remote=>true) do |f| %>
<ul>
<li>
<%= f.label(:email) %>
<%= f.text_field(:email) %>
</li>
<li>
<%= f.label(:content) %>
<%= f.text_area(:content, :size => "42x7") %>
</li>
<li>
<%= f.label(:preview, :class=>:preview )%>
<%= f.file_field :preview %>
</li>
</ul>
<%= image_submit_tag("blank.gif",:id=>"send_email", :class=>"action submit") %>
<%= link_to("Reset", {:controller=>'frontend',:action => 'index'},:remote => true, :class => 'action reset') %>
<% end %>
</div>
<%= javascript_include_tag 'jquery.form.js','jquery.rails','jquery.remotipart' %>
and my mailer...
class Contact < ActionMailer::Base
default :from => "xxxxx#gmail.com"
def wiadomosc(email)
#content=email[:content]
file=email[:preview]
attachments[file.original_filename] =File.read(file.path)
mail(
:subject=>"www.XXXXXX.pl - you've got a new message",
:reply_to =>"xxxxx#gmail.com",
:to => email[:email]
)
end
end
I came up with attachments[file.original_filename] =File.read(file.path) and it's adding attachments but the files are coruppted and cannot be opened...
Any thoughts or links would be greatly appreciated.

I've found that if you want to send email with attachment in rails, you need to attach it that way...
attachments[file.original_filename] = File.open(file.path, 'rb'){|f| f.read}
So the whole mailer method might look like this...
def message(email)
#content=email[:content]
unless email[:email_attachment].nil?
file=email[:email_attachment]
attachments[file.original_filename] = File.open(file.path, 'rb'){|f| f.read}
end
mail(
:subject=>"www.XXXXXX.pl - you've got a new message",
:reply_to =>"xxxxx#gmail.com",
:to => email[:email]
)
end
As for the validations, there are two methods that I've found. First one is quite obvious. You have to create a table in your database and validate form's inputs in model just like you always do. If you do it that way than everything will be saved in your database. It has some advantages, for exmple: you can use it as an archive or for some kind of statistics about your clients in your app or even put it through some sql triggers. However, if you don't want to save anything than you can create "tableless model" (Railscasts #193 and original Codetunes). All you have to do is to place this code at the begining of your model:
def self.columns() #columns ||= []; end
def self.column(name, sql_type = nil, default = nil, null = true)
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
end
Than you have to list your columns...
column :name, :string
column :company, :string
column :email, :string
column :content, :text
column :type_of_question, :string
column :email_attachment, :string
And after that you can place your model's code...
has_attached_file :email_attachment
EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
TYPES = ['application/zip', 'multipart/x-zip', 'application/x-zip-compressed']
validates :name, :presence => {:message => "Blah blah blah"}
validates :email, :presence => {:message => "Blah blah blah"},
:format=>{:unless=> Proc.new{|s| s.email.nil?|| s.email.empty? },
:with => EMAIL_REGEX, :message => "Blah blah blah"}
validates :content, :presence => {:message => "Blah blah blah"}
validates_inclusion_of :type_of_question, :in => ["Blah1", "Blah2", "Blah3"],
:message => "%{value} is not on the list of types"
validates_attachment_content_type :email_attachment, :content_type => TYPES,
:message => "The attachment has wrong extension..."
Right now, I'm using Gmail for sending emails but it's quite slooow. It takes about 2 minutes to send a short message with 2Kb test attachment. Have you got any suggestions about how to speed up this process? Maybe you can recomend another provider or solution?
PS. Don't forget to check out 'client side validations' Railscasts #263

Related

params of the model not being returned rails 3

I have made a few posts before this regarding how to add a favourite recipe to a user..I have an app where you can upload recipes once logged in, users can search the entire table for all recipes and view their own recipes in a member area..
Now I want users to be able to save their favourite recipes, so far I can save a favourite recipe as such, the output that I get is
[#<Favourite id: 1, user_id: 8, recipe_id: nil, created_at: "2012-11-06 19:25:34", updated_at: "2012-11-06 19:25:34">,
so i am getting the correct user_id but no params for the actual recipe, ie dish name, country of origin.
My models are like so
User
class User < ActiveRecord::Base
has_many :recipes
has_many :favourites
Recipe
has_many :ingredients
has_many :preperations
has_many :favourites
Favourite
belongs_to :user
belongs_to :recipe
My favourite controller looks like so
def create
#favourite = current_user.favourites.new(params[:recipe])
if #favourite.save
redirect_to my_recipes_path, :notice => "Recipe added to Favourites"
end
end
Add to favourites link
<%= link_to "Add to favorites", {:controller => 'favourites', :action => 'create'}, {:method => :post } %>
I hope I haven’t missed anything out, any help appreciated
You need to add extra information in the link and modify the create action
# View
<%= link_to "Add to favorites", favorite_path(:recipe_id => #recipe.id), {:method => :post } %>
# Controller
def create
#favourite = current_user.favourites.new(recipe_id: params[:recipe_id)
if #favourite.save
redirect_to my_recipes_path, :notice => "Recipe added to Favourites"
end
end
The problem is you are sending nothing to the controller in the param params[:recipe]
NOTE: remember the attr_accessible :user_id, :recipe_id inside Favorite model.
as stated
<%= link_to "Add to favorites", favorite_path(:recipe_id => #recipe.id), {:method => :post } %>
BUT this all depends on what #recipe is defined as in your controller - for example, if you have
#recipes = Recipie.all
And in the view you have
#recipes.all do |recipe|
Then in your link (within the block) you need to have:
<%= link_to "Add to favorites", favorite_path(:recipe_id => recipe.id), {:method => :post } %>
Does that help?
You're not sending any parameters through in the link.
<%= link_to "Add to favorites", {:controller => 'favourites', :action => 'create'}, {:method => :post } %>
This isn't enough to add a recipe to the favourites. What you'll need to do is pass through a recipe's id along with this link:
<%= link_to "Add to favorites", {:controller => 'favourites', :action => 'create', :recipe_id => recipe.id}, {:method => :post } %>
Or you could make this much shorter by using a routing helper:
<%= link_to "Add to favorites", add_to_favorites_path(:recipe_id => recipe), {:method => :post } %>
Defining that routing helper inside your config/routes.rb like this:
post '/favorites' => "favorites#create", :as => "add_to_favorites"
Then just find the recipe with params[:recipe_id] inside the controller and do what you need to do with it.

user and group join model validation

I have the models User and Group, and the join table Membership which uses has_many :through method. In my join table's form, I want the user to input a valid group name that an administrator has created to become a member of that group.
I have gotten the case where a valid name is entered to work but now I need some validation if they input a blank text box, or that the inputted group name exists in the database, I'd like some nice error messages. I thought this would be possible through some validates method?
membership partial _form.html.erb
<%= form_for(#membership) do |f| %>
<% if #membership.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#membership.errors.count, "error") %> prohibited this membership from being saved:</h2>
<ul>
<% #membership.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :group %><br />
<%= f.text_field :group %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
membership.rb
class Membership < ActiveRecord::Base
belongs_to :group
belongs_to :user
attr_accessible :user_id, :group_id
validates_uniqueness_of :group_id, :message => "can be only joined once", :scope => 'user_id'
validates_presence_of :group, :user
end
group.rb
class Group < ActiveRecord::Base
has_many :memberships, :dependent => :destroy
has_many :users, :through => :subscriptions
validates :name, :presence => true, :uniqueness => true
attr_accessible :name, :expiry
end
So need some direction as how the validation happens because the above validation in the membership and group models doesn't work, I get the error for both empty text box or name not in the database...
Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Edit: Added controller code below
def create
#group = Group.find_by_name(params[:membership][:group])
#membership = current_user.memberships.build(:group_id => #group.id)
respond_to do |format|
if #membership.save
format.html { redirect_to membership_url, :notice => 'Membership was successfully created.' }
format.json { render :json => #membership, :status => :created, :location => #membership }
else
format.html { render :action => "new" }
format.json { render :json=> #membership.errors, :status => :unprocessable_entity }
end
end
end
Add the following to your group model:
validate_uniqueness_of :name, :message => "a group already exists with that name"
validate_presence_of :name
The validations are called for all tables you are inserting to.
Edit:
Change your controller like this:
#membership = current_user.memberships.build(:group => #group)
You won't get the id called on nil error anymore. And if #group is nil, the validation in your membership will pick it up on the save attempt.
I solved this question by changing the text input box to a select field, and will add a password field to the model to solve my issue of unwanted users joining the group.
def create
#groups = Group.current
#group = Group.find_by_name(params[:membership][:group])
#membership = current_user.memberships.build(:group_id => #group.id)
After which my model validation works now below
validates_uniqueness_of :group_id, :message => "can be only be joined once", :scope => 'user_id'

Rails 3 - how to implement changing case of descriptions based on a check_box_tag (not part of model) in before_save callback in external class

I have an rails 3 application where there are multiple registrations (diagnosis, patient, laboratory test, service, client, user, supplier). Initially these will be populated by seeding the database. The requirement is for the description codes to be mixed case (capitalised first word) when either
1. specified by the application (some configuration setting - yet to be determined)
2. specified by data entry user
At present I have a model, view & controller for Diagnosis which contains two fields:
1. code (always to be capitalised)
2. description (First word capitalised based on check_box_tag value)
Presently I am using a before_save callback in the model to implement the conversion, but I cannot get it to only work when the check_box_tag is not selected i.e. its ignoring the check_box_tag.
I have tried changing the check_box_tag to a check_box adding an attr_assessor to the model (but not the sqlite3 db as it is not required to be stored).
This didn't work either.
How do I accomplish this? How do I override the option to use a checkbox from an internal application configuration file which results in either the checkbox being 'unavailable' or not visible if the application configuration specifies not user selectable?
Model (diagnosis.rb)
require 'DescriptionHelper'
class Diagnosis < ActiveRecord::Base
attr_accessible :code, description
string_correct_case = DescriptionHelper.new([:code, :description])
validates :code, :presence => true, :length => { :minimum => 4, :maximum => 4 }
validates :description, :presence => true
before_save string_correct_case
end
Callback in DescriptionHelper.rb
class DescriptionHelper
def initialize(attribute)
#attrs_to_manage = attribute
end
def before_save(record)
#attrs_to_manage.each do |attribute|
record.send("#{attribute}=", capitaliseWords(record.send("#{attribute}")))
end
end
private
def capitaliseWords(value)
value = value.mb_chars.downcase.to_s.gsub(/\b\w/) { |first| first.upcase }
end
end
Controller (diagnoses_controller.rb)
class DiagnosesController < ApplicationController
def new
#diagnosis = Diagnosis.new
end
def create
#diagnosis = Diagnosis.new(params[:diagnosis])
if #diagnosis.save
flash[:notice] = "Diagnosis created with params [#{#diagnosis.attributes.inspect}" #for debugging, once fixed will be just 'Diagnosis created.'
redirect_to #diagnosis
else
flash[:alert] = "Diagnosis not created."
render :action => "new"
end
end
.. other controller actions - edit, show, destroy
end
View (_form.html.erb)
<%= form_for(#daignosis) do |f| %>
<div class="field">
<%= f.label :code %>
<%= f.text_field :code %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= check_box_tag("diagnosis_desc_dont_convert", 1, false) %><%= f.label "Leave as entered" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
When this runs currently the check_box_tag is ignored.
When adding in the model an attar_assessor :description_correctcase and changing the view to use f.check_box 'description_correctcase' this is still ignored.
How does one get this to work?
Thanks in advance from a rails aspiring developer.
Finally got a solution to the problem, after reading and re-reading various SO solutions to component parts of my question. I'm not sure its correct in terms of rails, but it works.
If you can offer me a better solution I would certainly learn from this.
Here is my solution.
Model (diagnosis.rb)
require 'DescriptionHelper'
class Diagnosis < ActiveRecord::Base
attr_accessor :do_not_correctcase
attr_accessible :code, :description, :do_not_correctcase
before_save DescriptionHelper.new([:code, :description]), :if =>
lambda { |d| d.do_not_correctcase.to_s == '0' }
validates :code, :presence => true, :length => { :minimum => 4, :maximum => 4 }
validates :description, :presence => true
end
This I referenced from the following SO solution - https://stackoverflow.com/a/6388691/1108010
Controller (diagnoses_controller.rb)
class DiagnosesController < ApplicationController
def new
#diagnosis = Diagnosis.new
end
def create
#diagnosis = Diagnosis.new(params[:diagnosis])
#diagnosis.do_not_correctcase = params[:diagnosis][:do_not_correctcase]
logger.debug "New diagnoses: #{#diagnosis.attributes.inspect}"
logger.debug "Diagnosis should be valid: #{#diagnosis.valid?}"
logger.debug "code has value #{params[:code]}"
if #diagnosis.save
flash[:notice] = "Diagnosis created with params [#{#diagnosis.attributes.inspect}" #for debugging
redirect_to #diagnosis
else
flash[:alert] = "Diagnosis not created."
render :action => "new"
end
end
.. other controller actions - edit, show, destroy
end
I also changed the view to replace the check_box_tag with a check_box.
View (_form.html.erb)
<%= form_for(#daignosis) do |f| %>
<div class="field">
<%= f.label :code %>
<%= f.text_field :code %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.check_box 'do_not_correctcase' %><%= f.label "Leave as entered" %><br />
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
So despite getting this to work I'm not clear on are the following:
When inspecting the attributes with "#{#diagnosis.attributes.inspect}".
I assume that the reason the attr_accessor variable is not included in the New diagnosis output is that it is not part of the database table and therefore Active Reocrd does not instanciate it as part of the new record with #diagnosis.new
Could someone be kind enough to confirm that.
Why does the log have no value for logger.debug "code has value #{params[:code]}"? What causes the params[:code] to be null in the logger output?
Logfile contained the following entry:
Started POST "/diagnoses" for 127.0.0.1 at 2012-03-05 09:36:38 +0000
Processing by DiagnosesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"RW/mzkhavGeaIW0hVLn0ortTnbCDlrX+FfzH4neLLsA=", "diagnosis"=>{"code"=>"tt02", "description"=>"description for tt02", "do_not_correctcase"=>"1"}, "commit"=>"Create Diagnosis"}
New diagnosis: {"code"=>"tt02", "created_at"=>nil, "description"=>"description for tt02", "updated_at"=>nil}
Diagnosis should be valid: true
code has value
I would dearly like to know what is the correct way to do all this, as I feel this is not very DRY or clean.

New record not being saved with any values

I started the question differently, about a collection_select, but I found out that is not the problem.
This particular model won't save any data at all. It just ignores the values in the parameters. I can only save new records with NULL values (except for the timestamp fields).
See my comment for my latest try to fix it.
I have generated a few models with the handy scaffold command. Now I have tried to change a textbox to a collection_select for linking the new entity to the correct related one.
Using rails 3.1RC4 (hopefully this is not a bug).
In the _form.html.erb I use the following code:
<div class="field">
<%= f.label :category_id %><br />
<%= f.collection_select(:category_id, Admin::Category.all, :id, :name) %>
</div>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
...all other items...
<div class="actions">
<%= f.submit %>
</div>
After I click the submit button I receive error messages. It says that the name and permalink do not comply to the validation. I don't understand however, because in the logfiles I find this:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"my token is here", "admin_branche"=>{"category_id"=>"3", "name"=>"Verzekeraars", "permalink"=>"verzekeraars", "visible"=>"1"}, "commit"=>"Create Branche"}
To me it seems that the params contain all the needed values.
For the sake of completeness I will post my create method and model below.
So far I have tried switching back and forth between collection_select and f.coll... with no success. The current setup seems most appropriate to me, based on the logs.
I have also googled a lot, but haven't been able to find the answer. Question 2280106 on this site looks the same, but it had to do with attr_accessible which I have commented out in the model (I restarted the server afterwards and retried, just to be sure).
Help is much appreciated!
branche.rb:
class Admin::Branche < ActiveRecord::Base
# attr_accessible :name, :permalink
#relationships
has_many :courses, :as => :parent
belongs_to :category
#validations
validates :name, :presence => true, :length => {:maximum => 255}
validates :permalink, :presence => true, :length => { :within => 4..25 }
end
create action in the controller:
def create
#admin_branch = Admin::Branche.new(params[:admin_branch])
respond_to do |format|
if #admin_branch.save
format.html { redirect_to #admin_branch, notice: 'Branche was successfully created.' }
format.json { render json: #admin_branch, status: :created, location: #admin_branch }
else
format.html { render action: "new" }
format.json { render json: #admin_branch.errors, status: :unprocessable_entity }
end
end
end
In the controller, you're doing this:
#admin_branch = Admin::Branche.new(params[:admin_branch])
You should do this:
#admin_branch = Admin::Branche.new(params[:admin_branche])
If you look at the request parameters, the attributes are under "admin_branche", not "admin_branch".
I think that should solve your problems, if not, please let us know.
If you have problems with the generated inflections, you can completely customize them in the config/initializers/inflections.rb
just add something like this:
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'branch', 'branches'
end

Validation on a Complex Model for a multi-page form

I'm trying to write a registration using devise and active merchant. The form is complex in that my user object looks like this:
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
Now the PaymentProfile class has its own children, two items from active merchant:
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
Now I've overrided the RegistrationsController from Devise to handle the multi-page form using the solution from Ryan Bates multi-page form screencast (http://railscasts.com/episodes/217-multistep-forms). I had to tweak it a bit to get it working with Devise, but I was successful. Now because Ryan's multi-page form simply asked for different fields from the same model on different pages, he was able to override his valid? method by adding an :if block to his validate method a la:
validates_presence_of :username, :if => lambda { |o| o.current_step == "account" }
But in my case, I'm asking for all the fields on the first form from my parent model (User), and then asking for the all the fields from my two grandchild models (User:PaymentProfile:Address, User:PaymentProfile:Credit_Card) on teh second page.
The problem I'm facing is that although PaymentProfile.valid? returns errors based on ActiveMerchant's logic, the form itself doesn't render or even display those errors. The view code for the billing page looks like this:
<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 %>
I added a puts errors message in my code right after the valid? command and it shows as follows:
{: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"]]]}
Now the structure of this output doesn't match the output of a standard error output which is built off a single layer hash such as:
{:username=>["can't be blank"]}
So after showing you all of that, my questions are these:
a) how do I get the error output to show properly so that the form actually spits them out?
b) how do I prevent the parent.valid? from also validating the grandchildren.valid? when I'm not on that page? I can't use the :if => lambda... solution on child models because they don't know what the current_step is.
My apologies for such a long post, I just wanted to include as much information as possible. I've been wrestling with this for a week now and I can't seem to get past it. Any advice would be hugely helpful. Thanks in advance.
The reason the errors aren't showing is probably that they are populated on the base, not on the individual attributes. This happens in your validate_card and validate_address methods. Instead of adding errors to base, you should add them to the specific attribute that caused the error.
errors.add( attr , error )
Secondly, if you want to make your validations dependent on a certain state, as the screencast you mentioned, then you need to save the state flag with the model (probably best the parent). You can do this by hand or, better, you can use a gem for this (recommended): state_machine
Good luck.
On a high level, you seem to be using inheritance in your object modeling and this model is getting built in several forms, in almost 'wizard' like approach. My suggestion would be to model your objects to reflect, the actual forms like,
First part of the form collect basic User information : UserInformation model
Second Part of the form collect payment related information: PaymentInformation model (the Active merchant stuff)
and so on...
Where either the User model has one UserInformation, has one PaymentInformation and so on.
Essentially replace inheritance with Composition. Try and see if you can avoid extending the ActiveMerchant frame work too.
The above style, will give you more control over when you want to call #valid? on a subset of you data model. It get constructed part by part as the user move through the form.
Sorry, I dont have specific solution for you but a more general rewrite approach.
I am new to Ruby-on-Rails and I know this doesn't answer the questions above but you should try out Client-Side Validations and take a look at the Rails-casts. It may be helpful to you!