Custom Textmate Commands

April 1st, 2008

Textmate is a pretty decent editor for OSX. I'm not in love with it, but we use it frequently at Elevator Up.

There is a plethora of plugins for Textmate to ease editing in different contexts, but sometimes you want to hack you're own commands.

For small scripting tasks, I tend to continue to use ruby. So I enjoy it when I can continue using it for the commands.

You can typically use this shell for a ruby command, and your printed output gets replaced within Textmate:

#!/usr/bin/env ruby
$: << ENV['TM_SUPPORT_PATH'] + '/lib'
input_from_tm = STDIN.read
puts "hello world!"

Here is an example of code block toggling for Markdown.

#!/usr/bin/env ruby
$: << ENV['TM_SUPPORT_PATH'] + '/lib'

s = STDIN.read
ends_with_newline = s != s.chomp
lines = s.split("\n")
lines.each_with_index do |s, index|
  s =~ /^(\t)?(.*)/

  if $1
    print "#{$2}"
  else
    print "\t#{$2}"
  end

  print "\n" if index != lines.size - 1
end

print "\n" if ends_with_newline

And a screenshot to include the Textmate command options:

A picture of Textmate Bundle Editor

At Elevator Up we typically use Capistrano for deploying our applications. Though I do have a bookmark to investigate Vlad when I get some time.

We've been a bit slow to upgrade to Capistrano 2.x, and have a lot of existing applications that depend upon Capistrano 1.4 for deployment.

One tactic we've taken is to write a custom script to explicitly use 1.4 in conjunction with 2.x. A minor hack from the original, and we have:

#!/usr/bin/env ruby

begin
  require 'rubygems'
  gem 'capistrano', '<= 1.4.1'
rescue LoadError
  # no rubygems to load, so we fail silently
end

require 'capistrano/cli'

Capistrano::CLI.execute!

I threw this into a script called cap1.4 in /usr/local and can now do:

illian:~ zach$ cap1.4 -V
Capistrano v1.4.1
illian:~ zach$ cap -V
Capistrano v2.1.0

Obviously, a better approach would be to freeze in the capistrano gems per project, and access them via RAILS_ROOT/script/cap, but that too has been time sensitive. Notice a trend here?

Given a route :

ActionController::Routing::Routes.draw do |map|
  map.foo "foo/:first/:second", :controller => "foo", :action => "something"
end

Obviously you can pass in :first and :second via:

foo_path(:first => "the first param", :second => "the second param")

However, for a more concise call, you can accomplish the same via:

foo_path("the first param", "the second param")

It's not explicitly documented, but you end up doing the same thing with nested resource paths

Watch Star Wars via Telnet

March 30th, 2008

I love stuff like this. You can watch Star Wars via telnet.

Scene from Star Wars

I know it's old, but still fun.

I don't normally repost from other sites, but this was too good.

Stolen from Janson, with thanks to Implodr.

Testing content_for

March 29th, 2008

After we started writing view specs we came across views similar to this:

<p>Stuff in the main content area</p>

<% content_for "secondary_content" do %>
  <p>Stuff that will go in an side panel in the view </p>
<% end %>

Obviously when rendering the view inside the spec, only the blurb about main content area will be in the response.body. For instance the second spec written will fail:

describe "/path/to/view" do
  def do_render
    render "/path/to/view"
  end

  it "should have blurb about main content" do
    do_render
    response.should have_text(/main content/i)
  end

  ##
  # This won't pass.
  ##
  it "should have blub about side panel" do
    do_render
    response.should have_text(/side panel/i)
  end
end

However, you can shove something in your spec_helpers.rb to test items inside the content_for blocks.

def content_for(section)
  template.send(:instance_variable_get, "@content_for_#{section}") || ""
end

And rewrite your spec like this:

describe "/path/to/view" do
  def do_render
    render "/path/to/view"
  end

  it "should have blurb about main content" do
    do_render
    response.should have_text(/main content/i)
  end

  ##
  # This will now pass
  ##
  it "should have blub about side panel" do
    do_render
    content_for("secondary_content").should match(/side panel/i)
  end
end

The have_text() matcher won't work, but you can use the have_tag().

If you haven't toyed with content_for, I suggest looking into it. It will help clean and organize your views and layout(s).

Education vs Justification

March 23rd, 2008

Aaron, Janson and I were at Applebees a month ago discussing an estimate for a client. We started talking about a particular part of the estimate that would require us to take more time than usual. We worked on their project previously, and used a few testing practices that we've stopped since. I wanted the estimate to include the refactoring of the code, but not publicize the fact. Aaron wanted to communicate with the client our exact intent.

Now keep in mind, I have no qualms about transparency with clients. And I don't go to great lengths to keep clients in the dark, as some of my previous work environments have. My point was that refactoring the code and the tests was a necessary step of development. I didn't want to publicize those specific intentions because I didn't want to come across that they were optional. In other words, I didn't want our client to think that the one of our values, quality software in this case, was negotiable.

Now, I know I may be coming across rather harshly. And I may appear as the developer with blinders, who tells everyone "Just trust me, you won't get screwed." This was exactly how I portrayed my perspective to Aaron and Janson, who more or less disagreed with me for those exact reasons. We debated for a bit longer, while eating our lunch, and I finally had the Eureka moment.

Our job is a technical one. Not only does it involve many hours of learning, but it takes the obsessive-like passion to stay current. It is rather rare for clients to possess the technical knowledge, or the experience involved in constructing quality software. However they know their domain intimately, and for a developer to play the "trust me" card in a bit arrogant. As a developer, I often forget that.

Help educate them

I was feeling as if I had to justify our development practices to the client. It made me upset that I needed to have them approve my reasoning in deciding to refactor older code. Yet as I was getting worked up, it dawned on me, the client hadn't even seen the estimate. I was already prepping counter arguments, as well as previous experiences to sell my decisions which required more work.

Listening to Aaron and Janson, I realized the client didn't need justification. They weren't bringing my decisions into the light in order to criticize my knowledge as a developer. They merely wanted to match the impact of my decisions with what they knew. They sought to understand the reasoning behind my decisions.

As I've thought more about that conversation I've come to an important resolution in my career. I should never justify myself to a client, however I should always try to educate our clients. I should spend the effort to tell them the impact of taking alternate routes. I should find better ways to articulate myself and speak their language rather than swamp them with intimidating geek-speak.

Justification isn't evil

While my client's aren't developers, I am, and so is the rest of my team. This brought up the second big question and resolution "How do I know my decision is right?", which led me to: Always justify my decisions to myself, and to my team. I believe a key ingredient in improving ourselves is constant introspection. If I make a decision, and I can't debate myself or a team member on the end result, then it's probably heavily influenced by preference, or even fear.

I should always justify to myself why we test, and the rigorousness of our tests. I should always justify why we should use agile approaches to planning. I should evaluate the reasoning behind using git over svn and whether programming in ruby makes more sense than developing in java.

I've found that it's very easy to surround yourself with a community who evangelizes tools or practices, while not sincerely asking the honest questions. I've also seen a similar pitfalls in ignoring new technologies and trends, because a lack of self-justification.

ALIVE and Treetop

February 23rd, 2008

A sweet project

I’m proud to say really fun mini-project went live today. OKWU Alive for Oklahoma Wesleyan University, a very forward thinking college. They wanted a site that is mainly updated by twitter messages.

We used a combination of Radiant, Twitter4R, and an upcoming library Treetop that I heard about a RubyConf 2007. Ever since attending Nathan Sobo’s presentation I’ve wanted to put it to use, but kept putting it off.

The “challenge”

To give some context to the site, OKWU wanted to parse direct twitter messages and add them to the site. The thing that made this interesting, is that they wanted to be able to tag each message. Most messages take on the form of:


  tag : message

Now I could obviously use regular expressions to parse out both the tag and the message, but what fun is that?

Treetop to the rescue

Treetop is structured to take a grammar file, that can be brought into ruby code. Here is the grammar we used to define the twitter message:


  grammar Twitter
    rule status
      tag delimiter message
    end

    rule tag
      [a-zA-Z_0-9-]+
    end

    rule message
      .*
    end

    rule delimiter
      space* ':' space*
    end

    rule space
      ' '
    end
  end

If you haven’t worked with grammar specifications before, don’t feel overwhelmed. What this essentially says is “a twitter status (another definition of a message from twitter) is composed of a tag followed by a delimiter followed by a message.” With each part, you can find a more specific definition. For example, a tag can only take the form of alphanumerical characters, underscores and dashes.

“Ok, that’s neat, but how is it useful?”

The coolness comes in with the consumption of the grammar. Here’s the code that uses Treetop:


  require "treetop" 
  Treetop.load "twitter" 

  parser = TwitterParser.new
  parsed_results = parser.parse("awesomified : you won't believe it's that easy")

  tag = parsed_results.get_tag
  message = parsed_results.get_message
  puts "message: #{message} classified under: #{tag}" 

As you can see, Treetop loaded in the grammar and immediately gave me a TwitterParser. From there I parsed an example twitter message, and with the results I retrieved the tag and message.

“Wait, how did you get the tag and message?”

Well, I didn’t exactly show the entire grammar. Here’s the final one:


  grammar Twitter
    rule status
      tag delimiter message {
        def get_tag
          tag.text_value
        end

        def get_message
          message.text_value
        end
      }
    end

    rule tag
      [a-zA-Z_0-9-]+
    end

    rule message
      .*
    end

    rule delimiter
      space* ':' space*
    end

    rule space
      ' '
    end
  end

Almost identical to the above except…it has friggin’ ruby code attached! That means when given a status, I can call #get_tag and #get_message to return the items. Pretty doggone easy.

“Impressive, but how is this better than just using regular expressions”

So I will not deny the same thing could be accomplished with a single regex, but this looks sexy. And it has additional benefits. Lets say in the future they want to:

  1. Allow multiple tags
  2. Allow spaces, and commas to be valid tag delimiters
  3. Allow the tags to be optional

Here’s a grammar modified with those exact requests:


  grammar Twitter
    rule status
      (tags delimiter)? text {
        def get_tags
          if self.class.method_defined? "tags" 
            tags.get_tags
          else
            []
          end
        end

        def get_message
          text.text_value
        end
      }
    end

    rule tags
      tag optional_tags:(optional_tag*) {
        def get_tags
          [tag.get_tag] + optional_tags.elements.map { |e| e.get_tag }
        end
      }
    end

    rule optional_tag
      tag_delimiter tag {
        def get_tag
          tag.text_value
        end
      }
    end

    rule tag
      [a-zA-Z_0-9-]+ {
        def get_tag
          text_value
        end
      }
    end

    rule text
      .*
    end

    rule delimiter
      space* ':' space*
    end

    rule space
      ' '
    end

    rule tag_delimiter
      space* ',' space* / space+
    end
  end
Some examples and their output:

  results = parser.parse("tag1 : the message")
  results.get_tags      # => ["tag1"]
  results.get_message   # => "the message" 

  results = parser.parse("tag1 tag2, tag3 : the message")
  results.get_tags      # => ["tag1", "tag2", "tag3"]
  results.get_message   # => "the message" 

  results = parser.parse("the message")
  results.get_tags      # => []
  results.get_message   # => "the message" 

  results = parser.parse(": the message")
  results.get_tags      # => []
  results.get_message   # => ": the message" 

  # Yea, well not bad for only 15 min, lets chalk the last one up to user-error.

I want to thank Nathan Sobo for putting together such a useful and intuitive library. For more information about Treetop, you can check out the site as well as the mailing list.

Combined PGP Keys Into One

February 18th, 2008

After finding out today that pgp keys can have multiple email addresses, I revoked all but public key and combined all emails into one key.

How

Edit a key in the terminal:


> gpg --edit-key KEY_ID
Command> adduid
Add name and email address. Then remember to set a primary uid:

Command> uid NUMBER_IN_PARENTHS_TO_SELECT
Command> primary
Command> save

Should be all good. Remember to revoke the old keys and re-export the public key.

Additional Tip

You can share your public key with others easier by uploading it to a key server like MIT

Git Stash is Sweet

February 16th, 2008

Earlier I mentioned a method of storing away changes in a patch, rebasing and then applying the patch so you don’t have to commit beforehand.

Yesterday Janson found git-stash, and it is sweet.

  1. type “git stash”
  2. do your changes
  3. type “git stash apply”
  4. profit!