to-csv 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,52 +1,52 @@
1
- #encoding: utf-8
2
-
3
- require 'rubygems'
4
- require 'rake'
5
- require 'rake/clean'
6
- require 'rake/gempackagetask'
7
- load 'test/tasks.rake'
8
-
9
- TO_CSV_VERSION = '1.0.1'
10
- CLEAN.include('pkg')
11
-
12
- spec = Gem::Specification.new do |s|
13
- s.author = "Ícaro Leopoldino da Motta"
14
- s.email = "icaro.ldm@gmail.com"
15
- s.platform = Gem::Platform::RUBY
16
- s.required_ruby_version = '>= 1.8.6'
17
- s.name = "to-csv"
18
- s.summary = s.description = "Convert arrays to CSV (array of hashes, matrixes, ActiveRecord objects etc)."
19
- s.homepage = "http://github.com/ilmotta/to-csv"
20
- s.version = TO_CSV_VERSION
21
-
22
- s.add_dependency 'activesupport', '>= 2.3.5'
23
- s.add_development_dependency 'activerecord', '>= 2.3.5'
24
- s.add_development_dependency 'sqlite3-ruby', '>= 1.2.5'
25
-
26
- s.has_rdoc = true
27
- s.require_path = "lib"
28
- s.extra_rdoc_files = FileList['*.rdoc']
29
- s.files = FileList['init.rb', 'MIT-LICENSE', 'Rakefile', 'lib/**/*', 'test/**/*']
30
-
31
- s.post_install_message = %q{
32
- ========================================================================
33
-
34
- Thanks for installing ToCSV.
35
-
36
- If your Ruby version is lower than 1.9 you need to install fastercsv.
37
-
38
- $ sudo gem install fastercsv
39
-
40
- ========================================================================
41
-
42
- }
43
- end
44
-
45
- Rake::GemPackageTask.new(spec) do |pkg|
46
- pkg.define
47
- end
48
-
49
- task :build => [:clean, :repackage]
50
-
51
- task :default => :test
52
-
1
+ #encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'rake/clean'
6
+ require 'rake/gempackagetask'
7
+ load 'test/tasks.rake'
8
+
9
+ TO_CSV_VERSION = '1.0.2'
10
+ CLEAN.include('pkg')
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.author = "Ícaro Leopoldino da Motta"
14
+ s.email = "icaro.ldm@gmail.com"
15
+ s.platform = Gem::Platform::RUBY
16
+ s.required_ruby_version = '>= 1.8.6'
17
+ s.name = "to-csv"
18
+ s.summary = s.description = "Convert arrays to CSV (array of hashes, matrixes, ActiveRecord objects etc)."
19
+ s.homepage = "http://github.com/ilmotta/to-csv"
20
+ s.version = TO_CSV_VERSION
21
+
22
+ s.add_dependency 'activesupport', '>= 2.3.5'
23
+ s.add_development_dependency 'activerecord', '>= 2.3.5'
24
+ s.add_development_dependency 'sqlite3-ruby', '>= 1.2.5'
25
+
26
+ s.has_rdoc = true
27
+ s.require_path = "lib"
28
+ s.extra_rdoc_files = FileList['*.rdoc']
29
+ s.files = FileList['init.rb', 'MIT-LICENSE', 'Rakefile', 'lib/**/*', 'test/**/*']
30
+
31
+ s.post_install_message = %q{
32
+ ========================================================================
33
+
34
+ Thanks for installing ToCSV.
35
+
36
+ If your Ruby version is lower than 1.9 you need to install fastercsv.
37
+
38
+ $ sudo gem install fastercsv
39
+
40
+ ========================================================================
41
+
42
+ }
43
+ end
44
+
45
+ Rake::GemPackageTask.new(spec) do |pkg|
46
+ pkg.define
47
+ end
48
+
49
+ task :build => [:clean, :repackage]
50
+
51
+ task :default => :test
52
+
@@ -1,59 +1,63 @@
1
- require RUBY_VERSION < '1.9' ? 'fastercsv' : 'csv'
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
-
1
+ require RUBY_VERSION < '1.9' ? 'fastercsv' : 'csv'
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
+
11
+ #
12
+ # Returns a CSV string.
13
+ #
14
+ # ==== Available Options:
15
+ #
16
+ # 1. *options*
17
+ # +byte_order_marker+::
18
+ # If true, a Byte Order Maker (BOM) will be inserted at
19
+ # the beginning of the output. It's useful if you want to force
20
+ # MS Excel to read UTF-8 encoded files, otherwise it will just
21
+ # decode them as Latin1 (ISO-8859-1). Default: +false+.
22
+ # +only+::
23
+ # Same behavior as with the +to_json+ method.
24
+ # +except+::
25
+ # Same as +only+ option.
26
+ # +methods+::
27
+ # Accepts a symbol or an array with additional methods to be included.
28
+ # +timestamps+::
29
+ # Include timestamps +created_at+, +created_on+, +updated_at+ and
30
+ # +updated_on+. If false Default: +false+.
31
+ # +primary_key+::
32
+ # If +true+ the object's primary key will be added as an attribute,
33
+ # which in turn will be mapped to a CSV column. Default: +false+.
34
+ # +headers+::
35
+ # If this list is <tt>nil</tt> then headers will be in alphabetical order.
36
+ # If it is an empty array or <tt>false</tt>, no headers will be shown.
37
+ # If it is non empty, headers will be sorted in the order specified.
38
+ # <tt>:all</tt> can be used as a placeholder for all attributes not
39
+ # explicitly named.
40
+ # +locale+::
41
+ # In a Rails environment, it will automatically take the current locale
42
+ # and will use it to translate the columns to friendly headers.
43
+ # Methods will be translated from
44
+ # <tt>[:activerecord, :attributes, <model>]</tt>. If the translation
45
+ # is missing, then a simple humanize will be called.
46
+ #
47
+ # 2. *csv_options*
48
+ # Accepts all options listed in <tt>FasterCSV::DEFAULT_OPTIONS</tt>.
49
+ #
50
+ def to_csv(options = {}, csv_options = {}, &block)
51
+ return '' if empty?
52
+ csv_converter = ToCSV::Converter.new(self, options, csv_options, &block)
53
+ csv_converter.to_csv
54
+ end
55
+ end
56
+
57
+ ActiveRecord::NamedScope::Scope.send(:include, ToCSV) if defined?(ActiveRecord::NamedScope::Scope)
58
+
59
+ class Array
60
+ remove_method :to_csv
61
+ extend ToCSV
62
+ include ToCSV
63
+ end
@@ -1,175 +1,174 @@
1
- #encoding: utf-8
2
-
3
- module ToCSV
4
-
5
- CSVClass = RUBY_VERSION < '1.9' ? ::FasterCSV : ::CSV
6
-
7
- class Converter
8
-
9
- def initialize(data, options = {}, csv_options = {}, &block)
10
- @opts = options.to_options.reverse_merge({
11
- :byte_order_marker => ToCSV.byte_order_marker,
12
- :locale => ToCSV.locale || ::I18n.locale,
13
- :primary_key => ToCSV.primary_key,
14
- :timestamps => ToCSV.timestamps
15
- })
16
-
17
- @opts[:only] = Array(@opts[:only]).map(&:to_s)
18
- @opts[:except] = Array(@opts[:except]).map(&:to_s)
19
- @opts[:methods] = Array(@opts[:methods]).map(&:to_s)
20
-
21
- @data = data
22
- @block = block
23
- @csv_options = csv_options.to_options.reverse_merge(ToCSV.csv_options)
24
- end
25
-
26
- def to_csv
27
- build_headers_and_rows
28
-
29
- output = CSVClass.generate(@csv_options) do |csv|
30
- csv << @header_row if @header_row.try(:any?)
31
- @rows.each { |row| csv << row }
32
- end
33
-
34
- @opts[:byte_order_marker] ? "\xEF\xBB\xBF#{output}" : output
35
- end
36
-
37
- private
38
-
39
- def build_headers_and_rows
40
- send "headers_and_rows_from_#{ discover_data_type }"
41
- end
42
-
43
- def discover_data_type
44
- test_data = @data.first
45
- return 'ar_object' if instance_of_active_record? test_data
46
- return 'hash' if test_data.is_a? Hash
47
- return 'unidimensional_array' if test_data.is_a?(Array) && !test_data.first.is_a?(Array)
48
- return 'bidimensional_array' if test_data.is_a?(Array) && test_data.first.is_a?(Array) && test_data.first.size == 2
49
- 'simple_data'
50
- end
51
-
52
- def instance_of_active_record?(obj)
53
- obj.class.base_class.superclass == ActiveRecord::Base
54
- rescue Exception
55
- false
56
- end
57
-
58
- def headers_and_rows_from_simple_data
59
- @header_row = nil
60
- @rows = [@data.dup]
61
- end
62
-
63
- def headers_and_rows_from_hash
64
- @header_row = @data.first.keys if display_headers?
65
- @rows = @data.map(&:values)
66
- end
67
-
68
- def headers_and_rows_from_unidimensional_array
69
- @header_row = @data.first if display_headers?
70
- @rows = @data[1..-1]
71
- end
72
-
73
- def headers_and_rows_from_bidimensional_array
74
- @header_row = @data.first.map(&:first) if display_headers?
75
- @rows = @data.map { |array| array.map(&:last) }
76
- end
77
-
78
- def headers_and_rows_from_ar_object
79
- attributes = sort_attributes(filter_attributes(attribute_names))
80
- @header_row = human_attribute_names(attributes) if display_headers?
81
-
82
- @rows = if @block
83
- @data.map do |item|
84
- os = OpenStruct.new
85
- @block.call(os, item)
86
- marshal_dump = os.marshal_dump
87
- attributes.map { |attribute| marshal_dump[attribute.to_sym] || try_formatting_date(item.send(attribute)) }
88
- end
89
- else
90
- @data.map do |item|
91
- attributes.map { |attribute| try_formatting_date item.send(attribute) }
92
- end
93
- end
94
- end
95
-
96
- def display_headers?
97
- @opts[:headers].nil? || (Array(@opts[:headers]).any? && Array(@opts[:headers]).all? { |h| h != false })
98
- end
99
-
100
- def human_attribute_names(attributes)
101
- @opts[:locale] ? translate(attributes) : humanize(attributes)
102
- end
103
-
104
- def humanize(attributes)
105
- attributes.map(&:humanize)
106
- end
107
-
108
- def translate(attributes)
109
- ::I18n.with_options :locale => @opts[:locale], :scope => [:activerecord, :attributes, @data.first.class.to_s.underscore] do |locale|
110
- attributes.map { |attribute| locale.t(attribute, :default => attribute.humanize) }
111
- end
112
- end
113
-
114
- def try_formatting_date(value)
115
- is_a_date?(value) ? value.to_s(:db) : value
116
- end
117
-
118
- def is_a_date?(value)
119
- value.is_a?(Time) || value.is_a?(Date) || value.is_a?(DateTime)
120
- end
121
-
122
- def primary_key_filter(attributes)
123
- return attributes if @opts[:primary_key]
124
- attributes - Array(@data.first.class.primary_key.to_s)
125
- end
126
-
127
- def timestamps_filter(attributes)
128
- return attributes if @opts[:timestamps]
129
- return attributes if (@opts[:only] + @opts[:except]).any? { |attribute| timestamps.include? attribute }
130
- attributes - timestamps
131
- end
132
-
133
- def timestamps
134
- %w[ created_at updated_at created_on updated_on ]
135
- end
136
-
137
- def methods_filter(attributes)
138
- attributes | @opts[:methods]
139
- end
140
-
141
- def only_filter(attributes)
142
- return attributes if @opts[:only].empty?
143
- attributes & @opts[:only]
144
- end
145
-
146
- def except_filter(attributes)
147
- attributes - @opts[:except]
148
- end
149
-
150
- def attribute_names
151
- @data.first.attribute_names.map(&:to_s)
152
- end
153
-
154
- def filter_attributes(attributes)
155
- attributes = methods_filter(attributes)
156
- attributes = primary_key_filter(attributes)
157
- attributes = timestamps_filter(attributes)
158
- attributes = @opts[:only].any?? only_filter(attributes) : except_filter(attributes)
159
- attributes
160
- end
161
-
162
- def sort_attributes(attributes)
163
- attributes = attributes.map(&:to_s).sort
164
- return attributes if @opts[:headers].nil?
165
- headers = Array(@opts[:headers]).map(&:to_s)
166
- headers.delete_if { |attribute| attribute == 'false' }
167
- if index = headers.index('all')
168
- (headers & attributes).insert(index, (attributes - headers)).flatten
169
- else
170
- headers + (attributes - headers)
171
- end
172
- end
173
- end
174
- end
175
-
1
+ #encoding: utf-8
2
+
3
+ module ToCSV
4
+
5
+ CSVClass = RUBY_VERSION < '1.9' ? ::FasterCSV : ::CSV
6
+
7
+ class Converter
8
+
9
+ def initialize(data, options = {}, csv_options = {}, &block)
10
+ @opts = options.to_options.reverse_merge({
11
+ :byte_order_marker => ToCSV.byte_order_marker,
12
+ :locale => ToCSV.locale || ::I18n.locale,
13
+ :primary_key => ToCSV.primary_key,
14
+ :timestamps => ToCSV.timestamps
15
+ })
16
+
17
+ @opts[:only] = Array(@opts[:only]).map(&:to_s)
18
+ @opts[:except] = Array(@opts[:except]).map(&:to_s)
19
+ @opts[:methods] = Array(@opts[:methods]).map(&:to_s)
20
+
21
+ @data = data
22
+ @block = block
23
+ @csv_options = csv_options.to_options.reverse_merge(ToCSV.csv_options)
24
+ end
25
+
26
+ def to_csv
27
+ build_headers_and_rows
28
+
29
+ output = CSVClass.generate(@csv_options) do |csv|
30
+ csv << @header_row if @header_row.try(:any?)
31
+ @rows.each { |row| csv << row }
32
+ end
33
+
34
+ @opts[:byte_order_marker] ? "\xEF\xBB\xBF#{output}" : output
35
+ end
36
+
37
+ private
38
+
39
+ def build_headers_and_rows
40
+ send "headers_and_rows_from_#{ discover_data_type }"
41
+ end
42
+
43
+ def discover_data_type
44
+ test_data = @data.first
45
+ return 'ar_object' if instance_of_active_record? test_data
46
+ return 'hash' if test_data.is_a? Hash
47
+ return 'unidimensional_array' if test_data.is_a?(Array) && !test_data.first.is_a?(Array)
48
+ return 'bidimensional_array' if test_data.is_a?(Array) && test_data.first.is_a?(Array) && test_data.first.size == 2
49
+ 'simple_data'
50
+ end
51
+
52
+ def instance_of_active_record?(obj)
53
+ obj.class.base_class.superclass == ActiveRecord::Base
54
+ rescue Exception
55
+ false
56
+ end
57
+
58
+ def headers_and_rows_from_simple_data
59
+ @header_row = nil
60
+ @rows = [@data.dup]
61
+ end
62
+
63
+ def headers_and_rows_from_hash
64
+ @header_row = @data.first.keys if display_headers?
65
+ @rows = @data.map(&:values)
66
+ end
67
+
68
+ def headers_and_rows_from_unidimensional_array
69
+ @header_row = @data.first if display_headers?
70
+ @rows = @data[1..-1]
71
+ end
72
+
73
+ def headers_and_rows_from_bidimensional_array
74
+ @header_row = @data.first.map(&:first) if display_headers?
75
+ @rows = @data.map { |array| array.map(&:last) }
76
+ end
77
+
78
+ def headers_and_rows_from_ar_object
79
+ attributes = sort_attributes(filter_attributes(attribute_names))
80
+ @header_row = human_attribute_names(attributes) if display_headers?
81
+
82
+ @rows = if @block
83
+ attributes_class = Struct.new(*attributes.map(&:to_sym))
84
+ @data.map do |item|
85
+ @block.call(obj = attributes_class.new, item)
86
+ attributes.map { |attribute| obj[attribute] || try_formatting_date(item.send(attribute)) }
87
+ end
88
+ else
89
+ @data.map do |item|
90
+ attributes.map { |attribute| try_formatting_date item.send(attribute) }
91
+ end
92
+ end
93
+ end
94
+
95
+ def display_headers?
96
+ @opts[:headers].nil? || (Array(@opts[:headers]).any? && Array(@opts[:headers]).all? { |h| h != false })
97
+ end
98
+
99
+ def human_attribute_names(attributes)
100
+ @opts[:locale] ? translate(attributes) : humanize(attributes)
101
+ end
102
+
103
+ def humanize(attributes)
104
+ attributes.map(&:humanize)
105
+ end
106
+
107
+ def translate(attributes)
108
+ ::I18n.with_options :locale => @opts[:locale], :scope => [:activerecord, :attributes, @data.first.class.to_s.underscore] do |locale|
109
+ attributes.map { |attribute| locale.t(attribute, :default => attribute.humanize) }
110
+ end
111
+ end
112
+
113
+ def try_formatting_date(value)
114
+ is_a_date?(value) ? value.to_s(:db) : value
115
+ end
116
+
117
+ def is_a_date?(value)
118
+ value.is_a?(Time) || value.is_a?(Date) || value.is_a?(DateTime)
119
+ end
120
+
121
+ def primary_key_filter(attributes)
122
+ return attributes if @opts[:primary_key]
123
+ attributes - Array(@data.first.class.primary_key.to_s)
124
+ end
125
+
126
+ def timestamps_filter(attributes)
127
+ return attributes if @opts[:timestamps]
128
+ return attributes if (@opts[:only] + @opts[:except]).any? { |attribute| timestamps.include? attribute }
129
+ attributes - timestamps
130
+ end
131
+
132
+ def timestamps
133
+ @timestamps ||= %w[ created_at updated_at created_on updated_on ]
134
+ end
135
+
136
+ def methods_filter(attributes)
137
+ attributes | @opts[:methods]
138
+ end
139
+
140
+ def only_filter(attributes)
141
+ return attributes if @opts[:only].empty?
142
+ attributes & @opts[:only]
143
+ end
144
+
145
+ def except_filter(attributes)
146
+ attributes - @opts[:except]
147
+ end
148
+
149
+ def attribute_names
150
+ @data.first.attribute_names.map(&:to_s)
151
+ end
152
+
153
+ def filter_attributes(attributes)
154
+ attributes = methods_filter(attributes)
155
+ attributes = primary_key_filter(attributes)
156
+ attributes = timestamps_filter(attributes)
157
+ attributes = @opts[:only].any?? only_filter(attributes) : except_filter(attributes)
158
+ attributes
159
+ end
160
+
161
+ def sort_attributes(attributes)
162
+ attributes = attributes.map(&:to_s).sort
163
+ return attributes if @opts[:headers].nil?
164
+ headers = Array(@opts[:headers]).map(&:to_s)
165
+ headers.delete_if { |attribute| attribute == 'false' }
166
+ if index = headers.index('all')
167
+ (headers & attributes).insert(index, (attributes - headers)).flatten
168
+ else
169
+ headers + (attributes - headers)
170
+ end
171
+ end
172
+ end
173
+ end
174
+