Today I began helping out on an Intridea project that (by the client's request) uses Subversion as their revision control system instead of Git.  Initially I was a bit dismayed.  Hell, I actually felt a little sick to my stomach.  Git has treated me very well over the past year, and I hadn't touched a Subversion repo in even longer.

Git-svn had crossed my mind, but it wasn't until a coworker suggested it that I decided to give it a try.

The first thing was to get it installed.  I followed this tutorial and only had to make one small change.  Adding /opt/local/libexex/git-core to my path didn't work.  I did a quick whereis to find out that my git-svn executable is in the /usr/local/git/libexec/git-core/ directory.  I updated my path and everything was kosher.

Now to set up the repo.  Most of the tutorials that I found covered setting up a new svn repo with git-svn like this one, but in my case, the svn repo had been around for quite some time.  Being the wannabe-know-it-all that I am, I decided to just go ahead an clone the svn repo, like so:

git-svn clone http://path-to-the-svn-repo

After about ten minutes of watching the clone chug away, I thought something was up.  Well, it turns out that if you don't include the proper options, the clone will pull down all branches, tags, whatever.  I didn't want, or need all of that.  All I wanted was trunk.  After a brief glance at the git-svn docs, I tried again with the --trunk flag.

git-svn clone --trunk http://path-to-the-svn-repo

Aparently, the -s option also works.  It tells git-svn that you svn is in the standard layout with trunk, branches, tags, and whatnot.

This time it ran for only five minutes before I asked myself wtf was going on.  I realized that it was pulling down every single revision, which I really didn't need.  I would've stopped it and tried again with the --revision (-r) flag which allows you to specify a revision number, or range of revision number, but the clone was already at rev 900 out of 1100, so  I just let it ride.

Once that was done, I was good to go.  I could pretty much just treat it as a regular Git repo until I was ready to push any changes back to the svn repo.  I used this tutorial by Clinton Nixon, who I had recently seen do a talk on Scala at the Developer Day in Durham, to help guide my general git-svn workflow.

Comments (0)    git subversion git-svn svn

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}
end

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

    <% archives.each do |month, posts| %>
  • <%= link_to(month.strftime('%B')+" (#{posts.size})", archive_path(:month => month.month, :year => month.year)) %>
  • <% 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'
end

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.

Comments (0)    tips how-to

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"
end

Your join model, Tagging.

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

And your taggable models, Book and Movie.

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

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

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 => :tagings, 
                  :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.

Comments (8)    rails active_record

After continually receiving lame page-rankings on my previous blog attempt, I thought this time that I might spend a little more time to get a bit of my content into Google search results.  I've never really messed with SEO before, so this is a bit of a first for me.

Off the bat, I only really knew (thought I knew) of two ways to help increase page rank:

  1. meta keywords
  2. meta descriptions

Turns out I was wrong.  These days, the keywords don't really help all that much, and the description -- that's just used to display a little snippet of your content in the search results.  Despite their near-uselessness, I still implemented them anyway.  ;)

What my Googling did recommend was this:

  1. descriptive page titles
  2. canonical url tags
  3. submitting a site map

So I updated the post view to display the post title in the page title -- nothing too ground-breaking there.  I'm still up in the air about the canonical url tags since I know there's only one possible url to reach any of the content on this site, although I still may implement that later.

For the site map, I took a look at Google's Webmaster Tools which informed me that I could submit an RSS link ass my site map.  This led me to implement an RSS feed, which I hadn't yet done.  Within an hour of submitting the link, Google had index the whopping two posts that I had created.  Now I just need to migrate the content from my previous blog.

So hopefully all of that will be enough to get me a few more page views from people that aren't my friends or coworkers.

Comments (0)    seo google

google-apps-logo.jpgWant to use Gmail, or a Google Apps account, with your Rails app?  Well, it's gotten even easier than before.

The last time I set up and app to use Gmail's smtp, I had to use a plugin called ActionMailerTLS.  Well, not any more.  Ever since Rails 2.2.2, the tls functionality is built right in, but there's a catch.

While setting up notifications for this app, it took me a few minutes to realize what the problem was.  This app is running on Rails 2.2.2, but the emails were not being sent out.  WTF?  I went back through all of the write-ups I had and seen and finally caught it.

It only works if you're running Ruby version 1.8.7, not 1.8.6.

Fortunately my Dreamhost shared hosting already had 1.8.7 installed, so notifications are working perfectly.  So leave me a comment, and I'll be notified!

Comments (0)    rails gmail action_mailer_tls