csv_decision 0.0.1 → 0.0.2

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.coveralls.yml +2 -0
  3. data/.rubocop.yml +16 -4
  4. data/.travis.yml +10 -0
  5. data/CHANGELOG.md +2 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +21 -0
  8. data/README.md +133 -19
  9. data/benchmark.rb +143 -0
  10. data/csv_decision.gemspec +8 -6
  11. data/lib/csv_decision.rb +18 -4
  12. data/lib/csv_decision/columns.rb +69 -0
  13. data/lib/csv_decision/data.rb +31 -16
  14. data/lib/csv_decision/decide.rb +47 -0
  15. data/lib/csv_decision/decision.rb +105 -0
  16. data/lib/csv_decision/header.rb +143 -8
  17. data/lib/csv_decision/input.rb +49 -0
  18. data/lib/csv_decision/load.rb +31 -0
  19. data/lib/csv_decision/matchers.rb +131 -0
  20. data/lib/csv_decision/matchers/numeric.rb +37 -0
  21. data/lib/csv_decision/matchers/pattern.rb +76 -0
  22. data/lib/csv_decision/matchers/range.rb +76 -0
  23. data/lib/csv_decision/options.rb +80 -50
  24. data/lib/csv_decision/parse.rb +77 -23
  25. data/lib/csv_decision/scan_row.rb +68 -0
  26. data/lib/csv_decision/table.rb +34 -6
  27. data/spec/csv_decision/columns_spec.rb +86 -0
  28. data/spec/csv_decision/data_spec.rb +16 -3
  29. data/spec/csv_decision/decision_spec.rb +30 -0
  30. data/spec/csv_decision/input_spec.rb +54 -0
  31. data/spec/csv_decision/load_spec.rb +28 -0
  32. data/spec/csv_decision/matchers/numeric_spec.rb +84 -0
  33. data/spec/csv_decision/matchers/pattern_spec.rb +183 -0
  34. data/spec/csv_decision/matchers/range_spec.rb +132 -0
  35. data/spec/csv_decision/options_spec.rb +67 -0
  36. data/spec/csv_decision/parse_spec.rb +2 -3
  37. data/spec/csv_decision/simple_example_spec.rb +45 -0
  38. data/spec/csv_decision/table_spec.rb +151 -0
  39. data/spec/data/invalid/invalid_header1.csv +4 -0
  40. data/spec/data/invalid/invalid_header2.csv +4 -0
  41. data/spec/data/invalid/invalid_header3.csv +4 -0
  42. data/spec/data/invalid/invalid_header4.csv +4 -0
  43. data/spec/data/valid/options_in_file1.csv +5 -0
  44. data/spec/data/valid/options_in_file2.csv +5 -0
  45. data/spec/data/valid/simple_example.csv +10 -0
  46. data/spec/data/valid/valid.csv +4 -4
  47. data/spec/spec_helper.rb +6 -0
  48. metadata +89 -12
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../../lib/csv_decision/parse'
3
+ require_relative '../../lib/csv_decision'
4
4
 
5
5
  describe CSVDecision::Parse do
6
6
  it 'loads an empty decision table' do
@@ -15,7 +15,6 @@ describe CSVDecision::Parse do
15
15
  result = CSVDecision.parse(file)
16
16
 
17
17
  expected = [
18
- ['IN :input', 'OUT :output'],
19
18
  ['input', '']
20
19
  ]
21
20
 
@@ -27,7 +26,7 @@ describe CSVDecision::Parse do
27
26
  Dir[File.join(CSVDecision.root, 'spec/data/valid/*.csv')].each do |file_name|
28
27
  pathname = Pathname(file_name)
29
28
 
30
- it "loads CSV file #{pathname.basename}" do
29
+ it "loads CSV file: #{pathname.basename}" do
31
30
  expect { CSVDecision.parse(pathname) }.not_to raise_error
32
31
  expect(CSVDecision.parse(pathname)).to be_a CSVDecision::Table
33
32
  end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/csv_decision'
4
+
5
+ context 'simple example' do
6
+ data = <<~DATA
7
+ in :topic, in :region, out :team_member
8
+ sports, Europe, Alice
9
+ sports, , Bob
10
+ finance, America, Charlie
11
+ finance, Europe, Donald
12
+ finance, , Ernest
13
+ politics, Asia, Fujio
14
+ politics, America, Gilbert
15
+ politics, , Henry
16
+ , , Zach
17
+ DATA
18
+
19
+ it 'makes correct decisions for CSV string' do
20
+ table = CSVDecision.parse(data)
21
+
22
+ result = table.decide(topic: 'finance', region: 'Europe')
23
+ expect(result).to eq(team_member: 'Donald')
24
+
25
+ result = table.decide(topic: 'sports', region: nil)
26
+ expect(result).to eq(team_member: 'Bob')
27
+
28
+ result = table.decide(topic: 'culture', region: 'America')
29
+ expect(result).to eq(team_member: 'Zach')
30
+ end
31
+
32
+
33
+ it 'makes correct decisions for CSV file' do
34
+ table = CSVDecision.parse(Pathname('spec/data/valid/simple_example.csv'))
35
+
36
+ result = table.decide(topic: 'finance', region: 'Europe')
37
+ expect(result).to eq(team_member: 'Donald')
38
+
39
+ result = table.decide(topic: 'sports', region: nil)
40
+ expect(result).to eq(team_member: 'Bob')
41
+
42
+ result = table.decide(topic: 'culture', region: 'America')
43
+ expect(result).to eq(team_member: 'Zach')
44
+ end
45
+ end
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/csv_decision'
4
+
5
+ SPEC_DATA_VALID ||= File.join(CSVDecision.root, 'spec', 'data', 'valid')
6
+
7
+ describe CSVDecision::Table do
8
+ describe '#decide' do
9
+ context 'makes correct decisions for simple, text-only tables' do
10
+ examples = [
11
+ {
12
+ example: 'parses CSV file',
13
+ options: {},
14
+ data: Pathname(File.join(SPEC_DATA_VALID, 'simple_example.csv'))
15
+ },
16
+ {
17
+ example: 'parses data array',
18
+ options: {},
19
+ data: [
20
+ ['in :topic', 'in :region', 'out :team member'],
21
+ ['sports', 'Europe', 'Alice'],
22
+ ['sports', '', 'Bob'],
23
+ ['finance', 'America', 'Charlie'],
24
+ ['finance', 'Europe', 'Donald'],
25
+ ['finance', '', 'Ernest'],
26
+ ['politics', 'Asia', 'Fujio'],
27
+ ['politics', 'America', 'Gilbert'],
28
+ ['politics', '', 'Henry'],
29
+ ['', '', 'Zach']
30
+ ]
31
+ },
32
+ ]
33
+ examples.each do |test|
34
+ %i[decide decide!].each do |method|
35
+ it "#{method} correctly #{test[:example]} with first_match: true" do
36
+ options = test[:options].merge(first_match: true)
37
+ table = CSVDecision.parse(test[:data], options)
38
+
39
+ expect(table.send(method, topic: 'finance', region: 'Europe')).to eq(team_member: 'Donald')
40
+ expect(table.send(method, topic: 'sports', region: nil)).to eq(team_member: 'Bob')
41
+ expect(table.send(method, topic: 'culture', region: 'America')).to eq(team_member: 'Zach')
42
+ end
43
+
44
+ it "#{method} correctly #{test[:example]} with first_match: false" do
45
+ options = test[:options].merge(first_match: false)
46
+ table = CSVDecision.parse(test[:data], options)
47
+
48
+ expect(table.send(method, topic: 'finance', region: 'Europe'))
49
+ .to eq(team_member: %w[Donald Ernest Zach])
50
+ expect(table.send(method, topic: 'sports', region: nil))
51
+ .to eq(team_member: %w[Bob Zach])
52
+ expect(table.send(method, topic: 'culture', region: 'America'))
53
+ .to eq(team_member: 'Zach')
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'makes correct decisions for a table with regexps and ranges' do
60
+ examples = [
61
+ {
62
+ example: 'implicit regular expressions',
63
+ options: { regexp_implicit: true },
64
+ data: <<~DATA
65
+ in :age, in :trait, out :salesperson
66
+ 18..35, maniac, Adelsky
67
+ 23..40, bad|maniac, Bronco
68
+ 36..50, bad.*, Espadas
69
+ 51..78, , Thorsten
70
+ 44..100, !~ maniac, Ojiisan
71
+ > 100, maniac.*, Chester
72
+ 23..35, .*rich, Kerfelden
73
+ , cheerful, Swanson
74
+ , maniac, Korolev
75
+ DATA
76
+ },
77
+ {
78
+ example: 'explicit regular expressions',
79
+ options: { regexp_implicit: false },
80
+ data: <<~DATA
81
+ in :age, in :trait, out :salesperson
82
+ 18..35, maniac, Adelsky
83
+ 23..40, =~ bad|maniac, Bronco
84
+ 36..50, =~ bad.*, Espadas
85
+ 51..78, , Thorsten
86
+ 44..100, !~ maniac, Ojiisan
87
+ > 100, =~ maniac.*, Chester
88
+ 23..35, =~ .*rich, Kerfelden
89
+ , cheerful, Swanson
90
+ , maniac, Korolev
91
+ DATA
92
+ },
93
+ {
94
+ example: 'multiple in column references',
95
+ options: { regexp_implicit: false },
96
+ data: <<~DATA
97
+ in :age, in :age, in :trait, out :salesperson
98
+ >= 18, <= 35, maniac, Adelsky
99
+ >= 23, <= 40, =~ bad|maniac, Bronco
100
+ >= 36, <= 50, =~ bad.*, Espadas
101
+ >= 51, <= 78, , Thorsten
102
+ >= 44, <= 100, != maniac, Ojiisan
103
+ > 100, , =~ maniac.*, Chester
104
+ >= 23, <= 35, =~ .*rich, Kerfelden
105
+ , , cheerful, Swanson
106
+ , , maniac, Korolev
107
+ DATA
108
+ },
109
+ ]
110
+ examples.each do |test|
111
+ %i[decide decide!].each do |method|
112
+ it "#{method} correctly uses #{test[:example]} with first_match: true" do
113
+ options = test[:options].merge(first_match: true)
114
+ table = CSVDecision.parse(test[:data], options)
115
+
116
+ expect(table.send(method, age: 72)).to eq(salesperson: 'Thorsten')
117
+ expect(table.send(method, age: 25, trait: 'very rich')).to eq(salesperson: 'Kerfelden')
118
+ expect(table.send(method, age: 25, trait: 'maniac')).to eq(salesperson: 'Adelsky')
119
+ expect(table.send(method, age: 44, trait: 'maniac')).to eq(salesperson: 'Korolev')
120
+ expect(table.send(method, age: 101, trait: 'maniacal')).to eq(salesperson: 'Chester')
121
+ expect(table.send(method, age: 45, trait: 'cheerful')).to eq(salesperson: 'Ojiisan')
122
+ expect(table.send(method, age: 49, trait: 'bad')).to eq(salesperson: 'Espadas')
123
+ expect(table.send(method, age: 40, trait: 'maniac')).to eq(salesperson: 'Bronco')
124
+ end
125
+
126
+ it "#{method} correctly uses #{test[:example]} with first_match: false" do
127
+ options = test[:options].merge(first_match: false)
128
+ table = CSVDecision.parse(test[:data], options)
129
+
130
+ expect(table.send(method, age: 72))
131
+ .to eq(salesperson: %w[Thorsten Ojiisan])
132
+ expect(table.send(method, age: 25, trait: 'very rich'))
133
+ .to eq(salesperson: 'Kerfelden')
134
+ expect(table.send(method, age: 25, trait: 'maniac'))
135
+ .to eq(salesperson: %w[Adelsky Bronco Korolev])
136
+ expect(table.send(method, age: 44, trait: 'maniac'))
137
+ .to eq(salesperson: 'Korolev')
138
+ expect(table.send(method, age: 101, trait: 'maniacal'))
139
+ .to eq(salesperson: 'Chester')
140
+ expect(table.send(method, age: 45, trait: 'cheerful'))
141
+ .to eq(salesperson: %w[Ojiisan Swanson])
142
+ expect(table.send(method, age: 49, trait: 'bad'))
143
+ .to eq(salesperson: %w[Espadas Ojiisan])
144
+ expect(table.send(method, age: 40, trait: 'maniac'))
145
+ .to eq(salesperson: %w[Bronco Korolev])
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,4 @@
1
+ #comment,
2
+ IN :input,BAD :output
3
+ input,#output
4
+ ,#end comment
@@ -0,0 +1,4 @@
1
+ #comment,
2
+ IN :input,BAD :-
3
+ input,#output
4
+ ,#end comment
@@ -0,0 +1,4 @@
1
+ #comment,
2
+ IN :input,IN :
3
+ input,#output
4
+ ,#end comment
@@ -0,0 +1,4 @@
1
+ #comment,
2
+ IN :input,IN :a-b
3
+ input,#output
4
+ ,#end comment
@@ -0,0 +1,5 @@
1
+ #comment,
2
+ accumulate
3
+ IN :input,OUT :output
4
+ input,#output
5
+ ,#end comment
@@ -0,0 +1,5 @@
1
+ #comment,
2
+ accumulate
3
+ regexp_implicit
4
+ IN :input,OUT :output
5
+ input,#output
@@ -0,0 +1,10 @@
1
+ in:topic,in:region,out:team_member
2
+ sports,Europe,Alice
3
+ sports,,Bob
4
+ finance,America,Charlie
5
+ finance,Europe,Donald
6
+ finance,,Ernest
7
+ politics,Asia,Fujio
8
+ politics,America,Gilbert
9
+ politics,,Henry
10
+ ,,Zach
@@ -1,4 +1,4 @@
1
- #comment,
2
- IN :input,OUT :output
3
- input,#output
4
- ,#end comment
1
+ ,#input columns,#empty column,#output column,#empty column
2
+ #header,IN :input,,OUT :output,
3
+ #data,input,,#output,
4
+ ,,,#end comment,
data/spec/spec_helper.rb CHANGED
@@ -12,6 +12,12 @@
12
12
  # the additional setup, and require it from the spec files that actually need
13
13
  # it.
14
14
  #
15
+ require 'active_support'
16
+ require 'active_support/core_ext'
17
+ require 'json'
18
+ require 'coveralls'
19
+ Coveralls.wear!
20
+
15
21
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
22
  RSpec.configure do |config|
17
23
  # rspec-expectations config goes here. You can use an alternate
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: csv_decision
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Vickers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-18 00:00:00.000000000 Z
11
+ date: 2017-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '5.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '5.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ice_nine
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.11'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: values
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: benchmark-ips
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -100,28 +128,28 @@ dependencies:
100
128
  requirements:
101
129
  - - "~>"
102
130
  - !ruby/object:Gem::Version
103
- version: '3.5'
131
+ version: '3.7'
104
132
  type: :development
105
133
  prerelease: false
106
134
  version_requirements: !ruby/object:Gem::Requirement
107
135
  requirements:
108
136
  - - "~>"
109
137
  - !ruby/object:Gem::Version
110
- version: '3.5'
138
+ version: '3.7'
111
139
  - !ruby/object:Gem::Dependency
112
140
  name: rubocop
113
141
  requirement: !ruby/object:Gem::Requirement
114
142
  requirements:
115
143
  - - "~>"
116
144
  - !ruby/object:Gem::Version
117
- version: '0.51'
145
+ version: '0.52'
118
146
  type: :development
119
147
  prerelease: false
120
148
  version_requirements: !ruby/object:Gem::Requirement
121
149
  requirements:
122
150
  - - "~>"
123
151
  - !ruby/object:Gem::Version
124
- version: '0.51'
152
+ version: '0.52'
125
153
  - !ruby/object:Gem::Dependency
126
154
  name: rufus-decision
127
155
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +170,14 @@ dependencies:
142
170
  requirements:
143
171
  - - "~>"
144
172
  - !ruby/object:Gem::Version
145
- version: '0.12'
173
+ version: '0.15'
146
174
  type: :development
147
175
  prerelease: false
148
176
  version_requirements: !ruby/object:Gem::Requirement
149
177
  requirements:
150
178
  - - "~>"
151
179
  - !ruby/object:Gem::Version
152
- version: '0.12'
180
+ version: '0.15'
153
181
  description: CSV based Ruby decision tables.
154
182
  email:
155
183
  - brett@phillips-vickers.com
@@ -157,22 +185,54 @@ executables: []
157
185
  extensions: []
158
186
  extra_rdoc_files: []
159
187
  files:
188
+ - ".coveralls.yml"
160
189
  - ".gitignore"
161
190
  - ".rspec"
162
191
  - ".rubocop.yml"
192
+ - ".travis.yml"
193
+ - CHANGELOG.md
163
194
  - Gemfile
195
+ - LICENSE
164
196
  - README.md
197
+ - benchmark.rb
165
198
  - csv_decision.gemspec
166
199
  - lib/csv_decision.rb
200
+ - lib/csv_decision/columns.rb
167
201
  - lib/csv_decision/data.rb
202
+ - lib/csv_decision/decide.rb
203
+ - lib/csv_decision/decision.rb
168
204
  - lib/csv_decision/header.rb
205
+ - lib/csv_decision/input.rb
206
+ - lib/csv_decision/load.rb
207
+ - lib/csv_decision/matchers.rb
208
+ - lib/csv_decision/matchers/numeric.rb
209
+ - lib/csv_decision/matchers/pattern.rb
210
+ - lib/csv_decision/matchers/range.rb
169
211
  - lib/csv_decision/options.rb
170
212
  - lib/csv_decision/parse.rb
213
+ - lib/csv_decision/scan_row.rb
171
214
  - lib/csv_decision/table.rb
172
215
  - spec/csv_decision.rb
216
+ - spec/csv_decision/columns_spec.rb
173
217
  - spec/csv_decision/data_spec.rb
218
+ - spec/csv_decision/decision_spec.rb
219
+ - spec/csv_decision/input_spec.rb
220
+ - spec/csv_decision/load_spec.rb
221
+ - spec/csv_decision/matchers/numeric_spec.rb
222
+ - spec/csv_decision/matchers/pattern_spec.rb
223
+ - spec/csv_decision/matchers/range_spec.rb
224
+ - spec/csv_decision/options_spec.rb
174
225
  - spec/csv_decision/parse_spec.rb
226
+ - spec/csv_decision/simple_example_spec.rb
227
+ - spec/csv_decision/table_spec.rb
228
+ - spec/data/invalid/invalid_header1.csv
229
+ - spec/data/invalid/invalid_header2.csv
230
+ - spec/data/invalid/invalid_header3.csv
231
+ - spec/data/invalid/invalid_header4.csv
175
232
  - spec/data/valid/empty.csv
233
+ - spec/data/valid/options_in_file1.csv
234
+ - spec/data/valid/options_in_file2.csv
235
+ - spec/data/valid/simple_example.csv
176
236
  - spec/data/valid/valid.csv
177
237
  - spec/spec_helper.rb
178
238
  homepage: https://github.com/bpvickers/csv_decision.git
@@ -187,7 +247,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
187
247
  requirements:
188
248
  - - ">="
189
249
  - !ruby/object:Gem::Version
190
- version: 2.3.1
250
+ version: 2.3.0
191
251
  required_rubygems_version: !ruby/object:Gem::Requirement
192
252
  requirements:
193
253
  - - ">="
@@ -195,14 +255,31 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
255
  version: '0'
196
256
  requirements: []
197
257
  rubyforge_project:
198
- rubygems_version: 2.6.14
258
+ rubygems_version: 2.6.10
199
259
  signing_key:
200
260
  specification_version: 4
201
261
  summary: CSV based Ruby decision tables.
202
262
  test_files:
203
263
  - spec/csv_decision.rb
264
+ - spec/csv_decision/columns_spec.rb
204
265
  - spec/csv_decision/data_spec.rb
266
+ - spec/csv_decision/decision_spec.rb
267
+ - spec/csv_decision/input_spec.rb
268
+ - spec/csv_decision/load_spec.rb
269
+ - spec/csv_decision/matchers/numeric_spec.rb
270
+ - spec/csv_decision/matchers/pattern_spec.rb
271
+ - spec/csv_decision/matchers/range_spec.rb
272
+ - spec/csv_decision/options_spec.rb
205
273
  - spec/csv_decision/parse_spec.rb
274
+ - spec/csv_decision/simple_example_spec.rb
275
+ - spec/csv_decision/table_spec.rb
276
+ - spec/data/invalid/invalid_header1.csv
277
+ - spec/data/invalid/invalid_header2.csv
278
+ - spec/data/invalid/invalid_header3.csv
279
+ - spec/data/invalid/invalid_header4.csv
206
280
  - spec/data/valid/empty.csv
281
+ - spec/data/valid/options_in_file1.csv
282
+ - spec/data/valid/options_in_file2.csv
283
+ - spec/data/valid/simple_example.csv
207
284
  - spec/data/valid/valid.csv
208
285
  - spec/spec_helper.rb