├── VERSION ├── .gitignore ├── init.rb ├── lib ├── csv_builder.rb └── csv_builder │ ├── action_controller.rb │ └── template_handler │ └── base.rb ├── Rakefile ├── MIT-LICENSE ├── did_csv_builder.gemspec └── README /VERSION: -------------------------------------------------------------------------------- 1 | 0.1.4.beta2 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | rdoc 2 | pkg 3 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require 'csv_builder' 2 | 3 | ActionView::Template.register_template_handler 'csvbuilder', CsvBuilder::TemplateHandler::Base 4 | -------------------------------------------------------------------------------- /lib/csv_builder.rb: -------------------------------------------------------------------------------- 1 | require 'action_controller' 2 | require 'action_view' 3 | 4 | require 'fastercsv' 5 | require 'iconv' 6 | 7 | require 'csv_builder/action_controller' 8 | require 'csv_builder/template_handler/base' 9 | 10 | class ActionController::Base 11 | include CsvBuilder::ActionController 12 | end 13 | 14 | ActionView::Template.register_template_handler 'csvbuilder', CsvBuilder::TemplateHandler::Base 15 | -------------------------------------------------------------------------------- /lib/csv_builder/action_controller.rb: -------------------------------------------------------------------------------- 1 | module CsvBuilder 2 | module ActionController 3 | 4 | DEFAULT_CSV_BUILDER_OPTIONS = { :col_sep => ';' } 5 | 6 | def csv_builder(options) 7 | @csv_builder_options = options 8 | end 9 | 10 | private 11 | 12 | def compute_csv_builder_options 13 | options = DEFAULT_CSV_BUILDER_OPTIONS.dup 14 | options.merge!(@csv_builder_options || {}) 15 | options 16 | end 17 | 18 | end 19 | 20 | end 21 | 22 | 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake' 2 | require 'rake/rdoctask' 3 | 4 | desc 'Generate documentation for the csv_builder plugin.' 5 | Rake::RDocTask.new(:rdoc) do |rdoc| 6 | rdoc.rdoc_dir = 'rdoc' 7 | rdoc.title = 'CSV Builder' 8 | rdoc.options << '--line-numbers' << '--inline-source' 9 | rdoc.rdoc_files.include('README') 10 | rdoc.rdoc_files.include('lib/**/*.rb') 11 | end 12 | 13 | 14 | begin 15 | require 'jeweler' 16 | Jeweler::Tasks.new do |gem| 17 | gem.name = "did_csv_builder" 18 | gem.summary = %Q{CSV template Rails plugin} 19 | gem.description = %Q{CSV template Rails plugin} 20 | gem.email = "didier@nocoffee.fr" 21 | gem.homepage = "https://github.com/did/csv_builder" 22 | gem.authors = ["Tom Stuart"] 23 | gem.files = Dir.glob('lib/**/*.rb') 24 | end 25 | Jeweler::GemcutterTasks.new 26 | rescue LoadError 27 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 28 | end -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Econsultancy.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /did_csv_builder.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{did_csv_builder} 8 | s.version = "0.1.4.beta2" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= 11 | s.authors = [%q{Tom Stuart}] 12 | s.date = %q{2011-07-29} 13 | s.description = %q{CSV template Rails plugin} 14 | s.email = %q{didier@nocoffee.fr} 15 | s.extra_rdoc_files = [ 16 | "README" 17 | ] 18 | s.files = [ 19 | "lib/csv_builder.rb", 20 | "lib/csv_builder/action_controller.rb", 21 | "lib/csv_builder/template_handler/base.rb" 22 | ] 23 | s.homepage = %q{https://github.com/did/csv_builder} 24 | s.rdoc_options = [%q{--charset=UTF-8}] 25 | s.require_paths = [%q{lib}] 26 | s.rubygems_version = %q{1.8.5} 27 | s.summary = %q{CSV template Rails plugin} 28 | 29 | if s.respond_to? :specification_version then 30 | s.specification_version = 3 31 | 32 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 33 | else 34 | end 35 | else 36 | end 37 | end 38 | 39 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = CSV Builder 2 | 3 | The CSV Builder Rails plugin provides a simple templating system for serving 4 | dynamically generated CSV files from your application. 5 | 6 | 7 | == Requirements 8 | 9 | CSV Builder requires Rails v2.1. 10 | 11 | It also depends upon the FasterCSV gem http://fastercsv.rubyforge.org, 12 | which you can install with 13 | 14 | $ sudo gem install fastercsv 15 | 16 | 17 | == Example 18 | 19 | CSV template files are suffixed with '.csv.csvbuilder', for example 20 | 'index.csv.csvbuilder' 21 | 22 | Add rows to your CSV file in the template by pushing arrays of columns into the 23 | csv object. 24 | 25 | # First row 26 | csv << [ 'cell 1', 'cell 2' ] 27 | # Second row 28 | csv << [ 'another cell value', 'and another' ] 29 | # etc... 30 | 31 | You can set the default filename for that a browser will use for 'save as' by 32 | setting @filename instance variable in your controller's action method 33 | e.g. 34 | 35 | @filename = 'report.csv' 36 | 37 | You can also set the input encoding and output encoding by setting 38 | @input_encoding and @output_encoding instance variables. 39 | These default to 'UTF-8' and 'LATIN1' respectively. e.g. 40 | 41 | @output_encoding = 'UTF-8' 42 | 43 | You can also attach a csv file to mail sent out by your application by 44 | including a snippet like the following in your mailer method 45 | 46 | attachment "text/csv" do |attachment| 47 | attachment.body = render(:file => 'example/index.csv.csvbuilder') 48 | attachment.filename = 'report.csv' 49 | end 50 | 51 | You can change the separator character and that is as simple as writing this in your controller's 52 | action method: 53 | 54 | csv_builder :sep => ';' 55 | 56 | Actually, all default FasterCSV options can be overriden thanks to the previous statement. 57 | 58 | Copyright (c) 2008 Econsultancy.com, released under the MIT license 59 | -------------------------------------------------------------------------------- /lib/csv_builder/template_handler/base.rb: -------------------------------------------------------------------------------- 1 | module CsvBuilder 2 | module TemplateHandler 3 | 4 | # Template handler for csv templates 5 | # 6 | # Add rows to your CSV file in the template by pushing arrays of columns into csv 7 | # 8 | # # First row 9 | # csv << [ 'cell 1', 'cell 2' ] 10 | # # Second row 11 | # csv << [ 'another cell value', 'and another' ] 12 | # # etc... 13 | # 14 | # You can set the default filename for that a browser will use for 'save as' by 15 | # setting @filename instance variable in your controller's action method 16 | # e.g. 17 | # 18 | # @filename = 'report.csv' 19 | # 20 | # You can also set the input encoding and output encoding by setting 21 | # @input_encoding and @output_encoding instance variables. 22 | # These default to 'UTF-8' and 'LATIN1' respectively. e.g. 23 | # 24 | # @output_encoding = 'UTF-8' 25 | 26 | class Base < ActionView::TemplateHandler 27 | 28 | include ActionView::TemplateHandlers::Compilable 29 | 30 | def self.line_offset 31 | 9 32 | end 33 | 34 | def compile(template) 35 | <<-EOV 36 | begin 37 | 38 | unless defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) 39 | @filename ||= "\#{controller.action_name}.csv" 40 | controller.response.headers["Content-Type"] ||= 'text/csv' 41 | controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" 42 | end 43 | 44 | result = FasterCSV.generate(controller.send(:compute_csv_builder_options)) do |csv| 45 | #{template.source} 46 | end 47 | 48 | # Transliterate into the required encoding if necessary 49 | # TODO: make defaults configurable 50 | @input_encoding ||= 'UTF-8' 51 | @output_encoding ||= 'LATIN1' 52 | 53 | if @input_encoding == @output_encoding 54 | result 55 | else 56 | # TODO: do some checking to make sure iconv works correctly in 57 | # current environment. See ActiveSupport::Inflector#transliterate 58 | # definition for details 59 | # 60 | # Not using the more standard //IGNORE//TRANLIST because it raises 61 | # Iconv::IllegalSequence for some inputs 62 | c = Iconv.new("\#{@output_encoding}//TRANSLIT//IGNORE", @input_encoding) 63 | c.iconv(result) 64 | end 65 | 66 | rescue Exception => e 67 | RAILS_DEFAULT_LOGGER.warn("Exception \#{e} \#{e.message} with class \#{e.class.name} thrown when rendering CSV") 68 | raise e 69 | end 70 | EOV 71 | end 72 | 73 | 74 | end 75 | end 76 | end 77 | 78 | 79 | module ActionView # :nodoc: 80 | module TemplateHandlers 81 | # Template handler for csv templates 82 | # 83 | # Add rows to your CSV file in the template by pushing arrays of columns into csv 84 | # 85 | # # First row 86 | # csv << [ 'cell 1', 'cell 2' ] 87 | # # Second row 88 | # csv << [ 'another cell value', 'and another' ] 89 | # # etc... 90 | # 91 | # You can set the default filename for that a browser will use for 'save as' by 92 | # setting @filename instance variable in your controller's action method 93 | # e.g. 94 | # 95 | # @filename = 'report.csv' 96 | # 97 | # You can also set the input encoding and output encoding by setting 98 | # @input_encoding and @output_encoding instance variables. 99 | # These default to 'UTF-8' and 'LATIN1' respectively. e.g. 100 | # 101 | # @output_encoding = 'UTF-8' 102 | 103 | class CsvBuilder < TemplateHandler 104 | 105 | include Compilable 106 | 107 | attr_reader :csv_builder_options 108 | 109 | def self.line_offset 110 | 9 111 | end 112 | 113 | def compile(template) 114 | <<-EOV 115 | begin 116 | 117 | unless defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) 118 | @filename ||= "\#{controller.action_name}.csv" 119 | controller.response.headers["Content-Type"] ||= 'text/csv' 120 | controller.response.headers['Content-Disposition'] = "attachment; filename=\#{@filename}" 121 | end 122 | 123 | result = FasterCSV.generate do |csv| 124 | #{template.source} 125 | end 126 | 127 | # Transliterate into the required encoding if necessary 128 | # TODO: make defaults configurable 129 | @input_encoding ||= 'UTF-8' 130 | @output_encoding ||= 'LATIN1' 131 | 132 | if @input_encoding == @output_encoding 133 | result 134 | else 135 | # TODO: do some checking to make sure iconv works correctly in 136 | # current environment. See ActiveSupport::Inflector#transliterate 137 | # definition for details 138 | # 139 | # Not using the more standard //IGNORE//TRANLIST because it raises 140 | # Iconv::IllegalSequence for some inputs 141 | c = Iconv.new("\#{@output_encoding}//TRANSLIT//IGNORE", @input_encoding) 142 | c.iconv(result) 143 | end 144 | 145 | rescue Exception => e 146 | RAILS_DEFAULT_LOGGER.warn("Exception \#{e} \#{e.message} with class \#{e.class.name} thrown when rendering CSV") 147 | raise e 148 | end 149 | EOV 150 | end 151 | 152 | end 153 | end 154 | end 155 | --------------------------------------------------------------------------------