to-csv 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+