Falling Leaves: the Ardes blog

Changes

June 25th, 2010

Ray Drainville

You’ll also notice that the ability to comment on posts has been removed. The subject of site comments has emerged recently. The simple fact is that most comments posted are spam, and while they rarely make it onto the blog, they gunk up the system. Comments are still gratefully appreciated: just use our standard contact form.

You may also have noted that Ian has been quiet for some time. We split amicably: he left the company to pursue his own projects. They’re incredibly worthwhile: if you’re a developer, I strongly encourage you to have a look at what he’s got up at GitHub. And of course you can always follow him on Twitter.

Ian White

The first WIN for us to come out of the Rails/Merb merge is that the awesome provides API in merb (which Dan gave a compelling introduction to at RailsCamp4), has made its way to rails-3 (along with some tweaks to the API).

This is a win for us because our response_for plugin provides similar functionality (for rails 2 apps). So that's one less plugin for me to maintain.

Can't wait for RARB!

Ian White

I'm upgrading one of our apps to rails 2.2 and along with that, some plugin dependencies.

In this instance, I needed to monkey patch a method in attachment_fu to make it use the new i18n framework in Rails.

I got to thinking that it would be great if I could be reminded when this monkey patch is no longer necessary (ie. if I upgrade attachmnet_fu, and if it has made that method compatible with i18n). This was easily achieved, using rspec's pending.


pending: it's alive!

(you may want to skip straight to the pending monkey-patch stuff)

You may not know that it you pass a block to pending, it will still run that block, but it will expect it to fail. If the block passes, then rspec will tell you that the example is now working.

Here's an example: Nick is expanding on some specs for some code that I wrote.

it "fred@fang should not be a valid email" do
  @user.email = 'fred@fang'
  @user.should have(1).error_on(:email)
end

The above spec fails because currently, I don't have a complete email regexp. For whatever reason, I tell Nick that I'll look into that at some later point. What do we do next? We could open a ticket, or write a TODO, but what about that spec?

Using pending, we can add some live doco to our app:

it "fred@fang should not be a valid email" do
  pending "a better email regexp" do
    @user.email = 'fred@fang'
    @user.should have(1).error_on(:email)
  end
end

The above code will be shown as PENDING as long as the example fails. When someone gets around to writing a better regexp, the code will pass, and the rspec will fail the build telling you that the example is no longer pending.


Use pending to monitor your monkey-patches

So, we can use this neat feature of rspec to tell us when a monkey-patch is no longer required:

  • Write a spec that exhibits the desired behaviour
  • This spec should fail
  • Wrap the example(s) in pending block(s)
  • (optional, as your app specs might already test this) Write a spec that passes on the monkey-patched code.

In the example given above, all I had to do was spec out the behaviour that attachment_fu should exhibit, then wrap that in an informative pending block.

require File.dirname(__FILE__) + '/../spec_helper'

describe "attachment_fu and i18n" do
  describe "with an invalid attachment" do
    before do
      @model = mock('an attachment_fu model')
      @model.extend Technoweenie::AttachmentFu::InstanceMethods
      @model.stub!(:errors).and_return(mock('errors', :add => nil))
    
      # setup an invalid condition on the model
      @model.stub!(:attachment_options).and_return(:size => (2..3))
      @model.stub!(:size).and_return(1)
    end

    it "#attachment_attributes_valid? should not call default_error_messages" do
      pending "if this passes remove Asset#attachment_attributes_valid?" do
        ActiveRecord::Errors.should_not_receive(:default_error_messages)
        @model.send :attachment_attributes_valid?
      end
    end
  end
end

This means that if I upgrade to a version of attachment_fu that fixes the above problem, my specs will tell me my monkey-patch is no longer required.

refactorin' VC, 64 stylee

December 1st, 2008

Ian White

After railscamp#4, I went to #rorosyd's November meetup which experimented with 2 sessions of 6 lightning talk's, and was GREAT (but the lack of time for questions was not so great).

I gave one on refactoring VC (as in MVC) with response_for and inherit_views. For some reason, I had a serious bout of nostalgia as I was preparing the talk and came up with this (watch the for the 'bytes free' automatic countdown). If that pisses you off, then the slides will piss you off slightly less.

tabtab - freakin awesome!

November 23rd, 2008

Ian White

One of the projects to come out of RailsCamp 4 (photos) (well apparently it started on the flight from Birsbane) is Dr Nic's super awesome autocompletions - tabtab.

It's basically context sensitive autocompletion for your ruby (and shell) world (and you can add your own completions). Think git-bash-autocompletion - for everything! To get it installed:

sudo gem install tabtab
install_tabtab

Finally, add source ~/.tabtab.bash to the bottom of your ~/.profile

Here's an example of its use - using the github gem to checkout forks of one of my projects.


Adding your own completions

tabtab has a snazy ruby dsl to add completions. I tried adding them for the CI tool garlic and it took a whole minute!.

This was due to the fact you tabtab will parse your help file (if you tell it to) for the command line options, and the fact that I already had an abstraction in garlic to introspect the command-line/rake-tasks. If you don't have the latter it would take maybe 2 whole minutes.

After I pushed garlic, and on reinstall, I just had to run install_tabtab to have the garlic tabtab def automagically appear in my ~/.tabtab.bash.

Check out the comprehensive README. Thanks Dr Nic!

Ian White

Rails edge is getting lots of changes under the hood, consequently garlic has been kicking up a fuss for response_for and inherit_views...

So, I've rewritten them! Both plugins are now a lot cleaner, and have less LOC, due to some great changes under the actionpack hood.

inherit_views | response_for

inherit_views is a pretty simple concept. When you subclass a controller in rails, it would be great if you controller inherited its views from the the view path of the superclass controller. Here's how to use it (or you can just Skip ahead to the rewrite).

Using inherit_views

If you want every controller to inherit it's views from parent controllers, then do this:

class ApplicationController < ActionController::Base
  inherit_views

I think this is generally a bad idea, I use inherit views when I need to (when I subclass a controller):

class AccountImagesController < ImagesController
  inherit_views 'images'

When you use inherit_views, all view types are supported, as are partials. This means that with the following setup:

images/_form.html.erb
images/edit.html.erb

account_images/_form.html.erb

rendering 'edit' from AccountImages controller will render account_images/_form.html.erb inside images/edit.html.erb.

Rewriting inherit_views for edge

The inherit_views plugin used to acheive this by alias_method_chaining render on the controller and the view, and it would substitute an inherited view if the standard view couldn't be found. Along the way, it got patched for various changes in the rails internal, and well... it got pretty messy.

Edge rails has had a lot of template love recently, and one of the great things is that the notion of picking a template to render now has a single point of entry: ActionView::Base#_pick_template. (there is also _pick_partial_template, but that just massages the filename into the partial format, then calls _pick_template).

This made me stoked because all of the shennanigans in inherit_views could be swept away, and replaced with #_pick_template_from_inherit_view_paths. The only thing left for the plugin to do is to manage the inherit view paths (these are stored on the controller).

A note for current inherit_views users, I've removed some of the API which I never used (render_parent, and some other things). Let me know if you really need that stuff...

inherit_views | response_for

Ian White

Rails edge is getting lots of changes under the hood, consequently garlic has been kicking up a fuss for response_for and inherit_views...

So, I've rewritten them! Both plugins are now a lot cleaner, and have less LOC, due to some great changes under the actionpack hood.

inherit_views | response_for

Summary: response_for lets you specify a default response for an action. If that action (or a before_filter) doesn't perform a render or redirect, then the default response is rendered. You can also 'stack' responses - the latter ones override the former ones.

To get the full benefit of response_for, you need to write your actions with it in mind. It's not allways appropriate - but for some things it's great. if you have a set of actions that you use a lot (say if you're a resources_controller user), or if you use superclass controllers, then you'll probably get some benefits form response_for.

Before talking about the rewrite, I'll just give a before and after example of a common use case for response_for - subclassed controller action. If you want to skip this just head straight to the section regarding the rewrite.

Before

Here is a fairly standard controller with an action that does some stuff, then conditionally renders a response.

class SuperclassController < ApplicationController
  # the action does some stuff, then conditionally renders a repsonse
  # for :html, and :js
  def the_action
    do_some_stuff
   
    did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          flash[:notice] = "Well done you"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


There are two main reasons to override this action in a subclassed controller, (i) to change the response, and (ii) to change the logic. Both of these require a rewrite of the entire action! This is painful.

Code speaks louder than words, so here are two subclassed controllers, both of which add authentication, one which chnages the html response on success, the other which changes the logic. Click here to skip the code.

class Subclass1Controller < SuperclassController
  # We add authentication, and change the response
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          # We change the response
          flash[:notice] = "Well done #{current_user.display_name}"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


class Subclass2Controller < SuperclassController
  # We add authentication, and change the logic
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    # Here we change the logic, but we have to write out the response again
    did_yet_more_stuff = if do_some_more_stuff_for_user(current_user)
      do_yet_more_stuff
    else
      do_the_other_thing
    end
   
    respond_to do |format|
      if did_yet_more_stuff
        format.html do
          flash[:notice] = "Well done you"
          redirect_to the_done_url
        end
        format.js
      else
        format.html do
          flash[:notice] = "Bad boy"
          redirect_to the_bad_url
        end
        format.js { render :action => 'the_action' }
      end
    end
  end
end


After..

Here's the same effect, with response for. We do need to write the superclass action a little differently first. We just take the response out of the action. We need to add an instance variable, so the response can conditionally render different things.

class SuperclassController < ApplicationController
  # this time with response_for
  def the_action
    do_some_stuff
   
    @did_yet_more_stuff = if do_some_more_stuff
      do_yet_more_stuff
    else
      do_the_other_thing
    end
  end
 
  response_for :the_action do |format|
    if @did_yet_more_stuff
      format.html do
        flash[:notice] = "Well done you"
        redirect_to the_done_url
      end
      format.js
    else
      format.html do
        flash[:notice] = "Bad boy"
        redirect_to the_bad_url
      end
      format.js { render :action => 'the_action' }
    end
  end
end

Now, our two subclassed controllers look like this:

class Subclass1Controller < SuperclassController
  # We add authentication, and change the response
  before_filter :require_login
 
  # we only override the repsonse that has changed
  response_for :the_action do |format|
    if @did_yet_more_stuff
      format.html do
        flash[:notice] = "Well done #{current_user.display_name}"
        redirect_to the_done_url
      end
    end
  end
end


class Subclass2Controller < SuperclassController
  # We add authentication, and change the logic
  before_filter :require_login
 
  def the_action
    do_some_stuff
   
    # Here we change the logic, we don't need to touch the response
    @did_yet_more_stuff = if do_some_more_stuff_for_user(current_user)
      do_yet_more_stuff
    else
      do_the_other_thing
    end
  end
end


The rewrite

The original response_for had to do some pretty tricky messin about to kick in once the action had done its thing. Things got a whole lot easier when actionpack introduced the default_render hook.

default_render is called after your action if a response has not explicitly been rendered (or redirected). You can use this yourself in your controllers if you want to do something other than just rendering the default template. response_for alias_method_chains it to render any declared responses..

If you're a previous response_for user

Along with the rewrite, I changed the way repsonse_for works. It used to intercept respond_to, so that you could override any repsonse declared in your action. This resulted in a bunch of weird edge cases, and so I decided to ditch it (read more about this at the end of the response_for README). If you need the old functionality use the 0.1-stable branch.

inherit_views | response_for

RailsConf Talk

September 12th, 2008

Ray Drainville

My business partner Ian White is is a very busy man. Currently, he’s just arrived in his native Australia, but only a few days ago he attended & gave a talk at RailsConf Europe—you can find the précis for his talk, Writing Resources_Controller: Discovering REST Patterns in Rails, on their site.

Once he’s recovered from his jet lag, Ian’s planning on giving some more information on his talk here. Stay tuned!

Update

The slides have finally appeared on the oreilly RailsConfEurope page (near the bottom).

Ian White

Update: check out Nick's latest post for up to date info.


There's a bunch of articles that helpfully diagnose and solve the GRRRRowl + autotest + Leopard snafu.

In order to save me, and my cohorts, some time I've bundled it all up in a README + script + icons.

Being a new father, currently at my in-laws, I've reached for the nearest toy, and made bob the builder icons. I think they might even challenge hookercookerman's set (sadly, not on the web as afar as I can see - step up hookercookerman and post them).

Grab it

here (tarball, installation instructions inside)

P.S.

I made the screenshots with Photo booth's 'backdrop' feature, and a real toy. Finally, I can do composites (I just knew not learning photoshop would pay off).

Ian White

So you're using ssh keys for all your servers and github and whatnot, and you've been using SSHKeychain in OS X 10.4, to manage that, and not type your password in all the time (SSHKeychain is a gui for ssh-agent).

Now that you're on Leopard, this all Just Works. No need for SSHKeychain (although SSHKeychain does other things too), and everything is nicely integrated with Mac OS keychain services. There's a nice write up over here at Ormset i Noreg. Buried in the comments, Luke Redpath notes that macports users might not be seeing this coolness, as OpenSSL is a dependency of some common ports (like git), and so the macport's ssh is not compiled with the leopard funkiness.

To fix this (assuming a standard macports install):

  sudo mv /opt/local/bin/ssh /opt/local/bin/ssh-macports
  sudo mv /opt/local/bin/scp /opt/local/bin/scp-macports

Now, when you next push to github, or deploy to a server, you'll get something like this:

Hooray!

git hosting with Leopard

May 19th, 2008

Ian White

So you wanna host your own git repos? Got Leopard? Got Git? Read On...

Assumptions: You have git installed.


Create a git server on leopard with gitosis

Download and install gitosis

  mkdir src
  git clone git://eagain.net/gitosis.git
  cd gitosis
  sudo python setup.py install

Create a git user and group on the server

Create a unix user and group for git, using dscl: leopard's directory service cli

1. Find an unused uid and gid

  sudo dscl . list /Users uid
  sudo dscl . list groups gid

(check that, say, 401, is unused in both)

2. Create the git group

  sudo dscl . create groups/git
  sudo dscl . create groups/git gid 401

3. Create the git user

  sudo dscl . create users/git
  sudo dscl . create users/git uid 401
  sudo dscl . create users/git NFSHomeDirectory /Users/git
  sudo dscl . create users/git gid 401
  sudo dscl . create users/git UserShell /bin/bash
  sudo dscl . create users/git Password '*'

4. Create the git home directory (make this location match the end of line 3 above)

  sudo mkdir /Users/git
  sudo chown git /Users/git
  sudo chgrp git /Users/git

Create an ssh key, and copy it to the server

1. If you don't already have one, create an ssh key, on your local machine if it is not the server

  ssh-keygen -t rsa

2. Copy the public key to /tmp on the server

(if your local machine is the server)

  cp ~/.ssh/id_rsa.pub /tmp/my_key.pub

(if the server is different from your local machine)

  scp ~/.ssh/id_rsa.pub your.server.com:/tmp/my_key.pub

Setup gitosis in git user's home directory

1. Initialise gitosis [on the git server]

  sudo -H -u git gitosis-init < /tmp/my_key.pub
(you should see something like this:)
  Initialized empty Git repository in ./
  Reinitialized existing Git repository in ./

2. Make sure git's paths are set to your current ones (where you can see gitosis and git)

  sudo su git  (enter your password)
  echo "export PATH=$PATH" > ~/.bashrc
  exit

Update: drwestco advises that with git 1.6 you should append the following line to the git user's .bash_rc.

export PATH=$(git --exec-path):$PATH

Clone the gitosis repo to your local machine

  git clone git@your.server.com:gitosis-admin.git

If you see something like this, then you're all set

  remote: Counting objects: 5, done.
  remote: Compressing objects: 100% (4/4), done.
  remote: Total 5 (delta 0), reused 5 (delta 0)
  Receiving objects: 100% (5/5), done.

What next?

Get familiar with gitosis. scie.nti.st has a great writeup for *nix systems, which I used as a reference point. The end of that blog has some general intro to gitosis.

Troubles? Make sure that you can ssh to the server as git (make sure that Leopard's ssh settings allow any user to login, or edit /etc/sshd_config). Otherwise post your troubles here in the comments.

Congratulations to Ian!

May 7th, 2008

Ray Drainville

As of last night, Ian became a father! At around 11pm, his son was born. Let me be the first in a public forum to congratulate him :)

Hopefully, he’ll soon post pictures—and a name!

Ian White

Many of our plugins are now available on github: github.com/ianwhite

And OSS projects such as these can now be hosted free on lighthouse: ianwhite.lighthouseapp.com

The ones that you see on lighthouse have all had a good dusting off to make sure they're compliant with the very latest edge, and BC to 2.0.2. response_for is now branched to support edge and 2.0.2, but the other plugins haven't yet required this.

If you don't see one you use there, it means that I haven't deemed it being used by many people - so leave a comment to say otherwise

resources_controller: things

April 27th, 2008

Ian White

RC's got a github home now

The subversion repo will still continue to be maintained for the foreseeable future.

RC's also reported as being one of the things under the hood at naked.

Lastly, I've been cooking my CI with garlic.

Ian White

So I've been curled up in a ball, riding the git avalanche, trying to sort out my rails plugins - making sure they're getting tested against the latest and greatest.

Inspired by this ticket for rspec, git's coolness, some menthol snuff, and a lot of coffee, I came up with garlic.

It's an extremely lightweight set of rake tasks that let you test your plugins or app against various version of rails, and other dependencies.

If you want to see it in action (on one of my plugins), do this:

  git clone git://github.com/ianwhite/inherit_views
  cd inherit_views
  rake cruise

Sit back, watch it download all the dependencies, then create rails apps for each set, and run the rcov task for the plugin in each one... (the download only happens the first time you do it).

You configure it using a little dsl, like this:

garlic do
  # default paths are 'garlic/work', and 'garlic/repos'
  work_path "tmp/work"
  repo_path "tmp/repos"
  
  # repo, give a url, specify :local to use a local repo (faster
  # and will still update from the origin url)
  repo 'rails', :url => 'git://github.com/rails/rails' #, :local => "~/dev/vendor/rails"
  repo 'rspec', :url => 'git://github.com/dchelimsky/rspec'
  repo 'rspec-rails', :url => 'git://github.com/ianwhite/rspec-rails'
  repo 'inherit_views', :url => '.'
  
  # for target, default repo is 'rails', default branch is 'master'
  target 'edge'
  target '2.0-stable', :branch => 'origin/2-0-stable'
  target '2.0.2', :tag => 'v2.0.2'
  
  all_targets do
    prepare do
      plugin 'rspec'
      plugin 'rspec-rails', :branch => 'origin/aliased-render-partial' do
        sh "script/generate rspec -f"
      end
      plugin 'inherit_views'
    end
    
    run do
      cd "vendor/plugins/inherit_views" do
        sh "rake spec:rcov:verify"
      end
    end
  end
end

Notice that I'm using my fork of rpsec-rails, and the plugin specifies that it should use a particular branch 'aliased-render-partial'. The reason for this is that I have some outstanding tickets on rspec, which haven't been resolved. In the meantime, I can just use my patched version. If the patch gets accepted, I can just change the url, and garlic will inform me that I need to remove and run rake garlic:install_repos to get the new one. This is just making use of the awesomely cool coolness of git.

Also notice the block passed to the 'rspec-rails' plugin. This will be executed inside the rails target after the plugin has been installed. Finally the run block says what should happen for the actual CI. In this case cding into the plugin and running an rcov task.

It's new stuff

So it probably has bugs and stuff.