csv_decision 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +4 -0
- data/README.md +62 -28
- data/csv_decision.gemspec +1 -1
- data/doc/CSVDecision/CellValidationError.html +2 -2
- data/doc/CSVDecision/Columns/Dictionary.html +114 -20
- data/doc/CSVDecision/Columns/Entry.html +2 -2
- data/doc/CSVDecision/Columns.html +109 -27
- data/doc/CSVDecision/Data.html +2 -2
- data/doc/CSVDecision/Decide.html +2 -2
- data/doc/CSVDecision/Decision.html +21 -21
- data/doc/CSVDecision/Dictionary/Entry.html +508 -0
- data/doc/CSVDecision/Dictionary.html +265 -0
- data/doc/CSVDecision/Error.html +2 -2
- data/doc/CSVDecision/FileError.html +3 -3
- data/doc/CSVDecision/Header.html +37 -136
- data/doc/CSVDecision/Input.html +2 -2
- data/doc/CSVDecision/Load.html +2 -2
- data/doc/CSVDecision/Matchers/Constant.html +2 -2
- data/doc/CSVDecision/Matchers/Function.html +2 -2
- data/doc/CSVDecision/Matchers/Guard.html +92 -25
- data/doc/CSVDecision/Matchers/Matcher.html +14 -18
- data/doc/CSVDecision/Matchers/Numeric.html +2 -2
- data/doc/CSVDecision/Matchers/Pattern.html +2 -2
- data/doc/CSVDecision/Matchers/Range.html +2 -2
- data/doc/CSVDecision/Matchers/Symbol.html +2 -2
- data/doc/CSVDecision/Matchers.html +5 -5
- data/doc/CSVDecision/Options.html +2 -2
- data/doc/CSVDecision/Parse.html +6 -4
- data/doc/CSVDecision/Result.html +944 -0
- data/doc/CSVDecision/ScanRow.html +70 -80
- data/doc/CSVDecision/Table.html +134 -54
- data/doc/CSVDecision.html +5 -5
- data/doc/_index.html +18 -4
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +132 -62
- data/doc/index.html +132 -62
- data/doc/method_list.html +156 -60
- data/doc/top-level-namespace.html +2 -2
- data/lib/csv_decision/columns.rb +1 -8
- data/lib/csv_decision/decision.rb +45 -96
- data/lib/csv_decision/dictionary.rb +149 -0
- data/lib/csv_decision/header.rb +6 -133
- data/lib/csv_decision/matchers.rb +1 -2
- data/lib/csv_decision/parse.rb +18 -7
- data/lib/csv_decision/result.rb +180 -0
- data/lib/csv_decision/scan_row.rb +13 -7
- data/lib/csv_decision/table.rb +6 -5
- data/lib/csv_decision.rb +3 -1
- data/spec/csv_decision/columns_spec.rb +25 -4
- data/spec/csv_decision/examples_spec.rb +25 -0
- data/spec/csv_decision/matchers/guard_spec.rb +26 -9
- data/spec/csv_decision/table_spec.rb +48 -2
- metadata +7 -2
data/doc/_index.html
CHANGED
@@ -135,6 +135,13 @@
|
|
135
135
|
|
136
136
|
</li>
|
137
137
|
|
138
|
+
<li>
|
139
|
+
<span class='object_link'><a href="CSVDecision/Dictionary.html" title="CSVDecision::Dictionary (module)">Dictionary</a></span>
|
140
|
+
|
141
|
+
<small>(CSVDecision)</small>
|
142
|
+
|
143
|
+
</li>
|
144
|
+
|
138
145
|
<li>
|
139
146
|
<span class='object_link'><a href="CSVDecision/Columns/Dictionary.html" title="CSVDecision::Columns::Dictionary (class)">Dictionary</a></span>
|
140
147
|
|
@@ -151,9 +158,9 @@
|
|
151
158
|
<ul>
|
152
159
|
|
153
160
|
<li>
|
154
|
-
<span class='object_link'><a href="CSVDecision/
|
161
|
+
<span class='object_link'><a href="CSVDecision/Dictionary/Entry.html" title="CSVDecision::Dictionary::Entry (class)">Entry</a></span>
|
155
162
|
|
156
|
-
<small>(CSVDecision::
|
163
|
+
<small>(CSVDecision::Dictionary)</small>
|
157
164
|
|
158
165
|
</li>
|
159
166
|
|
@@ -338,6 +345,13 @@
|
|
338
345
|
|
339
346
|
</li>
|
340
347
|
|
348
|
+
<li>
|
349
|
+
<span class='object_link'><a href="CSVDecision/Result.html" title="CSVDecision::Result (class)">Result</a></span>
|
350
|
+
|
351
|
+
<small>(CSVDecision)</small>
|
352
|
+
|
353
|
+
</li>
|
354
|
+
|
341
355
|
</ul>
|
342
356
|
</ul>
|
343
357
|
|
@@ -387,9 +401,9 @@
|
|
387
401
|
</div>
|
388
402
|
|
389
403
|
<div id="footer">
|
390
|
-
Generated on
|
404
|
+
Generated on Fri Jan 5 21:43:58 2018 by
|
391
405
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
392
|
-
0.9.12 (ruby-2.
|
406
|
+
0.9.12 (ruby-2.4.0).
|
393
407
|
</div>
|
394
408
|
|
395
409
|
</div>
|
data/doc/class_list.html
CHANGED
@@ -43,7 +43,7 @@
|
|
43
43
|
|
44
44
|
<ul id="full_list" class="class">
|
45
45
|
<li id="object_" class="odd"><div class="item" style="padding-left:30px"><span class='object_link'><a href="top-level-namespace.html" title="Top Level Namespace (root)">Top Level Namespace</a></span></div></li>
|
46
|
-
<li id='object_CSVDecision' class='even'><div class='item' style='padding-left:30px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision.html" title="CSVDecision (module)">CSVDecision</a></span><small class='search_info'>Top Level Namespace</small></div><ul><li id='object_CSVDecision::CellValidationError' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/CellValidationError.html" title="CSVDecision::CellValidationError (class)">CellValidationError</a></span> < Error<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Columns' class='collapsed even'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision/Columns.html" title="CSVDecision::Columns (class)">Columns</a></span> < Object<small class='search_info'>CSVDecision</small></div><ul><li id='object_CSVDecision::Columns::Dictionary' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Columns/Dictionary.html" title="CSVDecision::Columns::Dictionary (class)">Dictionary</a></span> < Object<small class='search_info'>CSVDecision::Columns</small></div></li><li id='object_CSVDecision::
|
46
|
+
<li id='object_CSVDecision' class='even'><div class='item' style='padding-left:30px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision.html" title="CSVDecision (module)">CSVDecision</a></span><small class='search_info'>Top Level Namespace</small></div><ul><li id='object_CSVDecision::CellValidationError' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/CellValidationError.html" title="CSVDecision::CellValidationError (class)">CellValidationError</a></span> < Error<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Columns' class='collapsed even'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision/Columns.html" title="CSVDecision::Columns (class)">Columns</a></span> < Object<small class='search_info'>CSVDecision</small></div><ul><li id='object_CSVDecision::Columns::Dictionary' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Columns/Dictionary.html" title="CSVDecision::Columns::Dictionary (class)">Dictionary</a></span> < Object<small class='search_info'>CSVDecision::Columns</small></div></li></ul></li><li id='object_CSVDecision::Data' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Data.html" title="CSVDecision::Data (module)">Data</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Decide' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Decide.html" title="CSVDecision::Decide (module)">Decide</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Decision' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Decision.html" title="CSVDecision::Decision (class)">Decision</a></span> < Object<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Dictionary' class='collapsed even'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision/Dictionary.html" title="CSVDecision::Dictionary (module)">Dictionary</a></span><small class='search_info'>CSVDecision</small></div><ul><li id='object_CSVDecision::Dictionary::Entry' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Dictionary/Entry.html" title="CSVDecision::Dictionary::Entry (class)">Entry</a></span> < Struct<small class='search_info'>CSVDecision::Dictionary</small></div></li></ul></li><li id='object_CSVDecision::Error' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Error.html" title="CSVDecision::Error (class)">Error</a></span> < StandardError<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::FileError' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/FileError.html" title="CSVDecision::FileError (class)">FileError</a></span> < Error<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Header' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Header.html" title="CSVDecision::Header (module)">Header</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Input' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Input.html" title="CSVDecision::Input (module)">Input</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Load' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Load.html" title="CSVDecision::Load (module)">Load</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Matchers' class='collapsed even'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="CSVDecision/Matchers.html" title="CSVDecision::Matchers (class)">Matchers</a></span> < Object<small class='search_info'>CSVDecision</small></div><ul><li id='object_CSVDecision::Matchers::Constant' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Constant.html" title="CSVDecision::Matchers::Constant (class)">Constant</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Function' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Function.html" title="CSVDecision::Matchers::Function (class)">Function</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Guard' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Guard.html" title="CSVDecision::Matchers::Guard (class)">Guard</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Matcher' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Matcher.html" title="CSVDecision::Matchers::Matcher (class)">Matcher</a></span> < Object<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Numeric' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Numeric.html" title="CSVDecision::Matchers::Numeric (class)">Numeric</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Pattern' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Pattern.html" title="CSVDecision::Matchers::Pattern (class)">Pattern</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Range' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Range.html" title="CSVDecision::Matchers::Range (class)">Range</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li><li id='object_CSVDecision::Matchers::Symbol' class='collapsed'><div class='item' style='padding-left:60px'><span class='object_link'><a href="CSVDecision/Matchers/Symbol.html" title="CSVDecision::Matchers::Symbol (class)">Symbol</a></span> < Matcher<small class='search_info'>CSVDecision::Matchers</small></div></li></ul></li><li id='object_CSVDecision::Options' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Options.html" title="CSVDecision::Options (module)">Options</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Parse' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Parse.html" title="CSVDecision::Parse (module)">Parse</a></span><small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Result' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Result.html" title="CSVDecision::Result (class)">Result</a></span> < Object<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::ScanRow' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/ScanRow.html" title="CSVDecision::ScanRow (class)">ScanRow</a></span> < Object<small class='search_info'>CSVDecision</small></div></li><li id='object_CSVDecision::Table' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="CSVDecision/Table.html" title="CSVDecision::Table (class)">Table</a></span> < Object<small class='search_info'>CSVDecision</small></div></li></ul></li>
|
47
47
|
|
48
48
|
</ul>
|
49
49
|
</div>
|
data/doc/file.README.html
CHANGED
@@ -75,17 +75,17 @@ src="http://img.shields.io/badge/license-MIT-yellowgreen.svg"></a></p>
|
|
75
75
|
|
76
76
|
<h3 id="label-CSV+based+Ruby+decision+tables">CSV based Ruby decision tables</h3>
|
77
77
|
|
78
|
-
<p><code>csv_decision</code> is a RubyGem for CSV
|
79
|
-
|
80
|
-
|
78
|
+
<p><code>csv_decision</code> is a RubyGem for CSV based <a
|
79
|
+
href="https://en.wikipedia.org/wiki/Decision_table">decision tables</a>. It
|
80
|
+
accepts decision tables implemented as a <a
|
81
81
|
href="https://en.wikipedia.org/wiki/Comma-separated_values">CSV file</a>,
|
82
82
|
which can then be used to execute complex conditional logic against an
|
83
83
|
input hash, producing a decision as an output hash.</p>
|
84
84
|
|
85
85
|
<h3 id="label-Why+use+csv_decision-3F">Why use <code>csv_decision</code>?</h3>
|
86
86
|
|
87
|
-
<p>Typical “business logic” is notoriously illogical
|
88
|
-
and one-off exceptions. A decision table can
|
87
|
+
<p>Typical “business logic” is notoriously illogical - full of corner cases
|
88
|
+
and one-off exceptions. A decision table can express data-based decisions
|
89
89
|
in a way that comes more naturally to subject matter experts, who typically
|
90
90
|
prefer spreadsheet models. Business logic may then be encapsulated,
|
91
91
|
avoiding the need to write tortuous conditional expressions in Ruby that
|
@@ -93,9 +93,9 @@ draw the ire of <code>rubocop</code> and its ilk.</p>
|
|
93
93
|
|
94
94
|
<p>This gem and the examples below take inspiration from <a
|
95
95
|
href="https://github.com/jmettraux/rufus-decision">rufus/decision</a>.
|
96
|
-
(
|
97
|
-
|
98
|
-
|
96
|
+
(That gem is no longer maintained and CSV Decision has better decision-time
|
97
|
+
performance, at the expense of slower table parse times and more memory –
|
98
|
+
see <code>benchmarks/rufus_decision.rb</code>.)</p>
|
99
99
|
|
100
100
|
<h3 id="label-Installation">Installation</h3>
|
101
101
|
|
@@ -109,27 +109,26 @@ more memory – see <code>benchmarks/rufus_decision.rb</code>)</p>
|
|
109
109
|
<h3 id="label-Simple+example">Simple example</h3>
|
110
110
|
|
111
111
|
<p>This table considers two input conditions: <code>topic</code> and
|
112
|
-
<code>region</code
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
sports
|
119
|
-
|
120
|
-
finance
|
121
|
-
finance
|
122
|
-
|
123
|
-
politics
|
124
|
-
politics
|
125
|
-
|
126
|
-
| | Zach</code></pre>
|
112
|
+
<code>region</code>, labeled <code>in:</code>. Certain combinations yield
|
113
|
+
an output value for <code>team_member</code>, labeled <code>out:</code>.</p>
|
114
|
+
|
115
|
+
<pre class="code ruby"><code class="ruby">in:topic | in:region | out:team_member
|
116
|
+
---------+------------+----------------
|
117
|
+
sports | Europe | Alice
|
118
|
+
sports | | Bob
|
119
|
+
finance | America | Charlie
|
120
|
+
finance | Europe | Donald
|
121
|
+
finance | | Ernest
|
122
|
+
politics | Asia | Fujio
|
123
|
+
politics | America | Gilbert
|
124
|
+
politics | | Henry
|
125
|
+
| | Zach</code></pre>
|
127
126
|
|
128
127
|
<p>When the topic is <code>finance</code> and the region is
|
129
128
|
<code>Europe</code> the team member <code>Donald</code> is selected.</p>
|
130
129
|
|
131
130
|
<p>This is a “first match” decision table in that as soon as a match is made
|
132
|
-
execution stops and a single output
|
131
|
+
execution stops and a single output row (hash) is returned.</p>
|
133
132
|
|
134
133
|
<p>The ordering of rows matters. <code>Ernest</code>, who is in charge of
|
135
134
|
<code>finance</code> for the rest of the world, except for
|
@@ -138,7 +137,7 @@ colleagues <code>Charlie</code> and <code>Donald</code>. <code>Zach</code>
|
|
138
137
|
has been placed last, catching all the input combos not matching any other
|
139
138
|
row.</p>
|
140
139
|
|
141
|
-
<p>Here
|
140
|
+
<p>Here is the example as code:</p>
|
142
141
|
|
143
142
|
<p>“`ruby # Valid CSV string data = <<~DATA in :topic, in :region,
|
144
143
|
out :team_member sports, Europe, Alice sports, , Bob finance, America,
|
@@ -147,13 +146,17 @@ politics, America, Gilbert politics, , Henry , , Zach DATA</p>
|
|
147
146
|
|
148
147
|
<p>table = CSVDecision.parse(data)</p>
|
149
148
|
|
150
|
-
<p>table.decide(topic: 'finance', region: 'Europe')
|
151
|
-
team_member: 'Donald' table.decide(topic: 'sports',
|
152
|
-
region: nil)
|
153
|
-
'culture', region: 'America')
|
154
|
-
“`</p>
|
149
|
+
<p>table.decide(topic: 'finance', region: 'Europe') #=> {
|
150
|
+
team_member: 'Donald' } table.decide(topic: 'sports',
|
151
|
+
region: nil) #=> { team_member: 'Bob' } table.decide(topic:
|
152
|
+
'culture', region: 'America') #=> { team_member:
|
153
|
+
'Zach' } “`</p>
|
155
154
|
|
156
|
-
<p>An empty <code>in
|
155
|
+
<p>An empty <code>in:</code> cell means “matches any value”, even nils.</p>
|
156
|
+
|
157
|
+
<p>Note that all column header names are symbolized, so it's actually more
|
158
|
+
accurate to write <code>in :topic</code>; however spaces before and after
|
159
|
+
the <code>:</code> do not matter.</p>
|
157
160
|
|
158
161
|
<p>If you have cloned this gem's git repo, then the example can also be
|
159
162
|
run by loading the table from a CSV file:</p>
|
@@ -163,12 +166,12 @@ CSVDecision.parse(Pathname('spec/data/valid/simple_example.csv'))
|
|
163
166
|
</code></p>
|
164
167
|
|
165
168
|
<p>We can also load this same table using the option: <code>first_match:
|
166
|
-
false</code>, which means that
|
167
|
-
an array of hashes.</p>
|
169
|
+
false</code>, which means that <em>all</em> matching rows will be
|
170
|
+
accumulated into an array of hashes.</p>
|
168
171
|
|
169
172
|
<p><code>ruby table = CSVDecision.parse(data, first_match: false)
|
170
|
-
table.decide(topic: 'finance', region: 'Europe')
|
171
|
-
team_member: %w[Donald Ernest Zach]
|
173
|
+
table.decide(topic: 'finance', region: 'Europe') #=> {
|
174
|
+
team_member: %w[Donald Ernest Zach] } </code></p>
|
172
175
|
|
173
176
|
<p>For more examples see <code>spec/csv_decision/table_spec.rb</code>.
|
174
177
|
Complete documentation of all table parameters is in the code - see
|
@@ -177,26 +180,41 @@ Complete documentation of all table parameters is in the code - see
|
|
177
180
|
|
178
181
|
<h3 id="label-CSV+Decision+features">CSV Decision features</h3>
|
179
182
|
<ul><li>
|
183
|
+
<p>Either returns the first matching row as a hash (default), or accumulates
|
184
|
+
all matches as an array of hashes (i.e., <code>parse</code> option
|
185
|
+
<code>first_match: false</code> or CSV file option
|
186
|
+
<code>accumulate</code>).</p>
|
187
|
+
</li><li>
|
180
188
|
<p>Fast decision-time performance (see <code>benchmarks</code> folder).</p>
|
181
189
|
</li><li>
|
182
|
-
<p>In addition to simple
|
183
|
-
|
190
|
+
<p>In addition to simple strings, <code>csv_decision</code> can match basic
|
191
|
+
Ruby constants (e.g., <code>=nil</code>), regular expressions (e.g.,
|
192
|
+
<code>=~ on|off</code>), comparisons (e.g., <code>> 100.0</code> ) and
|
193
|
+
Ruby-style ranges (e.g., <code>1..10</code>)</p>
|
194
|
+
</li><li>
|
195
|
+
<p>Can compare an input column versus another input hash key - e.g.,
|
196
|
+
<code>> :column</code>.</p>
|
197
|
+
</li><li>
|
198
|
+
<p>Any cell starting with <code>#</code> is treated as a comment, and comments
|
199
|
+
may appear anywhere in the table. (Comment cells are always interpreted as
|
200
|
+
the empty string.)</p>
|
184
201
|
</li><li>
|
185
|
-
<p>Can use column
|
186
|
-
|
202
|
+
<p>Can use column symbol expressions or Ruby methods (0-arity) in input
|
203
|
+
columns for matching - e.g., <code>:column.zero?</code> or <code>:column
|
204
|
+
== 0</code>.</p>
|
205
|
+
</li><li>
|
206
|
+
<p>May also use Ruby methods in output columns - e.g.,
|
207
|
+
<code>:column.length</code>.</p>
|
187
208
|
</li><li>
|
188
209
|
<p>Accepts data as a file, CSV string or an array of arrays. (For safety all
|
189
210
|
input data is force encoded to UTF-8, and non-ascii strings are converted
|
190
211
|
to empty strings.)</p>
|
191
212
|
</li><li>
|
192
213
|
<p>All CSV cells are parsed for correctness, and helpful error messages
|
193
|
-
generated for bad
|
194
|
-
</li><li>
|
195
|
-
<p>Either returns the first matching row as a hash, or accumulates all matches
|
196
|
-
as an array of hashes.</p>
|
214
|
+
generated for bad input.</p>
|
197
215
|
</li></ul>
|
198
216
|
|
199
|
-
<
|
217
|
+
<h4 id="label-Constants+other+than+strings">Constants other than strings</h4>
|
200
218
|
|
201
219
|
<p>Although <code>csv_decision</code> is string oriented, it does recognise
|
202
220
|
other types of constant present in the input hash. Specifically, the
|
@@ -217,10 +235,10 @@ value: nil<br> table.decide(constant: 0) # returns value: 0<br>
|
|
217
235
|
table.decide(constant: BigDecimal('100.0')) # returns value:
|
218
236
|
BigDecimal('100.0')<br> “`</p>
|
219
237
|
|
220
|
-
<
|
238
|
+
<h4 id="label-Column+header+symbols">Column header symbols</h4>
|
221
239
|
|
222
|
-
<p>All input and output column names are symbolized, and
|
223
|
-
simple expressions that refer to values in the input hash.</p>
|
240
|
+
<p>All input and output column names are symbolized, and those symbols may be
|
241
|
+
used to form simple expressions that refer to values in the input hash.</p>
|
224
242
|
|
225
243
|
<p>For example: “`ruby data = <<~DATA in :node, in :parent, out :top?
|
226
244
|
, == :node, yes , , no DATA</p>
|
@@ -234,7 +252,8 @@ simple expressions that refer to values in the input hash.</p>
|
|
234
252
|
|
235
253
|
<p>Note that there is no need to include an input column for
|
236
254
|
<code>:node</code> in the decision table - it just needs to be present in
|
237
|
-
the input hash.
|
255
|
+
the input hash. The expression, <code>== :node</code> should be read as
|
256
|
+
<code>:parent == :node</code>. It can also be shortened to just
|
238
257
|
<code>:node</code>, so the above decision table may be simplified to:</p>
|
239
258
|
|
240
259
|
<p><code>ruby data = <<~DATA in :parent, out :top?
|
@@ -243,9 +262,9 @@ operators are also supported: <code>!=</code>, <code>></code>,
|
|
243
262
|
<code>>=</code>, <code><</code>, <code><=</code>. For more simple
|
244
263
|
examples see <code>spec/csv_decision/examples_spec.rb</code>.</p>
|
245
264
|
|
246
|
-
<
|
265
|
+
<h4 id="label-Input+guard+conditions">Input guard conditions</h4>
|
247
266
|
|
248
|
-
<p>Sometimes it's more convenient to write guard
|
267
|
+
<p>Sometimes it's more convenient to write guard expressions in a single
|
249
268
|
column specialized for that purpose. For example:</p>
|
250
269
|
|
251
270
|
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='heredoc_beg'><<~DATA</span>
|
@@ -264,7 +283,39 @@ column specialized for that purpose. For example:</p>
|
|
264
283
|
<span class='comment'>#=> { ID: '123456789012', ID_type: 'ISIN', len: 12 }
|
265
284
|
</span></code></pre>
|
266
285
|
|
267
|
-
<p>
|
286
|
+
<p>Input <code>guard:</code> columns may be anonymous, and must contain
|
287
|
+
non-constant expressions. In addition to 0-arity Ruby methods, the
|
288
|
+
following comparison operators are allowed: <code>==</code>,
|
289
|
+
<code>!=</code>, <code>></code>, <code>>=</code>, <code><</code>
|
290
|
+
and <code><=</code>. Also, regular expressions are supported - i.e.,
|
291
|
+
<code>=~</code> and <code>!~</code>.</p>
|
292
|
+
|
293
|
+
<h4 id="label-Output+if+conditions">Output if conditions</h4>
|
294
|
+
|
295
|
+
<p>In some situations it is useful to apply filter conditions <em>after</em>
|
296
|
+
all the output columns have been derived. For example:</p>
|
297
|
+
|
298
|
+
<pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='heredoc_beg'><<~DATA</span>
|
299
|
+
<span class='tstring_content'> in :country, guard:, out :ID, out :ID_type, out :len, if:
|
300
|
+
</span><span class='tstring_content'> US, :CUSIP.present?, :CUSIP, CUSIP8, :ID.length, :len == 8
|
301
|
+
</span><span class='tstring_content'> US, :CUSIP.present?, :CUSIP, CUSIP9, :ID.length, :len == 9
|
302
|
+
</span><span class='tstring_content'> US, :CUSIP.present?, :CUSIP, DUMMY, :ID.length,
|
303
|
+
</span><span class='tstring_content'> , :ISIN.present?, :ISIN, ISIN, :ID.length, :len == 12
|
304
|
+
</span><span class='tstring_content'> , :ISIN.present?, :ISIN, DUMMY, :ID.length,
|
305
|
+
</span><span class='tstring_content'> , :CUSIP.present?, :CUSIP, DUMMY, :ID.length,
|
306
|
+
</span><span class='heredoc_end'> DATA
|
307
|
+
</span>
|
308
|
+
<span class='id identifier rubyid_table'>table</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="CSVDecision.html" title="CSVDecision (module)">CSVDecision</a></span></span><span class='period'>.</span><span class='id identifier rubyid_parse'><span class='object_link'><a href="CSVDecision.html#parse-class_method" title="CSVDecision.parse (method)">parse</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_data'>data</span><span class='rparen'>)</span>
|
309
|
+
<span class='id identifier rubyid_table'>table</span><span class='period'>.</span><span class='id identifier rubyid_decide'>decide</span><span class='lparen'>(</span><span class='label'>country:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>US</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='label'>CUSIP:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>123456789</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='comment'>#=> {ID: '123456789', ID_type: 'CUSIP9', len: 9}
|
310
|
+
</span><span class='id identifier rubyid_table'>table</span><span class='period'>.</span><span class='id identifier rubyid_decide'>decide</span><span class='lparen'>(</span><span class='label'>CUSIP:</span> <span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>12345678</span><span class='tstring_end'>'</span></span><span class='comma'>,</span> <span class='label'>ISIN:</span><span class='tstring'><span class='tstring_beg'>'</span><span class='tstring_content'>1234567890</span><span class='tstring_end'>'</span></span><span class='rparen'>)</span> <span class='comment'>#=> {ID: '1234567890', ID_type: 'DUMMY', len: 10}
|
311
|
+
</span></code></pre>
|
312
|
+
|
313
|
+
<p>Output <code>if:</code> columns may be anonymous, and must contain
|
314
|
+
non-constant expressions. In addition to 0-arity Ruby methods, the
|
315
|
+
following comparison operators are allowed: <code>==</code>,
|
316
|
+
<code>!=</code>, <code>></code>, <code>>=</code>, <code><</code>
|
317
|
+
and <code><=</code>. Also, regular expressions are supported - i.e.,
|
318
|
+
<code>=~</code> and <code>!~</code>.</p>
|
268
319
|
|
269
320
|
<h3 id="label-Testing">Testing</h3>
|
270
321
|
|
@@ -277,21 +328,40 @@ bundle install rspec </code></p>
|
|
277
328
|
<h3 id="label-Planned+features">Planned features</h3>
|
278
329
|
|
279
330
|
<p><code>csv_decision</code> is still a work in progress, and will be enhanced
|
280
|
-
to support the following features: *
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
331
|
+
to support the following features: * Text-only input columns may be
|
332
|
+
indexed for faster lookup performance. * Input hash values may be
|
333
|
+
(conditionally) defaulted with a constant or a function call. * Output
|
334
|
+
columns may construct interpolated strings referencing column symbols. *
|
335
|
+
Supply a pre-defined library of functions that can be called within input
|
336
|
+
columns to implement matching logic or from the output columns to
|
337
|
+
formulate the final decision. * Available functions may be extended with a
|
338
|
+
user-supplied library of Ruby methods for tailored logic.</p>
|
339
|
+
|
340
|
+
<h3 id="label-Reasons+for+the+limitations+of+column+expressions">Reasons for the limitations of column expressions</h3>
|
341
|
+
|
342
|
+
<p>The simple column expressions allowed by <code>csv_decision</code> are
|
343
|
+
purposely limited for reasons of understandability and maintainability. The
|
344
|
+
whole point of this gem is to make decision rules easier to express and
|
345
|
+
comprehend as declarative, tabular logic. While Ruby makes it easy to
|
346
|
+
execute arbitrary code embedded within a CSV file, this could easily result
|
347
|
+
in hard to debug logic that also poses safety risks.</p>
|
348
|
+
|
349
|
+
<h2 id="label-Changelog">Changelog</h2>
|
350
|
+
|
351
|
+
<p>See <a href="./CHANGELOG.md">CHANGELOG.md</a> for a list of changes.</p>
|
352
|
+
|
353
|
+
<h2 id="label-License">License</h2>
|
354
|
+
|
355
|
+
<p>CSV Decision © 2017-2018 by <a
|
356
|
+
href="mailto:brett@phillips-vickers.com">Brett Vickers</a>. CSV Decision is
|
357
|
+
licensed under the MIT license. Please see the <a
|
358
|
+
href="./LICENSE">LICENSE</a> document for more information.</p>
|
289
359
|
</div></div>
|
290
360
|
|
291
361
|
<div id="footer">
|
292
|
-
Generated on
|
362
|
+
Generated on Fri Jan 5 21:43:59 2018 by
|
293
363
|
<a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
294
|
-
0.9.12 (ruby-2.
|
364
|
+
0.9.12 (ruby-2.4.0).
|
295
365
|
</div>
|
296
366
|
|
297
367
|
</div>
|