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 +7 -0
- data/.gitignore +2 -0
- data/Gemfile +2 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +18 -0
- data/data_list_converter.gemspec +22 -0
- data/lib/data_list_converter.rb +353 -0
- data/test/data_list_converter_test.rb +118 -0
- metadata +95 -0
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
data/Gemfile
ADDED
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
|