avatarharuki zaemon

Prevent accidental deployment with a prompt

By

This morning I went to push out a new version of an application to our staging environment on an Engine Yard slice. I knew I had done exactly that last night so I navigated through my bash history and hit enter. Two minutes later the new version had been deployed and I was about to walk out the door to do some chores before coming back to start playing with the app. Thankfully, Steve came online and informed me that production was broken.

A quick look through my bash history and it seemed I’d used the deploy to production rather than deploy to staging but, being in a hurry, hadn’t looked carefully enough. Of course some might argue that I should have looked more carefully. That I shouldn’t have deployed before heading out. All valid points but I very rarely have full control over what’s going on around me. So, while it’s all very well and good to hope that I will be more careful next time, that’s a bit like hoping global warming isn’t a reality: we all hope it’s not but maybe we should do something about it just in case?

And so I added the following at the start of the :production task in my deploy.rb file:

unless Capistrano::CLI.ui.agree("Are you sure you want to deploy to production? (yes/no): ")
  puts "Phew! That was a close call."
  exit
end

For all other environments, the deployment goes through without question. Attempt to deploy to production however, and I’m now forced to be explicit about my intentions.

Generating lots of little test cases

By

When writing code that is largely algorithmic, I find I end up writing specs that sit in a loop repeating the same operations over a set of data.

This works well enough but it has the downside that the tests abort as soon as a single failing case is detected which can lead to vicious cycles of fixing one case only to find you’ve broken another.

The solution is of course to break out each expectation – inputs and expected outputs – so they are all run and reported individually. Doing so by hand however, is tedious to say the least so why not generate them on the fly instead:

describe Fibonacci do
  [[0, 0], [1, 1], [2, 1], [3, 2], [4, 3], [5, 5], [6, 8]].each do |input, output|
    it "should generate #{output} for #{input}" do
      Fibonacci.calculate(input).should == output
    end
  end
end

This is such a elegant solution I’m not sure why it only just occurred to me.

Quickly Migrate all database times to UTC

By

If you’re thinking about updating to Rails 2.1 to get the timezone support, you’ll need to update all database records to UTC. Here’s a quick migration script to do just that:

class ConvertTimestampsToUtc < ActiveRecord::Migration
  # Assume all times were in UTC+10:00
  OFFSET = "interval '10 hours'"

  # Adjust any date/time column
  COLUMN_TYPES = [:datetime, :timestamp]

  def self.up
    adjust("-")
  end

  def self.down
    adjust("+")
  end

  private

  def self.adjust(direction)
    connection = ActiveRecord::Base.connection
    connection.tables.each do |table|
      columns = connection.columns(table).select { |column| COLUMN_TYPES.include?(column.type) }
      updates = columns.map { |column| "#{column.name} = #{column.name} #{direction} #{OFFSET}"}.join(", ")
      execute("UPDATE #{table} SET #{updates}") unless updates.blank?
    end
  end
end

As you can see, I’ve assumed that the dates were previously stored as AEST (UTC+10:00) so you’ll likely need to adjustthat and I’m also assuming PostgreSQL for date manipulation though it should be pretty simple to convert to run under MySQL. It may even work asis.

Deploying branches with Capistrano

By

This morning I had occasion to deploy a branch of a git repository to a staging server but hadn’t the foggiest idea how. A quick search through the capistrano source code revealed that I could use set :branch "branch_name" in my deploy script. I tried it and it worked. I then figured I would need to make a similar change across all my branches. Of course, I’m a lazy sod and wondered if there wasn’t a better way.

If you’re not familiar with git, the output of the git branch command is a list of branches with an asterisk marking the one currently checked out on your local machine. For example:

> git branch
* drupal_authentication
fragment_caching
master

So, I figured, what if I just parsed the output and searched for the branch marked as current:

set :branch, $1 if `git branch` =~ /\* (\S+)\s/m

Now I’m able to deploy whatever branch is current on my local machine from a single, shared, deploy script.

JavaScript Date Helpers

By

It’s all the rage these days to have timestamps displayed in words to indicate how long ago some event occurred. You know something like “less than a minute ago” or “about 2 months ago”, etc. You’ll see plenty of examples on news sites, blog entries, and bug tracking tickets to name but a few.

If you’ve ever had to build this kind of thing in Rails you’ll be familiar with all the Date Helper methods that make the task pretty trivial. The problem is that the result is fixed to whatever the date was when the page was rendered and as a result these timestamps go stale very quickly.

Save refreshing the page every minute – or hour or whatever – just to update the times, I figured what was needed was a little but of client-side action. Rather than send the text in the HTML, I decided to instead send the raw timestamps and have the browser periodically generate the textual representation.

To this end, I blatantly copied two methods from the afore-mentioned Rails helper – distance_of_time_in_words(from, to), and time_ago_in_words(from) – and, taking some liberties along the way, converted them to JavaScript:

function distanceOfTimeInWords(to) {
  var distance_in_milliseconds = to - this;
  var distance_in_minutes = Math.abs(distance_in_milliseconds / 60000).round();
  var words = "";

  if (distance_in_minutes == 0) {
    words = "less than a minute";
  } else if (distance_in_minutes == 1) {
    words = "1 minute";
  } else if (distance_in_minutes < 45) {
    words = distance_in_minutes + " minutes";
  } else if (distance_in_minutes < 90) {
    words = "about 1 hour";
  } else if (distance_in_minutes < 1440) {
    words = "about " + (distance_in_minutes / 60).round() + " hours";
  } else if (distance_in_minutes < 2160) {
    words = "about 1 day";
  } else if (distance_in_minutes < 43200) {
    words = (distance_in_minutes / 1440).round() + " days";
  } else if (distance_in_minutes < 86400) {
    words = "about 1 month";
  } else if (distance_in_minutes < 525600) {
    words = (distance_in_minutes / 43200).round() + " months";
  } else if (distance_in_minutes < 1051200) {
    words = "about 1 year";
  } else {
    words = "over " + (distance_in_minutes / 525600).round() + " years";
  }

  return words;
};

Date.prototype.timeAgoInWords = function() {
  return this.distanceOfTimeInWords(new Date());
};

Now all I do is periodically invoke a function that calls one or other of these methods and updates the text of whatever display element is appropriate. Even better, because the raw timestamps have timezone information in them, the display doesn’t suffer from, in my case here in Australia, always being 10 hours out because the server is sitting in the US with a US date/time.

Giving the Anchor tag some Ajax Lov'n

By

It seems I’m forever needing to submit links using an XMLHttpRequest rather than the default full-page refresh. One approach commonly used in the Rails community is to render each link with the JavaScript already in place. My preferred approach however is to keep the HTML as free from JavaScript as possible and unobtrusively add behaviour using LowPro.

LowPro already comes with a built-in behaviour for links but sometimes I need something little more complex than simply submitting the request and so I usually end up doing the following:

anchor = ...;
new Ajax.Request(anchor.href, { method: "get", parameters: ... });

Granted that’s not a lot of effort but it still felt as though I were repeating myself and that the overall intention of my code was largely obscured by the infrastructure. It then struck me that submitting a form using the Prototype JavaScript framework is almost trivial:

form = ...;
form.request({ parameters: ... });

So I cooked up a version for anchors as well:

Element.addMethods("A", {
  request: function(anchor, options) {
    new Ajax.Request(anchor.href, Object.extend({ method : "get" }, options || {}));
  }
});

Now I can submit links in pretty much the same was as I do forms:

anchor = ...;anchor.request({ parameters: ... });

I’m wondering what other possibilities might occur were I to add a serialize() method to extract the request parameters.

HTTP Ranges for Pagination?

By

Would it be a gross perversion to use HTTP ranges for pagination?:

Client asks the server what range types it accepts for people:

HEAD /people HTTP/1.1

Server responds:

Status: 200Accept-Ranges: pages; records

Client requests the first page of people:

GET /people HTTP/1.1Range: pages=1-1

Server Responds:

Status: 206Content-Range: pages 1-1/13

Finding the index of an item using a block

By

Ruby 1.9 has it but if you’re not that bleeding edge, you can have it now:

class Arraydef index_with_block(*args)
  return index_without_block(*args) unless block_given?
  each_with_index do |entry, index|
    return index if yield(entry)
  end
  nil
end
alias_method :index_without_block, :index
alias_method :index, :index_with_block

def rindex_with_block(*args)
  return rindex_without_block(*args) unless block_given?
  index = sizereverse_each do |entry|
    index -= 1
    return index if yield(entry)
  end
  nil
end
alias_method :rindex_without_block, :rindex
alias_method :rindex, :rindex_with_blockend

If you’re using Rails you can substitute the two calls each to alias_method with a single call to alias_method_chain.

UPDATE: Ruby 1.8.7 also has this.

It's OK for GET Requests to Update the Database

By

We’ve all been indoctrinated into associating the HTTP request methods POST, GET, PUT and DELETE with the standard database (aka CRUD) operations Create (INSERT), Read (SELECT), Update and Delete respectively. For the most part the analogue holds. When we make a GET request, our intention is to read whatever the server hands back. When we POST some data, our intention is to update something.

The general view however, seems to be that the HTTP methods relate directly to database operations. In fact many developers seem to think that they are in fact one in the same thing: POST is for INSERTing data, GET for SELECTing, etc. The popularity of which seems to have strengthened with the growing interest in REST and the wide-spread adoption of Rails 2. I don’t mean to imply that Rails is the culprit here. Nothing in Rails explicitly makes these assertions. However the fact that the idiom is explicitly referred to as CRUD resources certainly doesn’t help. In fact, the HTTP/1.1 Method Definitions specification explicitly states that so-called “Safe Methods” such as GET:

… SHOULD NOT have the significance of taking an action other than retrieval.

But what happens when we have a site that tracks where you have visited, updates the “last read date” on each record retrieved, or remembers the last search criteria you used? Each of these features requires recording information into some kind of database – be it relational or simply a log file.

What people seem to overlook is the paragraph that follows in the same document:

Naturally, it is not possible to ensure that the server does not generate side-effects … The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.

The HTTP methods should be used to indicate the user’s intention without regard to the underlying implementation. The web application is an abstraction so we need to model the interaction on that abstraction. If the user’s intention is to make a change to something then go ahead and use a PUT but if they’re only reading some data use a GET even if you know it involves some database writes.

It may seem somewhat esoteric but spending a bit of time thinking about what the user’s intention is exactly has helped me better flesh out an application’s API.

Getting Too Fancy with HTTP Response Codes

By

As part of my adoption of REST and all its goodness, I’ve started using HTTP response `s more, responsibly ;-) So, for example, instead of always returning 200 (OK) for just about everything, I’m using 201 (Created) with a Location header set to the new URL after a POST. For PUT, I send back 204 (No Content), a 404 (Not Found) after a GET for a resource that no longer exists, and a good old 200 (OK) after a successful DELETE or GET.

Interestingly, in the system I’m developing at present, an update (PUT) might actually cause a resource to move due to the application of server-side business rules. In this case, the 204 response also sets the Location header so that the client knows where it can be found.

All this was working beautifully on my local machine using both Safari and Firefox so once I was happy with the result I deployed it into the remote test site and started playing in FireFox. So far so good. Everything checks out. Next let’s try Safari…not so great.

Some bits of the application worked just fine but others seemed to have no effect. Then mysteriously things would start working again. Even stranger was the fact that hitting the browser’s refresh button had no effect either.

At first I suspected that nesting Ajax calls might be to blame but as everything seemed to work perfectly in FireFox and a Google search turned up nothing, I decided to do some more investigation.

I logged in to the server box and tailed the logs for signs of life. Everything looked normal. All the expected requests and responses were there but still nothing client side. Using Safari’s new Network Timeline I could see what the browser thought was going on. All the requests and responses were there but something was odd. In all but a few cases, the response code was 204 (No Content). I double checked the server logs but no, the server was definitely sending back the correct responses; a mixture of 204, 200 and 404 as appropriate.

On a hunch I went back and re-read the HTTP Status Codes document and in particular the definition for 204:

The server has fulfilled the request but does not need to return an entity-body … the client SHOULD NOT change its document view from that which caused the request to be sent …

That might actually explain it. If Safari received a 204 and interpreted that to mean “Don’t change anything” then hitting refresh would indeed have no effect even if my code subsequently went on to perform more asynchronous requests as a result.

So, I dutifully changed all the 204s to 200s and voila! Safari started to behave just as expected and FireFox continued to work as had previously.

I’ve also noticed a difference in the way both browsers handle 303 (Redirect) from within an XML HTTP Request: Safari performs the redirect and keeps all the headers as per the original, whereas FireFox seems to essentially construct an entirely new request. The upshot is that you can’t actually detect (server-side) an XML HTTP Request from FireFox if it is the result of a redirect.

I’m really not sure why the two browsers have such differing opinions of what the appropriate behaviour should be in either case but I hope this helps some other poor sod keep from pulling their hair out.

Drag & Drop Prioritizable Lists

By

Yes, it’s true, Scriptaculous already provides a Sortable that makes it almost trivial to enable drag’n’drop sorting of your HTML lists. Whenever an item is moved an onUpdate() event is called (if provided) allowing you to inspect the new order and presumably perform an AJAX request to record the change. In principle, this sounds great but I’ve never really liked it for a couple of reasons.

For a start, if you have any appreciable number of items updating each in the database just to re-order one seems somewhat unnecessary. Not withstanding the fact that we need to send all those ids to the server in the first place.

Secondly, if you’re doing any kind of filtering, it’s difficult at best to take the newly constructed ordering and apply that at the back-end; what happens to all the items that may be lurking in between that aren’t presently displayed?

Enter Prioritizable (itself built on top of Sortable).

You use it in much the same way as Sortable with the major difference being that the onUpdate() event is called with threearguments: the item that was moved, the sibling relative to which it was moved, and the relative position ("higher" or "lower"). And, if like me, you’re feeling a bit RESTful, it’s pretty easy to turn these arguments into a nice semantic URL and parameters as shown:

Prioritizable.create($("chores"), {
    onUpdate: function(item, position, sibling) {
        id = item.substring(6);                           // "chore_17" => "17"
        sibling_id = to.substring(6);                     // "chore_2" => "2"
        url = "/chores/" + sibling_id + "/" + position;   // "/chores/2/higher"
        new Ajax.Request(url, {
            method: "post",parameters: { id: id }
        });
    }
});

When the onUpdate() event is called we POST the id of the item to be moved to a path constructed from the id of the sibling and the relative position. Assuming the the user moves chore_17 just above chore_2 we would POST "id=17" to /chores/2/higher.

In practice, I combine this client-side behaviour with some server-side code that provides move_higher_than() and move_lower_than() methods that efficiently handle all the necessary database updates.

All the pieces mentioned will eventually be available alongside Cogent’s other Rails plugins but until then, here’s enough of the Javascript side of things to get you going.

var Prioritizable = {
    create: function(element) {
        options = Object.extend(arguments[1] || {}, {
            onChange: Prioritizable.onChange,
            onUpdate: Prioritizable.onUpdate
        });
        Sortable.create(element, options);
    },
    destroy: function(element) {
        Sortable.destroy(element);
    },
    onChange: function(item) {
        Sortable.options(item)._item = item;
    },
    onUpdate: function(element) {
        options = Sortable.options(element);
        item = options._item;
        options._item = null;
        sibling = item.previous();
        if (other) {
            position = "higher";
        } else {
            sibling = item.next();
            position = "lower";
        }
        options.onUpdate(item, position, sibling);
    }
};

Enjoy!

Getting Chronic to Parse Non-U.S. Dates

By

In an attempt to push yet more behaviour from my Rails controllers into model classes, I was extracting some code into a yet-to-be-published plugin that allows date and time columns to be set using more human-readable values. For examples:

>> task.completed_at = **"now"**>> task.completed_at=> Wed Apr 09 14:39:12 +1000 2008

Although my actual requirement was to support "now" and "today" I figured it would be rather cool if I could support anything that Chronic does. (If you haven’t used Chronic before, it’s a natural language date/time parser written in pure Ruby that understands a vast array of expressions including ranges.)

Naturally (no pun intended), Chronic also supports explicit dates such as “1/2/08”. As part of my testing however, I discovered that the date parsing is decidedly US-centric presuming, of course, that dates are specified as “month-day-year”. So for anyone living in say, Australia, it can be pretty frustrating to have Chronic.parse("1/2/08") return "Wed Jan 02 12:00:00 +1100 2008" rather than the expected "Fri Feb 01 12:00:00 +1100 2008".

The good news is, there is a solution. The bad news is, the solution is far from elegant. But first some context.

Chronic is actually written very nicely and the code is fairly easy to follow. In essence it works as follows: the input string is tokenized; each token is inspected to see if it’s a keyword such as a month name, day name, or a number, etc.; and finally tries to matches the sequence of tokens against a pattern such as “a day number followed by a month name and then a year.” The problem arises because the pattern for matching “month-day-year” comes before the one for “day-month-year” meaning that unless the first number is greater than 12, Chronic will always consider it to be a month.

The less than elegant solution is almost trivial and involves switching the order in which the patterns are matched. Doing so returns the desired result:

>> Chronic.parse("1/2/08")=> "Fri Feb 01 12:00:00 +1100 2008"

Which is all very well and good but now we have the reverse problem. What would be better is if we had a more general solution, one that allows us to specify the desired precedence when parsing:

>> Chronic.parse("1/2/08")                                    # Default to U.S. date formats=> Wed Jan 02 12:00:00 +1100 2008>> Chronic.parse("1/2/08", **:explicit_date_format => :non_us**)  # Prefer Non-U.S. formats=> Fri Feb 01 12:00:00 +1100 2008>> Chronic.parse("1/2/08", **:explicit_date_format => :us**)      # Prefer U.S. formats=> Wed Jan 02 12:00:00 +1100 2008

Among other things, this allows us to have users in Australia enter dates with one format and users in the U.S. another.

For anyone interested, I’ve pasted a diff for each approach below.

A less than elegant solution:

--- a/chronic-0.2.3/lib/chronic/handlers.rb+++ b/chronic-0.2.3/lib/chronic/handlers.rb@@ -13,8 +13,8 @@ module ChronicHandler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),-                 Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy),+                 Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],

A more general solution:

--- a/chronic-0.2.3/lib/chronic/chronic.rb+++ b/chronic-0.2.3/lib/chronic/chronic.rb@@ -43,7 +43,8 @@ module Chronicdefault_options = {:context => :future,-                         :ambiguous_time_range => 6}+                         :ambiguous_time_range => 6,+                         :explicit_date_format => :us}options = default_options.merge specified_options# ensure the specified options are valid@@ -51,6 +52,7 @@ module Chronicdefault_options.keys.include?(key) || raise(InvalidArgumentException, "#{key} is not a valid option key.")end[:past, :future, :none].include?(options[:context]) || raise(InvalidArgumentException, "Invalid value ':#{options[:context]}' for :context specified. Valid values are :past and :future.")+      [:us, :non_us].include?(options[:explicit_date_format]) || raise(InvalidArgumentException, "Invalid value ':#{options[:explicit_date_format]}' for :explicit_date_format specified. Valid values are :us and :non_us.")# store now for later =)@now = options[:now] -- - a/chronic-0.2.3/lib/chronic/handlers.rb+++ b/chronic-0.2.3/lib/chronic/handlers.rb@@ -3,41 +3,50 @@ module Chronicclass << selfdef definitions #:nodoc:-       @definitions ||=-      {:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],+     if @definitions.nil?+        us_date = [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),+                   Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),+                   Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),+                   Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),+                   Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),+                   Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),+                   Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),+                   Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),+                   Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy),+                   Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),+                   Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)]++        non_us_date = us_date.dup+        non_us_date[7] = us_date[8]+        non_us_date[8] = us_date[7]++          @definitions =+        {:time => [Handler.new([:repeater_time, :repeater_day_portion?], nil)],-       :date => [Handler.new([:repeater_day_name, :repeater_month_name, :scalar_day, :repeater_time, :time_zone, :scalar_year], :handle_rdn_rmn_sd_t_tz_sy),-                 Handler.new([:repeater_month_name, :scalar_day, :scalar_year], :handle_rmn_sd_sy),-                 Handler.new([:repeater_month_name, :scalar_day, :scalar_year, :separator_at?, 'time?'], :handle_rmn_sd_sy),-                 Handler.new([:repeater_month_name, :scalar_day, :separator_at?, 'time?'], :handle_rmn_sd),-                 Handler.new([:repeater_month_name, :ordinal_day, :separator_at?, 'time?'], :handle_rmn_od),-                 Handler.new([:repeater_month_name, :scalar_year], :handle_rmn_sy),-                 Handler.new([:scalar_day, :repeater_month_name, :scalar_year, :separator_at?, 'time?'], :handle_sd_rmn_sy),-                 Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),-                 Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy),-                 Handler.new([:scalar_year, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_day, :separator_at?, 'time?'], :handle_sy_sm_sd),-                 Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_year], :handle_sm_sy)],+         :date => {:us => us_date, :non_us => non_us_date},-       # tonight at 7pm-       :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),-                   Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),-                   Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],+         # tonight at 7pm+         :anchor => [Handler.new([:grabber?, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),+                     Handler.new([:grabber?, :repeater, :repeater, :separator_at?, :repeater?, :repeater?], :handle_r),+                     Handler.new([:repeater, :grabber, :repeater], :handle_r_g_r)],-       # 3 weeks from now, in 2 months-       :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),-                  Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),-                  Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],+         # 3 weeks from now, in 2 months+         :arrow => [Handler.new([:scalar, :repeater, :pointer], :handle_s_r_p),+                    Handler.new([:pointer, :scalar, :repeater], :handle_p_s_r),+                    Handler.new([:scalar, :repeater, :pointer, 'anchor'], :handle_s_r_p_a)],-       # 3rd week in march-       :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),-                   Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]-      }+         # 3rd week in march+         :narrow => [Handler.new([:ordinal, :repeater, :separator_in, :repeater], :handle_o_r_s_r),+                     Handler.new([:ordinal, :repeater, :grabber, :repeater], :handle_o_r_g_r)]+        }+      end+      @definitionsenddef tokens_to_span(tokens, options) #:nodoc:# maybe it's a specific date-      self.definitions[:date].each do |handler|+      self.definitions[:date][options[:explicit_date_format]].each do |handler|if handler.match(tokens, self.definitions)puts "-date" if Chronic.debuggood_tokens = tokens.select { |o| !o.get_tag Separator }

Fixing lowpro form submission failures

By

I finally worked out why my forms weren’t submitting when the user hits the ENTER key. lowpro serializes the button that was clicked along with any other parmeters when submitting a form via AJAX. However, when the user hits enter under FireFox, there is no button and consequently the browser barfs. Safari on the other hand tries to be too helpful and triggers an onclick event for the first submit button (which is why I never noticed it).

So anyway, rather than try to be too clever myself, I simply changed the parameter serialization in Remote.Form.onsubmit to look like:

parameters : this.element.serialize({ submit: this._submitButton ? this._submitButton.name : null })

Problem solved.

Culturally Sensitive JavaScript

By

JavaScript is a fantastic little language and with the likes of Prototype, Scriptaculous, and my newest favourite, lowpro, you can build some quite frankly, remarkable web applications.

One area where most web browsers fall down however is in their error-reporting, or lack thereof. A fact that has caused me to waste seemingly countless hours trying to find the source of some problem or other only to realise that a typo that had been staring me in the face the entire time was to blame!

Now, like just about any programming library I use these days, most JavaScript libraries use American english. initiali**z**e, capitali**z**e, you know what I’m talking about.

For the most part the use of ‘z’ instead of ’s’ isn’t too much of a problem but just recently I consistently tried to use lowpro’s addBehaviour method, only there isn’t one. It’s called addBehavior (sans ‘u’).

So today after about 20 minutes cursing and swearing at the spelling Steve asked “is there anyway you could create an alias?” Being JavaScript the answer is of course “abso-bloody-lutely!”:

Event.addBehaviour = Event.addBehavior;

You can alias just about anything this way.

No more will my code silently fail due to differences in spelling :)

Cognescentised

By

As of the first of this month (March, 2008) I’ve become a full-time employee of Cogent leading a product development arm of the business.

I’ve know all the cogent guys for some time now and over the years we’ve all become very good friends and colleagues. Many years ago while working together, Steve Hayes and I discussed starting something and whilst it sounded great in principle, my personal circumstances didn’t allow for it. In the meantime, Steve and Marty Andrews got their hands dirty and laid the foundation for what is promising to be a great company based on a set of values almost unheard of in, well, any industry really.

RedHill will still be around for many years to come with [Simian]/simian and Rails Plugins but my day to day software development will be well and truly under the Cogent banner.

So if you’re in Melbourne (or anywhere in the world for that matter) and you’ve been looking for a someone to get your Ruby on Rails project up and out the door, we’d love to hear from you.

TimePustule

By

I bought an Apple TimeCapsule for backups sniggers from the usual suspects. The fact is I had perfectly good backups working with an external 500GB LaCie drive but being the glutton for punishment thought I’d give TC a whirl.

So I unpacked it, hooked it up, configured it and then mysteriously, I couldn’t get it to work @ 5Ghz (a/n) which for me is an absolute must as my apartment building seems to have a at least one 2.4Ghz network for every apartment! By work I mean broadcast, be visible, locatable, whatever you want to call it. At 2.4Ghz, there it is. At 5Ghz, where did my TimeCapsule go?

The odd thing is that I made darn sure I had it configured identically to the AirportExtreme it was to replace which ran just dandy @ 5Ghz, even down to the passwords.

Having had it for 3 days with no joy and a couple of other users complaining of the same problem I decided it was time to take it back to the apple store today but before I do so, I thought I’d better erase the hard drive and reset to factory settings. For whatever reason (masochism?) I felt compelled to try giving it one more go, this time changing settings one at a time and restarting the capsule.

It starts off in 2.4Ghz g-mode so the first thing I changed was to have it run at 5Ghz n-mode leaving everything else at the factory default. Restart. There it is. I double check my network configuration (using the network utility). Yup, 5Ghz, 300Mbps. Ok. Change another setting. Restart. Still there.

Next, multicast rate. Chnage that from 6 to 24Mbps. Restart. Fine. Use wide-channels. Fine. Change country from the default New Zealand (go figure!) to Australia. Where did my wireless network go? WTF!?

Whatever changing the country does, it’s not good! At first I thought it might change the channel (which seems hard-wired to 36 by default) but no, it still thinks it’s broadcasting on channel 36. I can still connect to it via ethernet but it won’t show up over WiFi at 5Ghz.

So it’s now running quite happily (somewhere in NewZealand), locked up as tightly as the proverbial duck’s bum, at 5Ghz. I’m honestly still not convinced it’s as reliable as my AirportExtreme though and it seems to be slower than when I backed up to mac mini which, theoretically, should be slower as it had to make more network hops and compete with half-a-dozen CPU and Disk intensive application on it (including recording and playback of TV, movies, music, etc.)

Not happy Jan. How hard can it be to take an AirportExtreme and whack a bloody 1TB hard disk inside it!? Though what can I really expect of a one-dot-oh product from a vendor that seems to be slipping in quality as it’s market share increases.

Git: Pushing with Every Commit is Flawed

By

Yesterday, both James and Marty indicated that one thing they didn’t like with Git is that not every commit is pushed to the server. Actually what they were complaining about wasn’t that, it was that they couldn’t see what I was working on until I pushed to the server. I complained that kinda defeated the purpose of Git – to push on every commit – but couldn’t articulate why. I now can.

Last night I made a bunch of changes, a spike, to set me up for today. I dutifully pushed those changes to the server (as requested) and went to bed. Unknown to me at the time, though completely predictable, those changes (a spike remember) broke the app. Amusingly, only a day earlier, James had quipped that “we’re doing agile. shouldn’t the repository always reflect working software? what’s with all the broken functionality?”

Git allows me to make micro changes, to spike stuff all day long, and eventually when I’m confident there’s something for the world to see I can push those changes back to the origin. It gives me far greater control and flexibility over the nature and granularity of the changes I make. It means I can have greater trust that changes I make WONT affect other developers or “customers”. These benefits are immediately negated when I push on every commit.

Why don’t we have this problem with something like subversion? Good question. I think the answer is simple: because subversion forces me to work in a way that ensures that everything I commit is in full working order. It also means I tend to commit larger chunks and work a lot more on branches. I would argue that if that’s what we want to do then using Git doesn’t buy us much except “offline” mode which, by definition, doesn’t satisfy the “transparency” issue anyway.

I’m not using Git as a subversion replacement. I’m using it precisely because it allows me to do “more” than subversion. With this comes cultural change. I respect the need for greater transparency but I don’t believe that pushing (at least to the master branch) on every commit is the solution.

So perhaps flawed is too strong a word. Perhaps Ill-considered would be more appropriate.

Censorship

By

After I posted the last entry, I thought I’d better go share my experience with the Apple community. Let those that have been experiencing the same problems know where I’d got to.

I replied to my own discussion forum post with the following text:

So I’ve changed the country setting back from Australia to the default, New Zealand and magically it appears @ 5Ghz. WTF!?Which was then automatically translated into:

So I’ve changed the country setting back from Australia to the default, New Zealand and magically it appears @ 5Ghz. ***!?"_Since when did we start treating acronyms as potentially offensive? Maybe I meant, _“Why This Fixes it? (Which of course I didn’t but that’s not the point.)

“ABC!?” would reflect quite accurately what came out of my mouth when the piece of hardware failed to work as expected but I’m betting wouldn’t be censored.

Managing Views with ActiveRecord Migrations

By

I just added very simple view support to redhillonrails_core trunk. The changes give you the ability to create and drop views using create_view :name, "definition" and drop_view :name respectively as well as preserving the views in schema.rb.

WARNING: This is currently only supported for PostgreSQL. Creating views in MySQL will cause extra tables to be created in schema.rb! Probably not what you wanted.

Ambitious Scoping

By

If you haven’t checked out Ambition for generating ActiveRecord queries I highly recommend you do so. In a nutshell, it allows you to generate queries using the standard Ruby Enumeration idioms. Take for example the following snippet:

class Message < ActiveRecord::Base
  def self.unread
    select { |m| m.read_at == nil }.entries
  end
end

Message.unread
# => SELECT * FROM messages WHERE messages.read_at IS NULL

Notice anything missing? SQL perhaps?

Now clearly there’s a whole lotta magic going on here. That said, ambition does use the standard ActiveRecord finders to query the database. One of the side-effects of this is that when navigating associations, you automatically get all the appropriate scoping.

So, for example, assuming a has_many relationship between User and Message we can do something like this:

user = User.detect { |c| c.name == 'Simon Harris' }
# => "SELECT * FROM users WHERE (users.name = 'Simon Harris') LIMIT 1"

user.messages.unread
# => "SELECT * FROM messages WHERE messages.read_at IS NULL AND messages.user_id = 3"

Here we navigated from user to messages and selected only those that have no read_at date.

As you can see, the restriction by user_id was automatically inserted for us by ActiveRecord as we navigated the association. We didn’t have to do a thing. It just worked the way you would expect it to. Very cool!

However (there’s always a but) this only worked because we included an execution trigger in Messages.unread. These “kickers” as they’re known, include the standard Enueration methods first, entries, detect and size.

Without a kicker, the result of select is actually a query object that you can assign to a variable, call more selects on, or even store for later use! This last feature is kinda neat and leads to some nice stuff with partial caching but it also leads to some hidden complications.

If we remove the kicker from Message.unread so that it instead returns a query and call the kicker explicitly, this happens:

class Message < ActiveRecord::Base
  def self.unread
    select { |m| m.read_at == nil }
  end
end

user = User.detect { |c| c.name == 'Simon Harris' }
# => "SELECT * FROM users WHERE (users.name = 'Simon Harris') LIMIT 1"

user.messages.unread.entries
# => "SELECT * FROM messages WHERE messages.read_at IS NULL"

Where did all the scoping go?!

In the earlier example, the query was executed inside a method on an model class which, because it was called whilst navigating an association, means it was also executed using an appropriate with_scope.

In the second example however, because the query wasn’t actually executed until sometime later – ie outside any model classes and associations – all the scoping information has been forgotten, as if it were never there in the first place. Bbbbbut, you want to have all that Ambition goodness and you’d like your scoping as well right? So what’s a poor boy (or girl) to do?

I’m glad you asked. Here’s a patch to ambitious-activerecord that remembers the scoping that was in play at the time the query was constructed. It seems to work for all my current uses.

DISCLAIMER: very quick-and-dirty and thoroughly untested.