Forms are everywhere on the web. I am writing this post in a form, you will comment in the form below, and adding this post to your rss reader will require a form. The problem is, spammers love forms. The silver bullet for their attacks is the unchecked form. Here I will example one method I think will keep our application clean moving forward with multiple Recaptcha validated forms.

Ruby on Rails form helpers and validators make it really easy to create forms quickly, and in the past there seemed to be a lot people writing about it. Lately, there is a lack of clean examples for enterprise applications with the Recaptcha validator.

1) Create a form and a Recaptcha view

app/views/partners/index.html.erb

  <%= form_for @partner, :url => '/users/partnership_request' do |f| %>
    <% if @partner.errors.any? %>
      <% @partner.errors.full_messages.each do |msg| %>
        <%= msg %>
      <% end %>
    <% end %>
    <%= t('.contact_information') %>
 
    <%= f.label :name, t('.name') %>
    <%= f.text_field :name %>
 
    <%= f.label :email, t('.email') %>
    <%= f.text_field :email %>
 
    <%= f.label :phone, t('.phone') %>
    <%= f.text_field :phone %>
    <script>
      var RecaptchaOptions = {theme:'clean'};
    </script>
    <%= render '/shared/recaptcha' %>
    <%= f.submit t('.submit') %>
  <% end %>

In the above example I am using the i18t internationalization methods. This is not required although a good habit for your application. http://guides.rubyonrails.org/i18n.html

  <%= f.label :name, t('.name') %>

app/views/shared/recaptcha.html.erb

<%= javascript_include_tag "http://www.google.com/recaptcha/api/challenge?k=YOUR_PUBLIC_KEY" %>
 
<noscript>
  <iframe src="http://www.google.com/recaptcha/api/noscript?k=YOUR_PUBLIC_KEY" height="300" width="500" frameborder="0"></iframe>
  <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
  <input type="hidden" name="recaptcha_response_field" value="manual_challenge">
</noscript>

2) Create a Recaptcha account

Goto Recaptcha.com and create an account, register your domain and save the private and public key information Recaptcha provides.

3) Create base Recaptcha and Validator models

In your app/models directory of your application create a file named recaptcha.rb and validator.rb

app/models/validator.rb

class Validator
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend  ActiveModel::Naming
 
  def initialize(attributes = {})  
    attributes.each do |name, value|  
      send("#{name}=", value)  
    end  
  end  
 
  def persisted?  
    false  
  end  
end

app/models/recaptcha.rb

class Recaptcha
  RECAPTCHA_PRIVATE_KEY = 'YOUR_PRIVATE_KEY'
  def self.verify(args)
    url = 'http://www.google.com/recaptcha/api/verify'
    query_parts = []
    query_parts << "privatekey=#{RECAPTCHA_PRIVATE_KEY}"
    query_parts << "remoteip=#{args[:ip_address]}"
    query_parts << "challenge=#{args[:recaptcha_challenge_field]}"
    query_parts << "response=#{args[:recaptcha_response_field]}".gsub(' ', '+')
 
    begin
      response = Curl::Easy.http_get("#{url}?#{query_parts.join('&')}") do |curl|
        curl.connect_timeout = 2
        curl.timeout = 5
      end
    rescue Exception
      return false
    end
 
    result = response.body_str.split("\n")
    return result[0]
  end
end

4) Use a namespaced directory to store all specific model validations.

This will keep the application looking clean and readable for new developers and remind us where to put new validation when they are required.

app/models/validator/recaptcha_validator.rb

class Validator::RecaptchaValidator < Validator
  def validate(record)
    unless record.recaptcha == 'true'
      record.errors[:base] << I18n.t('activerecord.errors.messages.recaptcha')
    end
  end
end

app/models/validator/partner_validator.rb

class Validator::PartnerValidator < Validator
  validates_with Validator::RecaptchaValidator
 
  attr_accessor(
    :name, 
    :email,
    :phone, 
    :recaptcha
  )   
 
  validates(
    :name, 
    :email, 
    :phone, 
    :presence => true
  )
 
end

I like this setup for validators. It keeps the validators in one place and leaves the base classes untouched in the future. Our app should be set up to handle the spammers and future Rails programmers.

5 thoughts on “Ruby on Rails 3.1 Recaptcha Form with Validation

  1. There seem to be some typos in this post.

    app/views/shared/recaptcha.html.erb

    Should probably contain the public, not private, key I believe.

  2. Brett says:

    Thanks Valerie. I updated the post – really updated this time :P

  3. Still have the private key in the top URL for the javascript_include_tag.

  4. Linds says:

    Successfully installed gems but tried can’t find this page. app/views/shared/recaptcha.html.erb

    or do I have to create one?

  5. Brett says:

    Yes. You can create any missing files with the code provided and it should work. :) Please note this post is rather outdated, so things might be a little different today.

Leave a Reply

Your email address will not be published. Required fields are marked *