csv_decision 0.0.3 → 0.0.4

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +2 -0
  3. data/.gitignore +2 -1
  4. data/.travis.yml +2 -3
  5. data/CHANGELOG.md +19 -1
  6. data/README.md +49 -16
  7. data/{benchmark.rb → benchmarks/rufus_decision.rb} +1 -1
  8. data/csv_decision.gemspec +1 -1
  9. data/doc/CSVDecision/CellValidationError.html +143 -0
  10. data/doc/CSVDecision/Columns/Default.html +409 -0
  11. data/doc/CSVDecision/Columns/Dictionary.html +410 -0
  12. data/doc/CSVDecision/Columns/Entry.html +321 -0
  13. data/doc/CSVDecision/Columns.html +476 -0
  14. data/doc/CSVDecision/Constant.html +295 -0
  15. data/doc/CSVDecision/Data.html +344 -0
  16. data/doc/CSVDecision/Decide.html +434 -0
  17. data/doc/CSVDecision/Decision.html +604 -0
  18. data/doc/CSVDecision/Error.html +139 -0
  19. data/doc/CSVDecision/FileError.html +143 -0
  20. data/doc/CSVDecision/Function.html +229 -0
  21. data/doc/CSVDecision/Header.html +520 -0
  22. data/doc/CSVDecision/Input.html +305 -0
  23. data/doc/CSVDecision/Load.html +225 -0
  24. data/doc/CSVDecision/Matchers/Constant.html +242 -0
  25. data/doc/CSVDecision/Matchers/Function.html +342 -0
  26. data/doc/CSVDecision/Matchers/Matcher.html +325 -0
  27. data/doc/CSVDecision/Matchers/Numeric.html +277 -0
  28. data/doc/CSVDecision/Matchers/Pattern.html +600 -0
  29. data/doc/CSVDecision/Matchers/Range.html +413 -0
  30. data/doc/CSVDecision/Matchers/Symbol.html +280 -0
  31. data/doc/CSVDecision/Matchers.html +1529 -0
  32. data/doc/CSVDecision/Numeric.html +259 -0
  33. data/doc/CSVDecision/Options.html +445 -0
  34. data/doc/CSVDecision/Parse.html +270 -0
  35. data/doc/CSVDecision/ScanRow.html +746 -0
  36. data/doc/CSVDecision/Symbol.html +256 -0
  37. data/doc/CSVDecision/Table.html +1115 -0
  38. data/doc/CSVDecision.html +652 -0
  39. data/doc/_index.html +410 -0
  40. data/doc/class_list.html +51 -0
  41. data/doc/css/common.css +1 -0
  42. data/doc/css/full_list.css +58 -0
  43. data/doc/css/style.css +499 -0
  44. data/doc/file.README.html +264 -0
  45. data/doc/file_list.html +56 -0
  46. data/doc/frames.html +17 -0
  47. data/doc/index.html +264 -0
  48. data/doc/js/app.js +248 -0
  49. data/doc/js/full_list.js +216 -0
  50. data/doc/js/jquery.js +4 -0
  51. data/doc/method_list.html +683 -0
  52. data/doc/top-level-namespace.html +110 -0
  53. data/lib/csv_decision/columns.rb +15 -12
  54. data/lib/csv_decision/constant.rb +54 -0
  55. data/lib/csv_decision/decide.rb +5 -5
  56. data/lib/csv_decision/decision.rb +3 -1
  57. data/lib/csv_decision/function.rb +32 -0
  58. data/lib/csv_decision/header.rb +27 -18
  59. data/lib/csv_decision/input.rb +11 -8
  60. data/lib/csv_decision/matchers/constant.rb +18 -0
  61. data/lib/csv_decision/matchers/function.rb +11 -44
  62. data/lib/csv_decision/matchers/numeric.rb +5 -33
  63. data/lib/csv_decision/matchers/pattern.rb +26 -11
  64. data/lib/csv_decision/matchers/range.rb +21 -5
  65. data/lib/csv_decision/matchers/symbol.rb +20 -0
  66. data/lib/csv_decision/matchers.rb +85 -20
  67. data/lib/csv_decision/numeric.rb +38 -0
  68. data/lib/csv_decision/options.rb +36 -27
  69. data/lib/csv_decision/parse.rb +46 -39
  70. data/lib/csv_decision/scan_row.rb +19 -7
  71. data/lib/csv_decision/symbol.rb +73 -0
  72. data/lib/csv_decision/table.rb +24 -18
  73. data/lib/csv_decision.rb +25 -18
  74. data/spec/csv_decision/columns_spec.rb +1 -1
  75. data/spec/csv_decision/constant_spec.rb +60 -0
  76. data/spec/csv_decision/examples_spec.rb +119 -0
  77. data/spec/csv_decision/matchers/function_spec.rb +48 -28
  78. data/spec/csv_decision/matchers/numeric_spec.rb +4 -41
  79. data/spec/csv_decision/matchers/range_spec.rb +31 -61
  80. data/spec/csv_decision/matchers/symbol_spec.rb +65 -0
  81. data/spec/csv_decision/options_spec.rb +14 -2
  82. data/spec/csv_decision/parse_spec.rb +10 -0
  83. data/spec/csv_decision/table_spec.rb +112 -6
  84. data/spec/data/valid/simple_constants.csv +3 -3
  85. metadata +62 -7
  86. data/spec/csv_decision/simple_example_spec.rb +0 -75
  87. /data/spec/{csv_decision.rb → csv_decision_spec.rb} +0 -0
@@ -6,42 +6,47 @@
6
6
  module CSVDecision
7
7
  # Decision Table that accepts input hashes and makes decisions
8
8
  class Table
9
- # CSVDecision::Columns object - dictionary of all input and output columns
9
+ # @return [CSVDecision::Columns] Dictionary of all input and output columns.
10
10
  attr_accessor :columns
11
11
 
12
- # File path name if decision table loaded from a CSV file
12
+ # @return [File, Pathname, nil] File path name if decision table was loaded from a CSV file.
13
13
  attr_accessor :file
14
14
 
15
- # All options used to parse the table
15
+ # @return [Hash] All options, explicitly set or defaulted, used to parse the table.
16
16
  attr_accessor :options
17
17
 
18
- # Set if the table has any output functions (planned feature)
19
- attr_accessor :outs_functions
18
+ # Set if the table row has any output functions (planned feature)
19
+ # attr_accessor :outs_functions
20
20
 
21
- # Data rows - an array of arrays
21
+ # @return [Array<Array>] Data rows after parsing.
22
22
  attr_accessor :rows
23
23
 
24
- # Array of CSVDecision::ScanRow objects used to implement matching logic
24
+ # @return [Array<CSVDecision::ScanRow>] Scanning objects used to implement input matching logic.
25
25
  attr_accessor :scan_rows
26
26
 
27
- # Any array of CSVDecision::Table pre-loaded tables passed to this decision table
28
- # at load time. Used to allow this decision table to lookup values in other
29
- # decision tables. (Planned feature.)
30
- attr_reader :tables
27
+ # @return [Array<CSVDecision::ScanRow>] Used to implement outputting of final results.
28
+ attr_accessor :outs_rows
29
+
30
+ # @return Array<CSVDecision::Table>] pre-loaded tables passed to this decision table
31
+ # at load time. Used to allow this decision table to lookup values in other
32
+ # decision tables. (Planned feature.)
33
+ # attr_reader :tables
31
34
 
32
35
  # Main public method for making decisions.
33
36
  #
34
- # @param input [Hash] - input hash (keys may or may not be symbolized)
35
- # @return [Hash{Symbol => Object, Array<Object>}] decision
37
+ # @note Input hash keys may or may not be symbolized.
38
+ # @param input [Hash] Input hash.
39
+ # @return [Hash{Symbol => Object, Array<Object>}] Decision hash.
36
40
  def decide(input)
37
41
  Decide.decide(table: self, input: input, symbolize_keys: true).result
38
42
  end
39
43
 
40
- # Unsafe version of decide - will mutate the hash if set: column type
44
+ # Unsafe version of decide - will mutate the hash if +set: column+ type
41
45
  # is used (planned feature).
42
46
  #
43
- # @param input [Hash{Symbol => Object}] - input hash (all keys must already be symbolized)
44
- # @return [Hash{Symbol => Object, Array<Object>}]
47
+ # @param input (see #decide)
48
+ # @note Input hash must have its keys symbolized.
49
+ # @return (see #decide)
45
50
  def decide!(input)
46
51
  Decide.decide(table: self, input: input, symbolize_keys: false).result
47
52
  end
@@ -49,8 +54,8 @@ module CSVDecision
49
54
  # Iterate through all data rows of the decision table, with an optional
50
55
  # first and last row index given.
51
56
  #
52
- # @param first [Integer] start row
53
- # @param last [Integer, nil] last row
57
+ # @param first [Integer] Start row.
58
+ # @param last [Integer, nil] Last row.
54
59
  def each(first = 0, last = @rows.count - 1)
55
60
  index = first
56
61
  while index <= (last || first)
@@ -66,6 +71,7 @@ module CSVDecision
66
71
  @matchers = []
67
72
  @options = nil
68
73
  @outs_functions = nil
74
+ @outs_rows = []
69
75
  @rows = []
70
76
  @scan_rows = []
71
77
  @tables = nil
data/lib/csv_decision.rb CHANGED
@@ -4,7 +4,8 @@ require 'active_support/core_ext/object'
4
4
  require 'csv_decision/parse'
5
5
 
6
6
  # CSV Decision: CSV based Ruby decision tables.
7
- # Created December 2017 by Brett Vickers
7
+ # Created December 2017.
8
+ # @author Brett Vickers <brett@phillips-vickers.com>
8
9
  # See LICENSE and README.md for details.
9
10
  module CSVDecision
10
11
  # @return [String] gem project's root directory
@@ -12,23 +13,29 @@ module CSVDecision
12
13
  File.dirname __dir__
13
14
  end
14
15
 
15
- autoload :Data, 'csv_decision/data'
16
- autoload :Decide, 'csv_decision/decide'
17
- autoload :Decision, 'csv_decision/decision'
18
- autoload :Columns, 'csv_decision/columns'
19
- autoload :Header, 'csv_decision/header'
20
- autoload :Input, 'csv_decision/input'
21
- autoload :Load, 'csv_decision/load'
22
- autoload :Matchers, 'csv_decision/matchers'
23
- autoload :Options, 'csv_decision/options'
24
- autoload :Parse, 'csv_decision/parse'
25
- autoload :ScanRow, 'csv_decision/scan_row'
26
- autoload :Table, 'csv_decision/table'
16
+ autoload :Constant, 'csv_decision/constant'
17
+ autoload :Data, 'csv_decision/data'
18
+ autoload :Decide, 'csv_decision/decide'
19
+ autoload :Decision, 'csv_decision/decision'
20
+ autoload :Columns, 'csv_decision/columns'
21
+ autoload :Function, 'csv_decision/function'
22
+ autoload :Header, 'csv_decision/header'
23
+ autoload :Input, 'csv_decision/input'
24
+ autoload :Load, 'csv_decision/load'
25
+ autoload :Matchers, 'csv_decision/matchers'
26
+ autoload :Numeric, 'csv_decision/numeric'
27
+ autoload :Options, 'csv_decision/options'
28
+ autoload :Parse, 'csv_decision/parse'
29
+ autoload :ScanRow, 'csv_decision/scan_row'
30
+ autoload :Symbol, 'csv_decision/symbol'
31
+ autoload :Table, 'csv_decision/table'
27
32
 
28
- module Matchers
29
- autoload :Function, 'csv_decision/matchers/function'
30
- autoload :Numeric, 'csv_decision/matchers/numeric'
31
- autoload :Pattern, 'csv_decision/matchers/pattern'
32
- autoload :Range, 'csv_decision/matchers/range'
33
+ class Matchers
34
+ autoload :Constant, 'csv_decision/matchers/constant'
35
+ autoload :Function, 'csv_decision/matchers/function'
36
+ autoload :Numeric, 'csv_decision/matchers/numeric'
37
+ autoload :Pattern, 'csv_decision/matchers/pattern'
38
+ autoload :Range, 'csv_decision/matchers/range'
39
+ autoload :Symbol, 'csv_decision/matchers/symbol'
33
40
  end
34
41
  end
@@ -74,7 +74,7 @@ describe CSVDecision::Columns do
74
74
  end
75
75
 
76
76
  context 'rejects invalid CSV decision table columns' do
77
- Dir[File.join(SPEC_DATA_INVALID, 'invalid_columns*.csv')].each do |file_name|
77
+ Dir[File.join(SPEC_DATA_INVALID, 'invalid_header*.csv')].each do |file_name|
78
78
  pathname = Pathname(file_name)
79
79
 
80
80
  it "rejects CSV file #{pathname.basename}" do
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/csv_decision'
4
+
5
+ describe CSVDecision::Constant do
6
+ context 'cell string value recognition' do
7
+ cells = {
8
+ ':= 0.0' => { operator: ':=', value: '0.0' },
9
+ ':= 0.' => { operator: ':=', value: '0.' },
10
+ ':= .0' => { operator: ':=', value: '.0' },
11
+ '==0.0' => { operator: '==', value: '0.0' },
12
+ '== 0.' => { operator: '==', value: '0.' },
13
+ '==.0' => { operator: '==', value: '.0' },
14
+ '=0.0' => { operator: '=', value: '0.0' },
15
+ '= 0.' => { operator: '=', value: '0.' },
16
+ '=.0' => { operator: '=', value: '.0' },
17
+ '= nil' => { operator: '=', value: 'nil' },
18
+ ':= nil' => { operator: ':=', value: 'nil' },
19
+ '==nil' => { operator: '==', value: 'nil' }
20
+ }
21
+ cells.each_pair do |cell, expected|
22
+ it "recognises #{cell} as a constant" do
23
+ match = described_class::EXPRESSION.match(cell)
24
+ expect(match['operator']).to eq expected[:operator]
25
+ expect(match['value']).to eq expected[:value]
26
+ end
27
+ end
28
+ end
29
+
30
+ describe '#matches?' do
31
+ context 'constant matches value' do
32
+ data = [
33
+ ['= 1', 1],
34
+ ['== 1', 1],
35
+ [':=1', 1],
36
+ ['==.1', BigDecimal('0.1')],
37
+ [':= 1.1', BigDecimal('1.1')]
38
+ ]
39
+
40
+ data.each do |cell, value|
41
+ it "constant #{cell} matches #{value}" do
42
+ proc = described_class.matches?(cell)
43
+ expect(proc).to be_a(CSVDecision::Proc)
44
+ expect(proc.type).to eq :constant
45
+ expect(proc.function).to eq value
46
+ end
47
+ end
48
+ end
49
+
50
+ context 'does not match strings and non-constants' do
51
+ data = ['true', 'nil', 'false', ':column', '> 0', '!= 1.0', 'abc.*def', '-1..1', '0...3']
52
+
53
+ data.each do |cell|
54
+ it "cell #{cell} is not a non-string constant}" do
55
+ expect(described_class.matches?(cell)).to eq false
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../lib/csv_decision'
4
+
5
+ context 'simple examples' do
6
+ context 'simple example - strings-only' do
7
+ data = <<~DATA
8
+ in :topic, in :region, out :team_member
9
+ sports, Europe, Alice
10
+ sports, , Bob
11
+ finance, America, Charlie
12
+ finance, Europe, Donald
13
+ finance, , Ernest
14
+ politics, Asia, Fujio
15
+ politics, America, Gilbert
16
+ politics, , Henry
17
+ , , Zach
18
+ DATA
19
+
20
+ it 'makes correct decisions for CSV string' do
21
+ table = CSVDecision.parse(data)
22
+
23
+ result = table.decide(topic: 'finance', region: 'Europe')
24
+ expect(result).to eq(team_member: 'Donald')
25
+
26
+ result = table.decide(topic: 'sports', region: nil)
27
+ expect(result).to eq(team_member: 'Bob')
28
+
29
+ result = table.decide(topic: 'culture', region: 'America')
30
+ expect(result).to eq(team_member: 'Zach')
31
+ end
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
46
+
47
+ context 'simple example - constants' do
48
+ data = <<~DATA
49
+ in :constant, out :value
50
+ :=nil, :=nil
51
+ ==false, ==false
52
+ =true, =true
53
+ = 0, = 0
54
+ :=100.0, :=100.0
55
+ DATA
56
+
57
+ it 'makes correct decisions for CSV string' do
58
+ table = CSVDecision.parse(data)
59
+
60
+ result = table.decide(constant: nil)
61
+ expect(result).to eq(value: nil)
62
+
63
+ result = table.decide(constant: true)
64
+ expect(result).to eq(value: true)
65
+
66
+ result = table.decide(constant: false)
67
+ expect(result).to eq(value: false)
68
+
69
+ result = table.decide(constant: 0)
70
+ expect(result).to eq(value: 0)
71
+
72
+ result = table.decide(constant: BigDecimal('100.0'))
73
+ expect(result).to eq(value: BigDecimal('100.0'))
74
+ end
75
+ end
76
+
77
+ context 'simple example - symbols' do
78
+ data = <<~DATA
79
+ in :node, in :parent, out :top?
80
+ , ==:node, yes
81
+ , , no
82
+ DATA
83
+
84
+ it 'makes correct decisions' do
85
+ table = CSVDecision.parse(data)
86
+
87
+ result = table.decide(node: 0, parent: 0)
88
+ expect(result).to eq(top?: 'yes')
89
+
90
+ result = table.decide(node: 1, parent: 0)
91
+ expect(result).to eq(top?: 'no')
92
+
93
+ result = table.decide(node: '0', parent: 0)
94
+ expect(result).to eq(top?: 'no')
95
+ end
96
+ end
97
+
98
+ context 'makes correct decision for table with symbol ordered compares' do
99
+ data = <<~DATA
100
+ in :traded, in :settled, out :status
101
+ , :traded, same day
102
+ , >:traded, pending
103
+ , <:traded, invalid trade
104
+ , , invalid data
105
+ DATA
106
+
107
+ it 'decides correctly' do
108
+ table = CSVDecision.parse(data)
109
+
110
+ expect(table.decide(traded: '20171227', settled: '20171227')).to eq(status: 'same day')
111
+ expect(table.decide(traded: 20171227, settled: 20171227 )).to eq(status: 'same day')
112
+ expect(table.decide(traded: '20171227', settled: '20171228')).to eq(status: 'pending')
113
+ expect(table.decide(traded: 20171227, settled: 20171228 )).to eq(status: 'pending')
114
+ expect(table.decide(traded: '20171228', settled: '20171227')).to eq(status: 'invalid trade')
115
+ expect(table.decide(traded: 20171228, settled: 20171227 )).to eq(status: 'invalid trade')
116
+ expect(table.decide(traded: '20171227', settled: 20171228 )).to eq(status: 'invalid data')
117
+ end
118
+ end
119
+ end
@@ -13,17 +13,17 @@ describe CSVDecision::Matchers::Function do
13
13
 
14
14
  context 'cell value recognition' do
15
15
  cells = {
16
- ':= nil' => { operator: ':=', value: 'nil' },
17
- '== nil' => { operator: '==', value: 'nil' },
18
- '= nil' => { operator: '=', value: 'nil' },
19
- '==true' => { operator: '==', value: 'true' },
20
- ':=false' => { operator: ':=', value: 'false' },
16
+ ':=function' => { operator: ':=', name: 'function' },
17
+ ':=function()' => { operator: ':=', name: 'function', args:'()' },
18
+ ':=function(arg: value)' => { operator: ':=', name: 'function', args:'(arg: value)' },
19
+ ':= !function(arg: value)' => { operator: ':=', negate: '!', name: 'function', args:'(arg: value)' },
20
+ '== ! func(arg: val)' => { operator: '==', negate: '!', name: 'func', args:'(arg: val)' },
21
21
  }
22
22
  cells.each_pair do |cell, expected|
23
- it "recognises #{cell} as a constant" do
24
- match = described_class::FUNCTION_RE.match(cell)
23
+ it "recognises #{cell} as a function" do
24
+ match = CSVDecision::Function::FUNCTION_RE.match(cell)
25
25
  expect(match['operator']).to eq expected[:operator]
26
- expect(match['name']).to eq expected[:value]
26
+ expect(match['name']).to eq expected[:name]
27
27
  end
28
28
  end
29
29
  end
@@ -31,29 +31,49 @@ describe CSVDecision::Matchers::Function do
31
31
  describe '#matches?' do
32
32
  matcher = described_class.new
33
33
 
34
- context 'constant matches value' do
35
- data = [
36
- ['= nil', nil],
37
- [':= false', false],
38
- ['==true', true]
39
- ]
40
-
41
- data.each do |cell, value|
42
- it "comparision #{cell} matches #{value}" do
43
- proc = matcher.matches?(cell)
44
- expect(proc).to be_a(CSVDecision::Proc)
45
- expect(proc.type).to eq :constant
46
- expect(proc.function).to eq value
47
- end
48
- end
49
- end
50
-
34
+ # context 'symbol expression matches value to hash data' do
35
+ # examples = [
36
+ # { cell: ':col', value: 0, hash: { col: 0 }, result: true },
37
+ # { cell: ':col', value: '0', hash: { col: '0' }, result: true },
38
+ # { cell: ':col', value: 0, hash: { col: '0' }, result: false },
39
+ # { cell: ':col', value: '0', hash: { col: 0 }, result: false },
40
+ # { cell: ':col', value: 1, hash: { col: 0 }, result: false },
41
+ # { cell: ':key', value: 0, hash: { col: 0 }, result: false },
42
+ # { cell: '!=:col', value: 0, hash: { col: 0 }, result: false },
43
+ # { cell: '!=:col', value: '0', hash: { col: '0' }, result: false },
44
+ # { cell: '!=:col', value: 0, hash: { col: '0' }, result: true },
45
+ # { cell: '!=:col', value: '0', hash: { col: 0 }, result: true },
46
+ # { cell: '!=:col', value: 1, hash: { col: 0 }, result: true },
47
+ # { cell: '!=:key', value: 0, hash: { col: 0 }, result: true },
48
+ # { cell: '>:col', value: 1, hash: { col: 0 }, result: true },
49
+ # { cell: '>:col', value: 0, hash: { col: 1 }, result: false },
50
+ # { cell: '<:col', value: 0, hash: { col: 1 }, result: true },
51
+ # { cell: '<:col', value: 1, hash: { col: 0 }, result: false },
52
+ # { cell: '= :col', value: 0, hash: { col: 0 }, result: true },
53
+ # { cell: '==:col', value: 0, hash: { col: 0 }, result: true },
54
+ # { cell: ':=:col', value: 0, hash: { col: 0 }, result: true },
55
+ # { cell: '= :col', value: '0', hash: { col: 0 }, result: false },
56
+ # { cell: '>=:col', value: 1, hash: { col: 0 }, result: true },
57
+ # { cell: '>=:col', value: 0, hash: { col: 1 }, result: false },
58
+ # { cell: '<=:col', value: 0, hash: { col: 1 }, result: true },
59
+ # { cell: '<=:col', value: 1, hash: { col: 0 }, result: false },
60
+ # { cell: '<=:col', value: '1', hash: { col: 1 }, result: false },
61
+ # ]
62
+ #
63
+ # examples.each do |ex|
64
+ # it "cell #{ex[:cell]} matches value: #{ex[:value]} to hash: #{ex[:hash]}" do
65
+ # proc = matcher.matches?(ex[:cell])
66
+ # expect(proc).to be_a(CSVDecision::Proc)
67
+ # expect(proc.function.call(ex[:value], ex[:hash])).to eq ex[:result]
68
+ # end
69
+ # end
70
+ # end
51
71
 
52
- context 'does not match a function constant' do
53
- data = ['1', ':column', ':= 1.1', ':= abc', 'abc', 'abc.*def', '-1..1', '0...3']
72
+ context 'does not match a non-function string' do
73
+ data = ['1', 'abc', 'abc.*def', '-1..1', '0...3']
54
74
 
55
75
  data.each do |cell|
56
- it "cell #{cell} is not a comparision}" do
76
+ it "cell #{cell} is not a function" do
57
77
  expect(matcher.matches?(cell)).to eq false
58
78
  end
59
79
  end
@@ -10,25 +10,6 @@ describe CSVDecision::Matchers::Numeric do
10
10
  it { is_expected.to respond_to(:matches?).with(1).argument }
11
11
  end
12
12
 
13
- context 'cell value recognition' do
14
- cells = {
15
- '> -1' => { comparator: '>', value: '-1' },
16
- '>= 10.0' => { comparator: '>=', value: '10.0' },
17
- '< .0' => { comparator: '<', value: '.0' },
18
- '<= +1' => { comparator: '<=', value: '+1' },
19
- '!=0.0' => { comparator: '!=', value: '0.0' },
20
- ':= 0.0' => { comparator: ':=', value: '0.0' },
21
- '= 1.0' => { comparator: '=', value: '1.0' }
22
- }
23
- cells.each_pair do |cell, expected|
24
- it "recognises #{cell} as a comparision" do
25
- match = described_class::COMPARISON.match(cell)
26
- expect(match['comparator']).to eq expected[:comparator]
27
- expect(match['value']).to eq expected[:value]
28
- end
29
- end
30
- end
31
-
32
13
  describe '#matches?' do
33
14
  matcher = described_class.new
34
15
 
@@ -39,8 +20,8 @@ describe CSVDecision::Matchers::Numeric do
39
20
  ['> 1', 5],
40
21
  ['!= 1', 0],
41
22
  ['> 1', '5'],
42
- ['>= 1.1', BigDecimal.new('1.1')],
43
- ['<=-1.1', BigDecimal.new('-12')]
23
+ ['>= 1.1', BigDecimal('1.1')],
24
+ ['<=-1.1', BigDecimal('-12')]
44
25
  ]
45
26
 
46
27
  data.each do |cell, value|
@@ -53,26 +34,8 @@ describe CSVDecision::Matchers::Numeric do
53
34
  end
54
35
  end
55
36
 
56
- context 'numeric constant' do
57
- data = [
58
- ['== 1', 1],
59
- [':= 0', 0],
60
- ['==1.1', BigDecimal.new('1.1')],
61
- ['=-1.2', BigDecimal.new('-1.2')]
62
- ]
63
-
64
- data.each do |cell, value|
65
- it "constant expression #{cell} evaluates to #{value}" do
66
- proc = matcher.matches?(cell)
67
- expect(proc).to be_a(CSVDecision::Proc)
68
- expect(proc.type).to eq :constant
69
- expect(proc.function).to eq value
70
- end
71
- end
72
- end
73
-
74
- context 'does not match a numeric comparision' do
75
- data = ['1', ':column', ':= nil', ':= true', 'abc', 'abc.*def', '-1..1', '0...3']
37
+ context 'does not match non-numeric comparision' do
38
+ data = ['1', ':column', ':= nil', ':= true', ':= 0', 'abc', 'abc.*def', '-1..1', '0...3']
76
39
 
77
40
  data.each do |cell|
78
41
  it "cell #{cell} is not a comparision}" do
@@ -10,67 +10,37 @@ describe CSVDecision::Matchers::Range do
10
10
  it { is_expected.to respond_to(:matches?).with(1).argument }
11
11
  end
12
12
 
13
- context 'cell value matching' do
14
- ranges = {
15
- '-1..1' => { min: '-1', type: '..', max: '1', negate: '' },
16
- '! -1..1' => { min: '-1', type: '..', max: '1', negate: '!' },
17
- '!-1.0..1.1' => { min: '-1.0', type: '..', max: '1.1', negate: '!' },
18
- '!-1.0...1.1' => { min: '-1.0', type: '...', max: '1.1', negate: '!' }
19
- }
20
- ranges.each_pair do |range, expected|
21
- it "matches #{range} as a numeric range" do
22
- match = described_class::NUMERIC_RANGE.match(range)
23
- expect(match['min']).to eq expected[:min]
24
- expect(match['max']).to eq expected[:max]
25
- expect(match['type']).to eq expected[:type]
26
- expect(match['negate']).to eq expected[:negate]
27
- end
28
- end
29
-
30
- ranges = {
31
- 'a..z' => { min: 'a', type: '..', max: 'z', negate: '' },
32
- '!1...9' => { min: '1', type: '...', max: '9', negate: '!' },
33
- }
34
- ranges.each_pair do |range, expected|
35
- it "matches #{range} as an alphanumeric range" do
36
- match = described_class::ALNUM_RANGE.match(range)
37
- expect(match['min']).to eq expected[:min]
38
- expect(match['max']).to eq expected[:max]
39
- expect(match['type']).to eq expected[:type]
40
- expect(match['negate']).to eq expected[:negate]
41
- end
42
- end
43
- end
44
-
45
- describe '#range' do
46
- it 'constructs various numeric ranges' do
47
- ranges = {
48
- '-1..1' => { range: -1..1, negate: false },
49
- '! -1..1' => { range: -1..1, negate: true },
50
- '!-1.0..1.1' => { range: BigDecimal.new('-1.0')..BigDecimal.new('1.1'), negate: true },
51
- '!-1.0...1' => { range: BigDecimal.new('-1.0')...1, negate: true }
52
- }
53
- ranges.each_pair do |cell, expected|
54
- match = described_class::NUMERIC_RANGE.match(cell)
55
- negate, range = CSVDecision::Matchers::Range.range(match, coerce: :to_numeric)
56
- expect(negate).to eq expected[:negate]
57
- expect(range).to eq expected[:range]
58
- end
59
- end
60
-
61
- it 'constructs various alphanumeric ranges' do
62
- ranges = {
63
- 'a..z' => { range: 'a'..'z', negate: false },
64
- '!1...9' => { range: '1'...'9', negate: true },
65
- }
66
- ranges.each_pair do |cell, expected|
67
- match = described_class::ALNUM_RANGE.match(cell)
68
- negate, range = described_class.range(match)
69
- expect(negate).to eq expected[:negate]
70
- expect(range).to eq expected[:range]
71
- end
72
- end
73
- end
13
+ # context 'cell value matching' do
14
+ # ranges = {
15
+ # '-1..1' => { min: '-1', type: '..', max: '1', negate: '' },
16
+ # '! -1..1' => { min: '-1', type: '..', max: '1', negate: '!' },
17
+ # '!-1.0..1.1' => { min: '-1.0', type: '..', max: '1.1', negate: '!' },
18
+ # '!-1.0...1.1' => { min: '-1.0', type: '...', max: '1.1', negate: '!' }
19
+ # }
20
+ # ranges.each_pair do |range, expected|
21
+ # it "matches #{range} as a numeric range" do
22
+ # match = described_class::NUMERIC_RANGE.match(range)
23
+ # expect(match['min']).to eq expected[:min]
24
+ # expect(match['max']).to eq expected[:max]
25
+ # expect(match['type']).to eq expected[:type]
26
+ # expect(match['negate']).to eq expected[:negate]
27
+ # end
28
+ # end
29
+ #
30
+ # ranges = {
31
+ # 'a..z' => { min: 'a', type: '..', max: 'z', negate: '' },
32
+ # '!1...9' => { min: '1', type: '...', max: '9', negate: '!' },
33
+ # }
34
+ # ranges.each_pair do |range, expected|
35
+ # it "matches #{range} as an alphanumeric range" do
36
+ # match = described_class::ALNUM_RANGE.match(range)
37
+ # expect(match['min']).to eq expected[:min]
38
+ # expect(match['max']).to eq expected[:max]
39
+ # expect(match['type']).to eq expected[:type]
40
+ # expect(match['negate']).to eq expected[:negate]
41
+ # end
42
+ # end
43
+ # end
74
44
 
75
45
  describe '#matches?' do
76
46
  matcher = described_class.new
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../lib/csv_decision'
4
+
5
+ describe CSVDecision::Matchers::Symbol do
6
+ subject { described_class.new }
7
+
8
+ describe '#new' do
9
+ it { is_expected.to be_a CSVDecision::Matchers::Symbol }
10
+ it { is_expected.to be_a CSVDecision::Matchers::Matcher }
11
+ it { is_expected.to respond_to(:matches?).with(1).argument }
12
+ end
13
+
14
+ describe '#matches?' do
15
+ matcher = described_class.new
16
+
17
+ context 'symbol expression matches value to hash data' do
18
+ examples = [
19
+ { cell: ':col', value: 0, hash: { col: 0 }, result: true },
20
+ { cell: ':col', value: '0', hash: { col: '0' }, result: true },
21
+ { cell: ':col', value: 0, hash: { col: '0' }, result: false },
22
+ { cell: ':col', value: '0', hash: { col: 0 }, result: false },
23
+ { cell: ':col', value: 1, hash: { col: 0 }, result: false },
24
+ { cell: ':key', value: 0, hash: { col: 0 }, result: false },
25
+ { cell: '!=:col', value: 0, hash: { col: 0 }, result: false },
26
+ { cell: '!=:col', value: '0', hash: { col: '0' }, result: false },
27
+ { cell: '!=:col', value: 0, hash: { col: '0' }, result: true },
28
+ { cell: '!=:col', value: '0', hash: { col: 0 }, result: true },
29
+ { cell: '!=:col', value: 1, hash: { col: 0 }, result: true },
30
+ { cell: '!=:key', value: 0, hash: { col: 0 }, result: true },
31
+ { cell: '>:col', value: 1, hash: { col: 0 }, result: true },
32
+ { cell: '>:col', value: 0, hash: { col: 1 }, result: false },
33
+ { cell: '<:col', value: 0, hash: { col: 1 }, result: true },
34
+ { cell: '<:col', value: 1, hash: { col: 0 }, result: false },
35
+ { cell: '= :col', value: 0, hash: { col: 0 }, result: true },
36
+ { cell: '==:col', value: 0, hash: { col: 0 }, result: true },
37
+ { cell: ':=:col', value: 0, hash: { col: 0 }, result: true },
38
+ { cell: '= :col', value: '0', hash: { col: 0 }, result: false },
39
+ { cell: '>=:col', value: 1, hash: { col: 0 }, result: true },
40
+ { cell: '>=:col', value: 0, hash: { col: 1 }, result: false },
41
+ { cell: '<=:col', value: 0, hash: { col: 1 }, result: true },
42
+ { cell: '<=:col', value: 1, hash: { col: 0 }, result: false },
43
+ { cell: '<=:col', value: '1', hash: { col: 1 }, result: false },
44
+ ]
45
+
46
+ examples.each do |ex|
47
+ it "cell #{ex[:cell]} matches value: #{ex[:value]} to hash: #{ex[:hash]}" do
48
+ proc = matcher.matches?(ex[:cell])
49
+ expect(proc).to be_a(CSVDecision::Proc)
50
+ expect(proc.function.call(ex[:value], ex[:hash])).to eq ex[:result]
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'does not match a function' do
56
+ data = ['1', 'abc', 'abc.*def', '-1..1', '0...3', ':= false', ':= lookup?']
57
+
58
+ data.each do |cell|
59
+ it "cell #{cell} is not a function" do
60
+ expect(matcher.matches?(cell)).to eq false
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end