Incorporating Rails Components

As Rails is built up of many modular components, these components can be used individually just as they can be used as a framework. Here we will see how the pieces that make up Rails can be used in other Ruby code. We will walk through two modular components of Rails, ActiveRecord and ActionMailer, and see how to use them in standalone applications.

ActiveRecord is perhaps the easiest component to decouple from the rest of Rails, as it fulfills a purpose (object-relational mapping) that can be used in many different places. The basic procedure for loading ActiveRecord is simple; just define the connection, and then create the classes that inherit from ActiveRecord::Base:

	require 'rubygems'
	require 'active_record'

	ActiveRecord::Base.establish_connection(
	  # connection hash
	)

	class Something < ActiveRecord::Base # DB table: somethings
	end

The establish_connection function takes a hash of parameters needed to set up the connection. This hash is the same one that is loaded from database.yml when using Rails, so you could just pick up that file and load it:

	require 'yaml' # Ruby standard library
	ActiveRecord::Base.establish_connection(YAML.load_file('database.yml'))

If you are used to the features of edge Rails, you may not want to stick with the latest gem version of ActiveRecord. To use the latest edge, first check out ActiveRecord's trunk from Subversion:

	$ svn co http://svn.rubyonrails.org/rails/trunk/activerecord \
	         vendor/activerecord

Then, just require the active_record.rb file from that directory:

	require 'vendor/activerecord/lib/active_record'

Often, a console or desktop application needs to store persistent data, whether it be preference data or application data itself. A common solution is to use YAML, which can marshal and unmarshal most Ruby objects (round trip), while also being human-readable. However, YAML is verbose compared to binary data formats, which may be an issue when storing larger amounts of data. SOAP::Marshal from Ruby's standard library is similar; it can serialize objects into an (often quite verbose) XML representation. This approach has similar benefits and drawbacks to YAML.

Another option is to use Ruby's Marshal module, which dumps Ruby objects into a more concise byte stream. This uses less space, but it can be brittle. Though efforts are made to maintain backward compatibility across major Ruby versions, Ruby 1.9 has a new Marshal format that is not completely interoperable with Ruby 1.8.

For a more structured approach to persistent data storage, SQLite and ActiveRecord can provide a helpful balance. The data schema must be defined first and acted upon by a constrained set of operations (those permitted by SQL DML). But these constraints pay off; as the data store is completely separated from the application, the two halves can evolve separately. There is no need to recode data when an application is upgraded, save for application-level data changes.

Using ActiveRecord for this purpose is simple; just open a connection to a SQLite file (which will be created if it does not exist), and define the appropriate ActiveRecord classes.

	require 'rubygems'
	require 'active_record'

	ActiveRecord::Base.establish_connection(
	  :adapter  => :sqlite3,
	  :database => "db.sqlite3"
	)

	class Client < ActiveRecord::Base
	  has_many :conversations
	end

	class Conversation < ActiveRecord::Base
	  belongs_to :client
	end

	# Sample usage:
	def time_log
	  Client.find_all_by_active(true).each do |client|
	    # this uses ActiveRecord::Calculations to grab the sum
	    # in one SQL query
	    hours = client.conversations.sum('hours')

	    # format string gives us a nice table:
	    # First Client         5.00
	    # Another Client      12.40
	    printf "%-20s%5.2f", client.name, hours
	  end
	end

Using ActionMailer to send emails from outside of Rails is a simple process as well. It requires slightly more configuration, but not by much. First, load the framework, either using RubyGems or a newer Subversion checkout:

	# gem version
	require 'rubygems'
	require 'action_mailer'

	# or edge version
	require 'vendor/actionmailer/lib/action_mailer'

Next, set the outgoing mail server settings. All settings are optional.

	# Default is :smtp; also accepts :sendmail or :test
	ActionMailer::Base.delivery_method = :smtp

	ActionMailer::Base.server_settings = {
	  :address        => 'localhost',
	  :port           => 25,
	  :domain         => 'example.com', # HELO example.com
	  :authentication => :cram_md5,
	  :user_name      => 'me',
	  :password       => 'secret'
	}

ActionMailer needs a template directory in which to look for email templates:

	ActionMailer::Base.template_root = 'views'

Mailer classes and their email templates are defined just as they are in Rails:

	class Mailer < ActionMailer::Base
	  def quota_exceeded_notification(user)
	    from        "System Administrator <root@example.com>"
	    recipients  name_and_email(user)
	    subject     "Your account is over the quota"
	    body        {:user => user}
	  end

	  private

	  # "John Smith <jsmith@example.com>"
	  def name_and_email(user)
	    "#{user.full_name} <#{user.email}>"
	  end
	end

The template follows the usual pattern, and is located under our template root, in views/mailer/quota_exceeded_notification.erb:

	Dear <%= @user.name %>,
	Your account is over its storage quota. You are currently using
	<%= human_size(user.storage_used) %>, and your limit is
	<%= human_size(user.account.quota) %>.

	Please reduce your usage within 5 days or we will reduce it for you.

	Regards,
	The Management

Now, this Mailer class can be used just as if it were inside a Rails application. We'll look at one possible application for this next.