@awead

Technical notes and explications of code

Filters, Not Overrides

When refactoring some controllers, I realized it’s much nicer to filter actions on controllers instead of overriding them.

Take for example a controller that exists somewhere in your application or in a gem:

1
2
3
4
5
6
7
8
class BaseController < ApplicationController

  def index
    @terms = Terms.all
    render_all_terms
  end

end

I need to use that controller in my application, but have to add some additional stuff to it to use in my views:

1
2
3
4
5
6
7
8
9
10
class MyController < ApplicationController

  include BaseController

  def  index
    @my_terms = MyTerms.all
    super
  end

end

This will work and I’ll have both @terms and @my_terms in my views. However, I find it’s nicer, and a little bit less invasive, if I can work around BaseController without having override it:

1
2
3
4
5
6
7
8
9
10
11
class MyController < ApplicationController

  include BaseController

  before_filter :get_my_terms, only: :index

  def get_my_terms
    @my_terms = MyTerms.all
  end

end

The end result is the same, but I’ve accomplished it without having to change BaseController at all, thereby leaving its public interface untouched.

Git

This is my usual workflow when submitting patches, or even working on my own applications. There’s been a lot of focus on Git branching models as of late. This is a good one, for example. Mine isn’t always that complicated, but no matter how many topic branches, my workflow is pretty much the same.

Start with a topic branch, and make sure you’re current:

git checkout master
git pull
git checkout -b fixing-a-bug

Start working. Take a break… oh, it’s the weekend? Okay, I’ll try again next week. Make a few stabs at it over the weekend and finally get it fixed next week. Now your git log probably looks like:

cdf5389ff978e4ba87150ca599fe6c4dcb6674b3 yay, done!
445cbfcf9ae8b18667474675213612c24a75190d ugh, no.
4e79c46f3e2410a09ddf609d442741e2cf1e8266 got it fixed!
f4f18f3117498f9e0735095fc7f44d63dc3557fe uh oh, this broke something
2a6ed6e963248a138996381f482ef500dea29bcf stashing changes
480ec8c17cb4ba535a97782167d73ba14528730e first stab

If you’re anything like me, some fixes are a journey and you sometimes end up going places you didn’t intend. Your git log may reflect this. Let’s clean this up:

git rebase -i HEAD~6

Now we can squash all those commits down to one, well-written and polished commit that makes it look like we knew exactly what we were doing all along. Of course, you don’t have to, it’s just easier. Sometimes I do squash to more than one commit if there are two different issues at hand and I want the comments in the commit log to reflect that; otherwise, it’s just one big one.

If we’ve been pushing up to Github along the way, we’ll need to force update origin since our commits have now diverged:

git push origin +fixing-a-bug

If you’re the only person working on this repo, you’re probably fine unless you’re Tyler Durden. Otherwise, check on any commits from origin or upstream if you’re using a forked repo. Then, get this into master and rebase against your topic branch:

git checkout master
git pull
git checkout fixing-a-bug
git rebase master

Since you’ve squashed down to one commit, you only have to go through the rebase once, as opposed to multiple times for each commit. Push any changes back up to Github and let Travis run the tests (if you’re using continuous integration):

git push origin fixing-a-bug

Once everything is green, merge your changes to master:

git checkout master
git merge fixing-a-bug

Since you’re merging one commit from the branch, this will be fast-forwarded, and the commit you’ve pulled in will appear with all the other commits in master as if there was never any branch. For small projects, this is probably fine. Alternatively, you can leave your branch in Github and submit a pull request. I’ve even done this on repos where I’m the only one working. It feels odd submitting a PR to yourself, but it helps document the branch and merge process.

If you want to preserve the branch/merge process and you don’t want to submit PRs, when you merge into master:

git checkout master
git merge fixing-a-bug --no-ff

The --no-ff is short for “no fast-forward”. This essentially preserves the merge as a separate commit in the log. Yes, it makes the log a bit longer, but it’s good for clarity because you’ll see the history of the branch and merge process.

Finally, clean up!

git push origin :fixing-a-bug
get remote prune origin

This deletes the branch in Github and cleans up your local clone.

A Passenger in a Passenger

Here’s something I didn’t know you could do, until today…

I have a public website deployed under Passenger, and I wanted to deploy a beta version under the same FQDN. The problem is, I’m not using a sub-uris for the site. Essentially I want:

http://foo.com/
http://foo.com/beta

To be independently deployed Rails apps under Passenger. I thought I was going to need to do some magic with Passenger config and some url rewrites, but after tinkering with the Passenger config files—and the ubiquitous Googling—I discovered I can do it very easily with Passenger alone.

The key is, I deploy the beta site within the public folder of the main site. Here’s what the Passenger config looks like:

/etc/httpd/conf.d/foo.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<VirtualHost *:80>

  ServerName foo.com
  SetEnv GEM_HOME .bundle
  DocumentRoot /var/www/rails/foo/public

  <Directory /var/www/rails/foo/public>
    AllowOverride all
    Options -MultiViews
  </Directory>

  RailsBaseURI /beta
  <Directory /var/www/rails/foo/public/beta>
    AllowOverride all
    Options -MultiViews
  </Directory>

</VirtualHost>

This assumes that you have one server deploying one webapp. Apache looks to the /var/www/html directory to serve out files, so I’ve symlinked this to the public folder of foo:

/var/www/html -> /var/www/rails/foo/public

The master branch of the git repo is located at /var/www/rails/foo. If you had deployed multiple apps on one server, your html directory probably has multiple symlinks to the different public folders of all your Rails apps.

In order to deploy the beta version, I clone a new version of the same github repo and pull down the relevant beta branch. I can then symlink this repo inside the current public folder and Passenger will serve out the new site from there. Here’s a quick synopsis:

mkdir /var/www/rails/beta
cd /var/www/rails/beta
git clone http://github.com/you/foo
cd foo
git checkout -t origin/beta
[run your normal install procedures]
cd /var/www/rails/foo/public
ln -s /var/www/rails/beta/foo/public beta

And that’s it. It’s a bit convoluted, but it got my out of my position where I was stuck with only one server name and no sub-uris.

No Breaks

New year, yes I know, it’s late. I’ve been doing lots of front-end and web design work which I readily admit is not my strong suit. However, I stick to using the Bootstrap framework and try to work within its defaults as much as possible. I find this helps force me to be consistent and maintain control over layout.

Recently I had a sort-of mini realization that the break tag isn’t really that good to use. First of all, it has that weird “no closing tag so we tack one on at the end” thing, which just bothers me. There may be instances when you have to use a break tag or when it makes the most sense, but what I’ve been doing lately is asking myself, why am I using it in the first place?

Let’s say you have some plain text that you need to break up into sections:

Lorem ipsum dolor sit amet, <br/>
consectetur adipisicing elit, <br/>
sed do eiusmod tempor <br/>
incididunt ut labore <br/>
et dolore magna aliqua. <br/>

Nothing wrong with that. But, you might ask yourself what is it that’s significant about these strings that merits their breakage? Maybe these are lines of a poem, or some other structure. So why not reflect that in the code:

<div id="first-stanza">
  <span class="line">Lorem ipsum dolor sit amet,</span>
  <span class="line">consectetur adipisicing elit,</span>
  <span class="line">sed do eiusmod tempor</span>
  <span class="line">incididunt ut labore</span>
  <span class="line">et dolore magna aliqua.</span>
</div>

Then we just add a little css to take care of the line breaks:

.line {
  display: block;
}

Of course, you could use paragraph tags here as well. There are a multitude of options, but my point is that if you’re reaching for that break tag a lot, chances are there’s a more elegant solution that will better reflect the structure of your document. That has broader implications when we think about how our pages get linked to other pages and how we want the web to make sense of them. That’s easier when we’ve created a clear structure to interpret.

Rails Manifesto: Views

I’ve been developing with Ruby on Rails for about three years now, and while that’s not as long as some other folks, it’s long enough for me to have formulated some of my own personal programming maxims. One of these is about views. This past week, I was rewriting view code in order to completely remove all Ruby logic so that it was solely HTML code as much as possible. While you’re allowed to do lots of things in Rails views, I prefer to keep views what they’re supposed to be: just about display. To that end, I use lots of helper methods to handle the logic, and leave the view code as simple nested HTML blocks.

Views view, while Helpers help

Rails views allow you to insert any Ruby code you like directly into escaped HTML strings, so you can have elements of if/then logic mixed in with HTML all in the same page. Take, for example, this view code from the Blacklight plugin that displays a list of recent searches:

index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div id="content" class="span9">
<h1><%= t('blacklight.saved_searches.title') %></h1>

<%- if current_or_guest_user.blank? -%>
  
  <h2><%= t('blacklight.saved_searches.need_login') %></h2>

<%- elsif @searches.blank? -%>
  
  <h2><%= t('blacklight.saved_searches.no_searches') %></h2>
  
<%- else -%>
  <p>
  <%= link_to t('blacklight.saved_searches.clear.action_title'), clear_saved_searches_path, :method => :delete, :data => { :confirm => t('blacklight.saved_searches.clear.action_confirm') } %>
  </p>

  <h2><%= t('blacklight.saved_searches.list_title') %></h2>
  <table class="table table-striped">
  <%- @searches.each do |search| -%>
    <tr>
      <td><%= link_to_previous_search(search.query_params) %></td>
      <td><%= button_to t('blacklight.saved_searches.delete'), forget_search_path(search.id) %></td>
    </tr>
  <%- end -%>
  </table>

<%- end -%>

</div>

There are three options, each with some view code associated with it: first, if there is no current user logged in, display some text stating that the user should login; second, if there is a user logged in, but there are no saved searches in the @searches variable, then display some text that states this fact; finally, if we have some searches, then display those in a tabular format. There is nothing wrong with this code, it works just fine. If you’re happy with the way it looks, and you write code like that, I think that’s great and you can stop reading. However, I personally prefer a different way, and decided to refactor it.

I found the code a little hard to follow, and wanted a cleaner separation of Ruby logic from the actual HTML code so I could understand it better. If the view just expressed the appearance and the content of the page, it would make a lot more sense to me at first glance. To do this, I identified the primary function of the page: rendering the table of search results. I then separated the logic controlling that and gave it a method name defining it as clearly as possible:

searches_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
module SearchesHelper

  def render_saved_searches_table
    if current_or_guest_user.blank?
      # you need to login
    elsif @searches.blank?
      # you have no searches
    else
      # display the table
    end
  end

end

With the logic sketched, we can add back some of the view code where appropriate. In this case, the helper method can return a single HTML statement, but if it is more than that, the content should be rendered by a new partial:

searches_helper.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
module SearchesHelper

  def render_saved_searches_table
    if current_or_guest_user.blank?
      content_tag :h2, t('blacklight.saved_searches.need_login')
    elsif @searches.blank?
      content_tag :h2, t('blacklight.saved_searches.no_searches')
    else
      render "searches_table"
    end
  end

end

The index view is now much more concise and can be re-written to take advantage of Rails’ content_tag blocks:

index.html.erb
1
2
3
4
<%= content_tag :div, :id => "saved_searches", :class => "span9" do %>
  <%= content_tag :h1, t('blacklight.saved_searches.title') %>
  <%= render_saved_searches_table %> 
<% end %>

Now, we create a new partial called by the helper method to display the searches in a table format:

_searches_table.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<%= content_tag :p, link_to(t('blacklight.saved_searches.clear.action_title'), clear_saved_searches_path, :method => :delete, :data => { :confirm => t('blacklight.saved_searches.clear.action_confirm') }) %>

<%= content_tag :h2, t('blacklight.saved_searches.list_title') %>

<%= content_tag :table, :class => "table table-striped" do %>
  <% @searches.each do |search| %>
    <%= content_tag :tr do %>
      <%= content_tag :td, link_to_previous_search(search.query_params) %>
      <%= content_tag :td, button_to(t('blacklight.saved_searches.delete'), forget_search_path(search.id)) %>
    <% end %>
  <% end %>
<% end %>

Personally, I find the first line a bit too long. There are a lot of options that are passed to the link_to method, and I chose to isolate that using a helper method:

searches_helper.rb
1
2
3
4
5
  def render_clear_searches_link
    link_to t('blacklight.saved_searches.clear.action_title'),
      clear_saved_searches_path, :method => :delete,
      :data => { :confirm => t('blacklight.saved_searches.clear.action_confirm') }
  end

Then, the final view code for the table looks a little more manageable to me:

_searches_table.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<%= content_tag :p, render_clear_searches_link %>

<%= content_tag :h2, t('blacklight.saved_searches.list_title') %>

<%= content_tag :table, :class => "table table-striped" do %>
  <% @searches.each do |search| %>
    <%= content_tag :tr do %>
      <%= content_tag :td, link_to_previous_search(search.query_params) %>
      <%= content_tag :td, button_to(t('blacklight.saved_searches.delete'), forget_search_path(search.id)) %>
    <% end %>
  <% end %>
<% end %>

OCD: Obsessive, Compulsive Design

To some, the above may seem like overkill, and I do concede that point. For me, it’s a matter of personal taste and also a nice feeling of satisfaction when looking at the finished product. It also satisfies a creative component that I feel is very important in programming. Writing in any kind of programming language is a creative process and Ruby is an expressive language. The refactoring process allows us to indulge a bit in these aspects.

I started down this path recently when I read this post about using Sandi Metz’s Rules for Developers. Following these rules is somewhat of a challenge, and it’s been a gradual process to get myself to abide by them. While I don’t always follow them, even attempting to has helped my refactoring process immensely. As a result, they’ve played a large part in how I’ve changed my thinking about views in general. The ideas that I’ve tried to apply in this example are making methods as concise and descriptive as possible, as well as crafting your modules and methods to be self-explanatory, which I think showcases Ruby’s expressive potential.

Glob-ins

A couple of months ago, I spent a harrowing Friday afternoon deploying one of my Rails apps. All the tests were passing on my laptop and on the development server. However, once I got the same code on the production box, none of the spec tests were running. Everything was failing with same error:

NameError:
   undefined local variable or method `id' for main:Object

Not a very helpful error. So I did some initial digging around, but that didn’t yield any explanations. What was even more exasperating was that the code and environment were exactly the same in both test and production. I know because I spent a lot of time checking.

I ended up really digging into the Rails source and tracked the problem down to a line in my rspec config:

spec_helper.rb
1
2
3
4
5
6
7
8
9
RSpec.configure do |config|

  [...]

  config.global_fixtures = :all

  [...]

end

When specifying ActiveRecord fixtures, by default, rspec will look in spec/fixtures. If you load all of them in the folder, as I had specified above, it does a glob on the directory to build the list of fixtures.

In most cases, this probably doesn’t make a difference, but in my case it did because one fixture file had records that depended on the other. I had two fixture files, one for the users table and another for activities. Activities needed to be loaded first, which was happening on my laptop and on my development system. However, on my production server, the users fixture was loaded first instead. This at least explained the error message: Rails was complaining that it couldn’t find the “id” method because the activities records hadn’t been loaded yet.

The Goblin in the Glob

This left me puzzling over why files would load in a different order on two identical systems. It’s true that file globing will have different results with different operating systems, but the development and production systems were both CentOS 6, both updated with the same patches, ruby versions, and gems.

On the development system, I got:

irb(main):002:0> Dir["./*"]
=> [./activities.yml", "./users.yml"]

But production was:

irb(main):002:0> Dir["./*"]
=> ["./users.yml", "./activities.yml"]

I assumed that they would load alphabetically, but this was not the case. Ideally, you would just call .sort on the results and Ruby would then sort them alphabetically, but I needed to instruct Rspec to load them in a specific order. Fortunately, this was a one-line change:

spec_helper.rb
1
2
3
4
5
6
7
8
9
RSpec.configure do |config|

  [...]

  config.global_fixtures = :activities, :users

  [...]

end

Another problem remained with cucumber, which also utilized the same fixtures and needed them specified in the same order. Again, another simple fix using ActiveRecord’s new FixtureSet methods:

ActiveRecord::FixtureSet.create_fixtures(File.join(Rails.root, 'spec', 'fixtures'), [:activities, :users])

No Answer

I still do not have an answer as to why the globing order would be different on the exact same operating system. Perhaps that will be the topic of another post.

Testing Engines Under Rails4

Recently, I’ve been using engines a lot, and have run into problems with testing them. The current guide on creating Rails engines covers the basics of creating the engine and touches a bit on testing them. The gist is that there is a dummy application under test/dummy and the code you write in the engine is tested against it.

This works, but what if there’s an update to Rails? Your dummy app is still stuck at the same version it was when you first created it. A problem I run into is that I need to create generators that add code into the dummy app, to mimic that same process real users will go through when they are installing the engine. I need a way to recreate the dummy application each time and run a complete set of tests.

My solution, which I’ve borrowed from my colleagues in the Hydra community, is to create rake tasks that build the dummy app from scratch, go through the steps required to install the engine, and then run all the tests.

Generate the Engine

When creating the engine, I do the same as the guide but I exclude the testing framework because I prefer rspec:

rails plugin new blorgh --mountable -T

Go into your new engine and add rspec and rspec-rails as development dependencies:

s.add_development_dependency "rspec"
s.add_development_dependency "rspec-rails"

And then get rspec ready to use:

bundle install
rspec --init

This creates the initial spec directory, but now we need to create the dummy app inside it. Unfortunately, we can’t just run “rails new” and be done. Rails knows where inside an engine and actually prevents us from running the “new” command. So, we have to hack around this. There are two options: 1) delete the bin directory; 2) rename the bin directory.

I choose to rename my bin directory to sbin:

mv bin/ sbin/

I can then call any generators or other options inside my engine using sbin/rails and then when I run the plain rails command, I get it as if I wasn’t inside my engine. So now, I can create my new dummy app inside spec:

rails new spec/dummy

And you now have a brand-new rails app inside spec, ready for testing.

Using Rake Tasks

At this point, you’ll need to add the engine to your dummy app, and go through the process of initializing it to run your tests. You’re going to have repeat the process a lot, so why not automate it with a rake task.

First, add this to your engine’s Rakefile to include any tasks we creating in our tasks directory:

Dir.glob('tasks/*.rake').each { |r| import r }

Since these are tasks that are only associated with developing and testing your engine, as opposed to tasks that users will run when they’ve installed your engine, I put them in their own file, such as tasks/blorgh-dev.rake

tasks/blorgh-dev.rake
1
2
3
4
5
6
7
desc "Create the test rails app"
task :generate do
  unless File.exists?("spec/internal")
    puts "Generating rails app"
    system "rails new spec/internal"
  puts "Done generating test app"
end

This does the job of creating our test application, but it doesn’t actually hook our engine up to it. In order to do that, we have to add the gem to the dummy app’s Gemfile:

echo "gem 'blorgh', :path=>'../../../blorgh'" >> spec/dummy/Gemfile

This adds our engine to the dummy app, relative to the location of the dummy app itself.

Next, we’ll need to do the usual bits of running bundle install and any migrations within the dummy app. Here we need to expand our rake task to not only perform things within the dummy app, but also do it within a clean bundle environment. This is because don’t want the dummy app to use any our bundler settings that we might be using in our dev environment.

Lastly, we also need to be able to delete our dummy app and regenerate it if we update any of engine code or dependencies.

Taking all of this into account, our updated rake file looks something like this:

tasks/blorgh-dev.rake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
desc "Create the test rails app"
task :generate do
  unless File.exists?("spec/dummy")
    puts "Generating test app"
    system "rails new spec/dummy"
    `echo "gem 'blorgh', :path=>'../../../blorgh'" >> spec/dummy/Gemfile`
    Bundler.with_clean_env do
      within_test_app do
        puts "Bundle install"
        system "bundle install"
        puts "running migrations"
        system "rake blorgh:install:migrations db:migrate"
      end
    end
  end
  puts "Done generating test app"
end

desc "Delete test app and generate a new one"
task :regenerate do
  puts "Deleting test app"
  system "rm -Rf spec/dummy"
  Rake::Task["generate"].invoke
end

def within_test_app
  return unless File.exists?("spec/dummy")
  FileUtils.cd("spec/dummy")
  yield
  FileUtils.cd("../..")
end

Next Steps

This gets us started, but you’ll probably need to add more in order to get your tests working, and that largely depends on what your engine is going to do. Fortunately, you’re in a good position if you have to test any functionality within your engine.

Generators

Using generators is a good way to create any necessary config files or code that needs to be created within the dummy app. The solution I’m using places the generators inside the spec directory and then copies them to spec/dummy/lib/generators. Then, we can run the generators within the dummy app and perform the same actions users will take when installing the engine to their own applications.

Acknowledgments

Props and shoutouts to @jcoyne who’s came up with this technique.

Chruby

This is the third, in a sort-of-three-part-series on Ruby managers. In the first post, I discussed using rbenv in place of rvm which I had been using up until that point. I found rbenv to be more lightweight than rvm, and it proved to be a better fit for my production environment.

Chruby is an even more slimmed-down version of rbenv’s essential features. It simply changes your ruby to whichever version you need it to be. That’s it. The improvement, in my mind, is due to the fact that it doesn’t use shims or other shell acrobatics. This was causing me some grief when testing certain Rails- engine applications that would install and spin-up sample Rails applications in order to run all the tests. The particular problem with this was that it required global gems and not gems that were confined to a particular location or subfolder. When doing this under rbenv, you needed to run rehash and using rbenv-rehash didn’t seem to work either.

Rails Gotta Brand New bin/

The other issue is that Rails 4 now keeps all of your application executables in the local bin/ directory. This is a good thing, but since I was keeping bundler’s gem executables in the same directory, it was a bit of a mess. It didn’t seem like a good idea to mix a gems executables in the same directory as the custom executables from your application. The better solution was to go with a simpler bundler config for each directory:

.bundle/config
1
2
3
---
BUNDLE_DISABLE_SHARED_GEMS: '1'
BUNDLE_PATH: .bundle

and keep ~/.bundle/config empty. This way, everything works in its default state everywhere, unless I’ve specified a particular configuration for a given project. This is great if you want to have psudo-gemsets or groups of gems confined to a particular project while the rest of the system has whatever it needs.

Installing chruby

Very, very easy. I did it with Homebrew:

brew install chruby

I had to remove rbenv first, which had also been installed with Homebrew:

brew uninstall rbenv
rm -Rf ~/.rbenv

Installing new rubies is done with ruby-install, which rbenv also used, but here we use it in its original form:

brew install ruby-install
ruby-install ruby 1.9
ruby-install ruby 2.0

Which will install two rubies, the latest stable 1.9 version and the latest 2.0 version. Then if you want to switch, it’s just a matter of:

chruby ruby-1.9.3-p286
chruby ruby-2.0.0-p195

If you want a default ruby with each shell, say version 1.9, add these lines to your .bashrc, .bash_profile, or .profile:

source /usr/local/share/chruby/chruby.sh
chruby ruby-1.9.3-p286

Similarly, you could add a new file to /etc/profiles.d under RedHat if you wanted all your users to get specific rubies upon login.

Project Rubies and Gems

For projects that require specific versions of ruby, or anywhere else in your system where you want a certain version of ruby to run, simply put one of these in your directory with whatever ruby you wish:

.ruby-version
1
    1.9.3-p286

Project gems are done via bundler, using the config file I laid out at the beginning. Then, when you run bundle install, the gems are placed under .bundler. System-wide gems are installed with the gem command and placed in their default location under ~/.gem/ruby with the directory name of the current version.

Caveats

I did notice that when I switched from a global bundle config to a local per-directory config, the location of the gems moved from .bundle to .bundle/ruby/1.9.1 which was odd since I’m using ruby 1.9.3. Re-arranging and re-configuring the .bundle directory did the the trick without having to install anything:

cd .bundler
rm config
mkdir -p ruby/1.9.1
cp * ruby/1.9.1/
cd ..
bundle config --local path .bundle
bundle config --local disable_shared_gems '1'

Also, you should always use bundle exec within your projects to ensure you’re getting the correct executable from your bundle.

Future Plans

I’m still using rbenv in production. I may switch over the chruby, but for now I’m very satisfied with rbenv and will continue to use it for my servers and use chruby for my local development work.

Using Rbenv in Production

In the last post, I went over the procedures I used to install the Ruby environment manager, rbenv, and setup it for developing rails applications.

In this post, I’ll cover how I use rbenv on my production and test servers for deploying rails applications using Apache with Passenger.

The Environment

  • CentOS 6
  • ruby 1.9.3-p286
  • apache
  • passenger 4.0.5

Requirements

You’ll need development packages (gcc, etc.) and git. Easiest way to do that is install the development tools package:

$ yum groupinstall "Development Tools"

I installed git via yum as well, but by adding the Extra Packages for Enterprise Linux (EPEL) repository:

$ rpm -Uvh http://download.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
$ yum install git

Installation

This Rbenv wiki page recommends creating a user under which your applications are run and Rbenv would be installed under this user’s home directory. This certainly is a fine way to do it, but I want to have a global ruby available system-wide to any user. I chose to install Rbenv under /usr/local, my default location for any non-repo software. You could also choose /opt if you like. I also prefer to not have rbenv in a hidden folder. That way, when I list the contents of /usr/local, I can immediately see what software I have installed outside of Red Hat’s or CentOS’s normal repository.

Other than the location, the installation is pretty much the same as it was in development:

(as root)
$ cd /usr/local
$ git clone https://github.com/sstephenson/rbenv
$ cd rbenv
$ mkdir plugins
$ cd plugins
$ git clone https://github.com/sstephenson/ruby-build
$ git clone https://github.com/sstephenson/rbenv-vars

Because I want rbenv available globally, I add a new file under /etc/profile.d so new environment settings will be applied equally to every user:

/etc/profile.d/rbenv.sh
1
2
3
export RBENV_ROOT=/usr/local/rbenv
export PATH="$RBENV_ROOT/bin:$PATH"
eval "$(rbenv init -)"

After logging out and back in again, or re-sourcing .bash_profile, Rbenv should now be installed and correctly pathed:

$ rbenv install 1.9.3-p286
$ rbenv global 1.9.3-p286
$ rbenv rehash

Configuration

First, we’ll clear out any unwanted gems, then install the ones we need to deploy applications. For these systems, I install just bundler and passenger. Additionally, I configure gem for root so that it does not install any documentation:

$ gem list | awk '{print $1}' | xargs gem uninstall -a
$ echo "gem: --no-rdoc --no-ri" >> ~/.gemrc
$ gem install bundler passenger
$ rbenv rehash

Now we should have passenger and bundler avaialable from anywhere in the system, but we have to configure bundler to install any other gems locally to the application, and have rbenv-vars set the GEM_HOME variable accordingly:

$ bundle config --global path .bundle
$ bundle config --global bin bin
$ echo "GEM_HOME=.bundle" >> ~/.rbenv/vars
$ rbenv rehash

Now, when we run bundle install on any of our rails apps, additional gems will be installed the .bundle directory under the application.

Passenger

The last little bit involves configuring Passenger. Installing the apache module is straight-forward:

$ passenger-install-apache2-module

But when I configure my rails apps for deployment, I have apache set the GEM_HOME variable so Passenger knows to look under the .bundle directoryfor the additional gems. This can be done so that it’s set for any application you deploy:

/etc/httpd/conf.d/passenger.conf
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<VirtualHost *:80>
  ServerName my.prod.server
  SetEnv GEM_HOME .bundle

  RackBaseURI /app1
  <Directory /var/www/html/app1>
    Options -MultiViews
  </Directory>

  RackBaseURI /app2
  <Directory /var/www/html/app2>
    Options -MultiViews
  </Directory>
</VirtualHost>

Acknowledgements

http://www.vxnick.com/blog/2012/04/setting-up-rbenv-globally/

Moving to Rbenv

I’ve been a loyal RVM user for several years now, but recently I grew a bit tired of using it in production. It seemed too heavy-handed and updating was arduous. After some looking around, I settled on rbenv, mostly because it was simpler, smaller and could work well in all my environments: from my laptop, where I develop, to the different servers on which I deploy everything. Keeping it all identical across these is very important to me because I don’t want to have to re-think anything when releasing new code.

I’m not down on RVM at all… in fact, I still like it very much and if I were just developing and not involved in the production side of things, I’d probably still be using RVM. One of the particularly great things about RVM is its notion of gemsets, or groups of installed gems that can be used for one particular application. RVM also has default and global gemsets, which I liked to use for gems that were required for all applications. I liked to use this in my production servers where I could deploy all my applications with the same passenger gem.

As it turns out, we can use a properly-configured bundler to mimic an RVM gemset, and use the unadorned gem command to install gems that we want global to our system. Doing all of this and switching to rbenv took a little adjustment, but overall I’m very happy with it. Getting to that point required some research and some experimenting, which I’ll go over here.

Overview

Here’s the basic gist of my setup:

  • rbenv, with the ruby-build and rbenv-vars plugins, installs and manages your ruby versions, as well as sets some environment variables
  • gem installs rubies that are global to your system, akin to rvm’s global gemsets
  • bundler install gems specific to a project or application, akin to rvm’s named gemsets
  • tie it all together with environment variables

Development

First, you’ll need to make sure you have these installed:

On OSX, rbenv installation is a snap with Homebrew. Follow the instructions to install rbenv on Mac:

https://github.com/sstephenson/rbenv#homebrew-on-mac-os-x

Additionally, you’ll need the rbenv-vars plugin. I installed that manually:

$ cd ~/.rbenv/plugins
$ git clone https://github.com/sstephenson/rbenv-vars
$ rbenv rehash

Install your ruby of choice and make that global:

$ rbenv install 1.9.3-p286
$ rbenv global 1.9.3-p286
$ rbenv rehash

When rbenv installs ruby, it also installs some gems:

$ gem list

Since it has installed these with the gem command, these are essentially system-wide or global gems. I prefer to have more control over which gems are installed where, so I remove these and then only install gems I need globally. Presently, the only gems I use everywhere are bundler and rails:

$ gem list | awk '{print $1}' | xargs gem uninstall -a
$ gem install bundler rails

Now we configure bundler to store project-specific gems in a local .bundle directory and to install any gem executeables in a local bin directory:

$ bundle config --global path .bundle
$ bundle config --global bin bin

Lastly, we need to make our environment aware that additional gems are installed in .bundle. For that, we can use the rbenv-vars command which will set any environment variable we need. In this case, we need to set GEM_HOME to our .bundle directory:

$ echo "GEM_HOME=.bundle" >> ~/.rbenv/vars
$ rbenv rehash
Example project

So, here’s how this all works. Let’s say I want to create a new rails application and then tweak its ruby version and gems. First, I’ll install a new, blank app, then I’ll install a new version of ruby, set up that application to use it and install some gems specific to that application:

$ cd ~/Projects
$ rails new sample_app
$ cd sample_app
$ rbenv install 2.0.0-p247
$ echo "2.0.0-p247" > .ruby-version
$ rbenv rehash
$ echo "gem 'rspec'" >> Gemfile
$ bundle install

This all should look familiar, except note the .ruby-version file. This file can be used in any directory to indicate which ruby version rbenv should use. When you set the global ruby version above, rbenv wrote that version to ~/.rbenv/version which means that unless there’s a version explicitly named in .ruby-version, rbenv defaults to the version specified in ~/.rbenv version.

Next, take a look around:

$ ls -a
$ ls bin
$ ls .bundle
$ ls .bundle/gems
$ ruby --version
$ gem list

You’ll see that rspec is installed in the .bundle/gems directory and the rspec command is in the bin directory. Listing the gems will return all the gems both system-wide and what’s installed locally, and you should be using ruby version 2. If we move to a different directory, we should be back to our global defauls:

$ cd ..
$ ruby version
$ gem list

Caveats

Paths

When running local gem executeables such as rspec, you’ll either need to call them using bin/rspec or use bundler:

$ bundle exec rspec

Alternatively, you could preface your PATH with the local bin directory, so it looks there first:

$ PATH=bin:$PATH; export PATH

Personally, I choose to just call local executables by prefacing them with bin/ — it takes some getting used to at first, but I prefer this method because it makes me realize exactly what I’m doing.

Rails 4

When using this setup with Rails 4 applications, rails complained about bundler setting the local bin directory. Apparently, rails is now defaulting to using bin locally. So if you want to develop multiple versions of rails, including version 4, I would remove the BUNDLE_BIN setting in your global ~/.bundle/config file and set it locally to your applications that need it.

My updated setup, for Rails 3 and Rails 4 develpment looks like this:

$ cat ~/.bundle/config 
  ---
  BUNDLE_PATH: .bundle
$ cd ~/Projects/rails3_app
$ bundle config --local bin bin
$ cat .bundle/config
  ---
  BUNDLE_DISABLE_SHARED_GEMS: '1'
  BUNDLE_BIN: bin
$ cd ~/Projects/rails4_app
$ cat .bundle/config
  ---
  BUNDLE_DISABLE_SHARED_GEMS: '1'

That about covers it. Next post, I’ll go over how to do this same setup on test and production deployment servers.

Acknowledgments

Most of this was me googling to see how other people did it, and then making it work for my own particular needs. If it wasn’t for them, I couldn’t have done it:

Sam Stephenson’s Rbenv

Devoh’s ‘Implict Gemsets’