Mark Gandolfo's Blog

Hudson Ci Server Running Selenium/Webdriver Cucumber In Headless Mode Xvfb

Posted on July 01, 2010

I was determined to get our CI server running cucumber features in headless mode. I ran into a few webdriver problems which resulting in a small monkey patch (capybara patch being written and submitted) for capybara.

This is how I got it running on a fedora 12 server (and some tips on getting it working on an ubuntu server).

Install Xvfb

On fedora

  sudo yum install xorg-x11-server-Xvfb

on ubuntu

  sudo apt-get install xvfb

Running Xvfb

To run Xvfb, in console simply type, then set a environment variable to bind all X requests from the command line to go to the new virtual deskspace

  Xvfb -ac :99
  export DISPLAY=:99

This will launch a new virtual display bound to display 99.

Install X11VNC to see whats happening!!

This is very cool. You can actually set up a vnc server and bind it to the virtual display to see what is happening!!!

On Fedora

  sudo yum install x11vnc

On ubuntu

  sudo apt-get install x11vnc

To run

Simple run the following command! And point your VNC client to the installation server!

x11vnc -display :99

Test it by going to the command line and typing something like xterm or firefox!

Hudson

Install the hudson gem

The hudson gem is in the gems repo, but for docmentation go http://github.com/cowboyd/hudson.rb. I generally do this as the user "hudson".

  gem install term-ansicolor
  gem install yajl-ruby
  gem install httparty -v=0.5.2
  gem install builder -v=2.1.2
  gem install thor -v=0.13.6
  gem install hpricot 
  gem install hudson --pre

Install hudson

On the command line just type

  hudson server 

this will install hudson, and save configuration to the ~/.hudson directory

Checkout your application to this box

First thing is to ensure you have set up a git user and email in the global config

  git config --global user.name "hudson"
  git config --global user.email "hudson@myserver.com"

Then clone your app down, install all associated gems and create your testing database

  git clone git@github.com:username/mygitrepo.git
  rake gems:install
  rake db:create:all
  RAILS_ENV=test rake db:schema:load

Test cucumber/webdriver runs

Type cucumber in your apps directory

Create a new Hudson Project

Thanks to the hudson gem, you can set up the inital project skeleton via the command line (isn't technology great) Here we can use the hudson create command and tell hudson to use the current directory, and that hudson is installed on localhost:3001

  cd #{to_your_rails_app}
  hudson create . --host localhost --port 3001

Set up the hudson project

Log into the project, if its on your local computer, point your browser to http://localhost:3001 and click on your project, also skip this next section.

If you're installing hudson on a remote server you'll need to ssh port forward since hudson at this point is only listening on localhost.

  ssh -L 8123:localhost:3001 hudson@mytestserver.com

Then open a browser and point it to http://localhost:8123 and click on your project

Set up the build

  1. Remove all defined build sets.
  2. Add a new build step "Execute Shell" with the command "cp config/database.tests.yml config/database.yml"
  3. Add a new build step "Exceute Shell" with the command "RAILS_ENV=test rake gems:install"
  4. Add a new build step "Execute Shell" with the command "RAILS_ENV=test rake db:migrate"
  5. Add a new build step "Execute shell" with the command "spec spec" # if you have specs
  6. Add a new build step "Execute shell" with the command "DISPLAY=:99 cucumber" # with cucumber

Press the save button.

If webdriver doesn't append to your virtual display

If webdriver doesn't append to your Xvfb then you can put this script in /features/support/capybara_webdriver_patch.rb

# Added profile support for capybara, so we can run our tests in headless mode
class Capybara::Driver::Selenium < Capybara::Driver::Base
  def self.driver
    unless @driver
      profile = Selenium::WebDriver::Firefox::Profile.new
      profile.load_no_focus_lib = false
      @driver = Selenium::WebDriver.for :firefox, :profile => profile
      at_exit do
        @driver.quit
      end
    end
    @driver
  end
end

Some after thoughts

Some extras you should set up before you think your finished. They're outside of the scope of this document but there are plenty of docs out there on the interwebs who can give you all the information you need to rock and roll.

  • At the moment its open to everyone, go to global configuration and set up ACL's
  • Your builds aren't very continuous yet, you can schedule builds periodically by going into the project configuration area, or I believe you can tie it into githubs callback mechanisms

Observers In Rails

Posted on June 21, 2010

In an effort to reduce the clutter of before and after callbacks in your Rails models an Observer can be used. In a simple use case, you may want to give the user 500 credits in your online store when they signup.

Without an observer your User model would look something similar to

class User < ActiveRecord::Base
   def before_create
     self.credits = 500
  end
end

Which is fine, if thats all that is in your model. But once your model starts to grow its generally a good idea to keep the callbacks in a different location! Observers to the rescue! With a Rails Observer your code would look similar to

# models/user.rb
class User < ActiveRecord::Base
end

# models/user_observer.rb
class UserObserver < ActiveRecord::Observer
   def before_create(user)
     user.credits = 500
  end
end

Now tell Rails that you want your observer to be included

# config/environment.rb 
Rails::Initializer.run do |config|
  config.active_record.observers = :user_observer
end

Rails will automatically understand that this observer is for the user class based on the name (convention over configuration to the rescue). Although you can also have an observer where the model can't be inferred by the name, in this case you can explicitly define the models the observer should be observing.

For instance, lets say we have an Audit observer, for a number of models.

  class AuditObserver < ActiveRecord::Observer
    observe :user, :item

    def after_update(record)
      AuditTrail.new(record, "UPDATED")
    end
  end

This will observe the user model and on an observed update will create a new audit trail record. Sexy huh?

So no excuses, clean your code up, use observers! Its a sexy design pattern which I feel is completely underused!


How To Debug Rails Apps

Posted on June 20, 2010

Coming from PHP I used to rely heavily on the die() command to print out debugging information. In rails nobody could really tell me if there was an equivalent, I was told to "raise", which I'm sure everyone feels is a bit dirty to use.

So I thought I'd quickly put up a few ways I debug in Rails.

Ruby-Debug

Its a sexy little gem that lets you easily step through your code and even drop into an IRB session. To use it, make sure you have the gem or plugin installed and then.

require 'ruby-debug'; debugger

Ruby debug shines when running a script/server (mongrel, webrick, etc) but can also be used with passenger, but with limitations.

Abort

This is equivelent to the PHP die() command. It takes an argument, evals and prints the statement to the screen. Note that it does halt execution.

abort('hello world')

This is a nice hacky way to print something out to the screen very fast, no matter where you are in your application.

Debug

Debug lets you easily eval something to a screen in your views.

debug('hello world')

Now that you have a bunch of debugging commands with rails, please stop using raise() to debug!


Sqlite3 Uninitialized Constant Encoding

Posted on April 17, 2010

I was getting a crazy error while trying to migrate/create a sqlite3 database using rails 2.3.5 today. After trolling the interwebs, and the source code I finally found a hint in the stack trace that sqlite3 gem was being called half way through the filter chain.

So to fix.

gem uninstall sqlite3

And make sure you only use

gem install sqlite3-ruby

Happy hacking!


How To Alias A Class Method In Ruby

Posted on March 30, 2010

To alias a class method, you can't just use the alias/alias_method methods.

Lets say we have a simple System Event class that has a log() method.

class SystemEvent
  def self.log
    // Do things in here (maybe database / file log depending on log levels)
  end
end

If for some reason we would want to alias log to be log_me, Then I would have to lay out my class as follows

class SystemEvent
  def self.log
    // Do things in here (maybe database / file log depending on log levels)
  end
  
  class << self 
    alias :log_me :log
  end
end

Changing The Cookie Domain Path In Rails

Posted on March 24, 2010

In rails 2.3.4+ if you want to modify your cooking information set by your system, in any of your environment files you can simple overwrite them as follows.

config.action_controller.session = {
:session_key => '_my_session_id',
:secret => '..your_secret_code_here..',
:domain => ".domain.com"
}

Obviously if you only want to change your domain for instance you can simple do

config.action_controller.session[:domain] = ".domain.com"

Rails Plugin: Associated With?

Posted on January 16, 2010

I've just released a new plugin called associated_with, that allows you to check if two objects are associated.

Check out the write up: http://markgandolfo.com/pages/associated-with

Or the GitHub Repo: http://github.com/markgandolfo/associated_with


How To Integrate Delayed Job Into Your App

Posted on January 10, 2010

So a quick how to integrate delayed job into your app, since i've found a lot of the tutorials out there leave out some crucial information.

Firstly, I used Tobi's delayed_job since it comes with a few extra bits a pieces, like generators and the like.

Install the plugin

You can either install it using the rails command, or use git submodules (which i won't cover here)

script/plugin install git://github.com/tobi/delayed_job.git

Generate and run the migration

We'll run the generator script to generate our migration and then migrate the database up

script/generate delayed_job_migration

Which will generate a migration that looks like this. Have a read through the file. You're probably asking yourself where the object to be run is stored. Its actually stored in the "handler" field as a sexy yaml field.

  create_table :delayed_jobs, :force => true do |table|
    table.integer  :priority, :default => 0      # Allows some jobs to jump to the front of the queue
    table.integer  :attempts, :default => 0      # Provides for retries, but still fail eventually.
    table.text     :handler                      # YAML-encoded string of the object that will do work
    table.string   :last_error                   # reason for last failure (See Note below)
    table.datetime :run_at                       # When to run. Could be Time.now for immediately, or sometime in the future.
    table.datetime :locked_at                    # Set when a client is working on this object
    table.datetime :failed_at                    # Set when all retries have failed (actually, by default, the record is deleted instead)
    table.string   :locked_by                    # Who is working on this object (if locked)
    table.timestamps
  end

How to queue mail in your app

Lets say for example we have a AccountsMailer

class AccountMailer < ActionMailer::Base  
  def confirm(user)
    subject       'Your account has been created'
    recipients    user.email
    from          'you@email.com'
    sent_on       Time.zone.now
    content_type  'text/html'
    body "Welcome Aboard"
  end
end

We would normally send an email by doing something like this in our controllers

AccountMailer.deliver_confirm(@user)

With delayed job, we can simply do a

AccountMailer.send_later(:deliver_confirm,@user)

So delayed job adds a send_later class method, which takes an AccountMailer class method as its first param, and then the methods params as the next.

How to send the mail

Setting up actionmailer is outside of the scope of this post. So i'm assuming you have it set up already.

The jobs at this stage are being queued in your database, but we haven't started any sort of job to process the queue. To start the queue simple run

rake jobs:work

You should see the queue starting to process, or at idle if its empty.

This is great, but what happens if the rake task dies for some reason???? Daemons to rescue!!!

Daemonising delayed job queue processor

First install the daemons gem

sudo gem install daemons

Next create a script in your script/ directory.. I called mine delayed_job

#!/usr/bin/env ruby
require 'rubygems'
require 'daemons'
dir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
 
daemon_options = {
  :multiple   => false,
  :dir_mode   => :normal,
  :dir        => File.join(dir, 'tmp', 'pids'),
  :backtrace  => true
}
 
Daemons.run_proc('job_runner', daemon_options) do
  if ARGV.include?('--')
    ARGV.slice! 0..ARGV.index('--')
  else
    ARGV.clear
  end
  
  Dir.chdir dir
  RAILS_ENV = ARGV.first || ENV['RAILS_ENV'] || 'development'
  require File.join('config', 'environment')
  
  Delayed::Worker.new.start
end

Save that, and you can now simple do the below to start the queue.

script/delayed_job start

I would recommend reading up on the delayed_job and daemons extensions to understand the inner workings, but at least this is enough to get you up and running!

notes

You can also clear the queue by running

rake jobs:clear

How To: Rspec And Factory Girl

Posted on December 20, 2009

I thought i'd add a quick how to, on setting up Factory Girl and RSpec

First lets install some gems

The below command will install rspec and factory girl as well as a bunch of dependencies if you don't have them already.

sudo gem install rspec rspec-rails thoughtbot-factory_girl

And configure your app

Now in your rails app, add the following to your environment/test.rb file (you can leave the version symbol out to always fetch the latest).

# /config/environment/test.rb
config.gem "rspec",                   :lib => false,          :version => ">=1.2.6"
config.gem "rspec-rails",             :lib => 'spec/rails',   :version => ">=1.2.6"
config.gem "thoughtbot-factory_girl", :lib => "factory_girl", :version => ">=1.2.2", :source => "http://gems.github.com"

Lets generate the rspec skeleton and configure factories

Now that we have our gems set up ready to roll, in your rails app run the below command, This will generate a /spec directory and create a spec_helper.rb file.

script/generate rspec

Now in the spec directory create a factories folder. This will be the place you can store the definitions for each factory.

mkdir spec/factories

In your spec_helper.rb file, add the following require's to ensure everything is loaded when your tests run.

# spec/spec_helper.rb
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment'))
require 'spec/autorun'
require 'spec/rails'
require 'factory_girl'

Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}

Spec::Runner.configure do |config|
  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures  = false
end

Factory girl should magically find all of your factories in that folder, but in one of my applications it didn't. I'm not sure why, and i didn't have the time to check, so i manually included them. To do this, in your spec_helper.rb file, add the following line, right above the "Spec::Runner".

Dir[File.expand_path(File.join(File.dirname(__FILE__),'factories','**','*.rb'))].each {|f| require f}

An example of how it all comes together

And you should be good to roll.. I'll give you a quick example of a factory and a simple spec, just to get you running. So if I had a Company model, I could test that model as such

# spec/factories/companies.rb
Factory.sequence :name do |n|
  "company name #{n}" 
end

Factory.define :company do |c|
  c.name { Factory.next(:name) }
  c.add_attribute :description, 'company description' 
  c.add_attribute :subdomain, 'company_name '
end

So I'm defining the factory called :company, and it has three fields. A name, description and subdomain. You'll notice I've defined name a little differently than description and subdomain. Thats because I want my name to incremental (i.e. I don't want two company names to be the same). Now each time i create a company factory, the name will increment by 1.

For a full break down on all of the magick of factories, check out the Factory Girl Github page.

Next, lets do some rspec magick.

So in my spec/models/ directory create a new file called company_spec.rb and here is a basic company test, using Factories

require 'spec_helper'

describe Company do
  before(:each) do
    @company = Factory(:company)
  end

  it "should create a new instance given valid attributes" do
    @company.should be_instance_of(Company)
  end
end

You can see by doing Factory(:company) I'm building a new factory and storing it in @company. Its as simple as that, no more messy fixtures.

Here are some resources to help you with rspec

Cheat Sheet

Another Cheat Sheet (PDF WARNING)

And Another Cheat Sheet (PDF WARNING)

Good luck with testing!


Restart Your Rails Server Within Textmate

Posted on December 19, 2009

I tend to find myself needing to restart my rails server in development mode quite often, and found that mongrel like passenger also restarts when tmp/restart.txt is present. So I created a simple command for Textmate which will easily restart the server for my current app i'm working in.

To install:

cd ~/Library/Application Support/TextMate/Bundles/Ruby on Rails.tmbundle/Commands
wget http://markgandolfo.com/system/files/2/original/Restart_Server.tmCommand

Then restart Textmate/Reload Bundles.

To use it simple press ^⌥⌘ R buttons, and your app will restart


How To Install Imagemagick On Snow Leopard

Posted on December 18, 2009

There are so many blog posts out there detailing the many many steps needed to install imagemagick on snow leopard, i thought I'd set the record straight.

sudo port install imagemagick
sudo gem install rmagick

Get a coffee while imagemagick installs, because it takes quite a while. But it works at the end of the day, and we can continue being developers, rather than sysadmins!


Ordered Hashes In Ruby 1.8

Posted on December 17, 2009

So I came across a situation where I needed an ordered hash. After doing some research and talking to the #rubyonrails guys I've found I'm far from the only one who needs this, and in Ruby 1.9 there is a OrderedHash class. But until then I had to create my own, and with the help of the guys in #rubyonrails I finally got something working

# lib/ordered_hash.rb
class OrderedHash < Hash
  def initialize
    @keys = []
    super
  end

  def []=(key, value) 
    @keys << key unless member?(key)
    super
  end

  def each
    @keys.each {|key| yield key, self[key]}
  end

  def delete(key)
    @keys.delete key
    super
  end
end

Usage is as follows!

# Usage:
h = OrderedHash.new
h[2] = 3
h["x"] = "y"
h.delete(3)
h.each {|key| p key}

Yea its pretty simple, but I'm still pretty new at all of this, so I chalk this as another milestone!


Using To Param Ftw

Posted on December 17, 2009

I have a client who draws comics. He wanted the url to be /comics/12-02-2008 (where 12-02-2008 was the date the comic was published). I tried for a long while, and finally got my routes to do :controller/:action/:published_on instead of the standard :id. I did this only for the comics controller, and all others had :controller/:action/:id.

I kicked myself when I found to_param!

class Comic < ActiveRecord::Base
    def to_param
        "#{id}-#{published_on}"
    end 
end

This allows me to convert the :id symbol to :published_on. Much nicer than the old crusty routes!


Active Resource To A Php App

Posted on December 17, 2009

Lately I've been getting into rails, what can I say its a nice clean language. I also run a forum, its a forum written in php. Now I want to build apps for my php (vbulletin) forum, but I didn't want to do cross site database calls. I wanted my plug ins to be as independent as possible from the main vbulletin app..

In another website I've been writing, there are three components that interconnect to each other using activeresource! Since it was so easy to implement I thought I'd give it a go cross programming language! Heres how I did it!

First in PHP create your restfull program! I called mine rest.php :) I know very original!

Using a sexy XML library I created for php. From this simply class, I can easily create from a select from a database my xml!

<?php
    $userid = $_GET['user'];
    $result = $db->query_read("select userid, username, email from user where userid = '$userid' ");
    $count = mysql_num_rows($result);

    $xml = new XmlBuilder();
    $xml->filename("user.xml");

    for($i=0; $i<$count; $i++) {
        $user = $db->fetch_array($result);
        $xml->openTag("user");      

        foreach ($user as $tag => $value) {
            $xml->addfield($tag, $value);
        }
        $xml->closeTag();
    }
    echo $xml->build();
?>

So far so good! So if we go to file.php?user=1 we should get the user with id 1.. You could add all sorts of cool things here. But this is just an example to get you guys going.

Now finally ensure you have mod_rewrite working in apache! now if your php file is called rest.php, you'll need this rewrite rule to make the magic happen. Just add it to a .htaccess file, or in your vhost

RewriteRule ^/rest/users/(.*) /rest.php?user=$1 

In your rails app, now create a model named User, e.g. models/user.rb, and add

class User < ActiveResource::Base
  self.site = "http://domain.com/rest/"
end

and don't forget your corresponding controller. This will now fetch all requests from our rest.php file. If you need any help, please drop me a line!

Now for our rails app

in our model if we have set up

class User < ActiveResource::Base
  self.site = "http://domain.com/rest/"
end

And we can simply do this in our rails app! You'll see below the find method on the user model, and the resulting url it'll call.

User.find(:all)      # http://domain.com/rest/users.xml
User.find(1)        # http://domain.com/rest/users/1.xml
User.find(100)    # http://domain.com/rest/users/100.xml

So if your doing an :all, the app will look for model.xml. If your doing any other call, it'll look for model/?.xml. I hope this helps when building external active resource communicators.