diff --git a/README b/README index 3a3f67c..3f6a968 100644 --- a/README +++ b/README @@ -48,5 +48,11 @@ including a snippet like the following in your mailer method attachment.filename = 'report.csv' end +You can change the separator character and that is as simple as writing this in your controller's +action method: + + csv_builder :sep => ';' + +Actually, all default FasterCSV options can be overriden thanks to the previous statement. Copyright (c) 2008 Econsultancy.com, released under the MIT license diff --git a/init.rb b/init.rb index b703de1..4c0eb25 100644 --- a/init.rb +++ b/init.rb @@ -1,3 +1,3 @@ require 'csv_builder' -ActionView::Template.register_template_handler 'csvbuilder', ActionView::TemplateHandlers::CsvBuilder +ActionView::Template.register_template_handler 'csvbuilder', CsvBuilder::TemplateHandler::Base diff --git a/lib/csv_builder.rb b/lib/csv_builder.rb index e9eebbd..f185ac3 100644 --- a/lib/csv_builder.rb +++ b/lib/csv_builder.rb @@ -1,77 +1,12 @@ +require 'action_controller' +require 'action_view' + 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" - controller.response.headers["Content-Type"] ||= 'text/csv' - controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" - end - - result = FasterCSV.generate 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 +require 'csv_builder/action_controller' +require 'csv_builder/template_handler/base' - end - end -end +class ActionController::Base + include CsvBuilder::ActionController +end \ No newline at end of file diff --git a/lib/csv_builder/action_controller.rb b/lib/csv_builder/action_controller.rb new file mode 100644 index 0000000..4e8ec96 --- /dev/null +++ b/lib/csv_builder/action_controller.rb @@ -0,0 +1,22 @@ +module CsvBuilder + module ActionController + + DEFAULT_CSV_BUILDER_OPTIONS = { :col_sep => ';' } + + def csv_builder(options) + @csv_builder_options = options + end + + private + + def compute_csv_builder_options + options = DEFAULT_CSV_BUILDER_OPTIONS.dup + options.merge!(@csv_builder_options || {}) + options + end + + end + +end + + diff --git a/lib/csv_builder/template_handler/base.rb b/lib/csv_builder/template_handler/base.rb new file mode 100644 index 0000000..a65a166 --- /dev/null +++ b/lib/csv_builder/template_handler/base.rb @@ -0,0 +1,154 @@ +module CsvBuilder + module TemplateHandler + + # 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 Base < ActionView::TemplateHandler + + include ActionView::TemplateHandlers::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" + controller.response.headers["Content-Type"] ||= 'text/csv' + controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" + end + + result = FasterCSV.generate(controller.send(:compute_csv_builder_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 + + +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 + + attr_reader :csv_builder_options + + 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" + controller.response.headers["Content-Type"] ||= 'text/csv' + controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" + end + + result = FasterCSV.generate 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