Rails 3: First Impressions from a Veteran Ruby on Rails Guy, Page 2
You Can Easily Use Alternative Data Stores
You don't have to use ActiveRecord because Datamapper and Sequel are also supported out of the box. You can also use alternative backend datastores. I'll show you an example using the MongoDB data store with the Mongoid object relational mapper (ORM) and also another example using the Datamapper library. The Mongoid developers hook into the Rails 3 bootup process (that uses so-called "railties") to make MongoDB as easy to use as officially supported ORMs.
Using MongoDB with the Mongoid library
I am an extremely enthusiastic user of MongoDB both as a datastore for Web applications and for large-scale data analytics, which usually involves all write operations going to a master Mongo and queries used for analytics getting spread out over one or more read-only slaves. Using both read/write masters and read-only slaves is a useful deployment use case, so I am going to use the Mongoid ORM specifically in this example because it supports automatically sending all writes to the master and all reads to a slave.
If you want to work through this example, you need to install a few gems before building a new Rails project: mongoid, bson, and bson_ext. We will add these to the generated GemFile after creating a new Rails project:
rails new mongoid_example # optional: --skip-activerecordcd mongoid_example
If you add
--skip-activerecord then your Rails application will be created without adding in ActiveRecord and its support. In this example I'll show you how to explicitly remove ActiveRecord.
You also need to edit your GemFile and add the following (gem versions may change):
gem "mongoid", "2.0.0.beta.17"gem "bson_ext", "1.0.4"
You can then make sure that all of the bundled gems in your GemFile are installed, and then create a mongoid configuration file:
bundle install rails generate mongoid:config
This generates a config/mongoid.yml file that uses a single mongo on localhost; configuration options for setting up slaves are commented out. Now, if we generate a model, it will use MongoDB. If you don't need a relational database, then use the instructions on the Mongoid Web site for disabling ActiveRecord; i.e. edit the file config/application.rb to comment out the line
require 'rails/all' and add the railties that you need:
# require 'rails/allrequire "action_controller/railtie"#require "action_mailer/railtie"require "active_resource/railtie"
We can now generate a scaffold like the last example, but using MongoDB:
rails generate scaffold Post title:string content:stringrake db:migraterm public/index.html
This is not much of an example for using the features of MongoDB but it will get you started.
Tip: You usually will not normalize data in MongoDB storage as you do in a relational database. As a simple example, if your object models include a user and posts created by that user, it is good MongoDB style to have a user document schema that also contains data for the posts for the user. This sometimes leads to duplicated data. This is great for runtime performance because you should configure a mongo service with enough memory to keep all indices in memory, so reading a user's data (including posts) might cost just one disk seek and one read operation. The downside is dealing with updating and deleting replicated data.
Fortunately, with Model classes, it is reasonably easy to put all of the code to create, update and delete data for a model and nested objects in one place.
I like to use Datamapper in both Web apps and non-Web apps because of its flexibility to use multiple backends (relational databases, MongoDB, etc.), and you can span object models over multiple backends. While it has always been straightforward to use Datamapper with Rails, it got easier with Rails 3. You start by using a Rails template provided by the Datamapper developers when creating a new project:
rails new datamapper_example -m http://datamapper.org/templates/rails.rb
Note: Check the generated GemFile and make sure that it is using the version of Rails that you want, and edit the version if required. (I had to change the Rails version from 3.0.0.rc1 to 3.0.0.)
As we did in the last example, create a test scaffold, do a data migration. A route for
resources :post should have been created for you automatically when generating the scaffold (this was not done when using Mongoid).
Using Controller Responders
By using class-wide responders, you can make controllers that respond with XML, JSON, and so on much more precise. Responders can also be used to factor out common controller code for reuse. Here is a short example that just uses responders built into Rails:
class PostsController < ApplicationController::Base respond_to :html, :xml, :json def index respond_with(@users = User.all) endend
Developers are starting to write and distribute other responders, for Flash generation for example. If your Web application had to support generation and delivery of custom data formats or data standards like RDF, it would make sense to write a responder to handle new datatypes.
Rails was originally designed to make most decisions for you, offering simple, effective ways to solve common problems. With Rails 3, it certainly has moved away from this philosophy a little because now the platform can be highly customized. In the future I look forward to being able to use just the parts of Rails I need for any given application.
I have often used the Sinatra project for implementing Web services and very simple Web applications. As I get more experienced with the new features of Rails 3, I may start using Rails for very light projects instead of Sinatra (or I might stick with Sinatra).
About the AuthorMark Watson is a consultant specializing in Web applications, text mining, and artificial intelligence development. He is the author of 16 books and writes both a technology blog and an artificial intelligence blog (see www.markwatson.com). He lives in the mountains of Central Arizona with his wife Carol and a very feisty Meyers Parrot.