to-csv 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ == 1.0.0
2
+
3
+ * First release
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ícaro Leopoldino da Motta
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.
data/README.rdoc ADDED
@@ -0,0 +1,188 @@
1
+ = ToCSV
2
+
3
+
4
+ ToCSV is a gem for converting arrays to CSV by calling +to_csv+.
5
+ These arrays can contain different data structures, as long as they are homogeneous, like the ones
6
+ described below:
7
+
8
+ * A simple array of anything that responds to <tt>to_s</tt>: <tt>['Date', Time.now].to_csv</tt>
9
+ * An array of hashes: <tt>[ {'Name' => 'Icaro', 'Age' => 23}, {'Name' => 'Gabriel', 'Age' => 16} ].to_csv</tt>
10
+ * A matrix: <tt>[['Name', 'Age'], ['Icaro', 23], ['Gabriel', 16]].to_csv</tt>
11
+ * A hash like array: <tt>[ [['Name', 'Icaro'], ['Age', 23]], [['Name', 'Gabriel'], ['Age', 16]] ].to_csv</tt>
12
+ * An array of ActiveRecord objects: <tt>@users.to_csv(:except => [:password, :phone], :timestamps => true)</tt>
13
+
14
+
15
+ === Requirements
16
+
17
+ You must have FasterCSV installed:
18
+ $ sudo gem install fastercsv
19
+
20
+ And also ActiveSupport:
21
+ $ sudo gem install active_support
22
+
23
+ ToCSV has been tested with Ruby 1.8.6/1.8.7.
24
+
25
+
26
+ === Configuration
27
+
28
+ If you want to use this gem with Rails, put the following requirement in your environment.rb:
29
+
30
+ config.gem 'to-csv', :lib => 'to_csv', :source => 'http://gemcutter.org'
31
+
32
+ After that, if you need to globally configure the gem, just create a <i>to_csv.rb</i> file in <i>initializers</i>.
33
+
34
+ ToCSV.byte_order_marker = true
35
+ ToCSV.timestamps = true
36
+ ToCSV.locale = 'en-US'
37
+ ToCSV.primary_key = false
38
+ ToCSV.csv_options = { :col_sep => ',', :row_sep => "\r\n" }
39
+
40
+
41
+ == Examples
42
+
43
+ Let's start with the most simple example.
44
+
45
+ ['Alfred Hitchcock', 'Robert Mitchum', 'Lucille Ball'].to_csv
46
+ #=> "Alfred Hitchcock;Robert Mitchum;Lucille Ball\n"
47
+
48
+
49
+ Or, if we have an array of arrays (i.e. a matrix) we can create tabular data.
50
+ [
51
+ ['Name', 'Gender'],
52
+ ['Alfred', 'M'],
53
+ ['Robert', 'M'],
54
+ ['Lucille', 'F']
55
+ ].to_csv #=> "Name;Gender\nAlfred;M\nRobert;M\nLucille;F\n"
56
+
57
+
58
+ Almost always, when we generate CSV files, we want it to have appropriate
59
+ headers, so a better approach might be to use an array of hashes.
60
+
61
+ [
62
+ { 'Name' => 'Alfred', 'Gender' => 'M' },
63
+ { 'Name' => 'Robert', 'Gender' => 'M' },
64
+ { 'Name' => 'Lucille', 'Gender' => 'F' }
65
+ ].to_csv #=> "Gender;Name\nM;Alfred\nM;Robert\nF;Lucille\n"
66
+
67
+
68
+ Look carefully to the above output. You can see that when we use hashes we can
69
+ no longer be sure of the headers' order. When we are working with tabular data
70
+ the headers' order can be very important, thus we can use a somewhat similar
71
+ data structure:
72
+
73
+ [
74
+ [ ['Name', 'Alfred'], ['Gender', 'M'] ],
75
+ [ ['Name', 'Robert'], ['Gender', 'M'] ],
76
+ [ ['Name', 'Lucille'], ['Gender', 'F'] ]
77
+ ].to_csv #=> "Name;Gender\nAlfred;M\nRobert;M\nLucille;F\n"
78
+
79
+ That's a lot to type... The first example was much simpler...
80
+
81
+ There is the <tt>headers</tt> option. You can use it in all the examples above
82
+ to enable/disable headers from the output. Default is to show (true).
83
+
84
+ users = [{ 'Name' => 'Alfred', 'Gender' => 'M' }]
85
+ users.to_csv(:headers => false)
86
+
87
+
88
+ ==== Active Record Objects
89
+
90
+ When we're building our data like the previous examples we have very few options
91
+ compared to what can be passed when converting an array of AR objects. Again,
92
+ the easiest way:
93
+
94
+ # Anywhere in your app.
95
+ # By default, all available model attributes (DB columns) are going to be used
96
+ # except timestamps and the primary key of the record
97
+ @users = User.all
98
+ File.open('path/to/file.csv', 'w') { |io| io.puts @users.to_csv }
99
+
100
+
101
+ ==== Headers
102
+
103
+ You can control the order and the text of any header. You can accomplish that
104
+ in various ways.
105
+
106
+ By default all attribute/method names will be sorted in alphabetical order. So
107
+ imagine a person model have +name+, +age+ and +email+ as attributes, and you
108
+ want to get the following output:
109
+
110
+ Name | E-mail | Age
111
+ ... | ... | ..
112
+ ... | ... | ..
113
+
114
+ You can tell <i>to-csv</i> to use a specific locale. If you don't, it uses
115
+ your app current locale. It will try to translate attributes to a
116
+ more friendly text by using the scope <tt>[:activerecord, :attributes, <model name>]</tt>.
117
+ If the translation doesn't exist the header's text is going to be humanized.
118
+
119
+ The order of columns can be changed with the option +headers+. The way this
120
+ option works is very similar to the <tt>plugins</tt> method in your Rails
121
+ <i>environment.rb</i> file.
122
+
123
+ * If you pass +nil+ (default) then headers/columns will be in alphabetical order.
124
+ * If you pass an empty array or +false+, no headers will be shown.
125
+ * Instead, if you pass a non empty array, headers will be sorted in the order specified. <tt>:all</tt> can be used as a placeholder for all attributes not explicitly named.
126
+
127
+ So, in our example above, we can say:
128
+
129
+ @users.to_csv(:headers => [:name, :email, :age])
130
+
131
+ Or, using the placeholder +all+, which is not very useful here:
132
+
133
+ @users.to_csv(:headers => [:name, :email, :all])
134
+
135
+ If you want a completely different result you could, for instance, map all
136
+ users to a hash. Example:
137
+
138
+ # This makes use of a hash to completely change the CSV output.
139
+ @users.map do |user|
140
+ {
141
+ 'Name' => user.name,
142
+ 'Age' => user.age,
143
+ 'Total Comments' => user.comments.count
144
+ }
145
+ end.to_csv
146
+
147
+
148
+ ===== Passing a Block
149
+
150
+ Sometimes you may want to change just one value out of six for example. The best
151
+ way to go is to pass a block so that you don't have to repeat yourself writing
152
+ five headers and it's obvious values and also loosing I18n translations.
153
+
154
+ # The block yields a new OpenStruct for each object in the list. By calling
155
+ # methods on this struct you can change their default values.
156
+ @users.to_csv do |csv, user|
157
+ csv.date_of_birth = user.date_of_birth.to_s(:long)
158
+ end
159
+
160
+
161
+ ===== A More Complete Example
162
+
163
+ # index.html.haml
164
+ = link_to 'export (CSV)', users_url(:csv)
165
+
166
+ # index action in users controller
167
+ def index
168
+ @users = User.all
169
+ respond_to do |format|
170
+ format.html
171
+ format.csv { send_data @users.to_csv, :filename => 'users_report.csv' }
172
+ end
173
+ end
174
+
175
+
176
+ ==== Full Customization
177
+
178
+ You can always customize the output if you wish by building arrays of hashes,
179
+ arrays of arrays of bidimensional arrays etc :). Or you can obviously mix
180
+ anything you want and even use FasterCSV directly.
181
+
182
+ @user.to_csv { :only => [:name, :email] }, :col_sep => ','
183
+
184
+ There are other options for you to customize the output. Take a look at the
185
+ <tt>to_csv</tt> method documentation.
186
+
187
+ Copyright (c) 2009 Ícaro Leopoldino da Motta, released under the MIT license.
188
+
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ #encoding: utf-8
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'rake/clean'
5
+ require 'rake/gempackagetask'
6
+ load 'test/tasks.rake'
7
+
8
+ TO_CSV_VERSION = '1.0.0'
9
+ CLEAN.include('pkg')
10
+
11
+ spec = Gem::Specification.new do |s|
12
+ s.author = "Ícaro Leopoldino da Motta"
13
+ s.email = "icaro.ldm@gmail.com"
14
+ s.platform = Gem::Platform::RUBY
15
+ s.name = "to-csv"
16
+ s.summary = s.description = "Convert arrays to CSV (array of hashes, matrixes, ActiveRecord objects etc)."
17
+ s.homepage = "http://github.com/ilmotta/to-csv"
18
+ s.version = TO_CSV_VERSION
19
+
20
+ s.add_dependency 'fastercsv', '>= 1.5.0'
21
+ s.add_dependency 'activesupport', '>= 2.3.5'
22
+
23
+ s.has_rdoc = true
24
+ s.require_path = "lib"
25
+ s.extra_rdoc_files = FileList['*.rdoc']
26
+ s.files = FileList['init.rb', 'MIT-LICENSE', 'Rakefile', 'lib/**/*', 'test/**/*']
27
+ end
28
+
29
+ Rake::GemPackageTask.new(spec) do |pkg|
30
+ pkg.define
31
+ end
32
+
33
+ task :build => [:clean, :repackage]
34
+
35
+ task :default => :test
36
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'to_csv'
data/lib/to_csv.rb ADDED
@@ -0,0 +1,59 @@
1
+ require 'fastercsv'
2
+ require 'ostruct'
3
+ require 'active_support'
4
+ require 'to_csv/csv_converter'
5
+
6
+ module ToCSV
7
+ mattr_accessor :byte_order_marker, :locale, :primary_key, :timestamps
8
+ mattr_accessor :csv_options
9
+ self.csv_options = { :col_sep => ';' }
10
+ end
11
+
12
+ class Array
13
+
14
+ #
15
+ # Returns a CSV string.
16
+ #
17
+ # ==== Available Options:
18
+ #
19
+ # 1. *options*
20
+ # +byte_order_marker+::
21
+ # If true, a Byte Order Maker (BOM) will be inserted at
22
+ # the beginning of the output. It's useful if you want to force
23
+ # MS Excel to read UTF-8 encoded files, otherwise it will just
24
+ # decode them as Latin1 (ISO-8859-1). Default: +false+.
25
+ # +only+::
26
+ # Same behavior as with the +to_json+ method.
27
+ # +except+::
28
+ # Same as +only+ option.
29
+ # +methods+::
30
+ # Accepts a symbol or an array with additional methods to be included.
31
+ # +timestamps+::
32
+ # Include timestamps +created_at+, +created_on+, +updated_at+ and
33
+ # +updated_on+. If false Default: +false+.
34
+ # +primary_key+::
35
+ # If +true+ the object's primary key will be added as an attribute,
36
+ # which in turn will be mapped to a CSV column. Default: +false+.
37
+ # +headers+::
38
+ # If this list is <tt>nil</tt> then headers will be in alphabetical order.
39
+ # If it is an empty array or <tt>false</tt>, no headers will be shown.
40
+ # If it is non empty, headers will be sorted in the order specified.
41
+ # <tt>:all</tt> can be used as a placeholder for all attributes not
42
+ # explicitly named.
43
+ # +locale+::
44
+ # In a Rails environment, it will automatically take the current locale
45
+ # and will use it to translate the columns to friendly headers.
46
+ # Methods will be translated from
47
+ # <tt>[:activerecord, :attributes, <model>]</tt>. If the translation
48
+ # is missing, then a simple humanize will be called.
49
+ #
50
+ # 2. *csv_options*
51
+ # Accepts all options listed in <tt>FasterCSV::DEFAULT_OPTIONS</tt>.
52
+ #
53
+ def to_csv(options = {}, csv_options = {}, &block)
54
+ return '' if empty?
55
+ csv_converter = ToCSV::Converter.new(self, options, csv_options, &block)
56
+ csv_converter.to_csv
57
+ end
58
+ end
59
+
@@ -0,0 +1,169 @@
1
+ module ToCSV
2
+ class Converter
3
+
4
+ def initialize(data, options = {}, csv_options = {}, &block)
5
+ @opts = options.to_options.reverse_merge({
6
+ :byte_order_marker => ToCSV.byte_order_marker,
7
+ :locale => ToCSV.locale || ::I18n.locale,
8
+ :primary_key => ToCSV.primary_key,
9
+ :timestamps => ToCSV.timestamps
10
+ })
11
+
12
+ @opts[:only] = Array(@opts[:only]).map(&:to_s)
13
+ @opts[:except] = Array(@opts[:except]).map(&:to_s)
14
+ @opts[:methods] = Array(@opts[:methods]).map(&:to_s)
15
+
16
+ @data = data
17
+ @block = block
18
+ @csv_options = csv_options.to_options.reverse_merge(ToCSV.csv_options)
19
+ end
20
+
21
+ def to_csv
22
+ build_headers_and_rows
23
+
24
+ output = ::FasterCSV.generate(@csv_options) do |csv|
25
+ csv << @header_row if @header_row.try(:any?)
26
+ @rows.each { |row| csv << row }
27
+ end
28
+
29
+ @opts[:byte_order_marker] ? "\xEF\xBB\xBF#{output}" : output
30
+ end
31
+
32
+ private
33
+
34
+ def build_headers_and_rows
35
+ send "headers_and_rows_from_#{ discover_data_type }"
36
+ end
37
+
38
+ def discover_data_type
39
+ test_data = @data.first
40
+ return 'ar_object' if instance_of_active_record? test_data
41
+ return 'hash' if test_data.is_a? Hash
42
+ return 'unidimensional_array' if test_data.is_a?(Array) && !test_data.first.is_a?(Array)
43
+ return 'bidimensional_array' if test_data.is_a?(Array) && test_data.first.is_a?(Array) && test_data.first.size == 2
44
+ 'simple_data'
45
+ end
46
+
47
+ def instance_of_active_record?(obj)
48
+ obj.class.base_class.superclass == ActiveRecord::Base
49
+ rescue Exception
50
+ false
51
+ end
52
+
53
+ def headers_and_rows_from_simple_data
54
+ @header_row = nil
55
+ @rows = [@data.dup]
56
+ end
57
+
58
+ def headers_and_rows_from_hash
59
+ @header_row = @data.first.keys if display_headers?
60
+ @rows = @data.map(&:values)
61
+ end
62
+
63
+ def headers_and_rows_from_unidimensional_array
64
+ @header_row = @data.first if display_headers?
65
+ @rows = @data[1..-1]
66
+ end
67
+
68
+ def headers_and_rows_from_bidimensional_array
69
+ @header_row = @data.first.map(&:first) if display_headers?
70
+ @rows = @data.map { |array| array.map(&:last) }
71
+ end
72
+
73
+ def headers_and_rows_from_ar_object
74
+ attributes = sort_attributes(filter_attributes(attribute_names))
75
+ @header_row = human_attribute_names(attributes) if display_headers?
76
+
77
+ @rows = if @block
78
+ @data.map do |item|
79
+ os = OpenStruct.new
80
+ @block.call(os, item)
81
+ marshal_dump = os.marshal_dump
82
+ attributes.map { |attribute| marshal_dump[attribute.to_sym] || try_formatting_date(item.send(attribute)) }
83
+ end
84
+ else
85
+ @data.map do |item|
86
+ attributes.map { |attribute| try_formatting_date item.send(attribute) }
87
+ end
88
+ end
89
+ end
90
+
91
+ def display_headers?
92
+ @opts[:headers].nil? || (Array(@opts[:headers]).any? && Array(@opts[:headers]).all? { |h| h != false })
93
+ end
94
+
95
+ def human_attribute_names(attributes)
96
+ @opts[:locale] ? translate(attributes) : humanize(attributes)
97
+ end
98
+
99
+ def humanize(attributes)
100
+ attributes.map(&:humanize)
101
+ end
102
+
103
+ def translate(attributes)
104
+ ::I18n.with_options :locale => @opts[:locale], :scope => [:activerecord, :attributes, @data.first.class.to_s.underscore] do |locale|
105
+ attributes.map { |attribute| locale.t(attribute, :default => attribute.humanize) }
106
+ end
107
+ end
108
+
109
+ def try_formatting_date(value)
110
+ is_a_date?(value) ? value.to_s : value
111
+ end
112
+
113
+ def is_a_date?(value)
114
+ value.is_a?(Time) || value.is_a?(Date) || value.is_a?(DateTime)
115
+ end
116
+
117
+ def primary_key_filter(attributes)
118
+ return attributes if @opts[:primary_key]
119
+ attributes - Array(@data.first.class.primary_key.to_s)
120
+ end
121
+
122
+ def timestamps_filter(attributes)
123
+ return attributes if @opts[:timestamps]
124
+ return attributes if (@opts[:only] + @opts[:except]).any? { |attribute| timestamps.include? attribute }
125
+ attributes - timestamps
126
+ end
127
+
128
+ def timestamps
129
+ %w[ created_at updated_at created_on updated_on ]
130
+ end
131
+
132
+ def methods_filter(attributes)
133
+ attributes | @opts[:methods]
134
+ end
135
+
136
+ def only_filter(attributes)
137
+ return attributes if @opts[:only].empty?
138
+ attributes & @opts[:only]
139
+ end
140
+
141
+ def except_filter(attributes)
142
+ attributes - @opts[:except]
143
+ end
144
+
145
+ def attribute_names
146
+ @data.first.attribute_names.map(&:to_s)
147
+ end
148
+
149
+ def filter_attributes(attributes)
150
+ attributes = methods_filter(attributes)
151
+ attributes = primary_key_filter(attributes)
152
+ attributes = timestamps_filter(attributes)
153
+ attributes = @opts[:only].any?? only_filter(attributes) : except_filter(attributes)
154
+ attributes
155
+ end
156
+
157
+ def sort_attributes(attributes)
158
+ attributes = attributes.map(&:to_s).sort
159
+ return attributes if @opts[:headers].nil?
160
+ headers = Array(@opts[:headers]).map(&:to_s)
161
+ headers.delete_if { |attribute| attribute == 'false' }
162
+ if index = headers.index('all')
163
+ (headers & attributes).insert(index, (attributes - headers)).flatten
164
+ else
165
+ headers + (attributes - headers)
166
+ end
167
+ end
168
+ end
169
+ end
data/test/database.yml ADDED
@@ -0,0 +1,4 @@
1
+ sqlite3:
2
+ database: ":memory:"
3
+ adapter: sqlite3
4
+ timeout: 500
@@ -0,0 +1,2 @@
1
+ class Movie < ActiveRecord::Base
2
+ end
@@ -0,0 +1,19 @@
1
+ the_dark_knight:
2
+ id: 1
3
+ title: The Dark Knight
4
+ subtitles: English, French, Spanish
5
+ studio: Warner Home Video
6
+ number_of_discs: 2
7
+ dvd_release_date: <%= DateTime.new 2008, 12, 9 %>
8
+ created_at: <%= Date.new 2009, 12, 12 %>
9
+ updated_at: <%= Date.new 2009, 12, 12 %>
10
+
11
+ 2001_space_odyssey:
12
+ id: 2
13
+ title: 2001 - A Space Odyssey
14
+ subtitles: English, Spanish, French
15
+ studio: Warner Home Video
16
+ number_of_discs: 1
17
+ dvd_release_date: <%= DateTime.new 2007, 10, 23 %>
18
+ created_at: <%= Date.new 2009, 11, 11 %>
19
+ updated_at: <%= Date.new 2009, 11, 11 %>
@@ -0,0 +1,7 @@
1
+ icaro:
2
+ cod: 1
3
+ name: Icaro
4
+
5
+ gabriel:
6
+ cod: 2
7
+ name: Gabriel
@@ -0,0 +1,3 @@
1
+ class Person < ActiveRecord::Base
2
+ set_primary_key :cod
3
+ end
@@ -0,0 +1,12 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :movies, :force => true do |t|
3
+ t.string :title, :subtitles, :studio
4
+ t.integer :number_of_discs
5
+ t.datetime :dvd_release_date
6
+ t.timestamps
7
+ end
8
+
9
+ create_table :people, :primary_key => :cod, :force => true do |t|
10
+ t.string :name
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ require 'lib/activerecord_test_connector'
2
+
3
+ class ActiveRecordTestCase < Test::Unit::TestCase
4
+ if defined? ActiveSupport::Testing::SetupAndTeardown
5
+ include ActiveSupport::Testing::SetupAndTeardown
6
+ end
7
+
8
+ if defined? ActiveRecord::TestFixtures
9
+ include ActiveRecord::TestFixtures
10
+ end
11
+
12
+ self.fixture_path = ActiveRecordTestConnector::FIXTURES_PATH
13
+ self.use_transactional_fixtures = true
14
+
15
+ def self.fixtures(*args)
16
+ super
17
+ end
18
+ end
19
+
20
+ ActiveRecordTestConnector.setup
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'active_record/fixtures'
4
+
5
+ class ActiveRecordTestConnector
6
+ FIXTURES_PATH = File.join(File.dirname(__FILE__), '..', 'fixtures')
7
+
8
+ def self.setup
9
+ setup_connection
10
+ load_schema
11
+ add_load_path FIXTURES_PATH
12
+ end
13
+
14
+ private
15
+
16
+ def self.add_load_path(path)
17
+ dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
18
+ dep.load_paths.unshift path
19
+ end
20
+
21
+ def self.load_schema
22
+ ActiveRecord::Base.silence do
23
+ ActiveRecord::Migration.verbose = false
24
+ load File.join(FIXTURES_PATH, 'schema.rb')
25
+ end
26
+ end
27
+
28
+ def self.setup_connection
29
+ configurations = YAML.load_file(File.join(File.dirname(__FILE__), '..', 'database.yml'))
30
+ ActiveRecord::Base.establish_connection configurations['sqlite3']
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ require 'lib/activerecord_test_connector'
2
+
3
+ # setup the connection
4
+ ActiveRecordTestConnector.setup
5
+
6
+ # load all fixtures
7
+ Fixtures.create_fixtures(ActiveRecordTestConnector::FIXTURES_PATH, ActiveRecord::Base.connection.tables)
8
+
9
+ require 'to_csv'
@@ -0,0 +1,28 @@
1
+ "en-US":
2
+ date:
3
+ formats:
4
+ default: "%Y-%m-%d"
5
+ short: "%e %b"
6
+ long: "%B %e, %Y"
7
+ only_day: "%e"
8
+
9
+ day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
10
+ abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
11
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
12
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
13
+ order: [ :year, :month, :day ]
14
+
15
+ time:
16
+ formats:
17
+ default: "%a %b %d %H:%M:%S %Z %Y"
18
+ time: "%H:%M"
19
+ short: "%d %b %H:%M"
20
+ long: "%B %d, %Y %H:%M"
21
+ only_second: "%S"
22
+
23
+ datetime:
24
+ formats:
25
+ default: "%Y-%m-%dT%H:%M:%S%Z"
26
+
27
+ am: 'am'
28
+ pm: 'pm'
@@ -0,0 +1,147 @@
1
+ # formatos de data e hora
2
+ date:
3
+ formats:
4
+ default: "%d/%m/%Y"
5
+ short: "%d de %B"
6
+ long: "%d de %B de %Y"
7
+
8
+ day_names: [Domingo, Segunda, Terça, Quarta, Quinta, Sexta, Sábado]
9
+ abbr_day_names: [Dom, Seg, Ter, Qua, Qui, Sex, Sáb]
10
+ month_names: [~, Janeiro, Fevereiro, Março, Abril, Maio, Junho, Julho, Agosto, Setembro, Outubro, Novembro, Dezembro]
11
+ abbr_month_names: [~, Jan, Fev, Mar, Abr, Mai, Jun, Jul, Ago, Set, Out, Nov, Dez]
12
+ order: [ :day, :month, :year ]
13
+
14
+ time:
15
+ formats:
16
+ default: "%A, %d de %B de %Y, %H:%M h"
17
+ short: "%d/%m, %H:%M h"
18
+ long: "%A, %d de %B de %Y, %H:%M h"
19
+ am: ''
20
+ pm: ''
21
+
22
+ # date helper distanci em palavras
23
+ datetime:
24
+ distance_in_words:
25
+ half_a_minute: 'meio minuto'
26
+ less_than_x_seconds:
27
+ one: 'menos de 1 segundo'
28
+ other: 'menos de {{count}} segundos'
29
+
30
+ x_seconds:
31
+ one: '1 segundo'
32
+ other: '{{count}} segundos'
33
+
34
+ less_than_x_minutes:
35
+ one: 'menos de um minuto'
36
+ other: 'menos de {{count}} minutos'
37
+
38
+ x_minutes:
39
+ one: '1 minuto'
40
+ other: '{{count}} minutos'
41
+
42
+ about_x_hours:
43
+ one: 'aproximadamente 1 hora'
44
+ other: 'aproximadamente {{count}} horas'
45
+
46
+ x_days:
47
+ one: '1 dia'
48
+ other: '{{count}} dias'
49
+
50
+ about_x_months:
51
+ one: 'aproximadamente 1 mês'
52
+ other: 'aproximadamente {{count}} meses'
53
+
54
+ x_months:
55
+ one: '1 mês'
56
+ other: '{{count}} meses'
57
+
58
+ about_x_years:
59
+ one: 'aproximadamente 1 ano'
60
+ other: 'aproximadamente {{count}} anos'
61
+
62
+ over_x_years:
63
+ one: 'mais de 1 ano'
64
+ other: 'mais de {{count}} anos'
65
+ prompts:
66
+ year: "Ano"
67
+ month: "Mês"
68
+ day: "Dia"
69
+ hour: "Hora"
70
+ minute: "Minuto"
71
+ second: "Segundos"
72
+
73
+ # numeros
74
+ number:
75
+ format:
76
+ precision: 3
77
+ separator: ','
78
+ delimiter: '.'
79
+ currency:
80
+ format:
81
+ unit: 'R$'
82
+ precision: 2
83
+ format: '%u %n'
84
+ separator: ','
85
+ delimiter: '.'
86
+ percentage:
87
+ format:
88
+ delimiter: '.'
89
+ precision:
90
+ format:
91
+ delimiter: '.'
92
+ human:
93
+ format:
94
+ precision: 1
95
+ delimiter: '.'
96
+ storage_units:
97
+ format: "%n %u"
98
+ units:
99
+ byte:
100
+ one: "Byte"
101
+ other: "Bytes"
102
+ kb: "KB"
103
+ mb: "MB"
104
+ gb: "GB"
105
+ tb: "TB"
106
+
107
+ # Used in array.to_sentence.
108
+ support:
109
+ array:
110
+ words_connector: ", "
111
+ two_words_connector: " e "
112
+ last_word_connector: " e "
113
+
114
+ # Active Record
115
+ activerecord:
116
+ errors:
117
+ template:
118
+ header:
119
+ one: "Não foi possível gravar {{model}}: 1 erro"
120
+ other: "Não foi possível gravar {{model}}: {{count}} erros."
121
+ body: "Por favor, verifique o(s) seguinte(s) campo(s):"
122
+ messages:
123
+ inclusion: "não está incluído na lista"
124
+ exclusion: "não está disponível"
125
+ invalid: "não é válido"
126
+ confirmation: "não está de acordo com a confirmação"
127
+ accepted: "deve ser aceito"
128
+ empty: "não pode ficar vazio"
129
+ blank: "não pode ficar em branco"
130
+ too_long: "é muito longo (máximo: {{count}} caracteres)"
131
+ too_short: "é muito curto (mínimo: {{count}} caracteres)"
132
+ wrong_length: "não possui o tamanho esperado ({{count}} caracteres)"
133
+ taken: "já está em uso"
134
+ not_a_number: "não é um número"
135
+ greater_than: "deve ser maior do que {{count}}"
136
+ greater_than_or_equal_to: "deve ser maior ou igual a {{count}}"
137
+ equal_to: "deve ser igual a {{count}}"
138
+ less_than: "deve ser menor do que {{count}}"
139
+ less_than_or_equal_to: "deve ser menor ou igual a {{count}}"
140
+ odd: "deve ser ímpar"
141
+ even: "deve ser par"
142
+ attributes:
143
+ movie:
144
+ title: Título
145
+ subtitles: Legendas
146
+ number_of_discs: Número de Discos
147
+ dvd_release_date: Data de Lançamento do DVD
data/test/tasks.rake ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ desc 'Test to_csv plugin'
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.verbose = true
7
+ t.libs << 'test'
8
+ end
@@ -0,0 +1,148 @@
1
+ require 'test/unit'
2
+ require 'test/lib/activerecord_test_case'
3
+ require 'test/lib/load_fixtures'
4
+
5
+ class ToCsvTest < ActiveRecordTestCase
6
+ fixtures :movies
7
+
8
+ def setup
9
+ ToCSV.byte_order_marker = ToCSV.locale = ToCSV.primary_key = ToCSV.timestamps = false
10
+ ToCSV.csv_options = { :col_sep => ';' }
11
+ @movies = Movie.all
12
+ @people = Person.all(:order => :name)
13
+ store_translations('en-US', 'pt-BR')
14
+ end
15
+
16
+ def test_simple_array
17
+ csv = ['Alfred Hitchcock', 'Robert Mitchum', 'Lucille Ball'].to_csv
18
+ assert_equal "Alfred Hitchcock;Robert Mitchum;Lucille Ball\n", csv
19
+ end
20
+
21
+ def test_matrix
22
+ csv = [
23
+ ['Name', 'Age'],
24
+ ['Icaro', 22],
25
+ ['Gabriel', 16]
26
+ ].to_csv
27
+
28
+ assert_equal "Name;Age\nIcaro;22\nGabriel;16\n", csv
29
+ end
30
+
31
+ def test_array_of_matrixes
32
+ csv = [
33
+ [
34
+ ['Name', 'Alfred'],
35
+ ['Gender', 'M']
36
+ ],
37
+ [
38
+ ['Name', 'Robert'],
39
+ ['Gender', 'M']
40
+ ],
41
+ [
42
+ ['Name', 'Lucille'],
43
+ ['Gender', 'F']
44
+ ]
45
+ ].to_csv
46
+
47
+ assert_equal "Name;Gender\nAlfred;M\nRobert;M\nLucille;F\n", csv
48
+ end
49
+
50
+ def test_array_of_hashes
51
+ csv = [
52
+ {
53
+ 'Name' => 'Icaro',
54
+ 'E-mail' => 'icaro.ldm@gmail.com'
55
+ },
56
+ {
57
+ 'Name' => 'Gabriel',
58
+ 'E-mail' => 'gaby@gmail.com'
59
+ }
60
+ ].to_csv
61
+
62
+ order_01 = "Name;E-mail\nIcaro;icaro.ldm@gmail.com\nGabriel;gaby@gmail.com\n"
63
+ order_02 = "E-mail;Name\nicaro.ldm@gmail.com;Icaro\ngaby@gmail.com;Gabriel\n"
64
+
65
+
66
+ assert order_01 == csv || order_02 == csv
67
+ end
68
+
69
+ def test_without_options
70
+ assert_equal "Dvd release date;Number of discs;Studio;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv
71
+ end
72
+
73
+ def test_only_option
74
+ assert_equal "Title\nThe Dark Knight\n2001 - A Space Odyssey\n", @movies.to_csv(:only => :title)
75
+ assert_equal @movies.to_csv(:only => :title), @movies.to_csv(:only => [:title])
76
+ assert_equal "Studio;Title\nWarner Home Video;The Dark Knight\nWarner Home Video;2001 - A Space Odyssey\n", @movies.to_csv(:only => [:title, :studio])
77
+ end
78
+
79
+ def test_except_option
80
+ assert_equal "Dvd release date;Number of discs;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;2;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:except => :studio)
81
+ assert_equal @movies.to_csv(:except => :studio), @movies.to_csv(:except => [:studio])
82
+ assert_equal "Dvd release date;Number of discs;Studio\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video\n", @movies.to_csv(:except => [:title, :subtitles])
83
+ end
84
+
85
+ def test_timestamps_option
86
+ assert_equal "Created at;Number of discs\nSat Dec 12 00:00:00 -0200 2009;2\nWed Nov 11 00:00:00 -0200 2009;1\n", @movies.to_csv(:except => [:title, :subtitles, :studio, :dvd_release_date, :updated_at])
87
+ assert_equal "Created at;Number of discs\nSat Dec 12 00:00:00 -0200 2009;2\nWed Nov 11 00:00:00 -0200 2009;1\n", @movies.to_csv(:except => [:title, :subtitles, :studio, :dvd_release_date, :updated_at], :timestamps => false)
88
+ assert_equal "Created at;Number of discs\nSat Dec 12 00:00:00 -0200 2009;2\nWed Nov 11 00:00:00 -0200 2009;1\n", @movies.to_csv(:except => [:title, :subtitles, :studio, :dvd_release_date, :updated_at], :timestamps => true)
89
+ end
90
+
91
+ def test_headers_option
92
+ assert_equal "Icaro;23\n", ['Icaro', 23].to_csv(:headers => false)
93
+ assert_equal "Icaro;23\n", [ [:name, :age], ['Icaro', 23] ].to_csv(:headers => false)
94
+ assert_equal "Icaro;23\n", [ [[:name, 'Icaro'], [:age, 23]] ].to_csv(:headers => false)
95
+ assert_equal "Subtitles;Dvd release date;Number of discs;Studio;Title\nEnglish, French, Spanish;Mon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;The Dark Knight\nEnglish, Spanish, French;Mon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;2001 - A Space Odyssey\n", @movies.to_csv(:headers => :subtitles)
96
+ assert_equal "Mon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:headers => false)
97
+ assert_equal "Mon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:headers => [false])
98
+ assert_equal "Mon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:headers => [])
99
+ assert_equal "Title;Number of discs\nThe Dark Knight;2\n2001 - A Space Odyssey;1\n", @movies.to_csv(:headers => [:title, :number_of_discs], :only => [:number_of_discs, :title])
100
+ assert_equal "Title;Number of discs\nThe Dark Knight;2\n2001 - A Space Odyssey;1\n", @movies.to_csv(:headers => [:title, :all], :only => [:number_of_discs, :title])
101
+ assert_equal "Title;Number of discs\nThe Dark Knight;2\n2001 - A Space Odyssey;1\n", @movies.to_csv(:headers => :title, :only => [:number_of_discs, :title])
102
+ assert_equal "Dvd release date;Number of discs;Studio;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:headers => :all)
103
+ assert_equal "Dvd release date;Number of discs;Studio;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:headers => [:all])
104
+ assert_equal "Dvd release date;Studio;Subtitles;Title;Number of discs\nMon Dec 08 22:00:00 -0200 2008;Warner Home Video;English, French, Spanish;The Dark Knight;2\nMon Oct 22 22:00:00 -0200 2007;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey;1\n", @movies.to_csv(:headers => [:all, :subtitles, :title, :number_of_discs])
105
+ end
106
+
107
+ def test_locale_option
108
+ assert_equal "Data de Lançamento do DVD;Número de Discos;Studio;Legendas;Título\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:locale => 'pt-BR')
109
+ end
110
+
111
+ def test_primary_key_option
112
+ assert_equal "Name\nGabriel\nIcaro\n", @people.to_csv
113
+ assert_equal "Name\nGabriel\nIcaro\n", @people.to_csv(:primary_key => false)
114
+ assert_equal "Name\nGabriel\nIcaro\n", @people.to_csv(:primary_key => nil)
115
+ assert_equal "Cod;Name\n2;Gabriel\n1;Icaro\n", @people.to_csv(:primary_key => true)
116
+ assert_equal "Number of discs\n2\n1\n", @movies.to_csv(:primary_key => true, :only => [:number_of_discs])
117
+ assert_equal "Number of discs\n2\n1\n", @movies.to_csv(:only => [:number_of_discs, :id])
118
+ assert_equal "Dvd release date;Number of discs;Studio;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;2;Warner Home Video;English, French, Spanish;The Dark Knight\nMon Oct 22 22:00:00 -0200 2007;1;Warner Home Video;English, Spanish, French;2001 - A Space Odyssey\n", @movies.to_csv(:primary_key => true, :except => :id)
119
+ assert_equal "Name\nGabriel\nIcaro\n", @people.to_csv(:methods => :cod)
120
+ end
121
+
122
+ def test_block_passed
123
+ csv = @movies.to_csv do |csv, movie|
124
+ csv.title = movie.title.upcase
125
+ csv.number_of_discs = "%02d" % movie.number_of_discs
126
+ end
127
+ assert_equal "Dvd release date;Number of discs;Studio;Subtitles;Title\nMon Dec 08 22:00:00 -0200 2008;02;Warner Home Video;English, French, Spanish;THE DARK KNIGHT\nMon Oct 22 22:00:00 -0200 2007;01;Warner Home Video;English, Spanish, French;2001 - A SPACE ODYSSEY\n", csv
128
+ end
129
+
130
+ def test_default_settings
131
+ ToCSV.byte_order_marker = true
132
+ ToCSV.locale = 'pt-BR'
133
+ ToCSV.primary_key = true
134
+ ToCSV.timestamps = true
135
+ ToCSV.csv_options = { :col_sep => ',' }
136
+ assert_equal "\xEF\xBB\xBFCreated at,Data de Lançamento do DVD,Id,Número de Discos,Studio,Legendas,Título,Updated at\nSat Dec 12 00:00:00 -0200 2009,Mon Dec 08 22:00:00 -0200 2008,1,2,Warner Home Video,\"English, French, Spanish\",The Dark Knight,Sat Dec 12 00:00:00 -0200 2009\n", Array(@movies.first).to_csv
137
+ end
138
+
139
+ private
140
+
141
+ def store_translations(*locales)
142
+ locale_path = File.join(File.dirname(__FILE__), 'locales')
143
+ locales.each do |locale|
144
+ I18n.backend.store_translations locale, YAML.load_file(File.join(locale_path, "#{ locale }.yml"))
145
+ end
146
+ end
147
+ end
148
+
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: to-csv
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - "\xC3\x8Dcaro Leopoldino da Motta"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-23 00:00:00 -02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: fastercsv
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.5.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: activesupport
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.5
34
+ version:
35
+ description: Convert arrays to CSV (array of hashes, matrixes, ActiveRecord objects etc).
36
+ email: icaro.ldm@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - CHANGELOG.rdoc
43
+ - README.rdoc
44
+ files:
45
+ - init.rb
46
+ - MIT-LICENSE
47
+ - Rakefile
48
+ - lib/to_csv/csv_converter.rb
49
+ - lib/to_csv.rb
50
+ - test/database.yml
51
+ - test/fixtures/movie.rb
52
+ - test/fixtures/movies.yml
53
+ - test/fixtures/people.yml
54
+ - test/fixtures/person.rb
55
+ - test/fixtures/schema.rb
56
+ - test/lib/activerecord_test_case.rb
57
+ - test/lib/activerecord_test_connector.rb
58
+ - test/lib/load_fixtures.rb
59
+ - test/locales/en-US.yml
60
+ - test/locales/pt-BR.yml
61
+ - test/tasks.rake
62
+ - test/to_csv_test.rb
63
+ - CHANGELOG.rdoc
64
+ - README.rdoc
65
+ has_rdoc: true
66
+ homepage: http://github.com/ilmotta/to-csv
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options: []
71
+
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ version:
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: "0"
85
+ version:
86
+ requirements: []
87
+
88
+ rubyforge_project:
89
+ rubygems_version: 1.3.5
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Convert arrays to CSV (array of hashes, matrixes, ActiveRecord objects etc).
93
+ test_files: []
94
+