Simple Ruby on Rails contact form using ActiveModel and Pony

Sometimes you need a form that doesn't have an ActiveRecord model associated with it. A perfect example is a contact form. You don't need to store anything in the database but you probably still want to perform some validations and use the built in form helpers. That's where ActiveModel comes in.

A basic model

ActiveModel is a collection of modules that contain the functionality you get out of the box when extending ActiveRecord. You can pick and choose the elements you need in your model. For the contact form, lets start with a plain Ruby class called "Inquiry" and add some basic functionality from ActiveModel.

First we extend ActiveModel::Naming. This gives our class the model_name method and is required for some of the other ActiveModel modules.

Next we include the ActiveModel::Conversion module which gives our class methods like to_key which are required for some of the form helpers. If you found your way here because you were getting the undefined method 'to_key' error, this will solve your problem

Then we define three attributes: Name, email and message. These will make up the fields for our form. Our initialize method allows us to create instances of our class using the familiar Rails convention of passing a hash of attribute values. The deliver method will eventually contain the logic for emailing the Inquiry.

Last we create a persisted? method which is also required for some of the form helpers. We're not storing anything in the database so we just return false. If you omit this method you will get an undefined method 'persisted?' error when trying to use the form_tag helper.

Now lets create some routes, a simple controller and some views.

Pretty simple stuff. Add these files to your application and restart your server. Navigate to /inquiries/new on your server to submit your form and see the thank you page.

The only interesting thing here is in the create method of our InquiriesController. Where we would normally call @inquiry.save we are instead calling @inquiry.deliver. As mentioned, this method will be responsible for emailing the inquiry. It will also contain our validation logic which we will add next.

Adding validations

In order to access the built in Rails validation logic from our model all we need to do is include another module ActiveModel::Validations. Here's an updated version of our Inquiry class:

Here we've included the module and added some standard validation rules to our model. We've also updated our deliver method to return false unless the model is valid. Lets add some error messages to our form:

Update these files and try submitting an empty form. You should see error messages for the invalid fields.

Delivering the email

To deliver the email we're going to use Pony, a simple, convenient way to send email. Add it to your gemfile gem 'pony' and run bundle install.

Pony uses /usr/sbin/sendmail to send mail if it is available, otherwise it uses SMTP to localhost. We can override this and set some default options by creating an initializer. Add a pony.rb file to your config/initalizers directory.

This example uses a Gmail account to send the email but you can use any SMTP server you like. If you're following along make sure to substitute your own Gmail username and password. The "to" option should be the email address you want all your website inquiries delivered to.

There are more details about how to configure Pony in the documentation. If you're using Heroku see this article on configuring SendGrid.

Now that we have Pony configured let's update the deliver method in our Inquiry model.

The deliver method is pretty simple; just call the Pony.mail method passing in the values from our form. Also notice we are including the ActionView::Helpers::TextHelper module in order to use the simple_format method for the html version of our email.

You should now be able to fill out the form on your server and receive emails to your inbox.

Preventing Spam

If you use the contact form as-is you will very quickly start to get bombarded with spam in your inbox. Spam bots crawl the internet searching for forms to fill out and spread their message. Fortunately they are pretty easy to fool.

The Honeypot Captcha is an old technique that still works pretty well today. The basic principle is to add a new field to your form and hide it with CSS. Because the field is invisible it should never be filled in... by a human. Spam bots love to fill out every field in a form and 99% of the time they will ignore the CSS and put something in there. All we have to do is check that the field is blank before we send the email.

Lets add a new field to our form. We'll use a standard "nickname" field so the spam bots don't get suspicious and have a higher chance of filling it in.

Now lets add a validation to our Inquiry model that ensures the nickname field is blank.

That's it. Your form should be safe from spammers. Try unhiding the nickname field and filling it in. The validations will fail and the email will not be sent.

Conclusion

The final version of the files in this tutorial are available on Github. If there is enough interest, I will create part 2 of this tutorial which modifies the form to use AJAX and queues the email delivery in a background process for a more responsive user experience. Enjoy.


comments powered by Disqus