Archive for the 'ruby' Tag

A basic Git+Capistrano workflow

Saturday, July 7th, 2012

This post has been originally published on the Headshift Labs blog and is reposted here with permission.

~

Here in the London office, we use Git as a Version Control System and Capistrano to manage deployments for almost all our projects: not only Rails, but WordPress and Drupal as well. It’s a well tested tool and the development team has definitely benefited from having a unified way of deploying – regardless of the project language or the server.

As an aside, we already posted about VCS and Git in the past, so if you need an introduction you can head to that post, which despite being a couple of years old is still relevant.

The setup for the most basic applications usually involves two servers: a staging server and a production server. The staging server is used to show the application to the client and as a testbed to get them to accept user stories, while the production server hosts the real thing.

To cater for this scenario, we need to install the Capistrano Multistage Extension, which will allow us to create different deployment recipes for different servers.

We are not yet done, however: all the recipes are based on the master branch, which gives us very limited flexibility: we can only deploy what is on the HEAD of master at the time of running our cap deploy command. In a fast-moving project, with multiple developers working at the same time, it’s very unlikely that this will be what we want. We need more granular control on what we’re deploying.

To achieve this goal, we can create two Git remote branches: staging and production. We can then update the Capistrano configuration so that the recipe for the staging server will deploy the code committed to the staging branch, and the one for the production server will deploy the production branch.

Among other server-specific configurations, we will have to make sure our recipes are referencing the correct branch. This will mean, for example, adding the following line to the staging.rb recipe:

set :branch, 'staging'

This gives us fine grained control over what is deployed: we can merge the master branch into staging to get the latest changes, or just merge a subset of commits, or cherry pick the ones we want. Running cap staging deploy will then transfer to the server only the commits we selected.

The following image shows how a hypothetical repo may look at a given point in time:

 

The typical workflow to deploy the latest changes to staging (supposing we are on the master branch) is:

$ git checkout staging
$ git pull origin staging
$ git merge master
$ git push origin staging
$ cap staging deploy

If you know your Git, you’ll notice some of the commands are presented in their more verbose form. This is meant to make them safer, but if you know what you are doing you may as well just use git push and git pull. The same goes for rebasing, which is a topic worth a post all by itself.

It could be argued that forcing developers to go through the steps each time creates a useful barrier to prevent people from deploying without first thinking about it. On the other hand, it’s a rather long list of commands for a routine task, so it’s a perfect candidate for automation if you trust developers with deploying only when it’s needed.

This is not a fancy workflow and it requires a very minimal setup. It could be seen as standing in between very lean workflows as the one used by GitHub and more structured ones as the gitflow model. It’s not by any means the only workflow we use for our projects, but it’s common enough and it has worked successfully for a number of projects.

Cucumber to test mobile in a Rails project

Friday, July 6th, 2012

This post has been originally published on the Headshift Labs blog and is reposted here with permission.

~

Recently we had to add support for mobile devices to a Rails application we developed. We decided to go with the excellent suggestion we found in the Railscast about Mobile Devices. The gist of it is to add a method to the ApplicationController to set the mime-type to :mobile whenever the request user-agent is recognised as a mobile browser (see the corresponding ASCIIcast for the full code).

We have also used the format_fallback gem in order to avoid a proliferation of views and partials. In fact, in our application many of the partials are exactly the same in both the html and the mobile version, although they look different thanks to CSS and Javascript. What the gem does is to fallback to the html view whenever a mobile view is not found, thus avoiding the issue of having to duplicate each .html view in the application as a .mobile file. The gem is still a bit rough, but it fits our bill for this application.

Given the importance we give to BDD and TDD, however, this only gets you halfway on the road towards a proper implementation: testing for the mobile version of the application is still missing.

The most straightforward way to test the application using Cucumber is to make sure the test request headers include a user-agent string, thus mimicking a mobile device.

There are a couple of tutorials out there on how to do this (all pointing to comments for a blog post that has now disappeared without a trace). They provided a good foundation to start from, but ultimately we found out there’s an easier way to do it.

The core of this solution is a handful of lines, adding support for custom headers to Capybara’s default rack_test driver. The following code will make an add_headers method available to your steps.

# features/support/capybara_headers.rb:

module CapybaraHeadersHelper
  def add_headers(headers)
    headers.each do |name, value|
      page.driver.browser.header(name, value)
    end
  end
end
World(CapybaraHeadersHelper)

Now that the basic support is in place, we can write some steps to make our life easier.

# features/steps/mobile_steps.rb:

module MobileStepsHelper
  SAMPLE_AGENT_STRING = {
    "iPhone" => "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_0 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 Mobile/8A293 Safari/6531.22.7",
    "Android" => "HTC_Eris Mozilla/5.0 (Linux; U; Android 4.0; en-ca; Build/GINGERBREAD) AppleWebKit/528.5+ (KHTML, like Gecko) Version/3.1.2 Mobile Safari/525.20.1"
  }
end
World(MobileStepsHelper)

Given /^my user agent is "(.+)"$/ do |agent|
  add_headers({'User-Agent'=> agent})
end

Given /^I have an? (.+)$/ do |phone_name|
  add_headers("User-Agent" => SAMPLE_AGENT_STRING[phone_name])
end

Notice that, for the sake of brevity, I’ve included only a couple of user agents in the SAMPLE_AGENT_STRING hash. You can find lots more around the Web.

We can then write a feature such as:

Given I have an iPhone
When I visit the homepage
I should see content tailored to my mobile