Abhi On Rails

Ruby On Rails - What I learned recently

Write a named scope to fetch the data sorted by distance using Geokit

Recently I had to work on a feature "As a User ISBAT see the Business sorted by distance". We are using Geokit gem for manipulating the location. And we have the models 'Business' has many 'Location's.

I had to fetch the businesses in the order of the distance of the locations from the origin. The origin is the point which we will pass to the method. Since this method should be part of a chained named scope, I had to implement this as a named scope.

After done some searching I found a useful link from pivotallabs. At the end there is one comment from Keith:

named_scope :within, lambda{|o, d|
 origin = o
 distance_sql = self.distance_sql(origin)
 within = d
 {:select => "*, #{distance_sql} as distance",
  :conditions => "#{distance_sql} <= #{within}", 
  :order => 'distance asc'}}

This code helped me to create a named scope which will fetch the businesses in the order of the distance.

The test also passed for this named scope. But in the controller, where I am using this named scope along with other named scopes i,e, chained named scope, the named scope was not working as expected. It was throwing MySql Error with message "distance is undefined".

After done with some searching I found the link which explains that in chain named_scopes only conditions will get added, and selects will get ignored.

Then I tried by changing the 'distance' variable with the distance_sql as the order value. And it worked:

named_scope :by_location, lambda{|origin, within|
 distance_sql = Location.distance_sql(origin)
 {:select => "*",
  :conditions => "#{distance_sql} <= #{within}", 
  :order => '#{distance_sql}', :joins => :locations }}


acts_as_taggable_on gem have issue with after_save callback

Today I had spent a lot of time fixing an after_save callback issue related with acts_as_taggable_on gem. We're associating keyword tag to a model using the following command:

acts_as_taggable_on :keywords

And we've a after_save call back in that model which uses the updated keywords. But the issue was in the callback, if you reload the object you will lose the new keywords associated with it, because the keywords are not yet saved.

Then I Googled about this issue and found the following blog which helped me to solve the issue: http://blog.romanandreg.com/post/176944361/activerecord-callbacks-acts-as-taggable-gotcha I tried with the update_tags mentioned in the blog, but with the latest acts_as_taggable_on gem this fix is not working. Then I gone through the code of acts_as_taggable_on gem, and in core.rb I found the following line:

after_save :save_tags

Then I tried calling save_tags from my callback method. Thats it! It got fixed!!


Updated time_diff gem

Now with time_diff gem you can calculate the time differnce between two Time in the formatted form by passing a third parameter to the Time.diff() method.

If you are not passing any parameter then by default it will take the third parameter value as'%y, %M, %w, %d and %h:%m:%s' and it will return the formatted time difference in the form: '1 year, 2 months, 3 weeks, 4 days and 12:05:52'.

For hours, minutes and seconds have two formats, with text and without text. The parameter is with '%h', '%m', '%s' are the formats which won't add text with the value, ie., it return 3 for '3 hours'. The parameters with he formats '%H', '%N', '%S' will return the values with text, ie., '2 hours', '30 minutes'.

If you are using the default format ie., formats with comma seperated, then it will remove the intervals(year, month, ..) which are zero.

If you need only day and hours in the time difference you can specify that by passing parameter in the format of '%d %h'. This will calculate the formatted time diff text only for days and hours.