Rails 3.1 Engines: Part II – The gem

Rails 3.1 Engines: Part II – The gem

So you successfully read through the first part of this article, Rails 3.1 Engines: Part I – The engine? Then let’s pack our engine into a gem and load it into a host app!

Don’t forget that you can find the whole example engine code on my GitHub account.

Let’s share our engine

To this point, our engine is pretty isolated and useless: we want to load it into a host Rails app (or into many of our Rails apps, or even make it public and let other people come into the joy of having a nice fieldset helper method in their views!) to use its functionality from there. This is most comfortably done by making a gem out of it and deploying it to GitHub. So let’s see what steps are required for this.

Meta Information

We already looked at the small_view_helpers.gemspec file before, and there’s plenty of more information that we need to add before we can release our engine as a gem.

s.authors = ["Joshua Muheim"]
s.email = ["josh@example.com"]
s.homepage = "http://www.muheimwebdesign.ch"
s.summary = "Some useful HTML generating view helpers."
s.description = "So far, we are excited to offer you the fieldset helper. More to come soon!"

Then commit to git:

$ git add .
$ git commit -m "Added engine info"

Let’s build this gem!

Just do the following (from your engine’s root directory):

$ rake build

This build our gem and stores all relevant info in the newly created pkg/simple_view_helpers-0.0.1.gem file, from where we can install it in our own gemset immediately:

gem install pkg/simple_view_helpers-0.0.1.gem

Push to GitHub

To be able to release the gem to the public, we have to push it to GitHub first. First you have to create a new repository from within your GitHub account:

Then simply push our existing local repository to the newly created GitHub repo:

git remote add origin git@github.com:sientia-jmu/simple_view_helpers.git
git push -u origin master

Release it to RubyGems.org

One final step until everybody in the world can use our gem within his Rails project:

$ gem push pkg/simple_view_helpers-0.0.1.gem

Enter your RubyGems.org email and password, and our gem is immediately available at https://rubygems.org/gems/simple_view_helpers.

Include it into our host app

Create a host app

Now we want to use our fieldset helper within a “real” app. Let’s just create one for this purpose:

$ cd .. # Step outside the engine folder!
$ rails new my_host_app --skip-test-unit

We skip Test-Unit once again, as we want to use RSpec again (see here how to set RSpec up properly, I won’t go any deeper into testing here).

$ cd my_host_app
$ git init
$ git add .
$ git commit -m "Initial base"

Create a scaffold

Let’s just create a simple scaffold where we can use our fieldset helper later:

$ rails generate scaffold Post name:string title:string content:text

Add our gem to the Gemfile

To use our simple_view_helpers gem, we simply have to add it in our host app’s Gemfile:

gem 'simple_view_helpers'

Then run the bundle command:

$ bundle

This would normally fetch the gem from RubyGems.org, but as we installed it on our system before, it’s already available.

Let’s add a call to our fieldset helper within app/views/posts/index.html.erb:

<%= fieldset 'The gem works!' do %>
 It's incredible!
<% end %>

Now run the database migrations and start the development server (be sure to stop the one from the time developing the engine first, if it’s still running):

$ rake db:create
$ rake db:migrate
$ rails s

Now go to localhost:3000/posts and you should see your helper in action! :-)

How to update the gem

Like most other projects, a gem is never really perfect and is undergoing a lot of changes day by day. So how do we change the engine’s functionality and then release it as a new gem version? It’s pretty easy.

Change the engine’s functionality and release a new gem version

Let’s just say we want our fieldset to have a standard CSS class applied:

<fieldset class="simple_view_helpers">
  <% if legend %>
    <legend><%= legend %></legend>
  <% end %>
  <%= block %>
</fieldset>

Let’s also add some CSS styling! Create a file simple_view_helpers/app/assets/stylesheets/simple_view_helpers/fieldset.css with the following content:

fieldset.simple_view_helpers {
  background-color: #ddd;
}

And to make it available to our dummy app, we have to link it into the asset pipeline in spec/dummy/app/assets/stylesheets/application.css like so:

*= require_self
*= require simple_view_helpers
*= require_tree .

And we have to create a app/assets/stylesheets/simple_view_helpers.css file with the following content:

/*
 *= require_self
 *= require_tree .
 */

Refresh you browser, and see the style in action!

(Notice: if you add any JavaScript to your engine, you will have to link it to the asset pipeline in a similar way; see Rails Asset Pipeline for more info.)

Now we simply commit the changes to GitHub, then change the version number in lib/simple_view_helpers/version.rb to whatever we see fit (most probably 0.0.2), make another commit to GitHub with a useful message such as “Bumped to 0.0.2″ and then run rake release again.

But there’s an easier way to do this using the gem-release gem. Still, we first have to commit our changes to GitHub (be sure you’re in the root path of your engine):

$ git add .
$ git commit -m "Added CSS class to fieldset tag"

Now let’s install gem-release:

$ gem install gem-release

Now simply do this:

$ gem bump --push --release

Bump increments the gem’s version number (stored in lib/simple_view_helpers/version.rb), while the --push option pushs to GitHub, and the --release option tells RubyGems.org about the new version.

Updating the local gem

To use the newest version of the gem, we have to update it on our system:

$ bundle update

Now restart your server, and our <fieldset> tag will have a simple_view_helpers class, yaayyy (but the styles are missing, we forgot to load it into our host app’s asset pipeline (we only did it for our dummy app); just add the same *= require simple_view_helpers call to your app/assets/stylesheets/application.css file)!!

But this is pretty awkward! Isn’t there a faster way??

You’re right, this process is very slow, and especially when developing engines that are bound tightly to the host app it would often be more useful to develop it straight from within the host app so all the changes to the engine are immediately reflected there.

The good news: this is perfectly possible! The bad news: I don’t know any bad news about this topic. Let’s just do it!

Developing engine and host app side by side

There’s a nice trick to specify in the Gemfile that a gem shouldn’t be loaded from RubyGems.org, but from a local path on your own computer:

gem 'simple_view_helpers', path: '../simple_view_helpers'

That’s it. Restart your Rails server, and from now on, every change you do to the engine will be immediately reflected in our host app. Great stuff!

What about production?

This works great as long as we’re simply developing our host app and not deploying it. Because when deploying, we won’t have our own engines installed locally so we could load them with the :path option. In our company we’re using something like this in our Gemfile to work around this problem:

local_gems = ['simple_view_helpers']
  local_gems.each do |local_gem|
  local_path = "../#{local_gem}"
  options = File.directory?(local_path) ? {path: local_path} : {}
  gem local_gem, options
end

We simply check whether there is a local copy of the needed gem (as it is in our development environment), and if not, it gets it normally from RubyGems.org.

If your gems are not released on RubyGems.org (maybe because they must not be public and reside in a private GitHub repo), you can slightly modify the options = ... line:

options = File.directory?(local_path) ? {path: local_path} : {git: "git@github.com:your_company/#{local_gem}.git"}

And just for completeness: you could also check for the RAILS_ENV environment variable, something like this:

if ENV['RAILS_ENV'] == "production"
  gem 'my_gem', git: "git@github.com:my_company/my_gem.git"
else
  gem 'my_gem', path: '../my_gem'
end

You can even reference gems you are actively developing under each other using :path, and they will always be up to date when reloading the page, so you can develop as many engines in parallel, as you wish. :)

What’s next?

So far, we have seen how to create Rails engines and how to load them into a host app. But there’s some more things we can do to spice up our daily routine, so let’s head over to part III or this series: Rails 3.1 engines: Part III – The environment!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>