Shoulda Macro for acts_as_tree

I love writing tests and I love Shoulda.  Here is an acts_as_tree macro I threw together.


class Test::Unit::TestCase

  def self.should_act_as_tree(opts = {})
    klass = self.name.gsub(/Test$/, '').constantize

    foreign_key = get_options!([{:foreign_key => 'parent_id'}.merge(opts)], :foreign_key)

    context "To support acts_as_tree" do
      should have_db_column(foreign_key).of_type(:integer)
    end

    should "include ActsAsTree methods" do
      assert klass.include?(ActiveRecord::Acts::Tree::InstanceMethods)
      assert klass.methods.include? "root"
      assert klass.methods.include? "roots"
    end
  end
end

Popularity: 18%

Add An RSS Auto-Discovery Link in Rails

This is a follow-up to another RSS post I have. Once you’ve created an RSS feed for your site you should create an auto-discovery link for it so that browsers and RSS readers can find it.

Don’t know what I’m talking about? Go to pretty much any blog with a feed (in a somewhat recent browser) and you’ll see a little RSS icon in the address bar or toolbar if the browser auto-detected the feed .

Here’s an example from Firefox 2:

Firefox RSS Icon

Here’s IE7 (when the little star thingy appears, it’s found a feed):

IE RSS Icon

Browsers and readers know the feed is there because in the <head> of the page there is a <link> tag telling it the feed is there.

It looks like this:

<link rel="alternate" type="application/rss+xml" title="My Site's RSS Feed" href="http://mysite.com/feed/" />

Of course, you could just hard-code the tag into your Rails layout, but that’s not a good idea. A better idea is to use the Rails’ auto_discovery_link_tag view helper to do it. The helper method docs can be found in the ActionView::Helpers::AssetTagHelper module.

What you end up with is something that looks like:

<%= auto_discovery_link_tag(:rss, {:controller => "controller_name", :action => "feed", :title => "My Site's RSS Feed"}) %>

See the docs for the full details.

There are other helpful methods on that module too. There’s a method for creating stylesheet links and methods for creating paths to javascript files and images.

Popularity: 45%

Rails Command ‘Ruby Path’ Option

On my Mac the Rails’ default path for Ruby is /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby. This becomes an issue when copying code to UNIX/Linux servers where /usr/bin/ruby is default path. Fortunately, Macs have a symbolic link ruby -> ../../System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby.

You can set the path to /usr/bin/ruby using the -r option to the rails when creating your app: rails -r /usr/bin/ruby <myappname>

Popularity: 40%

Generating RSS in Rails

Generating RSS in Rails 2 is pretty easy.

In the simplest case, all that is required is an extra view file to create the RSS output.

Consider a simple (RESTful) blog example:

route.rb:

...
map.home '', :controller => 'posts', :action => 'index'

map.resources :posts
...

posts_controller.rb:


def index
  @posts = Post.find(:all)
end

When I generated the posts controller, Rails created an ‘index.html.erb’ file for the index action.  It’s an ugly file name, but it helps Rails figure out what to do: ‘index’ is the action, ‘html’ and ‘erb’ are the format and builder to use.  To generate RSS we want to use the XML Builder, so the file name to use is ‘index.rss.builder’. And the file contents should look something like this:


xml.instruct! :xml, :version => "1.0"
xml.rss :version => "2.0" do
  xml.channel do
    xml.title "My Blog"
    xml.description "My Fantastic Blog"
    xml.link posts_url

    for post in @posts
      xml.item do
        xml.title post.title
        xml.description post.content
        xml.pubDate post.created_at.to_s(:rfc822)
        xml.link post_url(post)
      end
    end
  end
end

Now that is really easy, but I’ve experienced two issues with this.

The first issue is with layouts. If the controller uses a layout for all actions (using layout => "mylayout") it will cause Rails to look for a layout file named ‘layouts/posts.rss.erb’. This can be bypassed by excluding the layout for the rss format, like so:


def index
  @posts = Post.find(:all)

  respond_to do |format|
    format.html
    format.rss  { render :layout => false }
  end
end

The other issue I experienced was with the will_paginate plug-in where my index method looks like this:


def index
  @posts = Post.paginate(:per_page => 10, :page => params[:page])
end

The problem is that only 10 posts will show up in the RSS feed, which is not what I want.

There are at least two good ways I came up with to deal with this issue. The my preferred way is create a new feed method that is excluded from the layout and returns all posts.


def feed
  @posts = Post.find(:all)
end

with a new route:


map.connect 'posts/feed.:format', :controller => 'posts', :action => 'feed'

The second way examines the request format and grabs the posts via paginate for HTML and find for RSS


def index
  if request.format.html?
    @posts = Post.paginate(:per_page => 10, :page => params[:page])
  else
    @posts = Post.find(:all)
  end

  respond_to do |format|
    format.html
    format.rss  { render :layout => fals }
  end
end

I like the first way better. Seems a little cleaner. I like small methods.

Popularity: 100%