samhuri.net


By Sami Samhuri

February 2006

SJ's Rails Bundle 0.2 for TextMate

Everything that you've seen posted on my blog is now available in one bundle. Snippets for Rails database migrations and assertions are all included in this bundle.

There are 2 macros for class-end and def-end blocks, bound to ⌃C and ⌃D respectively. Type the class or method definition, except for class or def, and then type the keyboard shortcut and the rest is filled in for you.

I use an underscore to denote the position of the cursor in the following example:

method(arg1, arg2_)

Typing ⌃D at this point results in this code:

def method(arg1, arg2)
  _
end

There is a list of the snippets in Features.rtf, which is included in the disk image. Of course you can also browse them in the Snippets Editor built into TextMate.

Without further ado, here is the bundle:

Download Download SJ's Rails Bundle 0.2

This is a work in progress, so any feedback you have is very helpful in making the next release better.

Intelligent Migration Snippets 0.1 for TextMate

This should be working now. I've tested it under a new user account here.

This does requires the syncPeople bundle to be installed to work. That's ok, because you should get the syncPeople on Rails bundle anyways.

When writing database migrations in Ruby on Rails it is common to create a table in the self.up method and then drop it in self.down. The same goes for adding, removing and renaming columns.

I wrote a Ruby program to insert code into both methods with a single snippet. All the TextMate commands and macros that you need are included.

See it in action

I think this looks cool in action. Plus I like to show off what what TextMate can do to people who may not use it, or don't have a Mac. It's just over 30 seconds long and weighs in at around 700kb.

Download Download Demo Video

Features

There are 3 snippets which are activated by the following tab triggers:

Installation

Run Quick Install.app to install these commands to your Download Download Intelligent Migration Snippets

This is specific to Rails migrations, but there are probably other uses for something like this. You are free to use and distribute this code. [syncPeople]: http://blog.inquirylabs.com/

TextMate: Insert text into self.down

UPDATE: I got everything working and it's all packaged up here. There's an installation script this time as well.

Thanks to a helpful thread on the TextMate mailing list I have the beginning of a solution to insert text at 2 (or more) locations in a file.

I implemented this for a new snippet I was working on for migrations, rename_column. Since the command is the same in self.up and self.down simply doing a reverse search for rename_column in my hackish macro didn't return the cursor the desired location.

That's enough introduction, here's the program to do the insertion:

#!/usr/bin/env ruby
def indent(s)
  s =~ /^(\s*)/
  ' ' * $1.length
end

up_line = 'rename_column "${1:table}", "${2:column}", "${3:new_name}"$0'
down_line = "rename_column \"$$1\", \"$$3\", \"$$2\"\n"

# find the end of self.down and insert 2nd line
lines = STDIN.read.to_a.reverse
ends_seen = 0
lines.each_with_index do |line, i|
  ends_seen += 1    if line =~ /^\s*end\b/
  if ends_seen == 2
    lines[i..i] = [lines[i], indent(lines[i]) * 2 + down_line]
    break
  end
end

# return the new text, escaping special chars
print up_line + lines.reverse.to_s.gsub('[$`\\]', '\\\\\1').gsub('\\$\\$', '$')

Save this as a command in your Rails, or syncPeople on Rails, bundle. The command options should be as follows:

The first modification it needs is to get the lines to insert as command line arguments so we can use it for other snippets. Secondly, regardless of the Re-indent pasted text setting the text returned is indented incorrectly.

The macro I'm thinking of to invoke this is tab-triggered and will simply:

TextMate: Move selection to self.down

UPDATE: This is obsolete, see this post for a better solution.

Duane's comment prompted me to think about how to get the drop_table and remove_column lines inserted in the right place. I don't think TextMate's snippets are built to do this sort of text manipulation. It would be nicer, but a quick hack will suffice for now.

Use MCDT to insert:

create_table "table" do |t|

end
drop_table "table"

Then press tab once more after typing the table name to select the code drop_table "table". I created a macro that cuts the selected text, finds def self.down and pastes the line there. Then it searches for the previous occurence of create_table and moves the cursor to the next line, ready for you to add some columns.

I have this bound to ⌃⌥⌘M because it wasn't in use. If your Control key is to the left the A key it's quite comfortable to hit this combo. Copy the following file into ~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Macros.

Move selection to self.down

This works for the MARC snippet as well. I didn't tell you the whole truth, the macro actually finds the previous occurence of (create_table|add_column).

The caveat here is that if there is a create_table or add_column between self.down and the table you just added, it will jump back to the wrong spot. It's still faster than doing it all manually, but should be improved. If you use these exclusively, the order they occur in self.down will be opposite of that in self.up. That means either leaving things backwards or doing the re-ordering manually. =/

TextMate Snippets for Rails Assertions

This time I've got a few snippets for assertions. Using these to type up your tests quickly, and then hitting ⌘R to run the tests without leaving TextMate, makes testing your Rails app that much more convenient. Just when you thought it was already too easy! (Don't forget that you can use ⌥⌘↓ to move between your code and the corresponding test case.)

This time I'm posting the .plist files to make it easier for you to add them to TextMate. All you need to do is copy these to ~/Library/Application Support/TextMate/Bundles/Rails.tmbundle/Snippets.

Assertion Snippets for Rails

If anyone would rather I list them all here I can do that as well. Just leave a comment.

(I wanted to include a droplet in the zip file that will copy the snippets to the right place, but my 3-hour attempt at writing the AppleScript to do so left me feeling quite bitter. Maybe I was just mistaken in thinking it would be easy to pick up AppleScript.)

Obligatory Post about Ruby on Rails

I'm a Rails newbie and eager to learn. I welcome any suggestions or criticism you have. You can direct them to my inbox or leave me a comment below.

I finally set myself up with a blog. I mailed my dad the address and mentioned that it was running Typo, which is written in Ruby on Rails. The fact that it is written in Rails was a big factor in my decision. I am currently reading Agile Web Development With Rails and it will be great to use Typo as a learning tool, since I will be modifying my blog anyways regardless of what language it's written in.

Clearly Rails made an impression on me somehow or I wouldn't be investing this time on it. But my dad asked me a very good question:

Rails? What is so special about it? I looked at your page and it looks pretty normal to me. I miss the point of this new Rails technique for web development.

It's unlikely that he was surprised at my lengthy response, but I was. I have been known to write him long messages on topics that interest me. However, I've only been learning Rails for two weeks or so. Could I possibly have so much to say about it already? Apparently I do.

Ruby on Rails background

I assume a pretty basic knowledge of what Rails is, so if you're not familiar with it now's a good time to read something on the official Rails website and watch the infamous 15-minute screencast, where Rails creator, David Heinemeier Hansson, creates a simple blog application.

The screencasts are what sparked my curiosity, but they hardly scratch the surface of Rails. After that I spent hours reading whatever I could find about Rails before deciding to take the time to learn it well. As a result, a lot of what you read here will sound familiar if you've read other blogs and articles about Rails. This post wasn't planned so there's no list of references yet. I hope to add some links though so please contact me if any ideas or paraphrasing here is from your site, or if you know who I should give credit to.

Rails through my eyes

Rails is like my Black & Decker toolkit. I have a hammer, power screwdriver, tape measure, needle-nose pliers, wire cutters, a level, etc. This is exactly what I need—no more, no less. It helps me get things done quickly and easily that would otherwise be painful and somewhat difficult. I can pick up the tools and use them without much training. Therefore I am instantly productive with them.

The kit is suitable for many people who need these things at home, such as myself. Companies build skyscrapers and huge malls and apartments, and they clearly need more powerful tools than I. There are others that just need to drive in a nail to hang a picture, in which case the kit I have is overkill. They're better off just buying and using a single hammer. I happen to fall in the big grey middle chunk, not the other two.

I'm a university student. I code because it's satisfying and fun to create software. I do plan on coding for a living when I graduate. I don't work with ancient databases, or create monster sites like Amazon, Google, or Ebay. The last time I started coding a website from scratch I was using PHP, that was around the turn of the millennium. [It was a fan site for a favourite band of mine.]

After a year or so I realized I didn't have the time to do it properly (ie. securely and cleanly) if I wanted it to be done relatively soon. A slightly customized MediaWiki promptly took it's place. It did all that I needed quite well, just in a less specific way.

The wiki is serving my site extremely well, but there's still that itch to create my own site. I feel if Rails was around back then I may have been able to complete the project in a timely manner. I was also frustrated with PHP. Part of that is likely due to a lack of experience and of formal programming education at that time, but it was still not fun for me. It wasn't until I started learning Rails that I thought "hey, I could create that site pretty quickly using this!"

Rails fits my needs like a glove, and this is where it shines. Many professionals are making money creating sites in Rails, so I'm not trying to say it's for amateurs only or something equally silly.

Web Frameworks and iPods?

Some might say I have merely been swept up in hype and am following the herd. You may be right, and that's okay. I'm going to tell you a story. There was a guy who didn't get one of the oh-so-shiny iPods for a long time, though they looked neat. His discman plays mp3 CDs, and that was good enough for him. The latest iPod, which plays video, was sufficiently cool enough for him to forget that everyone at his school has an iPod and he would be trendy just like them now.

Shocker ending: he is I, and I am him. Now I know why everyone has one of those shiny devices. iPods and web frameworks have little in common except that many believe both the iPod and Rails are all hype and flash. I've realized that something creating this kind of buzz may actually just be a good product. I feel that this is the only other thing the iPod and Rails have in common: they are both damn good. Enough about the iPod, everyone hates hearing about it. My goal is to write about the other thing everyone is tired of hearing about.

Why is Rails special?

Rails is not magic. There are no exclusive JavaScript libraries or HTML tags. We all have to produce pages that render in the same web browsers. My dad was correct, there is nothing special about my website either. It's more or less a stock Typo website.

So what makes developing with Rails different? For me there are four big things that set Rails apart from the alternatives:

  1. Separating data, function, and design
  2. Readability (which is underrated)
  3. Database migrations
  4. Testing is so easy it hurts

MVC 101 (or, Separating data, function, and design)

Now I'm sure you've heard about separating content from design. Rails takes that one step further from just using CSS to style your website. It uses what's known as the MVC paradigm: Model-View-Controller. This is a tried and tested development method. I'd used MVC before in Cocoa programming on Mac OS X, so I was already sold on this point.

Of course this is not exclusive to Rails, but it's an integral part of it's design.

Readability

Rails, and Ruby, both read amazingly like spoken English. This code is more or less straight out of Typo. You define relationships between objects like this:

class Article < Content
  has_many :comments, :dependent => true, :order => "created_at ASC"
  has_many :trackbacks, :dependent => true, :order => "created_at ASC"
  has_and_belongs_to_many :categories, :foreign_key => 'article_id'
  has_and_belongs_to_many :tags, :foreign_key => 'article_id'
  belongs_to :user
  ...

dependent => true means if an article is deleted, it's comments go with it. Don't worry if you don't understand it all, this is just for you to see some actual Rails code.

In the Comment model you have:

class Comment < Content
  belongs_to :article
  belongs_to :user

  validates_presence_of :author, :body
  validates_against_spamdb :body, :url, :ip
  validates_age_of :article_id
  ...

(I snuck in some validations as well)

But look how it reads! Read it out loud. I'd bet that my mom would more or less follow this, and she's anything but a programmer. That's not to say programming should be easy for grandma, but code should be easily understood by humans. Let the computer understand things that are natural for me to type, since we're making it understand a common language anyways.

Ruby and Ruby on Rails allow and encourage you to write beautiful code. That is so much more important than you may realize, because it leads to many other virtues. Readability is obvious, and hence maintainability. You must read code to understand and modify it. Oh, and happy programmers will be more productive than frustrated programmers.

Database Migrations

Here's one more life-saver: migrations. Migrations are a way to version your database schema from within Rails. So you have a table, call it albums, and you want to add the date the album was released. You could modify the database directly, but that's not fun. Even if you only have one server, all your configuration will be in one central place, the app. And Rails doesn't care if you have PostgreSQL, MySQL, or SQLite behind it. You can develop and test on SQLite and deploy on MySQL and the migrations will just work in both environments.

class AddDateReleased < ActiveRecord::Migration
  def self.up
    add_column "albums", "date_released", :datetime
    Albums.update_all "date_released = now()"
  end

  def self.down
    remove_column "albums", "date_released"
  end
end

Then you run the migration (rake migrate does that) and boom, your up to date. If you're wondering, the self.down method indeed implies that you can take this the other direction as well. Think rake migrate VERSION=X.

Along with the other screencasts is one on migrations featuring none other than David Hansson. You should take a look, it's the third video.

Testing so easy it hurts

To start a rails project you type rails project_name and it creates a directory structure with a fresh project in it. This includes a directory appropriately called test which houses unit tests for the project. When you generate models and controllers it creates test stubs for you in that directory. Basically, it makes it so easy to test that you're a fool not to do it. As someone wrote on their site: It means never having to say "I introduced a new bug while fixing another."

Rails builds on the unit testing that comes with Ruby. On a larger scale, that means that Rails is unlikely to flop on you because it is regularly tested using the same method. Ruby is unlikely to flop for the same reason. That makes me look good as a programmer. If you code for a living then it's of even more value to you.

I don't know why it hurts. Maybe it hurts developers working with other frameworks or languages to see us have it so nice and easy.

Wrapping up

Rails means I have fun doing web development instead of being frustrated (CSS hacks aside). David Hansson may be right when he said you have to have been soured by Java or PHP to fully appreciate Rails, but that doesn't mean you won't enjoy it if you do like Java or PHP.

Justin Gehtland rewrote a Java app using Rails and the number of lines of code of the Rails version was very close to that of the XML configuration for the Java version. Java has strengths, libraries available now seems to be a big one, but it's too big for my needs. If you're like me then maybe you'll enjoy Rails as much as I do.

You're not done, you lied to me!

Sort of... there are a few things that it seems standard to include when someone writes about how Rails saved their life and gave them hope again. For completeness sake, I feel compelled to mention some principles common amongst those who develop Rails, and those who develop on Rails. It's entirely likely that there's nothing new for you here unless you're new to Rails or to programming, in which case I encourage you to read on.

DRY

Rails follows the DRY principle religiously. That is, Don't Repeat Yourself. Like MVC, I was already sold on this. I had previously encountered it in The Pragmatic Programmer. Apart from telling some_model it belongs_to :other_model and other_model that it has_many :some_models nothing has jumped out at me which violates this principle. However, I feel that reading a model's code and seeing it's relationships to other models right there is a Good Thing™.

Convention over configuration (or, Perceived intelligence)

Rails' developers also have the mantra "convention over configuration", which you can see from the video there. (you did watch it, didn't you? ;) Basically that just means Rails has sane defaults, but is still flexible if you don't like the defaults. You don't have to write even one line of SQL with Rails, but if you need greater control then you can write your own SQL. A standard cliché: it makes the simple things easy and the hard possible.

Rails seems to have a level of intelligence which contributes to the wow-factor. After these relationships are defined I can now filter certain negative comments like so:

article = Article.find :first
for comment in article.comments do
  print comment unless comment.downcase == 'you suck!'
end

Rails knows to look for the field article_id in the comments table of the database. This is just a convention. You can call it something else but then you have to tell Rails what you like to call it.

Rails understands pluralization, which is a detail but it makes everything feel more natural. If you have a Person model then it will know to look for the table named people.

Code as you learn

I love how I've only been coding in Rails for a week or two and I can do so much already. It's natural, concise and takes care of the inane details. I love how I know that I don't even have to explain that migration example. It's plainly clear what it does to the database. It doesn't take long to get the basics down and once you do it goes fast.

Some TextMate snippets for Rails Migrations

My arsenal of snippets and macros in TextMate is building as I read through the rails canon, Agile Web Development... I'm only 150 pages in so I haven't had to add much so far because I started with the bundle found on the rails wiki. The main ones so far are for migrations.

Initially I wrote a snippet for adding a table and one for dropping a table, but I don't want to write it twice every time! If I'm adding a table in up then I probably want to drop it in down.

What I did was create one snippet that writes both lines, then it's just a matter of cut & paste to get it in down. The drop_table line should be inserted in the correct method, but that doesn't seem possible. I hope I'm wrong!

Scope should be source.ruby.rails and the triggers I use are above the snippets.

mcdt: Migration Create and Drop Table

createtable "${1:table}" do |t| $0 end ${2:droptable "$1"}

mcc: Migration Create Column

t.column "${1:title}", :${2:string}

marc: Migration Add and Remove Column

addcolumn "${1:table}", "${2:column}", :${3:string} ${4:removecolumn "$1", "$2"}

I realize this might not be for everyone, so here are my original 4 snippets that do the work of marc and mcdt.

mct: Migration Create Table

create_table "${1:table}" do |t| $0 end

mdt: Migration Drop Table

drop_table "${1:table}"

mac: Migration Add Column

add_column "${1:table}", "${2:column}", :${3:string}

mrc: Migration Rremove Column

remove_column "${1:table}", "${2:column}"

I'll be adding more snippets and macros. There should be a central place where the rails bundle can be improved and extended. Maybe there is...


Comments

Feb 19, 2006

This looks great! I agree, we should have some sort of central place for these things, and preferably something that's not under the management of the core Rails team as they have too much to worry about already.

Would you mind if I steal your snippets and put them in the syncPeople on Rails bundle?

Feb 19, 2006

Not at all. I'm excited about this bundle you've got. Keep up the great work.

Feb 20, 2006

Just added the snippets, Sami. I'll try to make a release tonight. Great work, and keep it coming!

P.S. I tried several ways to get the combo-snippets to put the pieces inside the right functions but failed. We'll see tomorrow if Allan (creator of TextMate) has any ideas.

Jump to view/controller in TextMate

Duane

came up with a way to jump to the controller method for the view you're editing, or vice versa in TextMate while coding using Rails. This is a huge time-saver, thanks!

Girlfriend X

This is hilarious! Someone wrote software that manages a "parallel" dating style.

In addition to storing each woman's contact information and picture, the Girlfriend profiles include a Score Card where you track her sexual preferences, her menstrual cycles and how she styles her pubic hair.

It's called Girlfriend X, but that's a link to an article about it. I didn't go to the actual website. I just thing it's amusing someone went through the trouble to do this. Maybe there's a demand for it. *shrug*

Urban Extreme Gymnastics?

This crazy russian goes all over the place scaling buildings, doing all sorts of flips, bouncing off the walls literally. He'd be impossible to catch.

Russian parkour (urban extreme gymnastics)

Touch Screen on Steroids

If you thought the PowerBook's two-finger scrolling was cool check out this touch screen:

Multi-Touch Interaction Research

"While touch sensing is commonplace for single points of contact, multi-touch sensing enables a user to interact with a system with more than one finger at a time, as in chording and bi-manual operations. Such sensing devices are inherently also able to accommodate multiple users simultaneously, which is especially useful for larger interaction scenarios such as interactive walls and tabletops."

This is really amazing. Forget traditional tablet PCs... this is revolutionary and useful in so many applications. I hope this kind of technology is mainstream by 2015.

First Post!

so it's 2am and i should be asleep, but instead i'm setting up a blog. i got a new desk last night and so today i finally got my apartment re-arranged and it's much better now. that's it for now... time to sleep.

(speaking of sleep, this new sleeping bag design makes so much sense. awesome.)