csv_decision 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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