By Robert Dempsey of Atlantic Dominion Solutions, LLC
Abstract
With Amazon Web Services™ (AWS), developers are no longer limited by expensive-to-scale infrastructure, lack of funds to build a data center, or a smaller IT staff. To meet increasing client demands to store ever-growing data, mine vast amounts of information, and gather information from the Internet, developers turn to AWS. Ruby and Rails are the perfect combination to work with the services. Getting started with most of the AWS solutions requires little or no experience with Ruby, Rails, or AWS, making the services extremely simple to use and highly addictive. This article, which assumes a basic understanding of the Ruby programming language and the Ruby on Rails framework, shows you step-by-step how easy it is to use freely available tools to integrate AWS into your Rails application.
Introduction to Amazon Web Services
Amazon Web Services (AWS) provides developers with direct access to a powerful suite of web services that enhance and power their applications.
- Amazon Elastic Compute Cloud (Amazon EC2) Beta enables "compute capacity in the cloud" by allowing the creation and management of virtual machine images to quickly increase or decrease capacity.
- Amazon Simple Storage Service (Amazon S3) provides scalable, online, persistent storage.
- Amazon Simple Queue Service (Amazon SQS) centralizes all messaging, ensuring that message delivery between geographically disparate applications or computers is not a problem.
- Amazon SimpleDB™ Limited Beta is a web service for running queries on structured data in real time.
- Many more AWS solutions are available.
REST or SOAP: Which Keeps Code Cleaner?
The two ways to work with web services by using Ruby and Rails are REST and SOAP. SOAP is "a protocol for exchanging XML-based messages over computer networks, normally using HTTP" (Wikipedia.com, SOAP). Action Web Service (the portion of Rails used to create and interact with SOAP and XML-RPC-based web services) will soon be removed from the core and implemented in a plugin. Action Web Service requires the creation of an application programming interface (API) definition and a controller, the use of dispatching modes, and more. A Ruby gem is available to help Rails developers use SOAP; however, because SOAP is being removed from the Rails core, REST is a better alternative.
Built into the core of Rails and featured heavily in Ruby on Rails 2, REST is a simple-to-implement architecture that uses HTTP and XML for communication. Whereas SOAP is a protocol, REST is a software architecture: a way of designing web applications. REST allows client applications to retrieve content from a site simply by knowing the appropriate URL to call. The XML that is returned contains and describes the content in a single response. With Ruby on Rails, you can code a REST-enabled application from the beginning, resulting in one code base that provides functionality and an API.
RESTful Development with Ruby on Rails
In Agile Web Development with Rails, author Dave Thomas states that in REST, "we use a simple set of verbs to operate on a rich set of nouns...the verbs correspond to HTTP methods (GET, PUT, POST, and DELETE)...the nouns are the resources in our application. We name those resources using URLs" (p. 407). In other words, to interact with a RESTful Rails application, the client simply calls URLs and sends and receives XML. URLs are automatically generated by a few lines of code in your config/routes.rb file. For example, suppose you have an inventory application that keeps track of products that you have stored in various warehouses. For simplicity, your database contains one table (authentication aside): products. Your routes.rb file contains the following:
ActionController::Routing::Routes.draw do |map| map.resources :products # Allow downloading Web Service WSDL as a file with an extension # instead of a file named 'wsdl' map.connect ':controller/service.wsdl', :action => 'wsdl' # Install the default route as the lowest priority map.connect ':controller/:action/:id.:format' map.connect ':controller/:action/:id' end
With these two lines (and a dash of code in your controllers), other applications have the ability to request and send product information to and from your database. Too easy, you say? Not in Rails. To prove how simple this process is, I'll show you how to create a small application that stores product information and takes advantage of Amazon S3 to store product images.
REST for a Bit
Now the moment you have been waiting for (or have skipped to): the code.
Note: Although I use a Mac for development, the commands that follow are the same regardless of your operating system. The code was developed by using Ruby 1.8.7 and Ruby on Rails 2.1.0.
You are going to create a little application that keeps track of products and their images. The application will take advantage of the Paperclip plugin from thoughtbot. This plugin can use the Amazon S3 service to store the images (and other types of files, if you like). I am focusing on Amazon S3 because it is one of the simplest of the AWS solutions to use, more applications now allow user-generated content that needs to be stored, purchasing storage becomes cost-prohibitive very quickly, and getting started with Ruby on Rails takes but a few lines of code.
Before delving into working with AWS, install the most recent version of Rails:
sudo gem install rails
Next, sign up for a free AWS account. After providing credit card information for the Amazon S3 service, you'll receive an Access Key ID. To connect to AWS with Rails, you will need to use a Ruby gem. Several Ruby gems interact with AWS, but you will use two written by RightScale. The two gems give you access to all AWS services. To install the gems, run the following command in your terminal:
sudo gem install right_aws right_http_connection
Next, create a new Rails application and tell it to use a MySQL database. Let's call the application aws_article:
Musashi:~/Documents/rails rdempsey$ rails aws_article --database mysql
That was easy! Next, you'll create your development and test databases, and configure your database.yml file to connect to them. Use the username and password appropriate for your database.
development: adapter: mysql encoding: utf8 database: aws_article_development username: root password: socket: /tmp/mysql.sock test: adapter: mysql encoding: utf8 database: aws_article_test username: root password: socket: /tmp/mysql.sock production: adapter: mysql encoding: utf8 database: aws_article_production username: root password: socket: /tmp/mysql.sock
Now it is time to download the Paperclip plugin. This plugin makes it easy to attach files or images to any model you create, and has excellent documentation. To install the plugin, run the following command in your Rails root directory :
svn export https://svn.thoughtbot.com/plugins/paperclip/trunk/ vendor/plugins/paperclip
To use Amazon S3, you need a config file. Create a file named s3.yml and save it in your config directory. In this file, add the Access Key ID and Secret Access Key that you received when you signed up for Amazon S3. Type your keys. (I am not giving you mine!)
Now that you are set up, you need to create your RESTful models and controllers. With Rails 2.1.0, when you run the "generate scaffold" command, the created controller will automatically be RESTful, and the required map.resource lines will be added to your routes.rb file. To create the scaffold model, controller, views, and a migration for your product, run the following:
script/generate scaffold Product name:string purchase_price:decimal sale_price:decimal photo_file_name:string photo_content_type:string photo_file_size:integer
By listing all the fields and data types that you are going to use, the scaffolded forms that are created will have all the fields we need. The migration will also have all your required fields. Open your migration file and add the limit, precision, and scale attributes where necessary so that your migration looks like the following:
class CreateProducts < ActiveRecord::Migration def self.up create_table :products do |t| t.string :name, :limit => 255 t.decimal :purchase_price, :precision => 10, :scale => 2 t.decimal :sale_price, :precision => 10, :scale => 2 t.string :photo_file_name t.string :photo_content_type t.integer :photo_file_size t.timestamps end end def self.down drop_table :products end end
Before moving ahead, add :html => { :multipart => true } to our form tag and Image: <%= f.file_field :photo %> so that you can upload files. Add this code to both the new and edit forms. The new form will looks like the following:
New product
<% form_for(@product, :html => { :multipart => true }) do |f| %>
<%= f.error_messages %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :purchase_price %>
<%= f.text_field :purchase_price %>
<%= f.label :sale_price %>
<%= f.text_field :sale_price %>
Image: <%= f.file_field :photo %>
<%= f.submit "Create" %>
<% end %>
<%= link_to 'Back', products_path %>
Next, open the config/routes.rb file and check to be sure that it includes the following:
map.resources :products
Note: map.resources. Those lines produce the URLs that will be used for your RESTful interface. What are the URLs, you ask? Good question. Here's the list for products:
| Controller Action | HTTP Method | URL | XML |
|---|---|---|---|
| index | GET | /products | /products.xml |
| show | GET | /products/1 | /products/1.xml |
| new | GET | /products/new | |
| edit | GET | /products/1;edit | |
| create | POST | /products | /products.xml |
| update | PUT | /products/1 | /products/1.xml |
| destroy | DELETE | /products/1 | /products/1.xml |
Open your controller and look for the following:
# GET /products
# GET /products.xml
def index
@products = Product.find(:all)
respond_to do |format|
format.html # index.rhtml
format.xml { render :xml => @products.to_xml }
end
end
Update your database by running a migration and ensuring that everything works correctly:
rake db:migrate
If you've seen any Rails code created before Rails 2.1, then you might recognize the @products = Product.find(:all) line in apps/controllers/product_controller.rb. Because Rails 2.1 has REST baked right in, you can now see the respond_to block in your controller.
# GET /products
# GET /products.xml
def index
@products = Product.find(:all)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @products }
end
end
Here, you can see that for the index method, the index file (located at /products) will return both HTML and XML. You have just accomplished two tasks with one command: You generated the scaffold code and created the RESTful API. Good stuff. After you fire up Mongrel and test your controller actions, you'll turn your attention to configuring the application to use Amazon S3.
Time to go to the Product model and update it to work with the Paperclip plugin. Here's what your Product model should look like:
class Product < ActiveRecord::Base
has_attached_file :photo,
:styles => { :medium => "300x300>",
:thumb => "100x100>" },
:storage => :s3,
:s3_credentials => "#{RAILS_ROOT}/config/s3.yml",
:path => ":attachment/:id/:style.:extension",
:bucket => 'introtoruby_development'
end
Note: Change your bucket name to the bucket in which you want to store your images.
That's it: No additional configuration required. You are now ready to rock. Open up a terminal session, change to the root directory of your application, type script/server, and then go to http://localhost:3000/products.
Create a new product and upload the image. If you look in your development database, you'll see the record for the uploaded file - photo_file_name. The Paperclip plugin also provides type and size information. To confirm that the image was uploaded to Amazon S3, add the following code as a tag in the show form:
<%= image_tag @product.photo.url %>
When you pull up the show page, the view retrieves the image from Amazon S3 and displays it on the page. Quick and simple.
Only an App
What do you do when all you have is an application? Deploy it, of course! Depending on your level of comfort, you have several great options for deploying the application onto Amazon EC2:
| Company | Required skill level | Deployment options |
|---|---|---|
| Morph Labs | Beginner | Use a Capistrano deploy file that they provide. |
| Heroku | Beginner | Create an app via their interface or import an existing application. |
| RightScale | I know a bit | RightScale provides the most control over your deployment with a full suite of tools. When the environment is set up, you can install and update your application with the click of a button. |
| PoolParty | I know a bit | They are working on Capistrano deployment, so for now, it's manual. |
| Elasticfox Firefox Extension for Amazon EC2 | I know a bit | The extension helps to create and manage instances. Deployment is done by using Capistrano. |
| Amazon Web Services | Expert | Command-line tools. |
Time for an Upgrade
You can deliver true power to your applications by using a combination of AWS solutions. Is doing so difficult? Not with Ruby and Rails. The RightScale Ruby gem makes it ultra simple. One easy way to extend the application that you built is to use Amazon SimpleDB (rather than MySQL) as the database. Martin Rehfeld has written an excellent article that shows you how to do just that.
Conclusion
Today's web-based applications are required to provide more storage, more compute power, and a greater level of reliability than ever before. With a pay-as-you-go model, AWS, combined with freely available Ruby gems and Rails plugins, provides enterprise-class capability to Ruby on Rails applications. Using the power of Ruby on Rails and RESTful web services, developers can get up and going in hours instead of days. Try it!
Learning More About AWS
This article highlights a few aspects of working with AWS. Visit the following web sites for resources on Ruby and Rails and to learn more.
AWS Resources
- Learn more about each web service on the AWS web site.
- The Developer Connection web site for AWS developers includes forums on AWS, a solutions catalog with examples of what your peers have built, and more.
- Part of the Developer Connection web site, the Resource Center has links to tutorials, code samples, technical documentation, and other resources for building your application on AWS.
Resources for Ruby and Rails Developers
- PeepCode Rails 2.1 PDF
- RightScale Ruby gem Documentation on RubyForge
- TopFunky REST Cheat Sheet
- PeepCode REST for Rails 2.1 Screencast
- Paperclip plugin from thoughtbot
- Ruby on Rails development blog from Atlantic Dominion Solutions
Ruby and AWS Real-World Examples
The following web sites use Amazon Web Services and Ruby on Rails:
References
- Amazon Web Services—Home Page
- Agile Web Development with Rails (Third Edition)—Sam Ruby, Dave Thomas, David Heinemeier Hansson, et al.
- Paperclip—thoughtbot
- Paperclip and Amazon S3—Clayton Lengel-Zigich
- The Ruby Roundup—Bill Siggelkow and James Mitchell
- SOAP Definition—Wikipedia.com
About the Author
After 8 years as an MCSE and project manager, Robert Dempsey jumped from IT management and PHP/Visual Basic.NET development to Ruby on Rails. He is the CEO and Founder of Atlantic Dominion Solutions, a web development firm specializing in Ruby on Rails, and the founder of Rails For All, a not-for-profit organization dedicated to educating business and developers about Ruby on Rails. In addition, Robert presents on a regular basis at the Orlando Ruby Users Group, and talks to Java user groups on topics including JRuby and Ruby on Rails.