Tuesday, January 15, 2008

Dynamic Calendar Helper - changing months

Lately I’ve started playing with Ruby on Rails and boy, am I excited. When I started, I’ve installed rails with apt-get on Ubuntu, which installed rails 1.2.5 for me. Only later (but not before I finished few sample apps) I learned that there is something called RoR 2 :) Nevertheless, I already convereted one of my sample apps to rails 2.0.2 and I’m getting hang of it.

In this app, I used DynamicCalendarHelper plugin to display data in a calendar. Although it does exactly what I needed, I found that configuration of next/previous month buttons is not a trivial task, so I decided to share my implementation (maybe someone can tell me if there is an easier approach).

My solution was to change routes.rb and add the following line for my “entries” controller:

map.connect 'entries/:year/:month',
  :controller => 'entries',
  :action => 'index'

Now when I enter url like ‘entries/2008/1’, I expect the calendar for January 2008 to be shown. I also expect that url ‘entries/1’ still has same effect (displaying entry with id with ‘show’ action). To implement this functionality, I had to add the following code to the controller (in index mehtod):

y = params.include?(:year) ? params[:year].to_i :
  Date.today.year
m = params.include?(:month) ? params[:month].to_i :
  Date.today.month
@display_date = Date.new(y, m, 1)

It creates a Date object whose year and month are either read from the parameters or (if they are not set) initialized from current year and month. In any case we use first day in month. Finally we can add a method in entries helper class which will return options hash for the calendar we want to display.

def calendar_options                                        
  prevd = @display_date - 1
  nextd = @display_date + 31
  {       
    :year => @display_date.year,
    :month => @display_date.month,
    :previous_month_text =>
      link_to("<< Prev",
        :year => prevd.year, :month = > prevd.month),                                               
    :next_month_text =>                                     
      link_to("Next >>",
        :year => nextd.year, :month = > nextd.month),
  }                                                         
end

Now it is easy to crate a calendar in the view itself:

<%=
calendar(calendar_options) do |d|
  # block
end
%>