Gavin Morrice

is a web and iOS developer from Edinburgh, Scotland.

more about me »

Blog Archive

Writing A Rails Application With TextMate

For Mac users, TextMate is powerful tool for writing code. But having a great tool is pretty pointless unless you know how to use it. By spending a few hours learning about TextMate’s features you can almost half the time it takes you to write code. This tutorial will walk you through creating a simple Rails app using some of TextMate’s more advanced features.

Before you start, I’d recommend you get the latest version of the Ruby on Rails TextMate bundle and install it. Also – I realise the application from this tutorial is far from perfect but I figured this would be a good way to introduce the features and show examples of how they can be used. If you have any suggestions please leave a comment below.

Lets create a new rails app, in your terminal type the following:

rails new text_mate_demo

Once the app has been created:

cd text_mate_demo && mate .

to open the full application in TextMate.

In true Rails fashion, this demo is going to create a simple blog site. To create our first model type the following in your terminal window:

rails g model Post title:string body:text
rails g model Comment name:string body:text post:references

Next, open comment.rb and press ctrl + | (or ctrl + shift + \) and 2 to run the migration.

Next, still in TextMate, type ctrl + alt + cmd + D to hide the project drawer – we won’t be using it in this tutorial. Instead we’ll navigate by pressing cmd + T and typing (part of) the name of the file you want to open. Lets go to post_test.rb and write some unit tests for our Post model. Type cmd + T followed by “poste” and hit enter.

For our unit tests we’ll create a setup method. Here’s a good chance to introduce tab triggers. Type “def” followed by the tab key. This should create a new empty method with the name highlighted. Name this method “setup” and hit tab again to jump down a line.

To setup our unit tests lets create a new Post instance and run validation checks on it.

require 'test_helper'

class PostTest < ActiveSupport::TestCase

  def setup    
    @post = Post.new
    @post.valid?
  end

end

On a new line type “test” and hit tab to create a new test. Lets call it “should not be valid without title”. As a shortcut to writing “assert_equal”, you can write “ase” and hit tab, then tab for the expected value and tab again for the actual value. I’ve also added another two tests to meet our requirements:

require 'test_helper'

class PostTest < ActiveSupport::TestCase

  def setup    
    @post = Post.new
    @post.valid?
  end

  test "should not be valid without title" do
    assert_equal @post.errors.on(:title), "can't be blank"
  end

  test "should not be valid without a body" do
    assert_equal @post.errors.on(:body), "can't be blank"
  end

  test "should have many comments" do
    @post = posts(:one)
    2.times { @post.comments.create! :name => "John Smith", :body => "This is a comment" }
  end

end

Save this page and then press ctrl + \ followed by 7 to test units. The three tests should fail. Now press alt + cmd + down to jump to post.rb.

Our unit tests specify that our posts should not be valid without a title or a body. To quickly add a “validates_presence_of” validation, type “vp” and then press tab. Now type “title” and then press tab and delete to clear the extra options (which we don’t require here).

For our second validation we’re going to try something different. On a new line, type “va” and then press esc. The escape key can be used to auto-complete any method, constant or variable name that’s already present on that document. This is a great way to save time retyping long method/variable names.

To add our has_many association, type “hm” and hit tab. Type “comment” and then tab, delete again.

These validations could do with being DRYed up but we’ll leave that for now. In the meantime, press cmd + / to start a comment and leave yourself a TODO note “TODO – DRY this up”.

class Post < ActiveRecord::Base    

  # TODO: - DRY this up
  validates_presence_of :title
  validates_presence_of :body

  has_many :comments

end

Now we need to create a Comment model so our third test passes.

rails g model Comment name:string body:text post:references
rake db:migrate

Quick note – jump to comments.yml and populate the “post” attribute like so:

one:
  name: MyString
  email: MyString
  post: one
  body: MyText

two:
  name: MyString
  email: MyString
  post: two
  body: MyText

Now jump to comment_test.rb, again using cmd + T and add some tests like so:

require 'test_helper'

class CommentTest < ActiveSupport::TestCase

  def setup
    @comment = Comment.new
    @comment.valid?
  end  

  test "should belong to post" do
    assert_equal( posts(:one), comments(:one).post )
  end

  test "should not be valid without name" do
    assert_equal(@comment.errors.on(:name), "can't be blank")
  end

  test "should not be valid without body" do
    assert_equal(@comment.errors.on(:body), "can't be blank")
  end

end

Again, run the tests, see the failures and then write the model:

class Comment < ActiveRecord::Base

  belongs_to :post

  validates_presence_of :name, :body

end

The tests should now pass.

There’re a whole bunch of shortcuts for commonly used methods. Most of them are used by writing a few letters and then hitting tab. To browse through them go to Bundles > Ruby on Rails.

This may be a good time to DRY up the Post model. Press ctrl + shift + T to have a look at the annotations you’ve left in your code. You should see 1 TODO in post.rb. Click on the link to jump to this comment and remove the duplication:

class Post < ActiveRecord::Base    

  validates_presence_of :title, :body

  has_many :comments

end

Now we need a controller for our posts. By pressing shift + alt + cmd + down you should see the goto menu. Press 1 for controller. Since there isn’t already a posts controller TextMate will ask if you’d like to create one. Click “create”. (From this point let’s just imagine we’ve written tests first).

Now we need an index action to display our posts. Again, using our “def” then tab shortcut, create an index action and try to add some code here to find all of the posts and order them by “created_at DESC”. But crap! We’ve forgotten the different options available for the find method. Here comes the coolest trick TextMate has to offer…

Highlight the find method and press ctrl + H. A window will pop up with either 2 or 3 options. If you only see 2, then select “Documentation for Selection” and select “ActiveRecord::Base::find”. If you do have the 3rd option “Documentation for Word” then select it. This gives you a chance to browse the Rails documentation for any method and see examples of how to use it. This is a really powerful tool! By pressing cmd + H you can see the documentation for practically any gem you’ve installed. It also works for other languages like HTML and CSS so it’s a great way to learn as you go.

You quickly read up on find and see the options available. Now you can add the required code to the index action:

class PostsController < ApplicationController

  def index
    @posts = Post.where(['created_at > ?', 3.months.ago]).order("created_at DESC").limit(5).eager_load(:comments)
  end

end

To collapse this method and neaten your controller, press alt + cmd + 2. You can collapse foldings at different levels by using alt + cmd + 1,2,3 etc.

With the cursor still in the index action, press alt + cmd + down to create the view template for the index action.

To create rhtml tags press ctrl + >. By pressing this again you can cycle through the various erb tags available. To create new html tags press ctrl + <. Using this method, add the post title to our new heading tags like so:

<% @posts.each do |post| %>
  <h3><%= post.title %></h3>
  <%= post.body %>
  <% post.comments.each do |comment| %>
  <% div_for comment do %>
    <p><%= comment.name %></p>
    <p><%= comment.body %></p>
  <% end %>
<% end %>

This tutorial could go on for quite some time but those are the commands that I feel are the most useful. You may have already noticed too that TextMate already has its own Rails tutorial which covers loads of the snippets etc. You can access this tutorial by pressing cmd + H and selecting “View Demo Help”.

If there’s anything you’d like to see here that I haven’t covered, feel free to leave a comment.


Keeping Your Dates and Times DRY with to_formatted_s

Raw dates and times in Ruby are not too user-friendly! The default times and datetimes in rails are pretty unattractive and contain more information than we usually need to display. For example:

@user.created_at # => Mon, 18 May 2009 19:51:51 +0100

A useful way around this is to use the strftime() method to specify the format of the date or time. So instead, we can use:

@user.created_at.strftime("%d %b %y") # => "18 May 09"

This way we only display the information we want to.

Specifying time with strftime() throughout an entire app is not ideal though; apart from anything else it’s not DRY. Also, if your strftime formats are quite elaborate, the chances of you mistyping them and leaving inconsistencies throughout your app are pretty high.

Enter: to_formatted_s()

to_formatted_s() is a method available to classes Date, Time and DateTime which converts the time format to a more practical format.

@user.created_at.to_formatted_s(:time) # => "20:06"

The default format is :default but you can specify any of the other pre-set formats by simply passing the name of the format as a parameter.

Classes Time and DateTime share these formats:

DATE_FORMATS  =  { 
  :db => "%Y-%m-%d %H:%M:%S", # => "2009-05-18 20:03:48"
  :number => "%Y%m%d%H%M%S", # => "20090518200545"
  :time => "%H:%M", # => "20:06"
  :short => "%d %b %H:%M", # => "18 May 20:06"
  :long => "%B %d, %Y %H:%M", # => "May 18, 2009 20:07"
  :long_ordinal => lambda { |time| time.strftime("%B #{time.day.ordinalize}, %Y %H:%M") }, # => "May 18th, 2009 20:07"
  :rfc822 => "%a, %d %b %Y %H:%M:%S %z" # => "Mon, 18 May 2009 20:08:25 +0100"
}

and the Date class has these formats:

DATE_FORMATS = {
  :short => "%e %b", #=> "18 May"
  :long => "%B %e, %Y", # => "May 18, 2009"
  :db => "%Y-%m-%d", # => "2009-05-18"
  :number => "%Y%m%d", #=> "20090518"
  :long_ordinal => lambda { |d| d.strftime("%B #{d.day.ordinalize}, %Y") }, # => "May 18th, 2009"
  :rfc822 => "%e %b %Y" # => "18 May 2009" 
}

(RFC822 is the standard for the format of ARPA internet text messages.)

As well as using these default time formats, you can also set your own formats that can be used throughout your entire app.

To do this, simply expand on the default DATE_FORMATS.

First, create a new file: config/initializers/datetime_formats.rb and add the following:

Date::DATE_FORMATS[:my_format] = lambda { |t| t.strftime("#{time.day.ordinalize} of %B, %Y") }
Time::DATE_FORMATS[:my_format] = lambda { |t| t.strftime("#{time.day.ordinalize} of %B, %Y") }

If you’re setting the same format(s) on both Date and Time then this can be further simplified to:

[Time, Date].map do |klass|
  klass::DATE_FORMATS[:my_format] = lambda { |t| t.strftime("#{time.day.ordinalize} of %B, %Y") }
end

Now all you have to do is call

<%= @user.created_at.to_formatted_s(:my_format) %>

or even simpler:

<%= @user.created_at.to_s(:my_format) %>

Much DRYer!

Jo Hund has put together a great cheat-sheet for stfrtime formats


Link Prefetching with HTML 5 and jQuery

HTML5 offers a cool new feature called link prefetching. Link Prefetching allows developers to specify resources (such as another page or an image) that can be preloaded when a user visits a page, so that they can be rendered faster when/if they are requested.

If, for example, you have a blog then you can logically assume that when someone visits your blog's homepage, they'll probably click on one of the first few links to posts too. In your blog's homepage HTML code, you could simply add the following tag for each blog post you would like to prefetch:

<link rel=prefetch href="http://myblog.com/posts/blog-post-url.html" />

When a compatible browser loads the homepage it will also try to load http://myblog.com/posts/blog-post-url.html too!

However, hard-coding links for each post like this is not ideal. If, like a blog, your website serves dynamic content then it may not be possible to know the urls in advance and constantly updating your HTML when a change is made would be tiring.

Instead, it would be better if we could do this dynamically using Javascript and jQuery.

Check out the following snippet:

// create an object named "app" which we can define methods on
var app = {
    // returns an array of each url to prefetch
    prefetchLinks: function(){
        // returns an array of each a.prefetch link's href
        var hrefs = $("a.prefetch").map(function(index, domElement){
            return $(this).attr("href");
        });
        // returns the array of hrefs without duplicates
        return $.unique(hrefs);
    },

    // adds a link tag to the document head for each of prefetchLinks()
    addPrefetchTags: function(){
        // for each prefetchLinks() ...
        this.prefetchLinks().each(function(index,Element){
            // create a link element...
            $("<link />", {
                // with rel=prefetch and href=Element...
                rel: "prefetch", href: Element
                // and append it to the end of the document head
            }).appendTo("head");            
        });
    },
}
// when the document is ready...
jQuery(function(){
  // call the method we defined above.
    app.addPrefetchTags();
}

I'll explain what's happening here in order of when it happens:

Once the DOM is ready, we look for each link ( <a> tag ) with class "prefetch". Next we stick each <a> tag's href (the url of the page we want to preload) in to an array. Because a link to the same page may appear more than once per page, we call $.unique() to remove any duplicates from this array.

Now we have an array of urls to prefetch, we simply create a new link tag for each on with rel="prefetch" and href="_url-to-prefetch_" and append it to the end of the head tag.

Now, within your code, simply add class="prefetch" to any link which you would like to improve the load speed on.

Ruby On Rails Helper Method

If you're using Ruby on Rails, I've written a simple helper method to make light work of adding prefetch links to your templates:

# add me to application_helper.rb
# <%= prefetch_link_to("click me", root_url) %> # => <a href="/" class="prefetch">click me</a>
def prefetch_link_to(name, path, options = {})
  options[:class] = options[:class] ? "#{options[:class]} prefetch" : "prefetch"
  link_to(name, path, options)
end

This can be used just like Rails's link_to method to create links with class="prefetch".

Some tips and caveats.

Prefetching can be a really effective way of speeding up your web page but, like Uncle Ben said "With great power...". Here are a few things to keep in mind:

  • Only use prefetching to fetch pages that you believe will receive most traffic. Things like a Terms of Use or Legal page will be rarely visited and, if they are, it will be by someone who is intent on reading and wont mind waiting an extra few milliseconds.
  • Prefetching can be used behind the scenes to load large images that are not immediately visible. For example, if you're using lightbox then you can prefetch full-sized images ahead of their thumbnails being clicked on.
  • Prefetching can screw up your visitor stats. If every page your visitors load also prefetches two or three other pages you can expect your visitor stats to grow. This will produce unreliable data. Because only the HTML is fetched though, I believe Google Analytics and other JS-based stat-reports should not be effected.
  • Don't prefetch external pages. You're not a charity (unless you are a charity)! Using prefetching on links that take visitors away from your site is only helping to take visitors away from your site faster!