Google Base on Rails

Wednesday, December 19th, 2007

I was surprised to come up empty handed when searching for a Google Base Rails plugin. I wanted something that would allow me to easily create a feed into Google Base using their API. I didn’t find anything so I took the quick and short term solution and created my own Google Base xml feed. Its based on RSS 2.0 so its not incredibly difficult but I could have saved a few minutes if it was already written for me so here it is. My Google Base xml generator in Ruby on Rails. Its not complete and only has the fields that I specifically wanted for my products. Your feed will likely contain other fields so check the Google Base docs for more information on customizing it. You’ll notice that I thought Google Base was going to pull my xml feed when I initially wrote this but it turns out I have to use the API and this is just good for generating the xml file which you then have to manually upload to Google Base.

First, I added this to route.rb

map.connect ‘google-base.xml’,
:controller => ‘google’,
:action => ‘base_feed’

Then I created controllers/google_controller.rb

class GoogleController < ApplicationController
def base_feed
@products = Product.find(:all)
end
end

And finally, I create views/google/base_feed.rxml

xml.instruct! :xml, :version=>"1.0"
xml.rss(:version=>"2.0", ‘xmlns:g’ => "http://base.google.com/ns/1.0"){
xml.channel{
xml.title("My Site products")
xml.link("http://brianmcquay.com/google-base.xml")
xml.description("My products are better than yours. You can touch them but I have to charge.")
xml.language(‘en-us’)
for product in @products
xml.item do
xml.title(product.name)
xml.link(product_url(product.id.to_s))
xml.description(product.description)
for photo in product.pictures
xml.tag! "g:image_link", photo_url(photo.id)
end
for category in product.categories
xml.tag! "g:product_type", category.name
end
xml.tag! "g:price", product.retail_price.to_s
xml.tag! "g:id", product.id.to_s
xml.tag! "g:payment_accepted", "Visa"
xml.tag! "g:payment_accepted", "MasterCard"
xml.tag! "g:tax_region", "Hawaii"
end
end
}
}

There are obviously calls to helper methods in the base_feed.rxml file like product_url and photo_url. I use those so I can easily generate pretty seo urls anywhere I need them. You’ll need to replace those with however you create your urls.

This should suffice for at most 31 days when all the products I just added will expire in Google Base. I doubt I’ll bother creating a Google Base Rails plugin unless I see a noticeable increase in traffic and sales so don’t hold your breath.

Rails form select integer drop-down helper method

Friday, October 12th, 2007

I’ve often come across situations while developing Rails apps where I just want a simple integer drop-down box. The default Rails helpers for selects and its options aren’t really geared for something simple like that. I don’t want to have to create a collection of integers and pass them into blocks or any other ridiculous workaround in my views. I want them clean and simple. I created a helper function which allows you to easily create integer drop downs. Just toss this in your application_helper.rb.

def select_with_integer_options (object, column, start, stop, default = nil)
output = “


end

And in your view simply call:

< %= select_with_integer_options (:someobject, :someattribute, 1, 20) %>

And you have yourself an integer dropdown from 1-20. I tried to make the options and select formatting and id/name conventions the same as the rest of the Rails select/option helper methods to keep things consistent.

class_table_inheritance with acts_as_taggable

Friday, August 24th, 2007

If I have:

class Product < ActiveRecord::Base
acts_as_taggable
end

class Subproduct < Product
class_table_inheritance
end

def find_tagged_with!(list)
find_by_sql([
"SELECT #{table_name}.* FROM #{table_name} " +
"WHERE #{table_name}.#{primary_key} in (" +
" SELECT taggable_id FROM taggings, tags " +
" WHERE tags.id=taggings.tag_id " +
" AND taggings.taggable_type = ? " +
" AND tags.name IN (?) " +
" GROUP BY taggable_id " +
" HAVING count(tags.id) >= ? " +
" )",
acts_as_taggable_options[:taggable_type], list, list.to_a.length
])
end

The problem is that:

Subproduct.find_tagged_with! (‘test’)

returns all the Products tagged with ‘test’ rather than all Subproducts. So I tried:

class Product < ActiveRecord::Base
end

class Subproduct < Product
class_table_inheritance
acts_as_taggable
end

but that does the same thing. The problem boils down to the find_tagged_with! method using acts_as_taggable_options[:taggable_type] which is defined as class_name_of_active_record_descendant elsewhere in the acts_as_taggable plugin. The solution is to rewrite find_tagged_with! to :

def find_tagged_with!(list)
find_by_sql([
"SELECT #{table_name}.* FROM #{table_name} " +
"WHERE #{table_name}.#{primary_key} in (" +
" SELECT taggable_id FROM taggings, tags " +
" WHERE tags.id=taggings.tag_id " +
" AND taggings.taggable_type = ? " +
" AND tags.name IN (?) " +
" GROUP BY taggable_id " +
" HAVING count(tags.id) >= ? " +
" )",
self.class.to_s, list, list.to_a.length
])
end

This should work even for classes that aren’t using class table inheritance since it’ll just use the class name.

Refs: http://wiki.rubyonrails.org/rails/pages/ActsAsTaggablePluginHowto

Parsing CSV files sent via form post in Ruby on Rails

Thursday, May 31st, 2007

I’m not sure why it took me a while to figure this out but it did. The Ruby CSV documentation is really weak and really only explains how to read from a file. I googled around and couldn’t find anyone else talking about how to parse a CSV file sent via a form post (StringIO). I didn’t want to save it to a temp file just so I could follow the CSV docs examples. Here’s what I eventually got to work. Its so short and simple I feel silly for not figuring it out sooner.


parsed_file = CSV::Reader.parse(params[:dump][:file])
parsed_file.each do |row|
p row[0]
end

Rails Tag Cloud

Friday, February 9th, 2007

I’ve searched around for a decent tag cloud for Rails and really liked this Rails Tag Cloud solution. Its simple, easy to setup, and I like how it separates the cloud from my other code. I made some comments to the post which improves the cloud by ordering tags better.

Goodbye (notso)fastcgi, Hello Mongrel

Friday, February 9th, 2007

I’ve just started using Mongrel instead of fastcgi for my Rails apps. Its only been a few days but I’m already loving the difference. Its so much faster than before. It wasn’t hard setting up either. Now Apache just proxies requests to Mongrel. I used this article titled Using Mongrel Cluster to set most of it up. I added a few minor customizations for my situation but nothing you can’t figure out from reading the article.