ActionView rcsv template handler

Posted by Trey Thu, 31 Jul 2008 15:07:00 GMT

The way the rjs and builder template handlers work in rails is nice. I do a lot of work with csv for generating reports, and having all the csv generation code in the controller doesn't seem to fit the MVC paradigm. I dug in to how ActionView template handlers work in Rails 2.1 and came up with the following solution.

The code uses the FasterCSV gem so you'll need that installed. One gotcha i noticed is that if format.csv is before format.html in the respond_to block the request gets sent incorrectly.

Code


RAILS_ROOT/config/initializers/template_handlers.rb

require 'rcsv_template_handler'
ActionView::Template.register_template_handler :rcsv, ActionView::TemplateHandlers::RCSV

RAILS_ROOT/lib/rcsv_template_handler.rb

require 'fastercsv'

module ActionView
  module TemplateHandlers
    class RCSV < TemplateHandler
      include Compilable

      def self.line_offset
        2
      end

      def compile(template)
        content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller")
        "#{content_type_handler}.content_type ||= Mime::CSV
" +
        "if controller.request.env['HTTP_USER_AGENT'] =~ /msie/i
" +
        "  controller.headers['Pragma'] = 'public'
" +
        "  #{content_type_handler}.content_type ||= Mime::TEXT
" +
        "  controller.headers['Cache-Control'] = 'no-cache, must-revalidate, post-check=0, pre-check=0'
" +
        "  controller.headers['Expires'] = '0'
" +
        "else
" +
        "  #{content_type_handler.content_type ||= Mime::CSV
" +
        "end
" +
        "csv_doc = ::FasterCSV.new(:row_sep = "
") do |csv|
#{template.source}
end"
      end

      def cache_fragment(block, name = {}, options = nil)
        @view.fragment_for(block, name, options) do
          eval('csv_doc', block.binding)
        end
      end
    end
  end
end

Usage


RAILS_ROOT/app/controllers/name_controller.rb

class NameController < ApplicationController
  def action
    @data = Data.find :all
    respond_to do |format|
      format.html # Render action.html.erb
      format.csv  # Render action.csv.rcsv
    end
  end
end

RAILS_ROOT/app/view/name/action.csv.rcsv

@data.each do |datum|
  csv << datum
end

Leave a comment

Comments