data_list_converter 0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f397cef319fe34bff9143714c69003b6e9f2cb2f
4
+ data.tar.gz: 96708c56c0bfd2a4e48ebff454ec004c3becda34
5
+ SHA512:
6
+ metadata.gz: 20f9d84f520b6039d6c4a2b7bb1c7e9367a16a18b30818170a98637742bccf9524d5a2fefaa919730eb4b736e66571f13ab9acea08ade8c29200d996b9a7f80a
7
+ data.tar.gz: 957d4cc8c98c9dc7cad99814ee4acaaa18cf222481c043926649b86241d4e5deb4db74d31e9631f97a428e10952a46ac152759a9ee24acaeda54a774cac11c62
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ Gemfile.lock
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 linjunhalida
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.md ADDED
@@ -0,0 +1,78 @@
1
+ # Data List Converter
2
+
3
+ Data List Converter is a tool to convert data between different formats.
4
+
5
+ Example:
6
+
7
+ ```ruby
8
+ data = [{name: 'James', age: '22'}, {name: 'Bob', age: '33'}]
9
+ DataListConverter.convert(:item_data, :table_data, data)
10
+ # => [["name", "age"], ["James", "22"], ["Bob", "33"]]
11
+ DataListConverter.convert(:item_data, :csv_file, data, csv_file: {filename: 'result.csv'})
12
+ DataListConverter.convert(:item_data, :xls_file, data, csv_file: {filename: 'result.csv'})
13
+
14
+ DataListConverter.convert(:csv_file, :item_data, {filename: 'result.csv'}) == data
15
+ ```
16
+
17
+ You can also add filter to this process:
18
+
19
+ ```ruby
20
+ filter = :limit
21
+ filter = {limit: {size: 2}}
22
+ filter = [{limit: {size: 12}}, {count: {size: 4}}]
23
+ convert(:item_iterator, :table_data, iter, table_iterator: {filter: filter})
24
+ ```
25
+
26
+ Please read [the source code](https://github.com/halida/data_list_converter/blob/master/lib/data_list_converter.rb) for more information,
27
+ also you can check [test examples](https://github.com/halida/data_list_converter/blob/master/test/data_list_converter_test.rb).
28
+
29
+ ## Data Types
30
+
31
+ - **item_data** like: `[{name: 'James', age: '22'}, ...]`
32
+ - **item_iterator** iterator for item_data, used like: iter.call{|item| out << item}
33
+ - **table_data** like: `[["name", "age"], ["James", "22"], ["Bob", "33"], ...]`
34
+ - **table_iterator** iterator for table_data
35
+ - **csv_file** file in csv format
36
+ - **xls_file** file in excel format
37
+ - **multi_sheet_data** like: `{'sheet1' => [columns, row, row, ...], 'sheet2' => [columns, row, row, ...]}`
38
+ - **multi_sheet_iterator** like: `{'sheet1' => table_iterator1, 'sheet2' => table_iterator2}`
39
+ - **records** ActiveRecord records, usage: `DataListConverter.convert(:records, :item_data, query, item_iterator: {columns: [:name, :age]})`
40
+
41
+
42
+ ## Filters
43
+
44
+ **item_iterator/table_iterator limit**: limit item_iterator result counts, usage: `DataListConverter.convert(:item_data, :table_data, item_data, item_iterator: {filter: {limit: {size: 2}}})`
45
+
46
+ **item_iterator count**: count item_iterator items, usage: `DataListConverter.convert(:xls_file, :item_data, {filename: 'result.xls'}, item_iterator: {filter: {count: {size: 10}}})`, it will print current item counts every `size`.
47
+
48
+ ## Extend
49
+
50
+ You can add your data types and filters, example:
51
+
52
+ ```ruby
53
+ DataListConverter.register_converter(
54
+ :records, :item_iterator, lambda { |records, options|
55
+ columns = options[:columns]
56
+ lambda { |&block|
57
+ records.find_each do |record|
58
+ item = columns.map do |column|
59
+ [column.first.to_sym, record.send(column[1])]
60
+ end.to_h
61
+ block.call(item)
62
+ end
63
+ }
64
+ })
65
+
66
+ DataListConverter.register_filter(
67
+ :item_iterator, :limit, lambda { |proc, options|
68
+ limit_size = options[:size] || 10
69
+ lambda { |&block|
70
+ limit = 0
71
+ proc.call do |item|
72
+ block.call(item)
73
+ limit += 1
74
+ break if limit >= limit_size
75
+ end
76
+ }
77
+ })
78
+ ```
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path("lib", __FILE__)
2
+
3
+ require 'bundler'
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ task :default => :test
7
+
8
+ task :test do
9
+ load 'test/data_list_converter_test.rb'
10
+ end
11
+
12
+ task :build do
13
+ sh 'gem build data_list_converter.gemspec'
14
+ end
15
+
16
+ task :upload do
17
+ sh 'gem push *.gem'
18
+ end
@@ -0,0 +1,22 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "data_list_converter"
5
+ s.version = "0.1"
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["linjunhalida"]
8
+ s.email = ["linjunhalida@gmail.com"]
9
+ s.homepage = "http://github.com/halida/data_list_converter"
10
+ s.summary = "convert data between different formats"
11
+ s.description = "Data List Converter is a tool to convert data between different formats."
12
+ s.licenses = ["MIT"]
13
+
14
+ s.add_development_dependency 'rake', '~> 10.4'
15
+ s.add_development_dependency 'minitest', '~> 5.7'
16
+ s.add_development_dependency 'spreadsheet', '~> 1.0'
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,353 @@
1
+ require 'csv'
2
+
3
+ # this class is used for convert data between types,
4
+ # so we can use it to easily export and import data.
5
+ #
6
+ class DataListConverter
7
+ CONVERTERS = {}
8
+ FILTERS = {}
9
+
10
+ class << self
11
+
12
+ def types
13
+ CONVERTERS.keys.flatten.uniq.sort
14
+ end
15
+
16
+ def register_converter(from_type, to_type, method)
17
+ @route_map = nil # clear cache
18
+ CONVERTERS[[from_type, to_type]] = method
19
+ end
20
+
21
+ def register_filter(type, name, method)
22
+ FILTERS[type] ||= {}
23
+ FILTERS[type][name] = method
24
+ end
25
+
26
+ # Example:
27
+ # convert(:item_iterator, :item_data, iter)
28
+ # convert(:item_iterator, :csv_file, iter, csv_file: {filename: 'result.csv'})
29
+ # convert(:csv_file, :item_data, {filename: 'result.csv'})
30
+ #
31
+ # can add filter:
32
+ # filter = :limit
33
+ # filter = {limit: {size: 2}}
34
+ # filter = [{limit: {size: 12}}, {count: {size: 4}}]
35
+ # convert(:item_iterator, :table_data, iter, table_iterator: {filter: filter})
36
+ def convert(from_type, to_type, from_value, options={})
37
+ methods = []
38
+ add_filter = lambda { |type|
39
+ filters = (options[type] || {}).delete(:filter)
40
+ return unless filters
41
+ methods += normalize_filters(type, filters)
42
+ }
43
+
44
+ route = find_route(from_type, to_type)
45
+ add_filter.call(route[0])
46
+
47
+ (0..(route.length-2)).map do |i|
48
+ from_type, to_type = route[i], route[i+1]
49
+ method = CONVERTERS[[from_type, to_type]]
50
+ raise "cannot find converter #{from_type} -> #{to_type}" unless method
51
+ methods.push([method, options[to_type] || {}])
52
+ add_filter.call(to_type)
53
+ end
54
+
55
+ methods.inject(from_value) do |v, method|
56
+ method, args = method
57
+ method.call(v, args)
58
+ end
59
+ end
60
+
61
+ def normalize_filters(type, filters)
62
+ # filter list as array
63
+ filters = [filters] unless filters.kind_of?(Array)
64
+ filters.map do |v|
65
+ # fix filter arguments
66
+ case v
67
+ # {:limit, {count: 12}} => [:limit, {count: 12}]
68
+ when Hash; v.first
69
+ # :debug => [:debug, {}]
70
+ when Symbol, String; [v, {}]
71
+ else; v
72
+ end
73
+ end.map do |name, args|
74
+ method = FILTERS[type][name] rescue raise("cannot find method for type #{type} filter #{name}")
75
+ [method, args]
76
+ end
77
+ end
78
+
79
+ # One type of data can be converted into any other types,
80
+ # we have a list of convert methods: CONVERTERS
81
+ # If we want to convert between types, like: convert item_data into csv_file,
82
+ # we need find all the intermidate data type, like: [:item_data, :item_iterator, :table_iterator, :csv_file]
83
+ def find_route(from_type, to_type)
84
+ raise Exception, "from_type should not equal to to_type: #{from_type}" if from_type == to_type
85
+ out = find_next_node([], [from_type], route_map, to_type)
86
+ raise Exception, "Route not found: #{from_type} -> #{to_type}" unless out
87
+ out
88
+ end
89
+
90
+ # iterate through the type convert graph, and find the route
91
+ def find_next_node (out, nodes, map, end_node)
92
+ nodes.each do |node|
93
+ return out + [node] if node == end_node
94
+ next unless next_nodes = map[node]
95
+
96
+ new_map = map.dup
97
+ new_map.delete(node)
98
+ result = find_next_node(out + [node], next_nodes, new_map, end_node)
99
+ return result if result
100
+ end
101
+ nil
102
+ end
103
+ private :find_next_node
104
+
105
+ # convert adjacency list into quick lookup hash
106
+ def route_map
107
+ @route_map ||= \
108
+ begin
109
+ CONVERTERS.keys.
110
+ inject({}) do |map, item|
111
+ map[item.first] ||= []
112
+ map[item.first] += [item[1]]
113
+ map
114
+ end
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+
121
+ # register types
122
+
123
+ # item_data: [{name: 'xx', value: 12}, ...]
124
+ # table_data: [['name', 'value'], ['xx', '12'], ..]
125
+ # item_iterator and table_iterator are iterators which yield each row
126
+ class DataListConverter
127
+ self.register_converter(
128
+ :item_iterator, :table_iterator, lambda{ |proc, options|
129
+ lambda { |&block|
130
+ columns = nil
131
+ proc.call do |item|
132
+ unless columns
133
+ columns = item.keys.map(&:to_sym)
134
+ block.call(columns.map(&:to_s))
135
+ end
136
+ # item_iterator key can be symbol or string
137
+ block.call(columns.map{ |c| item[c] || item[c.to_s] })
138
+ end
139
+ }
140
+ })
141
+ self.register_converter(
142
+ :table_iterator, :item_iterator, lambda{ |proc, options|
143
+ lambda {|&block|
144
+ columns = nil
145
+ proc.call do |row|
146
+ unless columns
147
+ columns = row.map(&:to_sym)
148
+ else
149
+ block.call(columns.zip(row).to_h)
150
+ end
151
+ end
152
+ }
153
+ })
154
+
155
+ def self.iterator_to_data(proc, options={})
156
+ out = []
157
+ proc.call { |d| out << d }
158
+ out
159
+ end
160
+ self.register_converter(
161
+ :item_iterator, :item_data, self.method(:iterator_to_data))
162
+ self.register_converter(
163
+ :table_iterator, :table_data, self.method(:iterator_to_data))
164
+
165
+ def self.data_to_iterator(data, options={})
166
+ lambda { |&block|
167
+ data.each do |d|
168
+ block.call(d)
169
+ end
170
+ }
171
+ end
172
+ self.register_converter(
173
+ :item_data, :item_iterator, self.method(:data_to_iterator))
174
+ self.register_converter(
175
+ :table_data, :table_iterator, self.method(:data_to_iterator))
176
+ end
177
+
178
+ # records
179
+ class DataListConverter
180
+ self.register_converter(
181
+ :records, :item_iterator, lambda { |records, options|
182
+ columns = options[:columns]
183
+ lambda { |&block|
184
+ records.find_each do |record|
185
+ item = columns.map do |column|
186
+ [column.first.to_sym, record.send(column[1])]
187
+ end.to_h
188
+ block.call(item)
189
+ end
190
+ }
191
+ })
192
+ end
193
+
194
+ # csv_file
195
+ class DataListConverter
196
+ self.register_converter(
197
+ :csv_file, :table_iterator, lambda { |input, options|
198
+ lambda { |&block|
199
+ CSV.open(input[:filename]) do |csv|
200
+ csv.each do |row|
201
+ block.call(row)
202
+ end
203
+ end
204
+ }
205
+ })
206
+ self.register_converter(
207
+ :table_iterator, :csv_file, lambda { |proc, options|
208
+ CSV.open(options[:filename], 'wb', force_quotes: true) do |csv|
209
+ proc.call do |row|
210
+ csv << row
211
+ end
212
+ end
213
+ })
214
+ end
215
+
216
+ # xls_file only works when installed spreadsheet gem
217
+ # multi_sheet_data = {
218
+ # 'sheet1' => [columns, row, row, ...],
219
+ # 'sheet2' => [columns, row, row, ...],
220
+ # }
221
+ begin
222
+ require 'spreadsheet'
223
+
224
+ class DataListConverter
225
+ self.register_converter(
226
+ :xls_file, :table_iterator, lambda { |input, options|
227
+ lambda { |&block|
228
+ book = Spreadsheet.open(input[:filename])
229
+ sheet = book.worksheet input[:sheet] || 0
230
+ sheet.each do |row|
231
+ block.call(row.to_a)
232
+ end
233
+ }
234
+ })
235
+ self.register_converter(
236
+ :table_iterator, :xls_file, lambda { |proc, options|
237
+ book = Spreadsheet::Workbook.new
238
+ sheet = book.create_worksheet(name: (options[:sheet] || "Sheet1"))
239
+ i = 0
240
+ proc.call do |row|
241
+ sheet.row(i).push *row
242
+ i += 1
243
+ end
244
+ book.write(options[:filename])
245
+ options[:filename]
246
+ })
247
+
248
+ self.register_converter(
249
+ :multi_sheet_iterator, :multi_sheet_data, lambda { |data, options|
250
+ data.map do |k, iter|
251
+ data = self.convert(:table_iterator, :table_data, iter)
252
+ [k, data]
253
+ end.to_h
254
+ })
255
+ self.register_converter(
256
+ :multi_sheet_data, :multi_sheet_iterator, lambda { |data, options|
257
+ data.map do |k, v|
258
+ iter = self.convert(:table_data, :table_iterator, v)
259
+ [k, iter]
260
+ end.to_h
261
+ })
262
+ self.register_converter(
263
+ :multi_sheet_iterator, :xls_file, lambda { |data, options|
264
+ book = Spreadsheet::Workbook.new
265
+ data.each do |name, table_iterator|
266
+ sheet = book.create_worksheet(name: name)
267
+ i = 0
268
+ table_iterator.call do |row|
269
+ sheet.row(i).concat(row)
270
+ i += 1
271
+ end
272
+ end
273
+ filename = options[:filename]
274
+ book.write(filename)
275
+ filename
276
+ })
277
+ self.register_converter(
278
+ :xls_file, :multi_sheet_iterator, lambda { |data, options|
279
+ book = Spreadsheet.open(data[:filename])
280
+ book.worksheets.map do |sheet|
281
+ iterator = lambda { |&block|
282
+ sheet.each do |row|
283
+ block.call(row.to_a)
284
+ end
285
+ }
286
+ [sheet.name, iterator]
287
+ end.to_h
288
+ })
289
+ end
290
+ rescue LoadError
291
+ nil
292
+ end
293
+
294
+
295
+ # filters
296
+ class DataListConverter
297
+ self.register_filter(
298
+ :table_iterator, :remove_debug, lambda{ |proc, options|
299
+ lambda { |&block|
300
+ columns = nil
301
+ debug_index = nil
302
+ proc.call do |row|
303
+ unless columns
304
+ columns = row
305
+ debug_index = columns.index('debug')
306
+ end
307
+ block.call(row[0..(debug_index-1)])
308
+ end
309
+ }
310
+ })
311
+
312
+ def self.iterator_limit(proc, options)
313
+ limit_size = options[:size] || 10
314
+ lambda { |&block|
315
+ limit = 0
316
+ proc.call do |item|
317
+ block.call(item)
318
+ limit += 1
319
+ break if limit >= limit_size
320
+ end
321
+ }
322
+ end
323
+ self.register_filter(
324
+ :item_iterator, :limit, self.method(:iterator_limit))
325
+ self.register_filter(
326
+ :table_iterator, :limit, self.method(:iterator_limit))
327
+
328
+ self.register_filter(
329
+ :item_iterator, :count, lambda{ |proc, options|
330
+ count = 0
331
+ size = options[:size] || 100
332
+ msg_format = options[:msg] || "on %{count}"
333
+ total_format = options[:total] || "total: %{total}"
334
+ out = options[:out] || STDOUT
335
+ total = 1
336
+ lambda { |&block|
337
+ proc.call do |item|
338
+ if item.keys == [:total]
339
+ total = item[:total]
340
+ out.write(total_format % {total: total})
341
+ out.write("\n")
342
+ else
343
+ block.call(item)
344
+ count += 1
345
+ msg = msg_format % {count: count, percent: (count / total.to_f * 100).round(2)}
346
+ out.write(msg + "\n") if count % size == 0
347
+ end
348
+ end
349
+ }
350
+ })
351
+ end
352
+
353
+
@@ -0,0 +1,118 @@
1
+ require 'minitest/pride'
2
+ require 'minitest/autorun'
3
+
4
+ require 'data_list_converter'
5
+
6
+ ITEM_DATA = [
7
+ {name: "James", score: '12'},
8
+ {name: "Bob", score: '33'},
9
+ ]
10
+
11
+ TABLE_DATA = [
12
+ ['name', 'score'],
13
+ ['James', '12'],
14
+ ['Bob', '33']
15
+ ]
16
+
17
+ CSV_DATA = %{
18
+ "name","score"
19
+ "James","12"
20
+ "Bob","33"
21
+ }.strip
22
+
23
+ describe DataListConverter do
24
+ describe :type do
25
+ it 'works' do
26
+ DataListConverter.types.must_equal(
27
+ [:csv_file, :item_data, :item_iterator, :multi_sheet_data, :multi_sheet_iterator,
28
+ :records, :table_data, :table_iterator, :xls_file])
29
+ end
30
+ end
31
+
32
+ describe :convert do
33
+ before :all do
34
+ @c = DataListConverter
35
+ end
36
+
37
+ it 'works' do
38
+ @c.convert(:item_data, :table_data, ITEM_DATA).must_equal TABLE_DATA
39
+ end
40
+
41
+ it 'has parameters' do
42
+ begin
43
+ @c.convert(:item_data, :csv_file, ITEM_DATA, csv_file: {filename: "test.csv"})
44
+ File.read("test.csv").strip.must_equal CSV_DATA
45
+ @c.convert(:csv_file, :item_data, {filename: "test.csv"}).must_equal(ITEM_DATA)
46
+ ensure
47
+ FileUtils.rm_f("test.csv")
48
+ end
49
+ end
50
+
51
+ it 'has filters' do
52
+ filter = :limit
53
+ item_data = [{name: "james"}] * 20
54
+ result = @c.convert(:item_data, :table_data, item_data,
55
+ item_iterator: {filter: filter})
56
+ result.must_equal([["name"]] + [["james"]]*10)
57
+
58
+ filter = {limit: {size: 2}}
59
+ item_data = [{name: "james"}] * 10
60
+ result = @c.convert(:item_data, :table_data, item_data,
61
+ item_iterator: {filter: filter})
62
+ result.must_equal [["name"], ["james"], ["james"]]
63
+
64
+ string = StringIO.new
65
+ filter = [{limit: {size: 12}}, {count: {size: 4, out: string, msg: ".%{count}"}}]
66
+ item_data = [{name: "james"}] * 20
67
+ result = @c.convert(:item_data, :table_data, item_data,
68
+ item_iterator: {filter: filter})
69
+ result.must_equal([["name"]] + [["james"]]*12)
70
+ string.string.must_equal ".4\n.8\n.12\n"
71
+ end
72
+
73
+ it 'has type xls_file' do
74
+ begin
75
+ @c.convert(:item_data, :xls_file, ITEM_DATA, xls_file: {filename: "test.xls"})
76
+ @c.convert(:xls_file, :item_data, {filename: "test.xls"}).must_equal ITEM_DATA
77
+
78
+ multi_sheet = {
79
+ "sheet1" => [['name'], ['james'], ['bob']],
80
+ "sheet2" => [['value'], ['21'], ['12']],
81
+ }
82
+ @c.convert(:multi_sheet_data, :xls_file, multi_sheet, xls_file: {filename: 'test.xls'})
83
+ @c.convert(:xls_file, :multi_sheet_data, {filename: 'test.xls'}).must_equal multi_sheet
84
+ ensure
85
+ FileUtils.rm_f("test.xls")
86
+ end
87
+ end
88
+ end
89
+
90
+ describe :filters do
91
+ it 'limit iterator' do
92
+ filter = {limit: {size: 2}}
93
+ item_data = [{name: "james"}] * 10
94
+ result = DataListConverter.convert(
95
+ :item_data, :table_data, item_data,
96
+ item_iterator: {filter: filter})
97
+ result.must_equal [["name"], ["james"], ["james"]]
98
+ end
99
+
100
+ it 'count item_iterator' do
101
+ iter = lambda { |&block|
102
+ total = 10000
103
+ block.call(total: total)
104
+ (1..total-1).each do |i|
105
+ block.call(id: i, value: i*i)
106
+ end
107
+ }
108
+ string = StringIO.new
109
+ filter = {count: {size: 4000,
110
+ out: string,
111
+ msg: "%{percent}%"}}
112
+ result = DataListConverter.convert(
113
+ :item_iterator, :table_data, iter,
114
+ item_iterator: {filter: filter})
115
+ string.string.split("\n").must_equal ['total: 10000', '40.0%', '80.0%']
116
+ end
117
+ end
118
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: data_list_converter
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - linjunhalida
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '10.4'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '10.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.7'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: spreadsheet
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: Data List Converter is a tool to convert data between different formats.
56
+ email:
57
+ - linjunhalida@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - MIT-LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - data_list_converter.gemspec
68
+ - lib/data_list_converter.rb
69
+ - test/data_list_converter_test.rb
70
+ homepage: http://github.com/halida/data_list_converter
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.4.8
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: convert data between different formats
94
+ test_files:
95
+ - test/data_list_converter_test.rb