Informative & Painless URIs

Posted by Carsten Nielsen
on Saturday, June 21

There is just something nice about those cute URIs that we have all grown to love, I’m sure you’ve seen them around: http://pork.ham/lists/32-grocery-list. Guess what, these are fun and easy to implement, here’s how!

First we need a method that can convert a normal title or name into something safe for use in a URI, here’s my answer:


class ::String

  # Returns a version of self that is safe for inclusion in a URI.
  def urilize
    strip.
    gsub(' ', '-').
    gsub(/[-]+/, '-').
    gsub('&', 'and').
    downcase.
    scan(/[a-z0-9\-]*/).to_s
  end

end

Users enter the darnedest data, so your urilize implementation will probably be tweaked over time. Anyways, the above String extension let’s us do stuff like:


'My Name Is Mud!'.urilize # => "my-name-is-mud"
'Awesome  &  Cool Stuff!!!'.urilize # => "awesome-and-cool-stuff"

Now we need a seamless way to get this functionality into our models so that they start throwing around all of these cool URIs for free. My answer to this is a sweet and simple mixin:


module Findable

  def to_param
    to_s.blank? ? id.to_s : "#{id}-#{to_s.urilize}"
  end

end

In order for our mixin to work we need our ActiveRecord models to have an overloaded to_s method that returns the title or name of the object. This is good practice and you should be doing it in any model where it makes sense to do so.

Anyways we can use our mixin as follows:


class Ticket < ActiveRecord::Base

  include Findable

  ...

  def to_s
    title
  end

  ...

end

Now imagine we have the following ticket in our database:


id: 6
created_by: Captain Obvious
assigned_to: Developer Dude
title: Feature Improvements
body: This massive feature that you implemented in far too little time is imperfect!

Assuming resourceful routes for Tickets we can do something like:


ticket = Ticket.find(6)
ticket_path(ticket) # => "/tickets/6-feature-improvements"

Neat.

Go To Helper Method: TextMate Command

Posted by Carsten Nielsen
on Wednesday, June 11

If you’re like me you use a lot of view helpers, stored in many different files. On big projects it’s not always easy to remember what helper came from where. You can use naming schemes and other organizational techniques, but you still have to take a few steps to get to the file and then find the method. Life is hard.

Or is it!? Life is easy when you use my Go To Helper Method command for TextMate! It does a simple grep of the current project’s /app/helpers directory (and sub-directories) for the method name in question and then opens the file and places you at the appropriate method. This approach works well because helper methods are usually all in the same namespace.

For the command to work properly you must have a TextMate Project of your application’s root open with at least the /app directory referenced. You can then run the command by placing the cursor on (or selecting) the name of a helper method in any Ruby, Haml or ERb file, then hitting the default key-sequence of Shift→Cmd→H; magic ensues.


#!/usr/bin/env ruby -w
require File.join(ENV['TM_SUPPORT_PATH'], 'lib/textmate')

PROJECT_DIR = ENV['TM_PROJECT_DIRECTORY']
METHOD_NAME = ENV['TM_CURRENT_WORD'] || ENV['TM_CURRENT_SELECTION']

def blank?(string)
  string.nil? || string.strip.eql?('')
end

def helpers_dir
  File.join(PROJECT_DIR, 'app/helpers/*')
end

def grep_and_go_to!
  method_name = Regexp.escape(METHOD_NAME)
  grep = `grep -rnE "def #{method_name}([\( ]{1}|$)" #{helpers_dir}`
  filepath, line = grep.match(/^.+:[\d]+/).to_s.strip.split(':')
  if filepath && line
    TextMate.go_to(:file => filepath, :line => line)
  else
    puts 'Could not find a matching Helper file'
  end
end

if blank?(PROJECT_DIR)
  puts 'Go To Helper Method only works in a project!'
elsif blank?(METHOD_NAME)
  puts 'Place the cursor over the name of a Helper method'
else
  grep_and_go_to!
end

The command works well inside Merb1 and Rails projects. It probably works with other stuff too if there are Ruby files in /app/helpers.

Update

If you think this is neat check out EasyOpen by Eiji Ienaga over on GitHub, it makes my simple command here look like childs play!

Download It Here

1. That’s if you’ve decided to keep your helpers in /app/helpers.