require 'fastercsv' require 'iconv' module ActionView # :nodoc: module TemplateHandlers # Template handler for csv templates # # Add rows to your CSV file in the template by pushing arrays of columns into csv # # # First row # csv << [ 'cell 1', 'cell 2' ] # # Second row # csv << [ 'another cell value', 'and another' ] # # etc... # # You can set the default filename for that a browser will use for 'save as' by # setting @filename instance variable in your controller's action method # e.g. # # @filename = 'report.csv' # # You can also set the input encoding and output encoding by setting # @input_encoding and @output_encoding instance variables. # These default to 'UTF-8' and 'LATIN1' respectively. e.g. # # @output_encoding = 'UTF-8' class CsvBuilder < TemplateHandler include Compilable def self.line_offset 9 end def compile(template) <<-EOV begin unless defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) @filename ||= "\#{controller.action_name}.csv" if controller.request.env['HTTP_USER_AGENT'] =~ /msie/i controller.response.headers['Pragma'] = 'public' controller.response.headers["Content-type"] = "text/plain" controller.response.headers['Cache-Control'] = 'no-cache, must-revalidate, post-check=0, pre-check=0' controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" controller.response.headers['Expires'] = "0" else controller.response.headers["Content-Type"] ||= 'text/csv' controller.response.headers["Content-Disposition"] = "attachment; filename=\#{@filename}" controller.response.headers["Content-Transfer-Encoding"] = "binary" end end result = FasterCSV.generate(@csv_options || {}) do |csv| #{template.source} end # Transliterate into the required encoding if necessary # TODO: make defaults configurable @input_encoding ||= 'UTF-8' @output_encoding ||= 'LATIN1' if @input_encoding == @output_encoding result else # TODO: do some checking to make sure iconv works correctly in # current environment. See ActiveSupport::Inflector#transliterate # definition for details # # Not using the more standard //IGNORE//TRANLIST because it raises # Iconv::IllegalSequence for some inputs c = Iconv.new("\#{@output_encoding}//TRANSLIT//IGNORE", @input_encoding) c.iconv(result) end rescue Exception => e RAILS_DEFAULT_LOGGER.warn("Exception \#{e} \#{e.message} with class \#{e.class.name} thrown when rendering CSV") raise e end EOV end end end end