Custom form with Validations using Rspec and Story Runner
August 25th, 2008
This is a custom Contact form with Validations without ActiveRecord. This works nicely without the use of the database or ActiveRecord model. It's quite easy with a gem called Validatable.
To Install:
sudo gem install validatable |
Add to your environment.rb file:
require "validatable" |
Controller
class ContactsController < ApplicationController layout 'site' def new @title = "Contact" @contact = Contact.new end def create @title = "Contact" @contact = Contact.new params[:contact] if @contact.valid? @contact.deliver flash[:notice] = "Contact Message Sent" redirect_to root_url else render :action => 'new' end end end |
Helper
module ContactHelper def error_messages_for_attribute(object, attribute) if object.errors.on(attribute) html = '<small class="errors"><ul>' object.errors.on(attribute).each do |message| html += "<li>" + message + "</li>" end html += "</ul></small>" end end end |
Model
class Contact # self is the class include Validatable attr_accessor :name, :email, :phone, :message validates_presence_of :name, :message => "Name is required." validates_format_of :email, :with => /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i, :message => "Email is not valid." validates_format_of :phone, :with => /^[0-9]{3,3}-[0-9]{3,3}-[0-9]{4,4}$/, :message => "Phone number is not valid (xxx-xxx-xxxx)." validates_presence_of :message, :message => "Name is required." class << self alias_method :new_without_params, :new def new params ={} returning new_without_params do |contact| params.each_pair do |key,value| contact.send "#{key}=", value end end end end def phone=(phone) # self is the object @phone = phone.gsub(/[^0-9]/, "").gsub(/^([0-9]{0,3})([0-9]{0,3})([0-9]{0,})$/) do |match| tmp = "" tmp += $1 if $1.size > 0 tmp += "-" + $2 if $2.size > 0 tmp += "-" + $3 if $3.size > 0 tmp end end def deliver if valid? ContactMailer.deliver_contact(self) else false end end end |
View
<div class="form_row"> <% form_tag '/contact/create' do %> <fieldset> <legend><%= @title %></legend> <p><label for="contact_name">Name</label> <%= text_field :contact, :name %> <%= error_message_on(@contact, :name) %></p> <p><label for="contact_email">Email</label> <%= text_field :contact, :email %> <%= error_message_on(@contact, :email) %></p> <p><label for="contact_phone">Phone</label> <%= text_field :contact, :phone %> <%= error_message_on(@contact, :phone) %></p> <p><label for="contact_message">Message</label> <%= text_field :contact, :message %> <%= error_message_on(@contact, :message) %></p> <p><%= submit_tag 'Submit', :class => "submit" %></p> </fieldset> <% end %> </div> |
Rspec Controller
require File.dirname(__FILE__) + '/../spec_helper' describe ContactsController do def mock_contact(params={}) params = {:deliver => true, :save => true, :valid? => true}.merge(params) @mock_contact ||= mock('contact', params) end describe "on GET to new" do it "should expose new contact as @contact" do Contact.should_receive(:new).and_return(@contact) get :new assigns(:contact).should == @contact end end describe "on POST to create" do it "should populate new contact with form data" do Contact.should_receive(:new).with({'these' => 'options'}).and_return(mock_contact) post :create, :contact => {'these' => 'options'} end describe "with valid contact" do it "should tell the contact to deliver" do Contact.should_receive(:new).and_return(mock_contact(:save => true)) mock_contact.should_receive(:deliver) post :create end it "should notify the user of successful submission" do Contact.should_receive(:new).and_return(mock_contact(:save => true)) post :create flash[:notice].should == "Contact Message Sent" end it "should redirect the user to the home page" do Contact.should_receive(:new).and_return(mock_contact(:save => true)) post :create response.should redirect_to('/') end end describe "with invalid contact" do it "should render the new contact form again" do Contact.stub!(:new).and_return(mock_contact(:valid? => false)) post :create response.should render_template('contacts/new') end end end end |
Rspec Model
require File.dirname(__FILE__) + '/../spec_helper' describe Contact do it "should assign name" do contact = Contact.new(:name => "Lance") contact.name.should == 'Lance' end it "should assign email" do contact = Contact.new(:email => "person@site.com") contact.email.should == 'person@site.com' end it "should assign phone" do contact = Contact.new(:phone => "555.555.1212") contact.phone.should == '555-555-1212' end it "should assign message" do contact = Contact.new(:message => "hey, how come you never call me?") contact.message.should == "hey, how come you never call me?" end it "should not assign non_existent_attribute" do lambda { Contact.new(:non_existent_attribute => "whatever") }.should raise_error(NoMethodError) end it "should have error for missing name" do contact = Contact.new contact.valid? contact.errors.on(:name).should == 'Name is required.' end it "should have error for missing email" do contact = Contact.new contact.valid? contact.errors.on(:email).should == 'Email is not valid.' end it "should have error for missing phome" do contact = Contact.new contact.valid? contact.errors.on(:phone).should =~ /Phone number is not valid/ end it "should have error for invalid phome" do contact = Contact.new(:phone => 'invalid phone number') contact.valid? contact.errors.on(:phone).should =~ /Phone number is not valid/ end end |
Rspec View
require File.dirname(__FILE__) + "/../../spec_helper" describe "/contacts/new" do it "should render the form" do assigns[:contact] = Contact.new render "/contacts/new.html.erb" response.should have_tag('form[action=?]','/contact/create') do with_tag('input[name=?]','contact[name]') with_tag('input[name=?]','contact[email]') with_tag('input[name=?]','contact[phone]') with_tag('input[name=?]','contact[message]') end end def expect_error_message_on(attribute) template.stub!(:error_message_on) assigns[:contact] = Contact.new template.should_receive(:error_message_on).with(assigns[:contact], attribute).and_return("message received") end it "should ask for error messages for name" do expect_error_message_on(:name) render "/contacts/new.html.erb" response.should include_text("message received") end it "should ask for error messages for email" do expect_error_message_on(:email) render "/contacts/new.html.erb" response.should include_text("message received") end it "should ask for error messages for phone" do expect_error_message_on(:phone) render "/contacts/new.html.erb" response.should include_text("message received") end it "should ask for error messages for message" do expect_error_message_on(:message) render "/contacts/new.html.erb" response.should include_text("message received") end end |
Story Runner
Steps
steps_for(:visitor_contacts) do Given "I am an anonymous visitor" do # no-op end When "I go to the contact form" do visits "/contact" # puts response.body end When "I submit the form with valid data" do fills_in 'Name', :with => 'Lance' fills_in 'Email', :with => 'lance@whatever.com' fills_in 'Phone', :with => '555-555-1212' fills_in 'Message', :with => 'Hello, what is this website about?' clicks_button "Submit" end When "I enter '$data' for $field" do |data, field| fills_in field, :with => data end When "I submit the form" do clicks_button "Submit" end Then /I should see '(.*)'/ do |text| response.should include_text(text) end end |
Stories
Story: visitor contacts your site As a visitor to your site I want to contact your site So that I can tell them how awesome the site is Scenario: visitor sends message Given I am an anonymous visitor When I go to the contact form And I submit the form with valid data Then I should see 'Contact Message Sent' Scenario: invalid phone format Given I am an anonymous visitor When I go to the contact form And I enter '123.4567' for Phone And I submit the form Then I should see 'Phone number is not valid' Scenario: missing name Given I am an anonymous visitor When I go to the contact form And I enter '' for Name And I submit the form Then I should see 'Name is required.' |

Sorry, comments are closed for this article.