to-csv 1.0.2 → 1.0.3

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/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.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
+ #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.3'
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.7'
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,63 +1,64 @@
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
+ require RUBY_VERSION < '1.9' ? 'fastercsv' : 'csv'
2
+ require 'ostruct'
3
+ require 'active_support/core_ext'
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 => ';', :encoding => 'UTF-8' }
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
64
+
@@ -1,174 +1,237 @@
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
-
1
+ # encoding: UTF-8
2
+
3
+ module ToCSV
4
+
5
+ CSVClass = RUBY_VERSION < '1.9' ? ::FasterCSV : ::CSV
6
+
7
+ class Converter
8
+ def initialize(data, options = {}, csv_options = {}, &block)
9
+ @opts = options.to_options.reverse_merge({
10
+ :byte_order_marker => ToCSV.byte_order_marker,
11
+ :locale => ToCSV.locale || ::I18n.locale,
12
+ :primary_key => ToCSV.primary_key,
13
+ :timestamps => ToCSV.timestamps
14
+ })
15
+
16
+ @opts[:only] = Array(@opts[:only]).map(&:to_s)
17
+ @opts[:except] = Array(@opts[:except]).map(&:to_s)
18
+ @opts[:methods] = Array(@opts[:methods]).map(&:to_s)
19
+
20
+ @data = data
21
+ @block = block
22
+ @csv_options = csv_options.to_options.reverse_merge(ToCSV.csv_options)
23
+ @associations = @opts[:include].kind_of?(Array) ? @opts[:include] : [@opts[:include]] if @opts[:include]
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
+ def build_headers_and_rows
39
+ send "headers_and_rows_from_#{ discover_data_type }"
40
+ end
41
+
42
+ def discover_data_type
43
+ test_data = @data.first
44
+ return 'ar_object' if instance_of_active_record? test_data
45
+ return 'hash' if test_data.is_a? Hash
46
+ return 'unidimensional_array' if test_data.is_a?(Array) && !test_data.first.is_a?(Array)
47
+ return 'bidimensional_array' if test_data.is_a?(Array) && test_data.first.is_a?(Array) && test_data.first.size == 2
48
+ 'simple_data'
49
+ end
50
+
51
+ def instance_of_active_record?(obj)
52
+ obj.class.base_class.superclass == ActiveRecord::Base
53
+ rescue Exception
54
+ false
55
+ end
56
+
57
+ def headers_and_rows_from_simple_data
58
+ @header_row = nil
59
+ @rows = [@data.dup]
60
+ end
61
+
62
+ def headers_and_rows_from_hash
63
+ @header_row = @data.first.keys if display_headers?
64
+ @rows = @data.map(&:values)
65
+ end
66
+
67
+ def headers_and_rows_from_unidimensional_array
68
+ @header_row = @data.first if display_headers?
69
+ @rows = @data[1..-1]
70
+ end
71
+
72
+ def headers_and_rows_from_bidimensional_array
73
+ @header_row = @data.first.map(&:first) if display_headers?
74
+ @rows = @data.map { |array| array.map(&:last) }
75
+ end
76
+
77
+ def headers_and_rows_from_ar_object
78
+ attributes = sort_attributes(filter_attributes(attribute_names))
79
+ if display_headers?
80
+ @header_row = human_attribute_names(attributes)
81
+ @header_row |= human_attribute_names(headers_from_association_ar_object) if @opts[:include]
82
+ end
83
+ @rows = if @block
84
+ attributes_class = Struct.new(*attributes.map(&:to_sym))
85
+ @data.map do |item|
86
+ @block.call(obj = attributes_class.new, item)
87
+ result = attributes.map { |attribute| obj[attribute] || try_formatting_date(item.send(attribute)) }
88
+ (result << rows_from_association_ar_object(item);result.flatten!) if @opts[:include] && @row_header_association
89
+ result
90
+ end
91
+ else
92
+ @data.map do |item|
93
+ result = attributes.map { |attribute| try_formatting_date item.send(attribute) }
94
+ (result << rows_from_association_ar_object(item);result.flatten!) if @opts[:include] && @row_header_association
95
+ result
96
+ end
97
+ end
98
+ end
99
+
100
+ def rows_from_association_ar_object(item)
101
+ @result = []
102
+ @associations.each do |association|
103
+ @row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}")
104
+ association = association.to_sym
105
+ case item.class.reflect_on_association(association).macro
106
+ when :has_many, :has_and_belongs_to_many
107
+ records = item.send(association).to_a
108
+ unless records.empty?
109
+ records.collect do |record|
110
+ @row_header_association.map do |attribute|
111
+ @result << try_formatting_date(record.send(attribute))
112
+ end
113
+ end
114
+ end
115
+ when :has_one, :belongs_to
116
+ if record = item.send(association)
117
+ @row_header_association.map do |attribute|
118
+ @result << try_formatting_date(record.send(attribute))
119
+ end
120
+ end
121
+ end
122
+ end
123
+ @result.flatten
124
+ end
125
+
126
+ def headers_from_association_ar_object
127
+ @header_association = []
128
+ @associations.each do |association|
129
+ association = association.to_sym
130
+ case @data.first.class.reflect_on_association(association).macro
131
+ when :has_many, :has_and_belongs_to_many
132
+ records = @data.first.send(association).to_a
133
+ unless records.empty?
134
+ instance_variable_set("@row_header_association_for_#{from_sym_to_string(association)}", records.first.attribute_names.map(&:to_s))
135
+ @row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}")
136
+ (1..records.size).each do |record|
137
+ @row_header_association.map do |header|
138
+ @header_association << "#{from_sym_to_string(association)}_#{record}_#{header}"
139
+ end
140
+ end
141
+ end
142
+ when :has_one, :belongs_to
143
+ instance_variable_set("@row_header_association_for_#{from_sym_to_string(association)}", @data.first.send(association).attribute_names.map(&:to_s))
144
+ @row_header_association = instance_variable_get("@row_header_association_for_#{from_sym_to_string(association)}")
145
+ @data.first.send(association).attribute_names.map do |header|
146
+ @header_association << "#{from_sym_to_string(association)}_#{header}"
147
+ end
148
+ end
149
+ end
150
+ @header_association.flatten
151
+ end
152
+
153
+ def display_headers?
154
+ @opts[:headers].nil? || (Array(@opts[:headers]).any? && Array(@opts[:headers]).all? { |h| h != false })
155
+ end
156
+
157
+ def human_attribute_names(attributes)
158
+ @opts[:locale] ? translate(attributes) : humanize(attributes)
159
+ end
160
+
161
+ def humanize(attributes)
162
+ attributes.map(&:humanize)
163
+ end
164
+
165
+ def translate(attributes)
166
+ ::I18n.with_options :locale => @opts[:locale], :scope => [:activerecord, :attributes, @data.first.class.to_s.underscore] do |locale|
167
+ attributes.map { |attribute| locale.t(attribute, :default => attribute.humanize) }
168
+ end
169
+ end
170
+
171
+ def try_formatting_date(value)
172
+ is_a_date?(value) ? value.to_s(:db) : value
173
+ end
174
+
175
+ def is_a_date?(value)
176
+ value.is_a?(Time) || value.is_a?(Date) || value.is_a?(DateTime)
177
+ end
178
+
179
+ def primary_key_filter(attributes)
180
+ return attributes if @opts[:primary_key]
181
+ attributes - Array(@data.first.class.primary_key.to_s)
182
+ end
183
+
184
+ def timestamps_filter(attributes)
185
+ return attributes if @opts[:timestamps]
186
+ return attributes if (@opts[:only] + @opts[:except]).any? { |attribute| timestamps.include? attribute }
187
+ attributes - timestamps
188
+ end
189
+
190
+ def timestamps
191
+ @timestamps ||= %w[ created_at updated_at created_on updated_on ]
192
+ end
193
+
194
+ def methods_filter(attributes)
195
+ attributes | @opts[:methods]
196
+ end
197
+
198
+ def only_filter(attributes)
199
+ return attributes if @opts[:only].empty?
200
+ attributes & @opts[:only]
201
+ end
202
+
203
+ def except_filter(attributes)
204
+ attributes - @opts[:except]
205
+ end
206
+
207
+ def attribute_names
208
+ @data.first.attribute_names.map(&:to_s)
209
+ end
210
+
211
+ def filter_attributes(attributes)
212
+ attributes = methods_filter(attributes)
213
+ attributes = primary_key_filter(attributes)
214
+ attributes = timestamps_filter(attributes)
215
+ attributes = @opts[:only].any?? only_filter(attributes) : except_filter(attributes)
216
+ attributes
217
+ end
218
+
219
+ def sort_attributes(attributes)
220
+ attributes = attributes.map(&:to_s).sort
221
+ return attributes if @opts[:headers].nil?
222
+ headers = Array(@opts[:headers]).map(&:to_s)
223
+ headers.delete_if { |attribute| attribute == 'false' }
224
+ if index = headers.index('all')
225
+ (headers & attributes).insert(index, (attributes - headers)).flatten
226
+ else
227
+ headers + (attributes - headers)
228
+ end
229
+ end
230
+
231
+ def from_sym_to_string(sym)
232
+ sym.to_s.gsub(':','')
233
+ end
234
+
235
+ end
236
+ end
237
+