Be Genius

Fork Me
Mugshot

Hi, I'm Bodaniel Jeanes.

I'm a Ruby developer from Brisbane, Australia

I work at Mocra where I hack on awesome code. Follow me, recommend me, and link with me.

Automatically open the last page for failed scenarios

When writing new Cucumber scenarios, developers will usually start with a failing scenario, incrementally making it pass, step by step. If this is a common workflow for you, you’ll probably have noticed a trend of having to re-run it each time a new step is failing, adding something like Then show me the page after the last passing step to see why the story is currently failing.

Chendo and I came up with a nifty little solution in just a few lines that really helps this process of seeing what webrat sees when a scenario fails. Throw this snippet of code in a file in your features/support/ directory:

1
2
3
4
5
After do |scenario| if scenario.status == :failed save_and_open_page end end

Using partial layouts to DRY out your views

The other day we got some HTML from the designer of one of our clients to implement as the new UI for an app we are building at Mocra. Overall the HTML was good but implementing it started to feel a little moist (i.e. not DRY).

For example, around all the apps form the client’s design had a rounded white box, the HTML of which went something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="content-wht"> <div class="content-wht-inside"> <fieldset> <div class="frow"> <label>Currency:</label> <select class="fs"><option>EUR</option></select> </div> <div class="frow frlast"> <label>File:</label> <input type="file" name="" /> </div> </fieldset> </div> </div> <div class="cround" align="center"> <img src="/images/round-corners-bottom.gif" alt="" /> </div>

It’s a lot of div tags to have to repeat around every form that has this style. Luckily a nice feature of Rails partials can help out. Create a shared partial file that will act as a layout like this:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- app/views/shared/_fieldset.html.erb --> <div class="content-wht"> <div class="content-wht-inside"> <fieldset> <%= yield %> </fieldset> </div> </div> <div class="cround" align="center"> <img src="/images/round-corners-bottom.gif" alt="" /> </div>

Then in your views that need to have this white box, simply wrap your content in that layout like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<% render :layout => 'shared/fieldset' do %> <div class="frow"> <label>Currency:</label> <select class="fs"><option>EUR</option></select> </div> <div class="frow frlast"> <label>File:</label> <input type="file" name="" /> </div> <% end %> <!-- Or, if the inner content is already in a partial --> <%= render, :partial => 'form', :layout => 'shared/fieldset' %>

64-bit Postgres and Rails on Snow Leopard

Dependencies

Install the GEOS and PROJ frameworks from here

Postgres

Download and Compile

1
2
3
4
http://ftp2.au.postgresql.org/pub/postgresql/source/v8.4.1/postgresql-8.4.1.tar.bz2 | tar xjf - cd postgresql-8.4.1 ./configure make && sudo make install

The files are now all installed in the right places, onwards to making them usable!

Create a postgres User and Group

This is adjusted from the instructions in the comments here).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Find unused Group ID and User ID: export GROUPID=`sudo dscl . -list /Groups PrimaryGroupID | ruby -e 'puts STDIN.read.scan(/\d+/m).map{|i|i.to_i}.uniq.sort.last.succ'` export USERID=`sudo dscl . -list /Users UniqueID | ruby -e 'puts STDIN.read.scan(/\d+/m).map{|i|i.to_i}.uniq.sort.last.succ'` # Create group: sudo dscl . -create /Groups/_postgres sudo dscl . -create /Groups/_postgres PrimaryGroupID $GROUPID sudo dscl . -append /Groups/_postgres RecordName postgres # Create user: sudo dscl . -create /Users/_postgres sudo dscl . -create /Users/_postgres UniqueID $USERID sudo dscl . -create /Users/_postgres PrimaryGroupID $GROUPID sudo dscl . -create /Users/_postgres UserShell /bin/bash sudo dscl . -create /Users/_postgres RealName "PostgreSQL Server" sudo dscl . -create /Users/_postgres NFSHomeDirectory /usr/local/pgsql sudo dscl . -append /Users/_postgres RecordName postgres

That’s the clean way of making a user (so it doesn’t show up in your Accounts preference pane, etc.

Clean up

1
2
3
4
5
6
7
sudo touch /var/log/psql.log sudo mkdir /usr/local/pgsql/data sudo chown -R postgres:postgres /usr/local/pgsql/data /var/log/psql.log # Put Postgres in your path export $PATH="/usr/local/pgsql/bin:$PATH" sudo sh -c 'echo /usr/local/pgsql/bin > /etc/paths.d/pgsql'

Thanks to Apple we have a nice way of setting the PATH across the entire system so every app should now know how to find the Postgres binaries and the data directory is now set up to store the database files — now, we just need to create them.

Initialise the Database

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Become the postgres user sudo su - postgres # If this throws next commant throws an error about shared memory, # try putting "these lines":http://gist.github.com/224815 into # /etc/sysctl.conf, rebooting, and trying again: initdb -D /usr/local/pgsql/data -E UTF8 # Start the database postgres -D /usr/local/pgsql/data >/var/log/psql.log 2>&1 & # Create a test database and check you can connect to it createdb test psql test # Just create a superuser for general dev tasks createuser postgres # we don't want to be the postgres user anymore exit

Your done with the Postgres setup now!

PostGIS

1
2
3
4
curl http://postgis.refractions.net/download/postgis-1.3.7SVN.tar.gz | tar xzf - cd postgis-1.3.7SVN ./configure --with-geosconfig=/Library/Frameworks/GEOS.framework/unix/bin/geos-config --with-projdir=/Library/Frameworks/PROJ.framework/unix make && sudo make install

Phew! That one was easy.

Postgres Ruby Gem

This is the part that everyone cares about the most. If this works your Rails apps should start working like a charm.

1
sudo env ARCHFLAGS='-arch x86_64' gem install pg

Ultraviolet Tools

A few weeks ago, I was modifying my clone of Enki (my blogging platform) to use Ultraviolet instead of CodeRay, as it supports using both TextMate themes and syntaxes. This means I can extend it fairly limitlessly and use TextMate itself to modify the themes an syntaxes.

However, Ultraviolet doesn’t come with the best tools to turn the TextMate syntax and theme files into files Ultraviolet can use, and the ones that it does provide puts those translated files INTO the gem on your system. This means that you can’t easily deploy or share them.

So, to remedy this I made a small gem Ultraviolet Tools that provides two command line tools: uv-create-syntax and uv-create-theme.

They aren’t very complicated, nor very documented, but they are straight-forward to use:

Install

Install Ultraviolet Tools with sudo gem install bjeanes-ultraviolet-tools -s http://gems.github.com.

Usage

Themes

Run uv-create-theme path/to/theme.tmTheme

Syntaxes

Run uv-create_syntax path/to/syntax.plist or uv-create_syntax path/to/bundle/with/syntaxes.tmBundle

Using the resulting files

Use my fork of ultraviolet as it has an option to change the load path for themes and syntaxes so that you can load them out of your application files.

Using fields_for can generate invalid HTML

Just a quick post so show you how fields_for form helper can cause invalid HTML when used in combination with accepts_nested_attributes_for in your views.

If you are for any reason editing/creating your child objects in a tabular form, your view might look something like this:

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
<% form_for(@todo_list) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <h3>To Do Items:</h3> <table> <thead> <tr> <th>To Do Item</th> <th>Due Date</th> <th>Done?</th> </tr> </thead> <tbody> <% f.fields_for :items do |item| %> <tr> <td><%= item.text_field :content %></td> <td><%= item.text_field :due_date %></td> <td><%= item.check_box :done %></td> </tr> <% end %> </tbody> </table> <%= f.submit 'Save' %> <% end %>

At first glance this looks fine. However, when the ToDoList model has accepts_nested_attributes_for :items, the fields_for helper also outputs a hidden field for each existing Item instance with it’s ID.

This means that we get the following HTML:

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
32
33
34
35
<form action="/to_do_lists/1" class="edit_to_do_list" id="edit_to_do_list_1" method="post"> <div style="margin:0;padding:0;display:inline"> <input name="_method" type="hidden" value="put" /> <input name="authenticity_token" type="hidden" value="CE2jyRoewbwsqu4eX8AFWFqsgrMfCN35Jzy6b43MhsA=" /> </div> <label for="to_do_list_name">Name</label> <input id="to_do_list_name" name="to_do_list[name]" size="30" type="text" value="Sup" /> <h3>To Do Items:</h3> <table> <thead> <tr> <th>To Do Item</th> <th>Due Date</th> <th>Done?</th> </tr> </thead> <tbody> <input id="to_do_list_items_attributes_0_id" name="to_do_list[items_attributes][0][id]" type="hidden" value="1" /> <tr> <td><input id="to_do_list_items_attributes_0_content" name="to_do_list[items_attributes][0][content]" size="30" type="text" value="arst" /></td> <td><input id="to_do_list_items_attributes_0_due_date" name="to_do_list[items_attributes][0][due_date]" size="30" type="text" value="2009-07-30" /></td> <td><input name="to_do_list[items_attributes][0][done]" type="hidden" value="0" /><input id="to_do_list_items_attributes_0_done" name="to_do_list[items_attributes][0][done]" type="checkbox" value="1" /></td> </tr> <input id="to_do_list_items_attributes_1_id" name="to_do_list[items_attributes][1][id]" type="hidden" value="2" /> <tr> <td><input id="to_do_list_items_attributes_1_content" name="to_do_list[items_attributes][1][content]" size="30" type="text" value="arst" /></td> <td><input id="to_do_list_items_attributes_1_due_date" name="to_do_list[items_attributes][1][due_date]" size="30" type="text" value="2009-07-30" /></td> <td><input name="to_do_list[items_attributes][1][done]" type="hidden" value="0" /><input id="to_do_list_items_attributes_1_done" name="to_do_list[items_attributes][1][done]" type="checkbox" value="1" /></td> </tr> </tbody> </table> <input id="to_do_list_submit" name="commit" type="submit" value="Save" /> </form>

The keen eye will notice the hidden <input> tags that are direct children of the tbody:

1
2
3
4
5
6
<tbody> <input id="to_do_list_items_attributes_0_id" name="to_do_list[items_attributes][0][id]" type="hidden" value="1" /> <tr><!-- clip! --></tr> <input id="to_do_list_items_attributes_1_id" name="to_do_list[items_attributes][1][id]" type="hidden" value="2" /> <tr><!-- clip! --></tr> </tbody>

This is not a valid place for an <input> tag.

I’ve put an example project demonstrating this on my GitHub

Using accepts_nested_attributes_for When the Child Object validates_presence_of Parent

I came across an interesting dilemma today in Rails 2.3 whilst trying to use accepts_nested_attributes_for.

The models I have were:

1
2
3
4
5
6
7
8
9
class Organization < ActiveRecord::Base has_many :users accepts_nested_attributes_for :users end class User < ActiveRecord::Base belongs_to :organization validates_presence_of :organization end

I had an “Organization” creation form which allowed an administrator to create an Organization and an initial user for it. However, even though I had filled in all the required fields to pass the validations I was constantly getting the following error: Organization can not be blank.

Digging deeper I found that when an object is added to an association via the association.build method, the parent object isn’t actually set:

1
2
3
4
5
6
>> o = Organization.new(:name => "Test") => #<Organization id: nil, name: "Test"> >> u = o.users.build(:name => "Test User") => #<User id: nil, organization_id: nil, name: "Test User"> >> u.organization => nil

Apparently this flaw also applies to accepts_nested_attributes_for. When I send through the User params through from my form normally with the Organization params, it creates the User object in the users association as you’d expect, but raises a validation error because the User instance doesn’t recognise that it is has a parent Organization

My quick 5 minute fix was to modify the Organization model thusly:

1
2
3
4
5
6
7
8
9
10
class Organization < ActiveRecord::Base has_many :users accepts_nested_attributes_for :users def users_attributes_with_self_assignment=(attributes) self.users_attributes_without_self_assignment = attributes users.each { |u| u.organization = self } end alias_method_chain :users_attributes=, :self_assignment end

Related Rails bugs seem to be here and here. This patch will also solve the problem and is currently in edge, but didn’t make it in time for Rails 2.3.

Using fields_for with Nested Attributes, Calling it Multiple Times

There has been quite an annoying problem that has been bugging @chendo and I today.

In short, fields_for when used with accepts_nested_attributes_for does not reset it’s index when it’s called multiple times for the same attribute.

Our scenario was the following:

We had a 3 levels of hierarchical data that we had to display in a <table>. Because of the nature of the data we were displaying one level as columns and the other level as rows. Unfortunately, HTML mandates that we group everything by rows—each column is really just a single cell. This means that we have to iterate over the “column” values not just once, but once for every row.

Now, fields_for takes care of naming your <input> tags magically with the correct indexes so that your models can accept the params directly and magic happens. However, evidently calling fields_for multiple times was not part of the original intention, and it seems resetting the index after each call was neglected.

@chendo couldn’t see a cleaner way of doing this so we added our own option to reset the index. We didn’t just override the behaviour in case there is a good reason to leave it, but here is our code:

1
2
3
4
5
6
7
8
9
10
11
class ActionView::Helpers::FormBuilder def fields_for_with_nested_attributes_with_index_reset(association_name, args, block) if args.last.is_a?(Hash) && args.last[:reset_index] @nested_child_index = nil end fields_for_without_nested_attributes_without_index_reset(association_name, args, block) end alias_method_change :fields_for_with_nested_attributes, :index_reset end

Using AlterEgo with ActiveRecord

Today after finding the gem AlterEgo and falling in love with the API, @chendo and I came to a sad realisation that its beauty was skin-deep. It seems it is not ActiveRecord-aware and overrides #state and #state= on your models. This means that your state column goes untouched and your models are always in the default state.

To get around this, @chendo and I hacked together this little monkey patch which does two things:

  1. It adds question methods for each of your defined states. So if you have states: active, inactive, and banned on your User model, your instances will have #active?, #inactive?, and #banned? defined.
  2. It properly sets and gets the state of the row and loads it into AlterEgo.

Here is the code:

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
32
33
34
35
36
37
38
39
40
41
42
module AlterEgo def self.included(base) base.instance_eval do # This will allow us to define a question method for each state in a class # For instance if user has states :active and :inactive, methods active? and inactive? # will be defined when first called def method_missing_with_states(method, *args, &block) begin method_missing_without_states(method, *args, &block) rescue NoMethodError => e state_found = false result = nil base.states.keys.each do |state| if method.to_s == "#{state}?" && !state_found self.class.send :define_method, "#{state}?" do self.state == state end state_found = true result = self.__send__(method) end end raise(e) unless state_found result end end alias_method_chain :method_missing, :states # state setter to write to database define_method :state= do |value| write_attribute(:state, value.to_s) end # state getter to read from database define_method :state_with_active_record do (read_attribute(:state) || (self.state = state_without_active_record)).to_sym end alias_method_chain :state, :active_record end end end

Snow Leopard for the Ruby Developer

After the 2009 WWDC release, I decided to give Snow Leopard a try on my production machine. Crossing my fingers, expecting a plethora of broken gems and databases, I wiped my hard drive clean and installed the latest build.

Much to my surprise, most things worked flawlessly and exactly as it did on Leopard.

What Ships with Snow Leopard

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
32
33
34
35
36
37
38
39
40
41
42
43
$ gem --version 1.3.1 $ ruby --version ruby 1.8.7 (2008-08-11 patchlevel 72) [universal-darwin10.0] $ gem list *** LOCAL GEMS *** actionmailer (2.2.2, 1.3.6) actionpack (2.2.2, 1.13.6) actionwebservice (1.2.6) activerecord (2.2.2, 1.15.6) activeresource (2.2.2) activesupport (2.2.2, 1.4.4) acts_as_ferret (0.4.3) capistrano (2.5.2) cgi_multipart_eof_fix (2.5.0) daemons (1.0.10) dnssd (0.6.0) fastthread (1.0.1) fcgi (0.8.7) ferret (0.11.6) gem_plugin (0.2.3) highline (1.5.0) hpricot (0.6.164) libxml-ruby (1.1.2) mongrel (1.1.5) needle (1.3.0) net-scp (1.0.1) net-sftp (2.0.1, 1.1.1) net-ssh (2.0.4, 1.1.4) net-ssh-gateway (1.0.0) rails (2.2.2, 1.2.6) rake (0.8.3) RedCloth (4.1.1) ruby-openid (2.1.2) ruby-yadis (0.3.4) rubynode (0.1.5) sqlite3-ruby (1.2.4) termios (0.9.4) xmpp4r (0.4)

Quite possibly these default gems and versions will change between now and the final release of Snow Leopard. For instance, this seed comes with Rails 2.2.2, even though we are already at 2.3.2. Snow Leopard doesn’t come out till September and by then we could have an even newer version of Rails that might be bundled.

What doesn’t work, and how to fix it.

Textmate – Everything works fine except for the following shortcuts: ⌘←, ⌘→, ⌘⇧←, and ⌘⇧→. These shortcuts are for moving the text insertion point to beginning and end of line, and selecting to beginning and end of line, respectively. The fix is easy, simply download and double click the TextMate macros attached to this ticket

Nokogiri – Nokogiri installs fine but gives error “native.bundle: mach-o, but wrong architecture” when used. Getting around this is as easy as running sudo gem install nokogiri -s http://tenderlovemaking.com/ to get the latest development release which includes a fix for this.

do_sqlite3 – I haven’t personally used this or tried to fix it but I saw report of it not working by @benlovell

Passenger PrefPane – Preference pane does not load. No known fix (it’s probably an easy fix but I haven’t looked enough into it to try).