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!

Nick Rutherford

GitHub has a useful page on how to install gems directly from github.

A recent OS X example is

$ sudo gem install spicycode-rcov
Building native extensions.  This could take a while...
Successfully installed spicycode-rcov-0.8.1.5.0
1 gem installed
Installing ri documentation for spicycode-rcov-0.8.1.5.0...
Installing RDoc documentation for spicycode-rcov-0.8.1.5.0...

MateWatch revisited '08

November 8th, 2008

Nick Rutherford

The Background

Recording worked hours can be something of a chore, thankfully help is at hand. Matewatch is a bare-minimum solution to the problem, providing you with the data you need (hours spent on a textmate project) and nothing more. It's free, inherently, in the sense that the Ruby code is being given away.

Ian White produced MateWatch and we use it in-house for time tracking. I haven't really used it yet so can't vouch for it, but a year on I doubt Ray and Ian would be using it if it wasn't doing what they wanted.

For installation and use instructions see the original posting

Gotcha

If you're doing this ~ now you may hit upon issues with the current Macports version of Rubyosa (0.4.0) when trying to start Matewatch.

I hit upon this error:

Library/Ruby/Gems/1.8/gems/rubyosa-0.4.0/lib/rbosa.rb:530: [BUG] Bus Error
ruby 1.8.6 (2008-03-03) [universal-darwin9.0]

Abort trap

The solution is to install an older version ruby gem and modify a source file for rubyosa to use this package instead of the most recent.

sudo gem install libxml-ruby --version 0.3.8.4 
sudo sed -i -e "s|require 'xml/libxml'|gem 'xml/libxml', '= 0.3.8.4'|" /Library/Ruby/Gems/1.8/gems/rubyosa-0.4.0/lib/rbosa.rb

My solution is from here and the issue is bug-reported here.

Nick Rutherford

A bash script to update macports system & installed ports, and the same for ruby gems.

Read the rest of this entry

A trivial bash script

October 4th, 2008

Nick Rutherford

Over the summer I was working with another company doing some build system work, which involved a lot of shell scripting (it was more to do with integration than make).

I’ve not got to the stage where I use Ruby on a daily basis for everything yet, which is where I used to be with Java (which was horrible for it!). Currently the weapon of choice is bash scripting, alas, I don’t have my handy O’Reilly book to reference anymore and I’m starting to forget things. Man pages and Google go a long way, but the reason I got the book in the first place was to save time!

Anyway, here’s what I’m using to update all my textmate bundles, which are a mixture of svn and git repos. Usual disclaimer: Y.M.M.V., don’t use it to baby-sit your kids while they play in traffic, etc. An obvious flaw with it is if git or svn fails you won’t get any feedback about it, and if the svn message changes it’ll stop working!


#!/bin/bash
cd ~/Library/Application\ Support/TextMate/Bundles/

export LC_CTYPE=en_GB.UTF-8

for f in ./*; do 
    cd "$f" #enter bundle dir
    echo "Updating $f" 
    git pull &> /dev/null \
        || svnres=$(svn update) \
        && [[ $svnres  = "Skipped '.'" ]] \
        && echo "*** Failed to update $f (is it a local setting not a repo?)" 
    cd .. #return to bundles dir
done

Feel free to comment / improve / re-implement in Ruby.

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

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).

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.

Ray Drainville

I don’t know how I missed this (well, I do, I've been ridiculously busy), but about a month ago Microsoft issued a statement about planned CSS standards support in the upcoming IE8. The good news is that CSS 2.1 support will be almost complete—good, but not great, considering that this standard is rather old now, as it’s been a “candidate recommendation since 2004”.

The bad news is the level of support for CSS3 elements. Look through that list & try to keep afloat in the sea of red “no”s that signify their CSS3 support—or lack thereof. Of course, CSS3 is still very much a work in progress and is unlikely to be a candidate recommendation for another decade, judging from the pace of “Last Calls” for various components of the CSS3 spec. Nevertheless, the developers behind Safari & Firefox have been implementing a lot of the spec. There’s some fantastic stuff in it (multi-column support! colours with opacity!), that I for one would like to use immediately—and which we do, on our own site & some others we’re currently developing. But for the foreseeable future, if you’re using IE, you won’t see these things.

Given the clusterfuck of discontent that was IE7, and that Safari & Firefox have cleaned their clocks with current & emerging standards support, one might think that Microsoft would take the opportunity to really push IE8 to be a 1st-class browser. But no, they’ve just decided to tread so softly, so slowly, that the net result will be that IE8 will be that much farther in the distance. I mean, IE8b has just passed the Acid2 test. Hooray. Safari & Opera have worked on complete compliance with Acid3—and have succeeded.

This sucks. There are fantastic things taking place in CSS, in particular CSS3—which you can experience right now in Firefox & especially Safari. And I’d like all my site’s viewers to be able to see these effects in action.

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.

Two CSS Debugging Tips

April 25th, 2008

Ray Drainville

Ian may claim to be doing nothing but git-work (and what an unfortunate name that is), but the fact of the matter is that we’re both eyebrow-deep in client work. This business has been running since April 1999 & we’ve never been so busy.

When you’re so busy, the last thing you want is a series of bugs that won’t seem to go away. Since most of my work is in the HTML/CSS/Rails templates realm, I’m speaking mostly of visual bugs, things that seemingly won’t budge no matter how much time you put into them. I thought I’d share two tips which have helped me squash a considerable number of visual bugs, one created by someone else & the other merely a process that I follow.

CSS Debugging Tip 1: Burn it all to the ground

First, the work supplied by someone else: Eric Meyer’s CSS Reset. If you’re unfamiliar with it, go & read up on it ASAP. For those unfamiliar with why you’d need to reset something your CSS, the answer is simple: different browsers have different default sizes for fonts, lists, buttons & a hell of a lot else. By resetting all these items via CSS, you help ensure that all your browsers—Safari, Firefox, Opera & the thorniest of them all, Internet Explorer—start from a common vantage point.

The idea is not new. For years I’ve employed a very basic, simple reset:

  html * { margin: 0; padding: 0; }

The problem is that this didn’t reset nearly enough things—and it often didn’t even reset everything it was supposed to! Eric’s reset is a total razing to the ground of all browser-specific sizing quirks & as such should be adopted by every designer. In my experience, it’s particularly useful for a project that you’re just starting up: my IE-specific stylesheets are a fraction of their former size. For pre-existing sites, however, it’s not going to be a magic bullet. You still assembled your CSS based on different assumptions & short of starting from scratch, it will be of more limited use (although still recommended in my book).

CSS Debugging Tip 2: Build it back up brick by brick

The second tip is merely a process that I’ve been following. I’m surely not the first person to do this, but since it doesn’t appear to be widely discussed I thought I’d write it up here.

I’m going to take a real-world example from a site we’re building. In this site, we’re using some Scriptaculous effects to aid in the creation of a visually compact secondary navigation for a database of imagery. The markup is simple:

    <h4>Section Heading</h4>
    <a href="subsection/1">Subsection 1</a>
    <a href="subsection/2">Subsection 2</a>
    ...

Clicking on the H4 tag will cause the subnavigation links to reveal themselves, or hide themselves if they’re already revealed.

Unless you’re viewing in Internet Explorer 7, that is—no, the problem for once isn’t IE6! In IE7, the links were invisible—they were clickable, but you couldn’t see them.

I tore out what little hair I had left, for weeks, trying to figure out this bug. I even thought it might have been Scriptaculous’ fault. It wasn’t, however & eventually I realised I was compounding the problem by adding CSS declarations instead of removing them—instead of trying to figure out what was triggering the bug, I was trying to pave over the bug. If you’re adding more declarations in order to fix a bug, you may very well be creating a shifting target, which is definitely not what you want to be doing. Particularly when you have a lot of other work to do.

The answer is just a process & a very simple one at that: take your CSS declarations & strip them out. Add them incrementally, viewing in the target browser every time, until you trigger the bug.

In the example above, removing a declaration of a:link { display: block; } rendered the links visible again in IE7. I don’t know why—I’ve used this declaration countless times with no problems—but nevertheless the declaration wasn’t necessary & it was causing a big problem.

Again, though, it’s the process of stripping away your CSS & then re-implementing your declarations which is the key. It’s all too easy to add more & more to your declarations, but remember: you may only be making the job of fixing your problems worse.

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.

dynamic resolution + prototype

February 27th, 2008

Ian White

You may have heard of the Dynamic CSS resolution switcher from Particletree, it's a neat bit of js that allows you to specify different css files depending on the browser resolution.

We love this script, but we had problems running it with Safari, and we are using prototype, so we wanted to make use of its goodness for the browser independent stuff.

So, if you're already using prototype, the following script achieves the same effect as the original particletree script. It also uses the dom:ready event, which fires after the dom is loaded, but before the screen is drawn, so you shouldn't see any 'twitch'.

Example

In the following example, we have three stylesheets that correspond to browser widths as follows:

  thin      up to 1020
  wide     1021...1400
  widest    above 1400

For non js users we want to default to 'wide' (the middle one). In order for this to work properly, we disable all of the non-default stylesheet links.

In your html

In your html you need to link to the three stylesheets, give them each a title attribute, and disable the ones you don't want non-js users to see.

  <link title="wide" href="/stylesheets/wide.css" rel="stylesheet" type="text/css" />
  <link title="thin" disabled="true" href="/stylesheets/thin.css" rel="stylesheet"  type="text/css" />
  <link title="widest" disabled="true" href="/stylesheets/widest.css" rel="stylesheet" type="text/css" />

And link to the script, we'll call it dynamic_css.js

  <script src="/javascripts/dynamic_css.js" type="text/javascript"></script>

The script

This is the script, it uses the title attribute of the stylesheets to disable or enable them. It is run when the dom is ready, and when the browser width changes.

// DYNAMIC RESOLUTION SWITCHER
// Originally from ParticleTree
// Simplified with Prototype by Ian White of Argument from Design 2008
// include prototype.js (>=1.6) before this file

// you need to edit this function as per your situation
function applyDynamicLayout() {
  var width = document.viewport.getWidth();
  if (width <= 1020 )                 { applyStylesheet("thin") }
  if (width > 1020 && width <= 1400)  { applyStylesheet("wide") }
  if (width > 1400)                   { applyStylesheet("widest") }
}

// you shouldn't need to edit past here
function applyStylesheet(title) {
  var i, stylesheet;
  for(i=0; (stylesheet = document.getElementsByTagName("link")[i]); i++) {
    // is it a stylesheet with a title attribute?
    if(stylesheet.getAttribute("rel").indexOf("style") != -1 && stylesheet.getAttribute("title")) {
      stylesheet.disabled = true;
      if (stylesheet.getAttribute("title") == title) {
        stylesheet.disabled = false;
      }
    }
  }
}

//Run applyDynamicLayout function when window is ready and when it resizes.
Event.observe(document, 'dom:ready', applyDynamicLayout);
Event.observe(window, 'resize', applyDynamicLayout);

That's it!

That's it, it works for us in Safari, Opera, FF and IE 6 or greater.

Also, you can have as many stylesheet links as you like with the same title. For example, let's say you have some IE6 specific kludges for the 'thin' layout. You would do this:

<!--[if lt IE 7]>
  <link title="thin" disabled="true" href="/stylesheets/thinie6.css" rel="stylesheet" type="text/css" />
<![endif]-->

Ruby muckin

February 12th, 2008

Ian White

So I was writing a Rakefile and found myself doing stuff like this:

  cmd = "svn co #{src} #{dest}"
  puts cmd
  system cmd

Everytime I wrote lines like this, a little corner of my heart died.

I dreamt of something like this:

  "svn co #{src} #{dest}".to :puts, :system

But it's hard to do this properly, because you need to get hold of the receiver (in the above case main, or some Rake task) or binding, so that the methods can be sent where they ought. With crippled Kernel#caller, the the demise of Binding.of_caller (Ruby-debug solved the problem but there's significant overhead) it was looking like I should just wait for the next ruby release.

But I frowned like Hiro and came up with this:

  pass("svn co #{src} #{dest}").to :puts, :system

Briefly, Object gets #pass which creates a PassProxy (basic object). It keeps hold of the receiver, but acts like the object (in this case the string). When you send #to(:method) to this PassProxy, it calls the original receiver with the object.

This is just sugar, but its nice sugar in some cases. Compare:

   # plain ruby
   msg = "oooh!"
   MyObj.foo msg
   MyObj.far msg
   MyObj.faz msg
   msg
   # => 'oooh!'
   

   # returning (ActiveSupport)
   returning "oooh!" do |s|
     MyObj.foo s
     MyObj.far s
     MyObj.faz s
   end
   # => 'oooh!'

   # (pass) (this code)
   MyObj.pass("oooh!").to :foo, :far, :faz
   # => 'oooh!'

But eat too much sugar and your teeth will fall out: I realised that this probably has limited usefulness (how often do you need to send the same object to a bunch of different methods?). But at least I learned some things in that last couple of hours of coding.

Go get the code from pastie if you're interested.

Thanks for reading