Your browser (Internet Explorer 6) is out of date. It has known security flaws and may not display all features of this and other websites. Learn how to update your browser.
X

Rails Solr Sunspot Geospatial Searches

Using Gem “sunspot_solr” 1.3.0

I have “lat” and “lng” attributes associated with my model. I defined searchable as:

model.rb

searchable do
  float :lat, :as => 'lat'
  float :lng, :as => 'lng'
end

controller.rb

def search 
  search = Model.search do
     # 25 miles hard coded for now                                                                       
     search_radius = 25
     adjust_solr_params do |params|
       params[:spatial]="{!radius=#{search_radius} sort=true}lat:#{current_user.lat},lng:#{current_user.lng}"
     end
  end
  search.results
end

ActiveRecord callbacks and destructive methods

Recently I added search functionality to my application. I implemented a search controller and model. An issue I faced was manipulating the search keywords entered by the user so the search query worked properly. I ended up doing the following:

search.rb

class Search < ActiveRecord::Base
  belongs_to :user
  attr_accessible :keywords, :category, :user
 
  validates_presence_of :keywords
 
 # DON'T CHAIN DESTRUCTIVE METHODS!
  before_save Proc.new{|search| search.keywords.strip!.downcase!}
 
  #....
end

I got some really weird behavior when the before_save callback fired and later learned not to chain destructive methods!
See this for more information.

So the solution was:

before_save Proc.new{|search| search.keywords = search.keywords.strip.downcase}

Alternatively I could implement a method to trim and downcase the keywords also:

before_save :clean_keywords
 
def clean_keywords
   keywords = keywords.strip.downcase
end

Ruby on Rails 3 RESTful and default routes

You have probably created some action in your controller class and can’t access it right? Well you need to have some default routing rules set up in order for rails to understand the url and route it to the appropriate action and controller. This will happen alot with AJAX calls where you simply want to go to an action with no ID. Or maybe you want to go to an action but supply a bunch of parameters without an ID.

class HomeController < ApplicationController
 
  def dothisaction
   # do some stuff
  end
end

Assuming you have defined the home controller as a RESTful controller by adding to your routes.rb

resources :home

In your webbrowser:

localhost/home/dothisaction

You get:

Started GET "/home/foo"
 
AbstractController::ActionNotFound (The action 'show' could not be found for HomeController):

What is happening? Rails is matching the RESTful rule:

home GET    /home/:id(.:format)                    {:controller=>"home", :action=>"show"}

to this URL request. We obviously don’t want that.

So if you want this controller to be RESTful, you must manually add each non RESTful route to your routes.rb like so:

match 'home/dothisaction' => "home#dothisaction"  #Must be before 'resources :home' line to take precedence 
resources :home

Or you can remove the “resources :home” line in the routes.rb and you can add (or uncomment)

match ':controller(/:action(/:id(.:format)))'

This line will automap any action method you have defined in a controller to a url. The parenthesis indicate that the argument is optional. So the url:

localhost/home/dothisaction/5.pdf
 
will map the parameters:
 
:action => "dothisaction"
:id => 5
:format => "pdf"

Leaving off the :id and :format parameters will work just as well.

Be aware that routing precedence is defined by the order the rules appear in routes.rb.

Example using ruby blocks, lambda and Proc

What really distinguishes ruby from other languages is the syntatic sugar of blocks. For example, blocks really come in handy when iterating thru a collection.

The example:

(1..10).collect{|i| i*i}
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

But how does the collect method work?

Let’s define an array containing all the prime number up to 100.
Note you will need to be running ruby 1.9.2 to access the Prime class:

require "prime"
prime_numbers = []
Prime.each(100) { |prime| prime_numbers << prime }
 => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]

Now let’s define our own “change!” method which takes a block, changing the value of each element in our prime_number array object according to the passed block. Note the “bang” or “!” is appended to a method when the behavior is destructive or object altering.

Define our custom method:

class << prime_numbers
  def change!(&b)
    puts "Calling change!"
    self.each_index { |i| self[i] = b.call(self[i]) }
  end
end
 
prime_numbers.change!{|p| p * p}
=> [4, 9, 25, 49, 121, 169, 289, 361, 529, 841, 961, 1369, 1681, 1849, 2209, 2809, 3481, 3721, 4489, 5041, 5329, 6241, 6889, 7921, 9409]

If we want to explicitly create a lambda or proc and pass that along instead:

square = lambda {|x| x * x}
prime_numbers.change!(&square)
=> [4, 9, 25, 49, 121, 169, 289, 361, 529, 841, 961, 1369, 1681, 1849, 2209, 2809, 3481, 3721, 4489, 5041, 5329, 6241, 6889, 7921, 9409]

The “&” operator turns the lambda or Proc object into a block. When “&” is used in the context of a method argument, it creates a Proc object from passed block so the method can now execute the “call” method on the in the block. The “call” method is a method in the “Proc” class.

Ruby Metaprogramming Basics

Understand Ruby Metaprogramming in this instructional video by learning how classes and object interact and what the “self” object is.

Dynamically update URL using jQuery

I am using jQuery FullCalendar for a project and the view can be either a day, a month, or a year. The user can move around the calendar by clicking back and forward arrows, and none of these clicks will cause a page refresh.

However the “start” and “end” dates change depending on the current calendar view. I created a button element on the page to print the current calendar view, which in my case will be all the “events” that fall within the start and end date range.

 <span id="print-schedule">Print</span>

The ‘print-schedule’ element has no click handler so I add one with some jQuery code:

 $('span#print-schedule').button().click(function() {                                        
      $.get("/events/printview",                                                                               
            { startdate: $.fullCalendar.formatDate(view.start, "dd-MM-yyyy"),                            
               enddate: $.fullCalendar.formatDate(view.end, "dd-MM-yyyy")                                 
             });
      return false;
 });

But notice the problem here: $.get is an AJAX call to the action “printview” in my events controller. Ajax will not return anything to the browser visible to the user. For this to work we want a full page redirect to occur. In order to do this, I had to rewrite the jquery method as follows:

  $('span#print-schedule').button().click(function() { 
                url = "/events/printview?startdate=" + $.fullCalendar.formatDate(view.start, "dd-MM-yyyy") + "&enddate=" +
                        $.fullCalendar.formatDate(view.end, "dd-MM-yyyy");
                        window.location.href = url;                                                                     
     });

Now the page redirects to the view page “printview” using the dynamically created url.

And the rails action code:

class EventsController < ApplicationController 
...
  def printview                                                                                               
      startdate = Date.parse(params[:startdate])                                                                    
      enddate = Date.parse(params[:enddate]) 
      @events = Event.find(:all, :conditions => ["DATE(starttime) >= ? and DATE(endtime) <= ? ", startdate, enddate ] )           
   end 
end

Here’s a screenshot of the calendar with the print button at the top

calendar
Jquery Full Calendar Screenshot

How to install RVM with ruby on rails

Learn how to install and configure RVM with both rails 3 and rails 2 apps.

RVM allows users to deploy each project with its own self-contained and dedicated environment–from the specific version of
ruby all the way down to the precise set of required gems to run the
application.

Ruby to Javascript Dates

Recently I was using the JQuery UI datepicker widget and needed to restrict dates to a range in the popup calendar selector. In order to do so I had to pass the Ruby Date to javascript. My goal is usually to write as little javascript/jquery code as possible, i.e. to do all the necessary conversions on the ruby/rails server side.

Once I got the format correct it was surprisingly easily.

Ruby code:

@min_date_str = date1.strftime("%B %d, %Y 00:00:00")
@max_date_str = date2.strftime("%B %d, %Y 00:00:00")

JQuery / Javascript Code:

jQuery(document).ready(function($) {
$("#start_date").datepicker({
        minDate: new Date("<%= @min_date_str %>"), 
        maxDate: new Date("<%= @max_date_str %>")
});
$("#end_date").datepicker({
        minDate: new Date("<%= @min_date_str %>"), 
        maxDate: new Date("<%= @max_date_str %>")
});

Note: Javascript Date objects include a Time component (unlike the Ruby Date object). Ruby would need a DateTime object to include the time component.