Railsmagazine60x60 An Overview of Refinery - a Rails CMS

by David Jones

Issue: Vol 2, Issue 2 - Field Day

published in October 2010

David jones

David Jones created Refinery CMS back in 2004 and is the Technical Partner at Resolve Digital, a Rails consulting firm with offices in California and New Zealand.

David also co-founded RefineryHQ.com, a fully hosted version of Refinery that allows you to create a site, customise your design and enter content all from your browser.

Back in 2004 there weren't any suitable Rails CMS options, so I created my own. Building site after site, I eventually started to combine the common elements into a project called mTools.

mTools was closed source for 5 years before being renamed to Refinery CMS and released as open source in mid 2009.

On August 29th, 2010 — two days after Rails 3 was released, Refinery announced full Rails 3 support, making it the first popular CMS to support Rails 3.

At Resolve Digital we've now built over 70 sites using Refinery and every time we sit down with the end user we gather feedback and make it easier for them to use. It's this feedback which has made Refinery different from other CMS options available.

But the end user experience is only half the story. To have a truly fantastic CMS, it needs to be a delight to the programmer.

Refinery is based off a few key principles:

  • "The Rails Way" where possible
    Refinery embraces conventions used in Rails, allowing any Rails programmer to leverage existing knowledge to get up and running quickly.
  • End user focused interface
    Refinery's user interface is simple, bright and attractive so end users feel invited and not overwhelmed.
  • Awesome developer tools
    Refinery makes it easy for developers to add functionality and change the front end look and feel.
  • Encourage and Help Others
    Refinery has an active community on Google Groups and IRC. If you ever have a question there is someone able and willing to assist.

What do you get out of the box?

  • Page management
    Manage site structure. Edit content using a WYSIWYG editor.
  • Image and file management
    Upload and insert images. Store and manage files. Use local storage or Amazon's S3.
  • Contact form & inquiry management
    Collect and manage inquiries from a contact form. Automatically filters spam.
  • User management
    Set up multiple users and manage which areas they can access.
  • Plugins
    Extend Refinery by creating Rails style engines using the engine generator.
  • Settings
    Manage site settings such as your site name and Google Analytics tracking code.
  • I18n Translations
    English, Danish, German, Spanish, French, Italian, Dutch, Norwegian Bokmål, Portuguese and Slovenian.

Installation

First you'll need to install the bundler and refinerycms gems:

gem install bundler

gem install refinerycms

Next create your Refinery project. This will create your database, install any required gems and set up a standard Rails app with Refinery included:

refinerycms path/to/project

Note this will by default setup your application with a SQLite database. The refinerycms command also supports --database which allows you to specify mysql, postgresql or sqlite3. Here's an example of Refinery being setup with MySQL

refinerycms path/to/project --database mysql

Finally, start your web server:

cd path/to/project

rails server

Visit: http://localhost:3000 in your browser to set up your first Refinery user.

Having Problems? Watch the "How to Install Refinery" screencast (http://tutorials.refinerycms.org/tutorials/how-to-install-refinery).

Just want to have a play? Check out the demo site at http://demo.refinerycms.com/refinery

Extending Refinery with Engines

Refinery ships with an engine generator that makes adding your own functionality a breeze. It works just like the Rails scaffold generator.

rails generate refinery_engine singular_model_name attribute:type [attribute:type ...]

Example: Products Engine

In this example, we're going to generate a new engine that will let us manage products. Each product will have a title, description, image and downloadable brochure. To create this new engine, at your application root run:

rails generate refinery_engine product title:string

  description:text image:image brochure:resource

bundle install

rake db:migrate

Finally, restart your web server if it is already running.

This single command will generate a new engine located at vendor/engines/products with all of the necessary backend views and some basic front end views. It even makes a customised form for adding and editing products, based on the field types specified which looks like this:

You can see that out of the box a new tab called "Products" has been added for the end user to manage their products. Multiline text fields automatically use the visual editor.

Refinery Plugin Generator Field Types

  • text - multiline text area with a visual editor
  • image - link to an image picker dialogue
  • resource - link to a resource picker dialogue
  • string and integer - standard one line input box
  • datetime, date and time - standard time and date drop downs

Now that our products engine is up and running, let's go a little deeper to see how an engine actually works.

The Anatomy of a Refinery Engine

Think of a Refinery engine as a mini Rails application running in your vendor/engines directory. Each engine specifies its own routes in the config directory and has its own views and controllers in its own app directory. Engines can even serve up their own images, stylesheets and javascripts from the engines public directory.

  products
|- app
| |- controllers
| | |- admin
| | | |- products_controller.rb
| | |- products_controller.rb
| |- models
| | |- product.rb
| |- views
| |- products
| | |- index.html.erb
| | |- show.html.erb
| |- admin
| |- products
| |- _form.html.erb
| |- _product.html.erb
| |- _sortable_list.html.erb
| |- edit.html.erb
| |- index.html.erb
| |- new.html.erb
|- config
| |- routes.rb
| |- locales
| |- en.yml
|- lib
|- products.rb

Engines That Come With Refinery

The Refinery project itself is actually broken up into 7 engines:

  • authentication
  • core
  • dashboard
  • images
  • pages
  • resources
  • settings

Each of these Refinery engines are very similar to the product engine above.

Crudify: The Backbone of Refinery Engines

Any Refinery engine, even the built-in ones, that focus on Create, Read, Update and Delete are driven by crudify. Crudify is a highly reusable module included with Refinery that gives you all the standard CRUD actions as well as reordering, searching and paging.

If we take our products engine and look at the Admin::ProductsController. It looks like:

class Admin::ProductsController < ApplicationController

  crudify :products

end

Most of the time crudify's defaults are bang on, but if you need to, you can easily customise how it works.

Let's just say we wanted to create a news engine. We'll want to order by date not position. We'll want to add the ability to make draft posts and they won't be sortable because they will order by date. Here's how you'd specify that:

class NewsItemsController < ApplicationController

  crudify :news_item, :order => "created_at DESC",

          :conditions => 'draft = false',

          :sortable => false

end

Customising The Front End

One of Refinery's key principles is 'The Rails Way' where possible. This means as a Rails developer, Refinery tries to use every bit of knowledge you already have.

This principle is apparent with how you customise the front end. To do this you use regular ERB views in app/views, exactly how you would customise the front end of any Rails applicaiton.

Customising How Pages Are Displayed

When a Page is rendered in Refinery, the @page variable is available to use in your view.

Pages have what's called "Page Parts". A "Page Part" is a single piece of content linked to this page. By default pages come with two page parts, "Body" and "Side Body". You can easily add more parts if you need to all pages by default of individual pages (follow this tutorial for instructions: http://tutorials.refinerycms.org/tutorials/how-to-change-the-default-and-individual-page-parts)

Here's an example of pages/show.html.erb using the @page instance variable and rendering its body and side_body content:

app/views/pages/show.html.erb

<div id='body_content'>

  <h1 id='body_content_page_title'>

    <%= @page.title %>

  </h1>

  <div id='body_content_left'>

    <%= @page[:body] %>

  </div>

  <div id='body_content_right'>

    <%= render :partial => "/shared/random_fact" %>

    <%= @page[:side_body] %>

  </div>

</div>

Using the two page parts like this is quite common so Refinery provides you with a partial called content_page which gives you the same basic view. This partial is usually all you need. With content_page the view above can be replaced with:

app/views/pages/show.html.erb using content_page partial

<% content_for :body_content_right do %>

   <%= render :partial => "/shared/random_fact" %>

   <%= @page[:side_body] %>

<% end %>

<%= render :partial => "/shared/content_page" %>

You can use content_for :body_content_right or :body_content_left to customise what goes into those sections. In this case we only need to set the right body content because it's the only non-standard item in the view above.

Tips and Tricks

Updating to the Latest Version of Refinery

Commits to Refinery occur daily and minor releases often come out weekly so keeping your app up to date is very beneficial. Updates provide new features and bug fixes and are usually safe to use as we strive to maintain backward compatibility.

Updating your existing Refinery application is easy. First edit your Gemfile to reflect the latest version of Refinery (the latest version number can be found at rubygems.org/gems/refinerycms):

Gemfile

gem 'refinerycms', '= 0.9.8'

Next run bundler to install this new version

bundle install

And finally run the Refinery update rake task to pull in the latest migrations

rake refinery:update

Setting Up Google Analytics

Default Refinery layouts have Google Analytics support built in. To start using Google Analytics on your site all you need to do is go into the "Settings" area and edit the setting called "Analytics Page Code". Replace "UA-XXXXXX-X" with your Google Analytics code to get up and running.

Deploying on Heroku

First follow the Refinery installation instructions above. Next go to http://heroku.com/ and click "sign up" for a free account.

Now you've got a Heroku account, install the Heroku gem and provide your account credentials:

gem install heroku
heroku list

Now it's time to create your Refinery application using the built in --heroku option

refinerycms myapp --heroku

Watch the output for these lines

Creating Heroku app..
Running: cd /path/to/app/myapp && heroku create
Creating random-site-name..... done
Created http://random-site-name.heroku.com/

This will output the URL for your Heroku hosted Refinery application. Your application should now be live at http://random-site-name.heroku.com

Adding Amazon S3 Support

Create a bucket called my_app_production and uncomment this line in your Gemfile:

gem 'aws-s3'

Next tell Heroku about your new S3 bucket.

heroku config:add S3_KEY=123key S3_SECRET=456secret S3_BUCKET=my_app_production

That's it! Heroku will restart your site and it should be live with S3 support.

Not working? Read the detailed tutorial: http://tutorials.refinerycms.org/tutorials/how-to-install-refinery-on-heroku

Generate Thumbnails On The Fly

image_fu is a built in helper that allows you to instantly create thumbnails out of any image saved in Refinery. Refinery uses Dragonfly (github.com/markevans/dragonfly) which is an on the fly image server that serves an image up in any size you request and then aggressively caches it.

image_fu also cleverly outputs the image dimensions on the image tag to decrease browser rendering time and prevent the page from "jumping" while loading.

Thumbnail Examples

Scale this product image to fit inside a 500x500 box but maintain the proportions.

<%= image_fu(@product.image, "500x500") %>

#=> <img src='/path/to/image/beanie_front.jpg' width='500' height='400'/>

Scale and crop this product image to be exactly 400x300 pixels but maintain the proportions.

<%= image_fu(@product.image, "400x300#c") %>

#=> <img src='/path/to/image/beanie_front.jpg' width='400' height='300'/>

Scale to exactly 200x200, skewing the proportions.

<%= image_fu(@product.image, "200x200!") %>

#=> <img src='/path/to/image/beanie_front.jpg' width='200' height='200'/>

Summary and the Future

Refinery is a great option for content managing your next Rails site. It supports Rails 3, it's backed by Resolve Digital (http://resolvedigital.com) and has a thriving community that continuously improves the project.

It is currently at version 0.9.8 and will be releasing version 1.0 later this year. Be sure to go to http://github.com/resolve/refinerycms and click "watch".