csv_decision 0.2.0 → 0.3.0

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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -1
  3. data/README.md +11 -8
  4. data/benchmarks/rufus_decision.rb +9 -1
  5. data/csv_decision.gemspec +1 -1
  6. data/doc/CSVDecision/CellValidationError.html +1 -1
  7. data/doc/CSVDecision/Columns/Dictionary.html +29 -29
  8. data/doc/CSVDecision/Columns.html +394 -47
  9. data/doc/CSVDecision/Data.html +2 -2
  10. data/doc/CSVDecision/Decide.html +23 -159
  11. data/doc/CSVDecision/Decision.html +370 -32
  12. data/doc/CSVDecision/Defaults.html +1 -1
  13. data/doc/CSVDecision/Dictionary/Entry.html +157 -55
  14. data/doc/CSVDecision/Dictionary.html +37 -21
  15. data/doc/CSVDecision/Error.html +1 -1
  16. data/doc/CSVDecision/FileError.html +1 -1
  17. data/doc/CSVDecision/Header.html +142 -1
  18. data/doc/CSVDecision/Index.html +741 -0
  19. data/doc/CSVDecision/Input.html +14 -61
  20. data/doc/CSVDecision/Load.html +1 -1
  21. data/doc/CSVDecision/Matchers/Constant.html +1 -1
  22. data/doc/CSVDecision/Matchers/Function.html +1 -1
  23. data/doc/CSVDecision/Matchers/Guard.html +13 -147
  24. data/doc/CSVDecision/Matchers/Matcher.html +13 -13
  25. data/doc/CSVDecision/Matchers/Numeric.html +1 -1
  26. data/doc/CSVDecision/Matchers/Pattern.html +1 -1
  27. data/doc/CSVDecision/Matchers/Proc.html +147 -14
  28. data/doc/CSVDecision/Matchers/Range.html +1 -1
  29. data/doc/CSVDecision/Matchers/Symbol.html +1 -1
  30. data/doc/CSVDecision/Matchers.html +55 -162
  31. data/doc/CSVDecision/Options.html +21 -21
  32. data/doc/CSVDecision/Parse.html +2 -180
  33. data/doc/CSVDecision/Result.html +220 -38
  34. data/doc/CSVDecision/ScanRow.html +69 -325
  35. data/doc/CSVDecision/Table.html +128 -40
  36. data/doc/CSVDecision/TableValidationError.html +1 -1
  37. data/doc/CSVDecision/Validate.html +1 -1
  38. data/doc/CSVDecision.html +4 -4
  39. data/doc/_index.html +8 -8
  40. data/doc/class_list.html +1 -1
  41. data/doc/file.README.html +13 -11
  42. data/doc/index.html +13 -11
  43. data/doc/method_list.html +206 -150
  44. data/doc/top-level-namespace.html +1 -1
  45. data/lib/csv_decision/columns.rb +87 -1
  46. data/lib/csv_decision/decision.rb +54 -29
  47. data/lib/csv_decision/defaults.rb +1 -1
  48. data/lib/csv_decision/dictionary.rb +32 -22
  49. data/lib/csv_decision/header.rb +17 -0
  50. data/lib/csv_decision/index.rb +107 -0
  51. data/lib/csv_decision/input.rb +45 -13
  52. data/lib/csv_decision/matchers/guard.rb +2 -0
  53. data/lib/csv_decision/matchers.rb +14 -8
  54. data/lib/csv_decision/options.rb +7 -19
  55. data/lib/csv_decision/parse.rb +12 -96
  56. data/lib/csv_decision/result.rb +10 -9
  57. data/lib/csv_decision/scan_row.rb +20 -44
  58. data/lib/csv_decision/table.rb +7 -4
  59. data/lib/csv_decision.rb +1 -1
  60. data/spec/csv_decision/columns_spec.rb +6 -6
  61. data/spec/csv_decision/data_spec.rb +0 -5
  62. data/spec/csv_decision/index_spec.rb +58 -0
  63. data/spec/csv_decision/input_spec.rb +7 -2
  64. data/spec/csv_decision/options_spec.rb +16 -1
  65. data/spec/csv_decision/parse_spec.rb +4 -5
  66. data/spec/csv_decision/table_spec.rb +70 -0
  67. data/spec/data/{valid → invalid}/empty.csv +0 -0
  68. data/spec/data/valid/index_example.csv +12 -0
  69. data/spec/data/valid/multi_column_index.csv +10 -0
  70. data/spec/data/valid/multi_column_index2.csv +12 -0
  71. data/spec/data/valid/options_in_file3.csv +13 -0
  72. metadata +16 -5
  73. data/lib/csv_decision/decide.rb +0 -45
@@ -490,5 +490,75 @@ describe CSVDecision::Table do
490
490
  end
491
491
  end
492
492
  end
493
+
494
+ context 'uses single column index to make correct decisions' do
495
+ examples = [
496
+ { example: 'evaluates single-column index CSV string',
497
+ options: { first_match: false },
498
+ data: <<~DATA
499
+ in:topic, in:region, out:team_member
500
+ sports, Europe, Alice
501
+ sports, , Bob
502
+ finance, America, Charlie
503
+ finance, Europe, Donald
504
+ finance, , Ernest
505
+ politics, Asia, Fujio
506
+ politics, America, Gilbert
507
+ politics, , Henry
508
+ sports, , Zach
509
+ finance, , Zach
510
+ politics, , Zach
511
+ DATA
512
+ },
513
+ { example: 'evaluates single-column index CSV file',
514
+ options: { first_match: false },
515
+ data: Pathname(File.join(SPEC_DATA_VALID, 'index_example.csv'))
516
+ }
517
+ ]
518
+ examples.each do |test|
519
+ %i[decide decide!].each do |method|
520
+ it "#{method} correctly #{test[:example]}" do
521
+ table = CSVDecision.parse(test[:data], test[:options])
522
+
523
+ expect(table.send(method, topic: 'politics', region: 'Arctic'))
524
+ .to eq(team_member: %w[Henry Zach])
525
+ end
526
+ end
527
+ end
528
+ end
529
+
530
+ context 'uses multi-column index to make correct decisions' do
531
+ examples = [
532
+ { example: 'evaluates multi-column index CSV string',
533
+ options: { first_match: true },
534
+ data: <<~DATA
535
+ guard:, in :type, IN :input, OUT :output
536
+ :number.present?, integer, none, :=0
537
+ :number.blank?, integer, none, :=nil
538
+ :number.present?, integer, one, :=1
539
+ :number.blank?, integer, one, :=nil
540
+ :string.present?, string, none, 0
541
+ :number.blank?, string, none, :=nil
542
+ :string.present?, string, one, 1
543
+ :number.blank?, string, one, :=nil
544
+ DATA
545
+ },
546
+ { example: 'evaluates multi-column index CSV file',
547
+ options: { first_match: true },
548
+ data: Pathname(File.join(SPEC_DATA_VALID, 'multi_column_index.csv'))
549
+ }
550
+ ]
551
+ examples.each do |test|
552
+ %i[decide decide!].each do |method|
553
+ it "#{method} correctly #{test[:example]}" do
554
+ table = CSVDecision.parse(test[:data], test[:options])
555
+
556
+ expect(table.send(method, number: 1, type: 'integer', input: 'none')).to eq(output: 0)
557
+ expect(table.send(method, number: nil, type: 'string', input: 'one')).to eq(output: nil)
558
+ expect(table.send(method, string: '1', type: 'string', input: 'one')).to eq(output: '1')
559
+ end
560
+ end
561
+ end
562
+ end
493
563
  end
494
564
  end
File without changes
@@ -0,0 +1,12 @@
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
+ sports,,Zach
11
+ finance,,Zach
12
+ politics,,Zach
@@ -0,0 +1,10 @@
1
+ #comment,
2
+ guard:,in :type, IN :input,OUT :output
3
+ :number.present?,integer,none,:=0
4
+ :number.blank?,integer,none,:=nil
5
+ :number.present?,integer,one,:=1
6
+ :number.blank?,integer,one,:=nil
7
+ :string.present?,string,none,0
8
+ :number.blank?,string,none,:=nil
9
+ :string.present?,string,one,1
10
+ :number.blank?,string,one,:=nil
@@ -0,0 +1,12 @@
1
+ #comment,
2
+ index : 2
3
+ guard:,in :type, IN :input,OUT :output
4
+ :number.present?,integer,none,:=0
5
+ :string.blank?,string,none,:=nil
6
+ :string.present?,string,none,0
7
+ :string.blank?,string,one,:=nil
8
+ :number.present?,integer,one,:=1
9
+ :number.blank?,integer,one,:=nil
10
+ :string.present?,string,one,1
11
+ :string.blank?,string,one,:=nil
12
+ :number.blank?,integer,none,:=nil
@@ -0,0 +1,13 @@
1
+ #comment,
2
+ accumulate, index : 1
3
+ regexp_implicit
4
+ IN :input,OUT :output
5
+ none,:=0
6
+ one,:=1
7
+ two,:=2
8
+ three,:=3
9
+ :=nil,none
10
+ :=0,none
11
+ :=1,one
12
+ :=2,two
13
+ :=3,three
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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Vickers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-13 00:00:00.000000000 Z
11
+ date: 2018-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -187,6 +187,7 @@ files:
187
187
  - doc/CSVDecision/Function.html
188
188
  - doc/CSVDecision/Guard.html
189
189
  - doc/CSVDecision/Header.html
190
+ - doc/CSVDecision/Index.html
190
191
  - doc/CSVDecision/Input.html
191
192
  - doc/CSVDecision/Load.html
192
193
  - doc/CSVDecision/Matchers.html
@@ -225,11 +226,11 @@ files:
225
226
  - lib/csv_decision.rb
226
227
  - lib/csv_decision/columns.rb
227
228
  - lib/csv_decision/data.rb
228
- - lib/csv_decision/decide.rb
229
229
  - lib/csv_decision/decision.rb
230
230
  - lib/csv_decision/defaults.rb
231
231
  - lib/csv_decision/dictionary.rb
232
232
  - lib/csv_decision/header.rb
233
+ - lib/csv_decision/index.rb
233
234
  - lib/csv_decision/input.rb
234
235
  - lib/csv_decision/load.rb
235
236
  - lib/csv_decision/matchers.rb
@@ -251,6 +252,7 @@ files:
251
252
  - spec/csv_decision/data_spec.rb
252
253
  - spec/csv_decision/decision_spec.rb
253
254
  - spec/csv_decision/examples_spec.rb
255
+ - spec/csv_decision/index_spec.rb
254
256
  - spec/csv_decision/input_spec.rb
255
257
  - spec/csv_decision/load_spec.rb
256
258
  - spec/csv_decision/matchers/function_spec.rb
@@ -263,14 +265,18 @@ files:
263
265
  - spec/csv_decision/parse_spec.rb
264
266
  - spec/csv_decision/table_spec.rb
265
267
  - spec/csv_decision_spec.rb
268
+ - spec/data/invalid/empty.csv
266
269
  - spec/data/invalid/invalid_header1.csv
267
270
  - spec/data/invalid/invalid_header2.csv
268
271
  - spec/data/invalid/invalid_header3.csv
269
272
  - spec/data/invalid/invalid_header4.csv
270
273
  - spec/data/valid/benchmark_regexp.csv
271
- - spec/data/valid/empty.csv
274
+ - spec/data/valid/index_example.csv
275
+ - spec/data/valid/multi_column_index.csv
276
+ - spec/data/valid/multi_column_index2.csv
272
277
  - spec/data/valid/options_in_file1.csv
273
278
  - spec/data/valid/options_in_file2.csv
279
+ - spec/data/valid/options_in_file3.csv
274
280
  - spec/data/valid/regular_expressions.csv
275
281
  - spec/data/valid/simple_constants.csv
276
282
  - spec/data/valid/simple_example.csv
@@ -308,6 +314,7 @@ test_files:
308
314
  - spec/csv_decision/data_spec.rb
309
315
  - spec/csv_decision/decision_spec.rb
310
316
  - spec/csv_decision/examples_spec.rb
317
+ - spec/csv_decision/index_spec.rb
311
318
  - spec/csv_decision/input_spec.rb
312
319
  - spec/csv_decision/load_spec.rb
313
320
  - spec/csv_decision/matchers/function_spec.rb
@@ -320,14 +327,18 @@ test_files:
320
327
  - spec/csv_decision/parse_spec.rb
321
328
  - spec/csv_decision/table_spec.rb
322
329
  - spec/csv_decision_spec.rb
330
+ - spec/data/invalid/empty.csv
323
331
  - spec/data/invalid/invalid_header1.csv
324
332
  - spec/data/invalid/invalid_header2.csv
325
333
  - spec/data/invalid/invalid_header3.csv
326
334
  - spec/data/invalid/invalid_header4.csv
327
335
  - spec/data/valid/benchmark_regexp.csv
328
- - spec/data/valid/empty.csv
336
+ - spec/data/valid/index_example.csv
337
+ - spec/data/valid/multi_column_index.csv
338
+ - spec/data/valid/multi_column_index2.csv
329
339
  - spec/data/valid/options_in_file1.csv
330
340
  - spec/data/valid/options_in_file2.csv
341
+ - spec/data/valid/options_in_file3.csv
331
342
  - spec/data/valid/regular_expressions.csv
332
343
  - spec/data/valid/simple_constants.csv
333
344
  - spec/data/valid/simple_example.csv
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # CSV Decision: CSV based Ruby decision tables.
4
- # Created December 2017.
5
- # @author Brett Vickers <brett@phillips-vickers.com>
6
- # See LICENSE and README.md for details.
7
- module CSVDecision
8
- # Main module for searching the decision table looking for one or more matches
9
- # @api private
10
- module Decide
11
- # Match the table row against the input hash.
12
- #
13
- # @param row [Array] Table row.
14
- # @param input [Hash{Symbol=>Object}] Input hash data structure.
15
- # @param scan_row [ScanRow]
16
- # @return [Boolean] Returns true if a match, false otherwise.
17
- def self.matches?(row:, input:, scan_row:)
18
- match = scan_row.match_constants?(row: row, scan_cols: input[:scan_cols])
19
- return false unless match
20
-
21
- return true if scan_row.procs.empty?
22
-
23
- scan_row.match_procs?(row: row, input: input)
24
- end
25
-
26
- # Main method for making decisions.
27
- #
28
- # @param table [CSVDecision::Table] Decision table.
29
- # @param input [Hash] Input hash (keys may or may not be symbolized)
30
- # @param symbolize_keys [true, false] Set to false if keys are symbolized and it's
31
- # OK to mutate the input hash. Otherwise a copy of the input hash is symbolized.
32
- # @return [Hash] Decision result.
33
- def self.decide(table:, input:, symbolize_keys:)
34
- # Parse and transform the hash supplied as input
35
- parsed_input = Input.parse(table: table, input: input, symbolize_keys: symbolize_keys)
36
-
37
- # The decision object collects the results of the search and
38
- # calculates the final result
39
- decision = Decision.new(table: table, input: parsed_input)
40
-
41
- # table_scan(table: table, input: parsed_input, decision: decision)
42
- decision.scan(table: table, input: parsed_input)
43
- end
44
- end
45
- end