Ideal setup for Ruby on Rails on Windows (Vagrant + Guard + Spring)

If you’re developing Ruby on Rails applications in Windows, you’ve probably found that tests run really slowly in the Windows environment. The only supported system to speed up tests is Spork and this hasn’t been updated in about a year or so. The ideal setup for any Windows developer is to develop in your host machine and test in a virtual machine. Vagrant is ideal for  this purpose.

Note: These instructions were written for a Ruby on Rails 4 application. If you are still developing Ruby on Rails 3 applications, the simplest way to get up and running is RailsInstaller.

Install Virtual Box and Vagrant

  1. Download and install the latest version of VirtualBox (Vagrant depends on VirtualBox)
  2. Download and install the latest version of Vagrant for Windows.

Clone the chef-rails-dev-box repository

  1. Download Git for Windows if you haven’t already.
  2. Clone chef-rails-dev-box using “git clone https://github.com/AppMaintainers/chef-rails-dev-box.git” to a directory on your machine (I like to install it in the root of my C drive).

Get ready to build and start your virtual machine

  1. Download and install the latest version of Ruby 1.9.3.
  2. Download and install the latest version of RubyGems.
  3. Run ‘gem install rubygems-update’.
  4. Download and install the latest DevKit.
  5. Modify your PATH environment variable so that you can run ruby and gem directly from your command line.
  6. Run ‘gem install bundler’.
  7. Build your virtual machine using the instructions from https://github.com/AppMaintainers/chef-rails-dev-box.git.

Set up the database (PostgreSQL)

I like running the development web server on the host machine (for reasons mentioned below). This means that you need to install PostgreSQL on your host machine.

  1. Download and install PostgreSQL.

Once you do so, you need to create the development and test users and database on the virtual machine. This can be quite tricky:

  1. Make sure that PostgreSQL is running with ‘sudo service postgresql restart’.
  2. Before you do anything else, make sure you follow the instructions here in order to make the default encoding of your databases UTF-8.
  3. Run ‘sudo su – postgres’.
  4. Run ‘createuser <name_of_user> -P -d’ to create a user. You will also be asked to immediately set the user’s password as well.
  5. Run ‘psql template1′.
  6. Run ‘CREATE DATABASE “<name_of_database>” OWNER <name_of_user>’ to create the development database.

Edit your pg_hba.conf file to look like this. As you can see I have the development and test databases set up to be authenticated via md5:

local all all trust
host all all 127.0.0.1/32 trust
host all all ::1/128 trust
host all all 10.0.2.0/24 trust
local indieinfinity-development indieinfinity md5
local indieinfinity-test indieinfinity md5

Set up continuous (and fast) tests

Finally, we are getting to the whole point of this setup: continuous (and fast) tests.

Currently there are three main solutions for running tests continously:

  1. Spork: There has been no development on this project for nearly a year now.
  2. Zeus: Zeus does not seem to currently work with Vagrant on Windows.
  3. Guard + Spring: This is the ideal solution when using Vagrant on Windows.

In order to get started with Guard + Spring:

Add the following code to your Gemfile:

group :development, :test do
  gem 'minitest'
  gem 'rspec', '~&gt; 3.0.0'
  gem 'rspec-rails', '~&gt; 3.0.0'
  gem 'spring'
  gem 'spring-commands-rspec'
end

group :guard do
  gem 'guard'
  gem 'guard-rails'
  gem 'guard-rspec', '~&gt; 4.2.7'
end
  • Run ‘bundle install’ in the root of your project.
  • Run ‘bundle exec spring binstub –all’.
  • Run ‘sudo gem update –system’ and ‘gem pristine –all’ in order to run spring efficiently.
  • Make sure that ‘bin/rspec’ works before you continue further.
  • Run ‘bundle exec guard -p’ in order to start Guard. -p specifies polling which is needed for this setup in order for Guard to detect changes to files.

My Guardfile

# A sample Guardfile
# More info at https://github.com/guard/guard#readme

guard 'rspec', cmd: 'bundle exec spring rspec' do
 watch(%r{^spec/.+_spec\.rb$})
 watch(%r{^lib/(.+)\.rb$}) { |m| &quot;spec/lib/#{m[1]}_spec.rb&quot; }
 watch('spec/spec_helper.rb') { &quot;spec&quot; }

 # Rails example
 watch(%r{^app/(.+)\.rb$}) { |m| &quot;spec/#{m[1]}_spec.rb&quot; }
 watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| &quot;spec/#{m[1]}#{m[2]}_spec.rb&quot; }
 watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| [&quot;spec/routing/#{m[1]}_routing_spec.rb&quot;, &quot;spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb&quot;, &quot;spec/acceptance/#{m[1]}_spec.rb&quot;] }
 watch(%r{^spec/support/(.+)\.rb$}) { &quot;spec&quot; }
 watch('config/routes.rb') { &quot;spec/routing&quot; }
 watch('app/controllers/application_controller.rb') { &quot;spec/controllers&quot; }

 # Capybara request specs
 watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| &quot;spec/requests/#{m[1]}_spec.rb&quot; }

 # Turnip features and steps
 watch(%r{^spec/acceptance/(.+)\.feature$})
 watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join(&quot;**/#{m[1]}.feature&quot;)][0] || 'spec/acceptance' }
end

guard 'rails' do
 watch('Gemfile.lock')
 watch(%r{^(config|lib)/.*})
end

Troubleshooting

  • Update all gems: Make sure that all gems are updated to their latest version (Run ‘bundle update’).
  • Certificate issues: If you get an error like ‘SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (OpenSSL::SSL::SSLError)’, you need to follow these instructions.
  • Git permission issues: If you get an error like ‘Permission denied (publickey)’ or ‘Could not open a connection to your authentication agent.’ when trying to clone the GitHub repository, you need to add your key to ssh-agent. Run eval “$(ssh-agent -s)” and ssh-add ~/.ssh/id_rsa’ in a Git Bash command prompt.
  • Bundle install fails: Run ‘sudo bundle install’ instead.
  • CPU usage is at 100%: Run bundle exec guard -p -l 10 (Change latency to prevent 100% CPU usage). This introduces a latency to how often Guard polls your files to detect changes.
  • RubyMine’s autosave kicks in every time a change is made: http://stackoverflow.com/questions/11996124/is-it-impossible-to-use-guard-with-rubymine

Paginating multiple models using will_paginate on the same page

The will_paginate plugin makes pagination for your models in Ruby on Rails ridiculously simple. However sometimes you’ll find yourself wanting to paginate over two or more models on a single page. For instance, you might want to display a list of users and administrators on a single page along with a pager for each model (assuming that users and administrators are stored in separate tables).

Controller code

The code is pretty simple, except that I am specifying the page to show to be equal to params[:user_page] and params[:administrator_page] respectively. Since we are allowing the ability to page over two models, we need two separate parameters to determine which page of users or administrators to show.

@users = User.paginate(:page => params[:user_page], :per_page => 10)
@administrators = Administrator.paginate(:page => params[:administrator_page], :per_page => 10)

View code

In the view all we need to do is make sure to set the param_value to the correct value to indicate to the plugin that we want to use a different parameter name for the page. The default is simply called ‘page’, but we need to make sure to use ‘user_page’ and ‘administrator_page’ instead for the two different models.

<%= will_paginate @users, :param_name => 'user_page' %>
<%= will_paginate @administrators, :param_name => 'administrator_page' %>

That’s it, you should now be able to page through your users and administrators on the same page.

Testing file uploads in Ruby on Rails with RSpec

While working on my latest project,I wanted to drive the design of my file upload code using Behavior Driven Development using Webrat and RSpec. For most people, I would recommend using a plugin like Paperclip rather than reinvent the wheel, but if you’re interested in seeing how to use RSpec to drive the design of file upload code, read on.

I’m a big fan of Behavior Driven Development and all the code you see below was driven by a customer’s acceptance test for a vital business feature. In order to keep this simple, I’m just going to describe how I used RSpec to drive the file upload code, but feel free to read the RSpec book to get more information on BDD with Ruby on Rails.

Designing the Document class

Since all our business rules and logic belongs to our domain model, the Document class has to be smart. It should know how to store uploaded files on the server, how to handle file name clashes etc. The controller should be able to simply call a method on this class to store an uploaded file correctly. This is the RSpec example for the save_upload method in the Document class:

it "should be able to save an upload with an unique name generated by using a timestamp" do
  # As of Rails 2.3.2, the framework passes an UploadedStringIO to the controller when uploading files
  # I'm creating a mock that I can pass to my save_upload method on my Document class.
  # The save_upload only requires that the passed in object respond to original_filename and read.
  uploaded_file = mock(ActionController::UploadedStringIO).as_null_object
  uploaded_file.stub!(:original_filename).and_return('test.doc')
  uploaded_file.stub!(:read).and_return("Some content")

  document = Document.new

  document.save_upload(uploaded_file)
  document.original_filename.should == "test.doc"
  # To prevent file name clashes, the saved file's new name is prepended with a timestamp
  document.new_filename.should =~ /\d{2}\d{2}\d{4}\d{2}\d{2}\d{2}-test.doc/
  # Finally, does the file exist on the file system and if so are the contents of this file correct?
  File.exist?("#{Rails.root}/public/system/documents/#{document.new_filename}").should == true
  File.open("#{Rails.root}/public/system/documents/#{document.new_filename}", "r") { |f| f.read.should == uploaded_file.read }
end

Implementing the Document class’s save_upload method

Once we’ve got the RSpec example, writing the actual code to make the example pass is pretty simple.

def save_upload(upload)
  self.original_filename = sanitize_file_name(upload.original_filename)
  self.new_filename = unique_filename
  # Write the file
  File.open(absolute_path, "wb") { |f| f.write(upload.read) }
end

private
  def absolute_path
    File.join(Rails.root, 'public/system/documents', self.new_filename)
  end

  def sanitize_file_name(filename)
    # Internet explorer returns the complete path to the file, instead of just the file name that was uploaded.
    # File.basename gives us just the filename and fixes the above issue with Internet Explorer.
    # Replace everything other than alphanumeric characters, periods or underscores with underscores to ensure
    # there are no illegal characters
    File.basename(filename).gsub(/[^\w\.\_]/, '_')
  end

  def unique_filename
    # Prepend a timestamp to the original filename to create a unique filename
    Time.now.strftime("%m%d%Y%H%M%S").to_s + "-#{self.original_filename}"
  end

Designing the controller code

Now that we’ve got all the business logic required for saving an uploaded file in the Document class, the actual code for the controller method is pretty simple. All we need to do is check that the document’s save_upload method is called with the correct parameter.

it "should save the uploaded document" do
  document = mock(Document).as_null_object

  Document.should_receive(:new).and_return(document)
  uploaded_file = mock(ActionController::UploadedStringIO).as_null_object
  params = { "id" => "1", "document" => uploaded_file }
  document.should_receive(:save_upload).with(uploaded_file)
  post :save_file, params
end

Implementing the controller code

Getting this RSpec example to pass is pretty simple. The controller code simply creates a new Document object and then calls the save_upload method with the correct form parameter.

  def save_file
    if params[:document] != nil
      document = Document.new
      document.save_upload(params[:document])
    end
  end

Sample nginx (with Phusion Passenger) configuration file to enable SSL

Read my earlier post if you want Phusion Passenger to compile nginx with SSL support. If your nginx server already supports SSL, read on.

Phusion Passenger fills up most of the configuration in nginx.conf for you with a nice set of defaults. You just have to do a little more work if you want to modify the nginx configuration file to enable SSL on your site.

The first thing to do is purchase a SSL certificate and install it on your server. If you don’t know how, here’s a great post on installing SSL on Ubuntu with nginx. Once you’ve installed the certificate, you should be good to go.

Let’s say that you’ve got a domain name called mydomain.com and the root for your Ruby on Rails application is located at /home/mydomain/current/public on your server. The following shows a sample of the configuration required to get SSL enabled for your application:

server  {
    	       listen 80;
               server_name mydomain.com;
               root /home/mydomain/current/public;
               passenger_enabled on;
               rails_env production;
        }

# HTTPS server
server  {
               listen 443;
               server_name mydomain.com;
               root /home/mydomain/current/public;
               passenger_enabled on;
               rails_env production;

               ssl on;
               ssl_certificate /etc/ssl/certs/mydomain.com.crt;
               ssl_certificate_key /etc/ssl/private/mydomain.com.key;

               ssl_session_timeout  5m;
        }