Author Archive

Faking a Will Paginate Collection on an Active Resource model

Tuesday, October 28th, 2008 by Scott Davis

To follow up on my other blog post about Paginating an ActiveResource model and to_xml I figured I should include the client-side code so you can see how to actually use will paginate helpers out of the box to paginate an ActiveResource request.

This is example code from my model:

  def self.paginate(*args, &block)
    options = args.pop
    page, per_page, total_entries = wp_parse_options(options)
 
    WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
      count_options = options.except :page, :per_page, :total_entries, :finder
      find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page) 
 
      args << find_options
      # @options_from_last_find = nil
      find_results = self.find(*args, &block)
      pager.replace find_results
      # magic counting for user convenience:
      pager.total_entries = find_results.total_entries unless find_results.blank?
    end
  end

Example View code:

<%= render :partial => 'list' %>
<%= will_paginate(@model)%>

Facebox With Prototype

Tuesday, October 14th, 2008 by Scott Davis

After many failed attempts of trying to find a modal window that worked like facebox and used prototype, I gave up and started to write one from scratch. In the process I found the first port made by Phil Burrows. It wasn’t perfect but it was enough for me to build off of so with a few tweaks I was able to allow window resizing and let the box center itself. You can view the changes on my github.

Screenshot:

My Version of Facebox in our timetracker application

My Version of Facebox in our timetracker application

Making WillPaginate and Rails to_xml play nice with ActiveResource

Friday, October 10th, 2008 by Scott Davis

We are currently working on a project that involves Flex and active resource + will_paginate and we needed to be able to paginate the xml transactions easily. Unfortunately, will_paginate and to_xml don’t play nicely when it comes to adding the current_page, total_pages, and page attributes to the xml. After many failed attempts I went looking around github and found in a few forks of will paginate that some people had solved this problem but, I didn’t want to install another version of the gem to risk breaking other apps on the server so I did it the rails way!

I started by creating a module that opens up the will_paginate collection class and includes ActiveResource and alias method chain the to_xml method to include these values. Example code below.

#enviroment.rb
...
require 'to_xml_extensions'
#lib/to_xml_extensions.rb
module WillPaginateHelpers
  include ActiveSupport::CoreExtensions::Array::Conversions
  def to_xml_with_collection_type(options = {})
        serializeable_collection.to_xml_without_collection_type(options) do |xml|
          xml.tag!(:current_page, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[current_page.class.name]}, current_page)
          xml.tag!(:per_page, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[per_page.class.name]}, per_page)
          xml.tag!(:total_entries, {:type => ActiveSupport::CoreExtensions::Hash::Conversions::XML_TYPE_NAMES[total_entries.class.name]}, total_entries)
        end.sub(%{type="array"}, %{type="collection"})
      end
      alias_method_chain :to_xml, :collection_type
 
      def serializeable_collection #:nodoc:
        # Ugly hack because to_xml will not yield the XML Builder object when empty?
        empty? ? returning(self.clone) { |c| c.instance_eval {|i| def empty?; false; end } } : self
      end
end
 
WillPaginate::Collection.send(:include, WillPaginateHelpers)

This now gives me the proper xml when I call to_xml

<?xml version="1.0" encoding="UTF-8"?>
<time-cards type="collection">
  <current_page type="integer">1</current_page>
  <per_page type="integer">25</per_page>
  <total_entries type="integer">108</total_entries>
  <time_card>
    <approved type="boolean">false</approved>
    <billable type="boolean">false</billable>
    <created_at type="datetime">2008-10-10T14:04:13-04:00</created_at>
    <date type="datetime">2008-10-10T14:04:13-04:00</date>
    <has_been_billed type="boolean">false</has_been_billed>
    <has_been_paid type="boolean">true</has_been_paid>
    <hours type="float">2.0</hours>
    <id type="integer">98</id>
    <is_overtime type="boolean">false</is_overtime>
    <task_id type="integer">6</task_id>
    <updated_at type="datetime">2008-10-10T14:04:13-04:00</updated_at>
    <user_id type="integer">1</user_id>
  </time_card>
  ...
</timecards>

Advanced Model Based searches in rails

Monday, July 21st, 2008 by Scott Davis

After watching a railscast episode on advanced searching I thought I would give it a try. So I came up with a slightly modified version that would handle my search.

Model

class ExportSearch
 
  def timecards
    find_cards
  end
 
  def users(u)
    @u = u
  end
 
  def projects(p)
    @p = p
  end
 
  def tasks(t)
    @t = t
  end
 
  def dates(date1, date2)
    @d1 = date1
    @d2 = date2
  end
 
  def clients(c)
    @c = c
  end
 
private
 
  def find_cards
    TimeCard.find(:all, :conditions => conditions, :include => {:task => :project}, :order => :date)
  end
 
  def projects_conditions
    ["tasks.project_id IN (?)", @p] unless @p.blank?
  end
 
  def client_conditions
    ["projects.client_id IN (?)", @c] unless @c.blank?
  end
 
  def date_conditions
    ["date BETWEEN ? AND ?", @d1, @d2] unless (@d1.blank? || @d2.blank?)
  end
 
  def task_conditions
    ["task_id IN (?)", @t] unless @t.blank?
  end
 
  def users_conditions
    ["user_id IN (?)", @u] unless @u.blank?
  end
 
  def conditions
    [conditions_clauses.join(' AND '), *conditions_options]
  end
 
  def conditions_clauses
    conditions_parts.map { |condition| condition.first }
  end
 
  def conditions_options
    conditions_parts.map { |condition| condition[1..-1] }.flatten
  end
 
  def conditions_parts
    private_methods(false).grep(/_conditions$/).map { |m| send(m) }.compact
  end
end

Controller

    search = ExportSearch.new
    search.users(params[:export][:users].join(',')) unless params[:export][:users].blank?
    search.tasks(params[:export][:tasks].join(',')) unless params[:export][:tasks].blank?
    search.projects(params[:export][:projects].join(',')) unless params[:export][:projects].blank?
    search.dates(start_date, end_date)
 
    @time_cards = search.timecards

map.resources and custom nested routes

Thursday, June 5th, 2008 by Scott Davis

I encountered an error in rails trying to create a nested route in rails 2.x

map.import_time_cards 'users/:user_id/time_cards/import',
:controller => 'time_cards',
:action => 'import'

Wasn’t setting up a route for users because this route was being setup automatically and overwritten by:

map.resources :users,
:has_many => [:notes, :addresses, :expenses, :time_cards] ,
:collection => [:login, :logout, :disable, :enable]

So after digging around on the rails api I discovered that map.resources takes a block so my solution to this problem was :

map.resources(:users,
:has_many => [:notes, :addresses, :expenses] ,
:collection => [:login, :logout, :disable, :enable]) do |user|
user.resources :time_cards, :collection => [:import]
end

By using a block this tells rails to include route to ‘users/1/time_cards/import’ instead of appending import as the id for the show route.