Photo-1 Photo-2 Photo-3 Photo-4 Photo-5 Photo-6



Full-time web developer. Part-time smart ass.

I'm Brent Collier.

After a year and a half as an engineer on Twitter's Trust & Safety team, I'm looking for my next gig. Contact me if you know of something interesting.


Archive links, quick and easy

Posted on 04/14/2009

So I finally got around to replacing the dummy text in the sidebar with real, working archive links, and it was stupidly easy.  Check it out...

First, I added a helper method to take all published posts and group them by the month they were created.

def archives
  @archives = Post.published.in_order.all.group_by { |t| t.created_at.beginning_of_month}

Then I used that helper method to display links for each month in which posts were created.

  <% archives.each do |month, posts| %>
    <li><%= link_to(month.strftime('%B')+" (#{posts.size})", archive_path(:month => month.month, :year => month.year)) %></li>
  <% end %>

You probably noticed the archive_path named route that the links point to. Here's how I set it up.

map.archive '/posts/:year/:month', :controller => 'posts', :action => 'archive'

That route points to the archive action of the posts controller which merely retrieves the posts created within the specified month and renders the list view.

def archive
  @posts = Post.published.in_order.from_month(Time.parse "#{params[:month]}/#{params[:year]}").paginate(:page => params[:page], :per_page => 5)
  render :action => 'index'

The archive action uses a from_month named scope on the Post model to restrict the find to all posts created within a given month.

named_scope :from_month, lambda { |month| {:conditions => {:created_at => month..(month + 1.month)}} }

That's it. That's all it takes to set up simple archive links for your blog.


Polymorphic many-to-many associations in Rails

Posted on 04/12/2009

Setting up polymorphic associations is ridiculously easy in Rails.  Setting up many-to-many associations in Rails is also ridiculously easy in Rails.  However, setting up polymorphic many-to-many associations in Rails is more difficult, but only slightly.

Recently, on an Intridea client project, I had one particular model that had many-to-many associations with several other models in the app.  The thought of multiple join tables in the database didn't sit well with me, so I decided to consolidate things a bit.

Now I can't use the actual models from the app without possibly giving away the yet-to-be-launched app.  So instead, I'll use an example that we're all familiar with, tags and taggings.

You know the drill.  You have your common model, Tag.

class Tag < ActiveRecord::Base
  has_many :taggings, :dependent => :destroy
  has_many :books, :through => :tagings, :source => :taggable, :source_type => "Book"
  has_many :movies, :through => :tagings, :source => :taggable, :source_type => "Movie"

Your join model, Tagging.

class Tagging < ActiveRecord::Base
  belongs_to :taggable, :polymorphic => true
  belongs_to :tag

And your taggable models, Book and Movie.

class Book < ActiveRecord::Base
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings
class Movie < ActiveRecord::Base
  has_many :taggings, :as => :taggable
  has_many :tags, :through => :taggings

Everything in Tagging, Movie, and Book is pretty much your standard setup.  This gives you all the normal associations.  You've got tag.taggings, book.tags, movie.tags, and tag.taggable.

It gets a little trickier when you want to find all movies (or books) associated with a tag.  There are a number of ways that this can be done.  You could use some crazy joined finder call on Movie, or find all Taggings with a particular taggable type (or a named scope) and collect the movies, or do it like up above.

has_many :movies, :through => :taggings, 
                  :source => :taggable, 
                  :source_type => "Movie"

This association allows us to access a movies with a particular tag directly from the tag object itself.  It's just your typical has_many association, but with a few more options.  The :through option says that movies can be accessed through our join model, tagging.  Rails normally would expect there to be a movies association on tagging, but the :source option tells Rails to examine the taggable association instead.  Similarly, the :source_type option specifies the class (or type) of the polymorphic association that we trying to retrieve.

So remember when you want bidirectional class-specific many-to-many polymorphic associations, :source and :source_type are your friend.