Navigation

ADS specializes in using Ruby on Rails to build advanced, scalable, database-backed web sites for organizations of all sizes. Find out more at our website.

Atlantic Dominion Solutions

delegate not in the Rails API

By: Matt Elhotiby | Tags:

The delegate method allows you to add contained objects methods to your current objects methods. For example suppose you have two models User and Organization….

class User < ActiveRecord::Base
  belongs_to :organization
class Organization < ActiveRecord::Base

has_many :users

Ok now lets say that you want to access the taxid for the employee but the taxid is in the organizations table, you would normally have to access it like this….

@a_user.organization.taxid

but with the delegate method you can insert

class User < ActiveRecord::Base
  belongs_to :organization
  delegate :taxid, :to => :organization

and now to can skip the middle man and do…

@a_user.taxid

which returns the same result

Share this post

Have you ever wanted to create a form with the nice ActiveRecord Validations, but not use a database or ActiveRecord model? Like if you wanted to just have the result of the form emailed. An example might be a contact form or a special inquiry form that requires a name, an email, and perhaps a phone number.

Well, you can, and it”s quite easy too. Especially with the help of a little gem called Validatable.

To install:

sudo gem install validatable

There are at least 2 ways to include this in your Rails project, I’m going to cheat and use the shortcut method of including it in the environment.rb file to save on time and typing (and your patience):

Add to your environment.rb file:

require "validatable"

Next, lets create a new class file called contact.rb. You can put this in lib, models, or some other place, it”s up to you. I put it in my models directory for this example.

The contents of contact.rb will look like the following:

class Contact
  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)."
 
  def phone=(phone)
    @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?
      Notification.deliver_contact(self)
    else
      false
    end
  end
end

Oh my, doesn”t that look familiar? It looks just like an ActiveRecord model, except, gasp! It doesn”t inherit from ActiveRecord::Base. It”s just a plain Ruby class. Spiffy!

I”m assuming everything else looks familiar to you, yes? Swell, moving on then.

You will also notice I have a Notification mailer in there.

Note: I”m assuming you also know how to set up a mailer and the respective views. If you do not, comment below and I will add those pieces to this post if you desire.

Try it out, open up your console and play around.

>> c = Contact.new
=> #<Contact:0x218e974>
>> c.name = "Chris"
=> "Chris"
>> c.valid?
=> false
>> c.errors.full_messages
=> ["Email Email is not valid.", "Phone Phone number is not valid (xxx-xxx-xxxx)."]
>> c.email = "chris@techcfl.com"
=> "chris@techcfl.com"
>> c.phone = "1234567890"
=> "1234567890"
>> c.valid?
=> true
>> c.name
=> "Chris"
>> c.email
=> "chris@techcfl.com"
>> c.phone
=> "123-456-7890"
>> c.message = "Stillwater? Wtf is that?"
=> "Stillwater? Wtf is that?"

Cool! Now let”s put this into Rails context and see how to use it for handling validations in our controller and view.

In the controller where you want to handle your contact form, you might have something like the following:

def contact
  @contact = Contact.new
  if request.post?
    @contact.name = params[:name]
    @contact.email = params[:email]
    @contact.phone = params[:phone]
    @contact.ticker = params[:ticker]
    if @contact.valid?
      @contact.deliver
      flash[:notice] = "Thank you for you interest."
      redirect_to root_url
    end
  end
end

Here is an example view with custom error handling:

<% form_tag contact_path do %>
  <table>
    <tr>
      <td>
        <label for="name">Name:</label>
      </td>
      <td>
        <%= text_field_tag "name", @contact.name %>
        <%= error_messages_for_attribute(@contact, :name) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="email">Email:</label>
      </td>
      <td>
        <%= text_field_tag "email", @contact.email %>
        <%= error_messages_for_attribute(@contact, :email) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="phone">Phone:</label>
      </td>
      <td>
        <%= text_field_tag "phone", @contact.phone %>
        <%= error_messages_for_attribute(@contact, :phone) %>
      </td>
    </tr>
    <tr>
      <td>
        <label for="message">Message:</label>
      </td>
      <td>
        <%= text_area_tag "message", @contact.message %>
        <%= error_messages_for_attribute(@contact, :message) %>
      </td>
    </tr>
    <tr>
      <td colspan="2">
        <%= submit_tag "Submit" %>
      </td>
    </tr>
  </table>
<% end %>

You will notice a custom error_messages_for_attribute helper method I created, here is the code I used for that:

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

I hope you found this useful.

Share this post

Control.Tabs is a simply wonderful JavaScript extension for the Prototype library. In one project I’m using in multiple places and it fits the requirements perfectly. There is, however, a weird problem with one page with IE where instead of displaying the form like this: Read the rest of this entry »

Share this post