data_list_converter 0.3.10 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85c9410c6b8c64a1af696f42485e098f43f432c2
4
- data.tar.gz: 930b7bb74e9d441e183451d4329e639c06aa3cdf
3
+ metadata.gz: 7862bf6ab71eb4187b03ba25e7bd199ea9d3eb51
4
+ data.tar.gz: f095138e55e9cc72c1691628c782a9c7ec5a203d
5
5
  SHA512:
6
- metadata.gz: b53cb6e80ac57b256b74100421b12d14c9e1f57bee172807bb0f6437efe27e2c2c8915705d52b0e1ff01855188a6e4a3ac274138f7623cbec648a187ba349631
7
- data.tar.gz: c87df4a93536be36f7aec7d88c3c93325b39fe03ab09414d4d1533024569e6d550f46cfff724df48bd39612bd6b52727473acbe20b1fdd273b92194b4f9286fc
6
+ metadata.gz: dfaa0989bafc3ae7e8d8675827b5728dd0476e1f62f79021b121407af2fa05ee4b47456f1db22aa46eb3aadfb4549a4ca3d0cf0c85bd0f8e6334c5105b36f1aa
7
+ data.tar.gz: f9056d920a5164dc109490c809fd8d57174ffcc30d6851f52cb676937befce297e42ff89c702b60b2d2b825c3edc55b8b744538744296bb6c83c36e4a1bcf9ad
data/README.md CHANGED
@@ -22,61 +22,73 @@ DataListConverter.convert(:xls_file, :multi_sheet_item_data, {filename: 'result.
22
22
  You can also add filter to this process:
23
23
 
24
24
  ```ruby
25
- filters = [
26
- # filter with default options
27
- :limit,
28
- # filter with options
29
- {limit: {size: 2}},
30
- # multiple filters
31
- [{limit: {size: 12}},
32
- {count: {size: 4}}],
33
- ]
34
- filters.map do |filter|
35
- data = [{name: "james"}] * 10
36
- DataListConverter.convert(:item_data, :table_data, data, table_iterator: {filter: filter})
37
- end
25
+ data = (1..20).map{|i| {name: "user-#{i}", age: i+20}}
26
+ # filter with default options (limit 10)
27
+ DataListConverter.convert(:item_data, :table_data, data, item_iterator: {filter: :limit})
28
+ # filter with options
29
+ DataListConverter.convert(:item_data, :table_data, data, item_iterator: {filter: {limit: {size: 2}}})
30
+ # multiple filters
31
+ DataListConverter.convert(:item_data, :table_data, data, item_iterator: {filter: [{limit: {size: 12}}, {count: {size: 4}}]})
38
32
  ```
39
33
 
40
- Please read [the source code](https://github.com/halida/data_list_converter/blob/master/lib/data_list_converter/) for more information,
41
- also you can check [test examples](https://github.com/halida/data_list_converter/blob/master/test/).
42
-
43
34
  ## Data Types
44
35
 
36
+ Default data types:
37
+
45
38
  - **item_data** like: `[{name: 'James', age: '22'}, ...]`, keys should be symbol.
46
39
  - **item_iterator** iterator for item_data, used like: iter.call{|item| out << item}
47
40
  - **table_data** like: `[["name", "age"], ["James", "22"], ["Bob", "33"], ...]`
48
41
  - **table_iterator** iterator for table_data
49
- - **csv_file** file in csv format
50
- - **xls_file** file in excel format, should install `spreadsheet` gem, and `require 'data_list_converter/types/xls_file'`
51
- - **xlsx_file** file in excel xml format, should install `rubyXL` gem, and `require 'data_list_converter/types/xlsx_file'`
52
42
  - **multi_sheet** Contains several data with sheets:
53
43
  - **multi_sheet_table_iterator**: like: `{sheet1: table_iterator1, sheet2: table_iterator2}`
54
44
  - **multi_sheet_table_data**: like: `{sheet1: [['name', 'age'], ...], sheet2: ...}`
55
45
  - **multi_sheet_item_iterator**: like: `{sheet1: item_iterator1, sheet2: item_iterator2}`
56
46
  - **multi_sheet_item_data**: like: `{sheet1: [{name: 'James', age: 32}], sheet2: ...}`
57
- - **records** ActiveRecord records, usage: `DataListConverter.convert(:records, :item_data, Users.where(condition), item_iterator: {columns: [:name, :age]})`
58
47
 
48
+ Plugin data types, should required first by `require 'data_list_converter/types/#{type}'`
49
+
50
+ - **csv_file** file in csv format
51
+ - **xls_file** file in excel format, should install `spreadsheet` gem first
52
+ - **xlsx_file** file in excel xml format, should install `rubyXL` gem first
53
+ - **records** ActiveRecord records
54
+
55
+ Please check [test examples](https://github.com/halida/data_list_converter/blob/master/test/types_test.rb) to see how to use those types.
59
56
 
60
57
  ## Filters
61
58
 
62
- **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}}})`
59
+ **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}}})`, default limit size is 10.
63
60
 
64
- **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`.
61
+ **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`, please [see here](https://github.com/halida/data_list_converter/blob/master/lib/data_list_converter/filters/count.rb) for more options.
65
62
 
66
- Please check more [test examples](https://github.com/halida/data_list_converter/blob/master/test/filters_test.rb)
63
+ Please see [test examples](https://github.com/halida/data_list_converter/blob/master/test/filters_test.rb) to learn how to use filter.
64
+
65
+ ## helpers
66
+
67
+ - **types**: Get current valid types.
68
+ - **routes**: Get current valid routes.
69
+ - **file_types**: Get current file types, which is the types has `_file` suffix.
70
+ - **get_file_format**: Get file type by filename, which compare file extension with `file_types`. `DataListConverter.get_file_format('xxx.xls') == :xls_file`
71
+ - **save_to_file**: Save data to file, it will use `get_file_format` to find proper file format. `DataListConverter.save_to_file(filename, data, data_format=:item_data)`
72
+ - **load_from_file**: Get data from file, it will use `get_file_format` to find proper file format. `DataListConverter.load_from_file(filename, data_format=:item_data)`
73
+ - **unify_item_data_keys**: Sometimes in the `item_data` list, each data keys don't exactly same, so use this function to fix it, example: `DataListConverter.unify_item_data_keys([{a: 12}, {b: 11}]) == [{a: 12, b: nil}, {a: nil, b: 11}]`.
74
+ - **flatten**: Flatten multi level item_data list into one level, example: `DataListConverter.flatten({a: {b: 12}, c: {d: {e: 11}}}) == {:"a:b"=>12, :"c:d:e"=>11}`, can change seperator: `DataListConverter.flatten(data, '_')`
67
75
 
68
76
  ## Extend
69
77
 
70
78
  You can add your own data types and filters, example:
71
79
 
72
80
  ```ruby
73
- DataListConverter.register_converter(:records, :item_iterator) do |records, options|
74
- columns = options[:columns]
81
+ DataListConverter.register_converter(:records, :item_iterator) do |input, options|
82
+ query = self.parameter(input, :query, :input)
83
+ columns = self.parameter(input, :columns, :input)
84
+ display = input[:display] || columns
85
+
75
86
  lambda { |&block|
76
- records.find_each do |record|
77
- item = columns.map do |column|
78
- [column.first.to_sym, record.send(column[1])]
79
- end.to_h
87
+ query.pluck(*columns).each do |data|
88
+ item = {}
89
+ data.each_with_index do |d, i|
90
+ item[display[i]] = d
91
+ end
80
92
  block.call(item)
81
93
  end
82
94
  }
@@ -94,14 +106,3 @@ DataListConverter.register_filter(:item_iterator, :limit) do |proc, options|
94
106
  }
95
107
  end
96
108
  ```
97
-
98
-
99
- ## Todo
100
-
101
- - check type exists
102
- - route not found, list all routes
103
- - register can bind method again
104
- - better API
105
- - better error message
106
- - load file/save file with auto format by extname
107
- - report error when cannot find type
data/changelog.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 0.4
2
+
3
+ Add parameter check, type check, change record type API.
4
+
1
5
  ## 0.3
2
6
 
3
7
  Refactor.
@@ -17,6 +17,8 @@ Gem::Specification.new do |s|
17
17
  s.add_development_dependency 'spreadsheet', '~> 1.0'
18
18
  s.add_development_dependency 'rubyXL', '~> 3.3'
19
19
  s.add_development_dependency 'pry', '~> 0.10.1'
20
+ s.add_development_dependency 'sqlite3', '~> 1.3'
21
+ s.add_development_dependency 'activerecord', '~> 4.2'
20
22
 
21
23
  s.files = `git ls-files`.split("\n")
22
24
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -21,6 +21,7 @@ class DataListConverter
21
21
  puts "#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\t#{msg}"
22
22
  end
23
23
 
24
+ # register_converter(:item_data, :item_iterator){ |data, options| ... }
24
25
  def register_converter(from_type, to_type, &block)
25
26
  @route_map = nil # clear cache
26
27
  CONVERTERS[[from_type, to_type]] = block
@@ -91,7 +92,11 @@ class DataListConverter
91
92
  # If we want to convert between types, like: convert item_data into csv_file,
92
93
  # we need find all the intermidate data type, like: [:item_data, :item_iterator, :table_iterator, :csv_file]
93
94
  def find_route(from_type, to_type)
95
+ [from_type, to_type].each do |type|
96
+ raise Exception, "cannot find type: #{type}" unless self.types.include?(type)
97
+ end
94
98
  raise Exception, "from_type should not equal to to_type: #{from_type}" if from_type == to_type
99
+
95
100
  # map wide search
96
101
  checked = Set.new
97
102
  checking = Set.new([from_type])
@@ -124,7 +129,10 @@ class DataListConverter
124
129
  checking += Set.new(next_nodes) - checked
125
130
  end
126
131
  end
127
- raise Exception, "Route not found: #{from_type} -> #{to_type}"
132
+
133
+ log = ["Route not found: #{from_type} -> #{to_type}", "Current routes:"]
134
+ log += self.routes.map{|from, to| "#{from} -> #{to}"}
135
+ raise Exception, log.join("\n")
128
136
  end
129
137
 
130
138
  # convert adjacency list into quick lookup hash
@@ -5,6 +5,7 @@ class DataListConverter
5
5
  msg_format = options[:msg] || "on %{count}"
6
6
  total_format = options[:total] || "total: %{total}"
7
7
  out = options[:out] || STDOUT
8
+
8
9
  total = 1
9
10
  lambda { |&block|
10
11
  proc.call do |item|
@@ -23,6 +23,10 @@ class DataListConverter
23
23
  CONVERTERS.keys.flatten.uniq.sort
24
24
  end
25
25
 
26
+ def self.routes
27
+ CONVERTERS.keys
28
+ end
29
+
26
30
  def self.file_types
27
31
  matcher = /(.*)_file$/
28
32
  DataListConverter.types.select do |type|
@@ -69,4 +73,10 @@ class DataListConverter
69
73
  end
70
74
  end
71
75
 
76
+ def self.parameter(data, key, type)
77
+ raise Exception, "`#{type}` should be hash, not `#{data.class}`: #{data}" unless data.kind_of?(Hash)
78
+ raise Exception, "Need `#{key}` for `#{type}`, current: #{data}" if not data.has_key?(key)
79
+ data.fetch(key)
80
+ end
81
+
72
82
  end
@@ -3,7 +3,8 @@ require 'csv'
3
3
  class DataListConverter
4
4
  self.register_converter(:csv_file, :table_iterator) do |input, options|
5
5
  lambda { |&block|
6
- CSV.open(input[:filename]) do |csv|
6
+ filename = self.parameter(input, :filename, :input)
7
+ CSV.open(filename) do |csv|
7
8
  csv.each do |row|
8
9
  block.call(row)
9
10
  end
@@ -12,7 +13,8 @@ class DataListConverter
12
13
  end
13
14
 
14
15
  self.register_converter(:table_iterator, :csv_file) do |proc, options|
15
- CSV.open(options[:filename], 'wb', force_quotes: true) do |csv|
16
+ filename = self.parameter(options, :filename, :csv_file)
17
+ CSV.open(filename, 'wb', force_quotes: true) do |csv|
16
18
  proc.call do |row|
17
19
  csv << row
18
20
  end
@@ -1,13 +1,15 @@
1
1
  class DataListConverter
2
2
 
3
3
  def self.marshal_file_to_data(input, options=nil)
4
- File.open(input[:filename]) do |f|
4
+ filename = self.parameter(input, :filename, :input)
5
+ File.open(filename) do |f|
5
6
  Marshal.load(f)
6
7
  end
7
8
  end
8
9
 
9
10
  def self.marshal_data_to_file(data, options)
10
- File.open(options[:filename], 'w+') do |f|
11
+ filename = self.parameter(options, :filename, :marshal)
12
+ File.open(filename, 'w+') do |f|
11
13
  Marshal.dump(data, f)
12
14
  end
13
15
  options[:filename]
@@ -1,13 +1,20 @@
1
1
  # ActiveRecords
2
2
  class DataListConverter
3
3
 
4
- self.register_converter(:records, :item_iterator) do |records, options|
5
- columns = options[:columns]
4
+ # columns = [:table_column1, :table_column2, ...]
5
+ # display = [:display_name1, :display_name2, ...]
6
+ # convert(:records, :item_data, query: query, columns: columns, display: display)
7
+ self.register_converter(:records, :item_iterator) do |input, options|
8
+ query = self.parameter(input, :query, :input)
9
+ columns = self.parameter(input, :columns, :input)
10
+ display = input[:display] || columns
11
+
6
12
  lambda { |&block|
7
- records.find_each do |record|
8
- item = columns.map do |column|
9
- [column[0].to_sym, record.send(column[1])]
10
- end.to_h
13
+ query.pluck(*columns).each do |data|
14
+ item = {}
15
+ data.each_with_index do |d, i|
16
+ item[display[i]] = d
17
+ end
11
18
  block.call(item)
12
19
  end
13
20
  }
@@ -5,7 +5,8 @@ class DataListConverter
5
5
 
6
6
  self.register_converter(:xls_file, :table_iterator) do |input, options|
7
7
  lambda { |&block|
8
- book = Spreadsheet.open(input[:filename])
8
+ filename = self.parameter(input, :filename, :input)
9
+ book = Spreadsheet.open(filename)
9
10
  sheet = book.worksheet input[:sheet] || 0
10
11
  sheet.each do |row|
11
12
  block.call(row.to_a)
@@ -21,8 +22,9 @@ class DataListConverter
21
22
  sheet.row(i).push *row
22
23
  i += 1
23
24
  end
24
- book.write(options[:filename])
25
- options[:filename]
25
+ filename = self.parameter(options, :filename, :xls_file)
26
+ book.write(filename)
27
+ filename
26
28
  end
27
29
 
28
30
  self.register_converter(:multi_sheet_table_iterator, :xls_file) do |data, options|
@@ -35,13 +37,14 @@ class DataListConverter
35
37
  i += 1
36
38
  end
37
39
  end
38
- filename = options[:filename]
40
+ filename = self.parameter(options, :filename, :xls_file)
39
41
  book.write(filename)
40
42
  filename
41
43
  end
42
44
 
43
- self.register_converter(:xls_file, :multi_sheet_table_iterator) do |data, options|
44
- book = Spreadsheet.open(data[:filename])
45
+ self.register_converter(:xls_file, :multi_sheet_table_iterator) do |input, options|
46
+ filename = self.parameter(input, :filename, :input)
47
+ book = Spreadsheet.open(filename)
45
48
  book.worksheets.map do |sheet|
46
49
  iterator = lambda { |&block|
47
50
  sheet.each do |row|
@@ -5,7 +5,8 @@ class DataListConverter
5
5
 
6
6
  self.register_converter(:xlsx_file, :table_iterator) do |input, options|
7
7
  lambda { |&block|
8
- book = RubyXL::Parser.parse(input[:filename])
8
+ filename = self.parameter(input, :filename, :input)
9
+ book = RubyXL::Parser.parse(filename)
9
10
  sheet = book.worksheets[input[:sheet] || 0]
10
11
  sheet.each do |row|
11
12
  next unless row
@@ -26,7 +27,7 @@ class DataListConverter
26
27
  end
27
28
  i += 1
28
29
  end
29
- filename = options[:filename]
30
+ filename = self.parameter(options, :filename, :xlsx_file)
30
31
  book.write(filename)
31
32
  filename
32
33
  end
@@ -55,13 +56,14 @@ class DataListConverter
55
56
  i += 1
56
57
  end
57
58
  end
58
- filename = options[:filename]
59
+ filename = self.parameter(options, :filename, :xlsx_file)
59
60
  book.write(filename)
60
61
  filename
61
62
  end
62
63
 
63
- self.register_converter(:xlsx_file, :multi_sheet_table_iterator) do |data, options|
64
- book = RubyXL::Parser.parse(data[:filename])
64
+ self.register_converter(:xlsx_file, :multi_sheet_table_iterator) do |input, options|
65
+ filename = self.parameter(input, :filename, :input)
66
+ book = RubyXL::Parser.parse(filename)
65
67
  book.worksheets.map do |sheet|
66
68
  iterator = lambda { |&block|
67
69
  sheet.each do |row|
@@ -1,3 +1,3 @@
1
1
  class DataListConverter
2
- VERSION = "0.3.10".freeze
2
+ VERSION = "0.4.0".freeze
3
3
  end
data/test/base_test.rb CHANGED
@@ -40,6 +40,12 @@ describe DataListConverter do
40
40
  string.string.must_equal ".4\n.8\n.12\n"
41
41
  end
42
42
 
43
+ it 'test data format exists' do
44
+ -> {
45
+ DataListConverter.convert(:item_data, :ppp, {a: 12})
46
+ }.must_raise Exception
47
+ end
48
+
43
49
  end
44
50
 
45
51
  end
data/test/types_test.rb CHANGED
@@ -99,5 +99,36 @@ describe DataListConverter do
99
99
  end
100
100
  end
101
101
  end
102
+
103
+ describe :records do
104
+ specify do
105
+ require 'sqlite3'
106
+ require 'active_record'
107
+
108
+ ActiveRecord::Base.establish_connection(
109
+ adapter: 'sqlite3',
110
+ database: ':memory:'
111
+ )
112
+
113
+ ActiveRecord::Schema.define do
114
+ create_table :users, force: true do |t|
115
+ t.string :name
116
+ t.integer :age
117
+ end
118
+ end
119
+
120
+ class User < ActiveRecord::Base; end
121
+ (1..10).each{ |i| User.create(name: "user-#{i}", age: i+20) }
122
+
123
+ query = User.where("age > 25 and age < 27")
124
+ columns = [:name, :age]
125
+ @c.convert(:records, :item_data, query: query, columns: columns).
126
+ must_equal([{name: "user-6", age: 26}])
127
+
128
+ display = [:n, :a]
129
+ @c.convert(:records, :item_data, query: query, columns: columns, display: display).
130
+ must_equal([{n: "user-6", a: 26}])
131
+ end
132
+ end
102
133
 
103
134
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_list_converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.10
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - linjunhalida
@@ -80,6 +80,34 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.10.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: sqlite3
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activerecord
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '4.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '4.2'
83
111
  description: Data List Converter is a tool to convert data between different formats.
84
112
  email:
85
113
  - linjunhalida@gmail.com