At work we are starting to switch our legacy systems from an old server side language (that shall not be named) to Ruby on Rails. Rails offers a ton of convenient features. However, the one thing that had me stumped was how to make reusable widgets. I’m talking about making gems full of reusable components that will be used in multiple projects. This may sound trivial, but what if you want to make reusable header, menu, sidebar, and footer widgets that are in a gem? Fortunately as of Rails 3.1 we have some help.
Start your engines!
Rails has feature called engines that allows you to wrap a Rails application up and share it with other rails applications. Packaging an engine up into a gem gives a nifty way to redistribute reusable rails components across applications. Confused? Here is a diagram.
Everything in blue is a reusable widget that is from a gem. The brown portion is the current application. You may be asking why is this useful? Let me give you an example. In our organization we have lot’s of applications. Calendaring, time tracking, invoicing, etc. Each application needs the same header, footer, menu, etc. To copy this code to each application wouldn’t be very DRY. Not to mention the maintenance involved when one of these components requires a change.
Creating a mountable gem
To get started we need to create a gem that can be mounted into another rails application. We can create this gem using the command below. The –mountable switch will make this gem using a mountable engine. The –dummy-path=spec/dummy switch is optional, however it will create a special dummy folder (with a dummy rails app) for running rspec tests to test our mountable engine gem.
rails plugin my_core_gem --mountable -T --full --dummy-path=spec/dummy
Now that our gem is setup the next step is to open the gemspec file and edit all the TODO: information.
Cell that gem!
In order to separate our components into little pieces, we need to organize them within our mountable engine. To do this we will use a rails project called “cells”.
For more info
To add cells we need to add the following to our gemfile.
Then we need to run “bundle update” to retrieve the cells gem.
Next, we want to create a cell. Let’s create the header. We can run the following.
rails generate cell header show
Rails will create a header cell (in app/cells) with a show action. In the app/cells/header folder it will create a show.html.erb file. This will be our partial layout for the show action. Let’s throw our header markup in there.
Publish our gem locally
Next we want to install our gem locally so that we can bring it into another project. If do not want your gem to be local and instead want it on a server you will need to to set that up. However, setting up a private gem server is out of the scope of this article.
To install our gem locally run the following
Consuming our gem from another app
Finally, we will want to pull in our mountable gem into a different project. To do this we will need to create a new project
Then we need to edit the gemfile to add our new mountable gem and the cells plugin. Like so…
Again, we will need to run “bundle update” to pull in our cells, and core gems.
Lastly, we can use our cells in our layout files using the render_cell method. Rails is smart enough to search the mounted gem which contains our header, sidebar, and footer gems.
<%= stylesheet_link_tag "gem_consumer" %>
<%= stylesheet_link_tag "application", :media => "all" %>
<%= csrf_meta_tags %>
<%= render_cell :header, :show %>
<%= yield %>
<%= render_cell :sidebar, :show %>
<%= render_cell :footer, :show %>
A sample application.html.erb file
Building reusable components for Rails doesn’t need to be complicated and difficult. Using mountable engines, cells, and gems we can build components that are reusable for others across multiple applications.