Blog

Saturday, December 26, 2009

Maintenance and Monitoring Checklist for your Ruby on Rails Application

Before your application goes into production there are some maintenance and monitoring tasks you need to consider. These tasks include:

  1. Backups
  2. Log Rotation
  3. Monitoring
  4. Scheduled Jobs

Backups

You need to maintain offsite backups for your application’s:

  • database,
  • shared assets, and
  • source code repository.

I have a set of backup scripts (executed via a nightly cron process) that dumps the database for each of my applications, which are in turn archived, and then downloaded to my server. Shared assets, those files uploaded via your application typically located in the public folder, are synced to my server and then archived. As my source control is hosted on Github, I don’t need to worry about backing this up.

Log Rotation

Installing the logrotate package will take care of your system logs, but you will need to set up log rotation specifically for your Rails application. I typically deploy my applications using Moonshine, a server provisioning tool from the folks at Rails Machine, so log rotation has been addressed. There maybe some other logs that are generated by your application such as Ferret server. Consider these files in your applications log folder:

  • production.log
  • mongrel.log (not necessary if you deploy on Passenger of course)
  • ferret.log (other log producing components)

Monitoring

Monitoring is split into four categories:

  • Monitoring Services
  • Performance Monitoring
  • External Monitoring
  • Exception Notification

Monitoring services such as Apache, MySQL or even processes that are apart of your application, such as the Ferret server can be monitored by Monit or God. If these services go down unexpected, they are automatically restarted by the monitor.

Performance Monitoring can be broken down further into Server and Application Performance. Munin is a good candidate for the first, while service providers such as New Relic and Scout are good for the later. With Server monitoring you will also want to consider the consumption of resources. For example, how much disk space do I have left? How much RAM do I have left?

An external monitoring service is useful. Services such as Monstatic, 24×7, or Pingdom can notify you when your server is inaccessible.

Finally, the Exception Notification plugin suits my needs to be notified of an exception, but you may prefer a service to manage this such as Airbrake Bug Tracker or Exceptional.

Cron Jobs (Schedule Jobs)

Sometimes you will need to run scheduled jobs against your application such as managing recurring subscriptions. Make sure these are installed and running. It’s best to make these a part of your deployment using whenever or again, my preference is using Moonshine.

If you are you are using ActiveRecord-based sessions in your application, then you will definitely need to setup a task to remove old records in the sessions table.

Sunday, December 13, 2009

Designing Dwayne Brown Studio: Focus your website on key customer tasks

Summary: Focusing your website on key customer tasks maximizes your budget and lead generation.

I position myself as a Ruby on Rails developer in Montreal for database web applications. However, I often have to put on other hats for a project, depending on the budget and the team members involved. In the case of Dwayne Brown Studio, I was responsible for not only the programming of web pages and custom content management system, but I was also responsible for the visual design, interaction, and architecture of the site.

When defining the architecture of a website, I use a customer-centered design approach. A customer persona, a fictional representation of the target audience for your website, is defined. In this case, it was Helen Brightman, a communications manager for a private school in Ottawa. The key task for Helen is to navigate the portfolio to identify photos related to education.

Online photography portfolios are often poorly designed. They suffer from overly “creative” navigation tools, small or abstract thumbnails, and no captions, making it difficult for people like Helen to identify relevant imagery. Even Dwayne’s previous website suffered from some of these issues.

Using an agile development technique know as Do The Simplest Thing That Could Possibly Work, I focused my efforts on creating a simple portfolio that was easy to navigate.

Three guiding objectives were used in designing the portfolio:

  1. Help Helen identify relevant imagery
  2. Allow Helen to scan photos quickly
  3. Reduce pogosticking—jumping back from thumbnails to detail pages

This resulted in the following designs:

Dividing the portfolio into sections where related photos are grouped together, allows Helen to easily identify relevant imagery:

Large thumbnails on a scrolling page allows Helen to scan photos quickly:

A navigation control allows Helen to navigate through photos without having to return to the thumbnails page:

By identifying the key tasks of your customer, you can focus the web design process on what really matters and avoid “creative” solutions that just don’t work for your target audience.

Sunday, December 6, 2009

Use Stubs and Expectations to Simplify Testing with Named Scopes

Summary: Use stubs to return the original class for a named scope, and use expectations to specify scope requirements to simplify testing

Testing with chained named scopes that have complex attributes such as conditions can be tricky. I try to combine named scopes with clear names to communicate intent, rather than have long chains floating around in code. This can be done simply by creating a plain old method, with scoped procedures or this hack. This is a good way to keep named scopes DRY when using a specific combination across your application code. Of course you should test your named scopes.

However, consider the situation when adding a simple extension to your named scope that doesn't warrant a new named scope or combination. As an example I have the following code in a catalog application:

class Catalog
  ...
  # Returns a set of ordered page numbers based on the product page numbers
  def page_numbers
    Product.printable.find(:all, :select => "DISTINCT products.page_number").
      collect {|product| product.page_number}
  end
  ...
end

The printable named scope is quite complex and has been tested. I wanted to avoid duplicating test data (although factories would help here). For this method I was only concerned about a collection of products and it's page numbers, not that fact it was scoped. But it is a requirement that the products are scoped to only those that are printable.

Stubbing and setting expectations help us simplify the test and express the requirement. By stubbing the named scope and returning the Product class we can focus on creating test data for which we are concerned -- products with page numbers. The expectation "find printable products", ensures that we have scoped products as required.

class CatalogTest < ActiveSupport::TestCase

  context "with page numbers" do

    setup do
      Product.stubs(:printable).returns(Product)      
    end

    context "generally" do
      setup do
         Catalog.page_numbers
      end

      before_should "find printable products" do
        Product.expects(:printable).returns(Product)
      end      
    end

    context "when no products" do
      should "return an empty array" do
        assert_equal [], Catalog.page_numbers
      end
    end

    context "when multiple products" do
      setup do
        Factory(:catalog_with_page_numbers)
      end

      should "return an array of page numbers" do
         assert_equal [0, 1, 2, 3], Catalog.page_numbers
      end     
    end
  end
end

Although setting this expectation increases coupling to the implementation, we are justified in that it expresses the requirement in the test example.

Combining Named Scopes

This is a useful way to combine named scopes:
# add to lib/named_scopes.rb for example
class ActiveRecord::Base
  def self.named_scopes(name, &block)
    metaclass.send(:define_method, name) {|*args| block.call(*args) }
  end
end 
Usage:
class Product
  named_scope :live, :conditions => {:active => true}
  named_scope :created_recently, :conditions => [created_at > ?", Time.now - 10.days]
  named_scopes(:recently_released) { live.created_recently  }   
end
Source: http://august.lilleaas.net/combining_named_scopes (no longer available)

Please note this blog is no longer maintained. Please visit CivilCode Inc - Custom Software Development.