Mark Gandolfo's Blog
Hudson Ci Server Running Selenium/Webdriver Cucumber In Headless Mode Xvfb
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
- Remove all defined build sets.
- Add a new build step "Execute Shell" with the command "cp config/database.tests.yml config/database.yml"
- Add a new build step "Exceute Shell" with the command "RAILS_ENV=test rake gems:install"
- Add a new build step "Execute Shell" with the command "RAILS_ENV=test rake db:migrate"
- Add a new build step "Execute shell" with the command "spec spec" # if you have specs
- 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
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
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
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
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
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?
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
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
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
Another Cheat Sheet (PDF WARNING)
And Another Cheat Sheet (PDF WARNING)
Good luck with testing!
Restart Your Rails Server Within Textmate
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
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
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
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
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.