data_list_converter 0.1

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.
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