Monday, December 22, 2008

A Threesome in Chicago

Though the title might suggest something really explicit but you could read on as there is nothing that might warrant reader's 
discretion. 

It was in mid July that I was asked to travel to Chicago to visit a prospective client by my employer. I reached there fully geared with 7 pair of formal shirt and trousers. These lasted for a good 2 weeks (10 week days) and I had to take a visit to the washing room to clean the clothes for the next couple of weeks. It was then that I realize that I had to shelve out $2 for washing and drying of clothes and a $8 for a detergent pack (which is a one time investment for 4~5 months considering I'm a single bachelor). 

That day I could not rest easily considering the fact that I had spent a total of Rs 100 /- just for washing of clothes and had to iron it myself after that.

It was then that I realized it was time to act and act in haste. It was then that the threesome started!

My parents had packed in 3 jeans and about 5 t-shirt. I then started wearing jeans to office considering the fact that they need to be washed less frequently than their formal counterparts.

I started wearing jeans to office and a good two weeks passed by. I was trying out all the possible permutations and combinations of clothes that I could do with those 3 jeans and 5 t-shirt

2 weeks passed by and I did not find any need to go back to the dreaded money sucking washing room. A month passed by and still there was no need to visit the laundry room and like this days turned to weeks, weeks turned to month and I was still good wearing those jeans and shirt. I myself was at awe with the ruggedness of the jeans and the shirt. At last I ended up not washing my clothes at all in the laundry room.

Points to be noted:
I did wash the necessary clothes that includes my drawers and socks each day after taking bath.
The fact that I could survive in 3 jeans and those 5~6 t-shirt is due to the fact that Chicago as a city was very clean. I only wore the jeans while going to office. Once in home I would change to shorts. So that helped maintaining the newness of the jeans.

That wasn't the threesome I had in mind when I set out for my first visit to the States. But at the end of it I am glad to have done that!


Thursday, December 13, 2007

Calculate GeoCode using google-geocode

To calculate the latitude and the longitude based on the address provided by the user we will have to use a couple of libraries namely
  • 'rubygems'
  • 'google_geocode'
To install it use the following command in your console
gem install google-geocode
documentation

Now when ever the user tries to save the address we can have a callback associated with it..

So in the users model we would define
before_save :calculateCoordinates

def calculateCoordinates
  • catch(:done) do
    • fullAddress = address+','+city+','+state+','+zip
    • loc = Geocode.lookup(fullAddress)
    • if loc
      • puts "calculated geocode"
      • self.lat = loc[:latitude]
      • self.lon = loc[:longitude]
    • else
      • puts "Unable to calculate the geocode"
    • end
  • end
end

The hash that is returned is used to populate the user's model lat and lon methods before saving to the database.
If the code is unable to calculate the geo-code we can take necessary action in the else part.

The catch here is that I have refactored the code and use a Geocode class to do my ugly geo-code calculation. The file that contains my geocode class is /app/model/geocode.rb

  • class Geocode < ActiveRecord::Base
    • require 'rubygems'
    • require 'google_geocode'

    • def self.lookup(location)
      • g = GoogleGeocode.new GOOGLE_GEO_API_KEY
      • begin
        • location = g.locate location
        • {:latitude => location.latitude, :longitude => location.longitude}
      • rescue GoogleGeocode::AddressError
        • return false
      • end
    • end
  • end

The lookup class method would return a hash containing the latitude and longitude when it is able to calculate the geo code or false when it encounters a problem while calculate the geo code for the bad address.

The
GOOGLE_GEO_API_KEY is obtained by registering here

Tuesday, August 21, 2007

How to customize attachment_fu file names

For uploading files, it's pretty hard to beat attachment_fu. But it can be overkill for smaller projects.

One issue is that attachment_fu uses id partioning. This is a great way to overcome native file system limitations when you have more than 32,000 attachments. By segmenting files into different directories, you can have millions of attachments, if necessary. Empahsis on "if necessary". It usually isn't.

Also, attachment_fu preserves original filenames. While this make sense for many projects, sometimes you need to have control over the naming of attachments.

Since a lot of people use Mike Clark's excellent File Upload Fu tutorial, let's use that as our starting point for customizing file names.

If we complete the tutorial, here's how attachment_fu will store our first image upload:

public/mugshots/0000/0001/chunkybacon.png
public/mugshots/0000/0001/chunkybacon_thumb.png

Hmmm, not bad. But I'd like to customize things:

* Images should be stored in public/images/
* Thumbnails should be organized by size
* ID partioning (0000/0001/) should be disabled
* Images should be renamed with the Mugshot id

So let's open up our Mugshot model and tweak it a bit.

  • class Mugshot <>
  • has_attachment :content_type => :image,
        • :storage => :file_system,
        • :max_size => 500.kilobytes,
        • :resize_to => '320x200>',
        • :thumbnails => { :thumb => '100x100>' },
        • :path_prefix => 'public/images/mugshots'
        • validates_as_attachment # To validate the size of the file being uploaded

  • def full_filename(thumbnail = nil)
    • file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix]
    • case self.thumbnail
      • when "thumb"
        • File.join(RAILS_ROOT, file_system_path, 'thumb', thumbnail_name_for(thumbnail, self.parent_id))
      • else
        • File.join(RAILS_ROOT, file_system_path, 'fullsize', thumbnail_name_for(thumbnail, self.id))
    • end
  • end

  • def thumbnail_name_for(thumbnail = nil, asset = nil)
    • extension = filename.scan(/\.\w+$/)
    • return "#{asset}#{extension}"
  • end
  • end

Now, when we upload an image, it will be stored like so:

public/images/mugshots/fullsize/2.png
public/images/mugshots/thumb/2.png

How does this work?

Well, first, we customize the :path_prefix value in has_attachment to set the base location of our files.

Second, we override the full_filename method to force attachment_fu to save each thumbnail type into its own directory. This way, all large thumbnails are stored in images/mugshots/fullsize and all small thumbnails are stored in images/mugshots/thumb. (By default, attachment_fu stores all thumbnail sizes for an object in a single directory.)

Lastly, we override the thumbnail_name_for method to customize the filename to our liking... in this case, the file name will consist of the parent mugshot id, plus the original file's file extension.

That's all we need to do... now our files are stored exactly where we want them!

(Thanks to AirBlade Software for showing the way.)

Important Note :---

In order to make use of the functionality provided by attachment_fu you need to create an ActiveRecord model with at least the following attributes:

  • content_type: what sort of content you are storing. This is used by web browsers to know how to present this information to users (open an external application, show embedded using a plugin, etc).
  • filename: a pointer to the image location
  • size: the size in bytes of the attachment

Wednesday, August 8, 2007

Sort a table with Rails (ajax way!)

Sorting of tables has been a trouble to many beginners as me. But I did stumble upon this which helped me kick start the issue ( Well it almost did the job for me!).

We are going to use a couple of helpers to do almost all of the work for us. You may use me why helpers? Well it is to reinforce the DRYness of Rails. We can use the methods inside the over multiple views.

So if you want the helpers to be used across all the controllers the best place to put them is under /app/helpers/application_helper.rb else you could put it in your controller specific helper.
Here goes the code and the explanation of the helpers that we will be using.

The first helper is nothing but necessary. It is called sort_td_class_helper, and his only goal is to add a class="sortup" if the column is currently the one used to sort the table, or a class="sortdown" if it is used to sort in reverse order. The only utility is to allow, with CSS, to indicates to the user which column is currently used as a sorting field.

The code is nothing interesting :

def sort_td_class_helper(param)
result = 'class="sortup"' if params[:sort] == param
result = 'class="sortdown"' if params[:sort] == param + " DESC"
return result
end

Then, we have a second helper, called sort_link_helper.
def sort_link_helper(text, param)
key = param
key += " DESC" if params[:sort] == param
options = {
:url => {:overwrite_params => {:sort => key, :page => nil}},
:update => 'table',
:before => "Element.show('spinner')",
:success => "Element.hide('spinner')"
}
html_options = {
:title => "Sort by this field",
:href => url_for(:action => 'list', :params => @params.merge({:sort => key, :page => nil}))
}
link_to_remote(text, options, html_options)
end

This helper takes two arguments :

  • text, which is just the text to be displayed as the column header and sort link
  • param, which is the name of the request parameter associated with the column.

The first two lines define a new variable, called key, which gets the value of the param argument, ie the sort key. The string DESC is concatenated to it if param is already the sorting key. This is used to implement sorting in both ascending and descending order. If the user select a sort link, it will sort by ascending order ; if it selects the same link again, it will sort by descending order, and so on.

The rest of the helper is to define the options for the final call to the link_to_remote function.

  • the options hash, used for the javascript Ajax link, contains the url to be called to generate the new HTML, the id of the element to update, and two before and success actions to show/hide the spinner image.
  • the html_options hash, used for the HTML link. It generates the content of the href attribute with the url_for Rails function.

Here is what the sort_link_helper returns for a call with the strings "Title" and "title" as text and param arguments :

<a href="/item/list?sort=qty" onclick="Element.show('spinner'); new Ajax.Updater('table', '/item/list?sort=qty', {asynchronous:true, evalScripts:true, onSuccess:function(request){Element.hide('spinner')}}); return false;" title="Sort by this field">Quantity</a>

The view would use the helper function like

<td <%= sort_td_class_helper "name" %>>
<%= sort_link_helper "Name", "name" %>
</td>
The controller would use the params[:sort] for obtaining the field to sort with and the manner in which it has to sort, i.e desc or asc.

Started Bloggin Finally!

Well, after getting inspired from a ton of blogs, websites and forums for a web application I developed with no knowledge of Ruby or Rails!, I decided it was finally time to go one step further, Write a blog. No body panics, I am not going to post the substandard code that I produce but the best code that I think is available for the topic from the net. Well you might find the spoilers in the form of my code in between so beware!