csv_decision2 0.5.1

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +3 -0
  3. data/.coveralls.yml +2 -0
  4. data/.gitignore +14 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +30 -0
  7. data/.travis.yml +6 -0
  8. data/CHANGELOG.md +85 -0
  9. data/Dockerfile +6 -0
  10. data/Gemfile +7 -0
  11. data/LICENSE +21 -0
  12. data/README.md +356 -0
  13. data/benchmarks/rufus_decision.rb +158 -0
  14. data/csv_decision2.gemspec +38 -0
  15. data/doc/CSVDecision/CellValidationError.html +143 -0
  16. data/doc/CSVDecision/Columns/Default.html +589 -0
  17. data/doc/CSVDecision/Columns/Dictionary.html +801 -0
  18. data/doc/CSVDecision/Columns/Entry.html +508 -0
  19. data/doc/CSVDecision/Columns.html +1259 -0
  20. data/doc/CSVDecision/Constant.html +254 -0
  21. data/doc/CSVDecision/Data.html +479 -0
  22. data/doc/CSVDecision/Decide.html +302 -0
  23. data/doc/CSVDecision/Decision.html +1011 -0
  24. data/doc/CSVDecision/Defaults.html +291 -0
  25. data/doc/CSVDecision/Dictionary/Entry.html +1147 -0
  26. data/doc/CSVDecision/Dictionary.html +426 -0
  27. data/doc/CSVDecision/Error.html +139 -0
  28. data/doc/CSVDecision/FileError.html +143 -0
  29. data/doc/CSVDecision/Function.html +240 -0
  30. data/doc/CSVDecision/Guard.html +245 -0
  31. data/doc/CSVDecision/Header.html +647 -0
  32. data/doc/CSVDecision/Index.html +741 -0
  33. data/doc/CSVDecision/Input.html +404 -0
  34. data/doc/CSVDecision/Load.html +296 -0
  35. data/doc/CSVDecision/Matchers/Constant.html +484 -0
  36. data/doc/CSVDecision/Matchers/Function.html +511 -0
  37. data/doc/CSVDecision/Matchers/Guard.html +503 -0
  38. data/doc/CSVDecision/Matchers/Matcher.html +507 -0
  39. data/doc/CSVDecision/Matchers/Numeric.html +415 -0
  40. data/doc/CSVDecision/Matchers/Pattern.html +491 -0
  41. data/doc/CSVDecision/Matchers/Proc.html +704 -0
  42. data/doc/CSVDecision/Matchers/Range.html +379 -0
  43. data/doc/CSVDecision/Matchers/Symbol.html +426 -0
  44. data/doc/CSVDecision/Matchers.html +1567 -0
  45. data/doc/CSVDecision/Numeric.html +259 -0
  46. data/doc/CSVDecision/Options.html +443 -0
  47. data/doc/CSVDecision/Parse.html +282 -0
  48. data/doc/CSVDecision/Paths.html +742 -0
  49. data/doc/CSVDecision/Result.html +1200 -0
  50. data/doc/CSVDecision/Scan/InputHashes.html +369 -0
  51. data/doc/CSVDecision/Scan.html +313 -0
  52. data/doc/CSVDecision/ScanRow.html +866 -0
  53. data/doc/CSVDecision/Symbol.html +256 -0
  54. data/doc/CSVDecision/Table.html +1470 -0
  55. data/doc/CSVDecision/TableValidationError.html +143 -0
  56. data/doc/CSVDecision/Validate.html +422 -0
  57. data/doc/CSVDecision.html +621 -0
  58. data/doc/_index.html +471 -0
  59. data/doc/class_list.html +51 -0
  60. data/doc/css/common.css +1 -0
  61. data/doc/css/full_list.css +58 -0
  62. data/doc/css/style.css +499 -0
  63. data/doc/file.README.html +421 -0
  64. data/doc/file_list.html +56 -0
  65. data/doc/frames.html +17 -0
  66. data/doc/index.html +421 -0
  67. data/doc/js/app.js +248 -0
  68. data/doc/js/full_list.js +216 -0
  69. data/doc/js/jquery.js +4 -0
  70. data/doc/method_list.html +1163 -0
  71. data/doc/top-level-namespace.html +110 -0
  72. data/docker-compose.yml +13 -0
  73. data/lib/csv_decision/columns.rb +192 -0
  74. data/lib/csv_decision/data.rb +92 -0
  75. data/lib/csv_decision/decision.rb +196 -0
  76. data/lib/csv_decision/defaults.rb +47 -0
  77. data/lib/csv_decision/dictionary.rb +180 -0
  78. data/lib/csv_decision/header.rb +83 -0
  79. data/lib/csv_decision/index.rb +107 -0
  80. data/lib/csv_decision/input.rb +121 -0
  81. data/lib/csv_decision/load.rb +36 -0
  82. data/lib/csv_decision/matchers/constant.rb +74 -0
  83. data/lib/csv_decision/matchers/function.rb +56 -0
  84. data/lib/csv_decision/matchers/guard.rb +142 -0
  85. data/lib/csv_decision/matchers/numeric.rb +44 -0
  86. data/lib/csv_decision/matchers/pattern.rb +94 -0
  87. data/lib/csv_decision/matchers/range.rb +95 -0
  88. data/lib/csv_decision/matchers/symbol.rb +149 -0
  89. data/lib/csv_decision/matchers.rb +220 -0
  90. data/lib/csv_decision/options.rb +124 -0
  91. data/lib/csv_decision/parse.rb +165 -0
  92. data/lib/csv_decision/paths.rb +78 -0
  93. data/lib/csv_decision/result.rb +204 -0
  94. data/lib/csv_decision/scan.rb +117 -0
  95. data/lib/csv_decision/scan_row.rb +142 -0
  96. data/lib/csv_decision/table.rb +101 -0
  97. data/lib/csv_decision/validate.rb +85 -0
  98. data/lib/csv_decision.rb +45 -0
  99. data/spec/csv_decision/columns_spec.rb +251 -0
  100. data/spec/csv_decision/constant_spec.rb +36 -0
  101. data/spec/csv_decision/data_spec.rb +50 -0
  102. data/spec/csv_decision/decision_spec.rb +19 -0
  103. data/spec/csv_decision/examples_spec.rb +242 -0
  104. data/spec/csv_decision/index_spec.rb +58 -0
  105. data/spec/csv_decision/input_spec.rb +55 -0
  106. data/spec/csv_decision/load_spec.rb +28 -0
  107. data/spec/csv_decision/matchers/function_spec.rb +82 -0
  108. data/spec/csv_decision/matchers/guard_spec.rb +170 -0
  109. data/spec/csv_decision/matchers/numeric_spec.rb +47 -0
  110. data/spec/csv_decision/matchers/pattern_spec.rb +183 -0
  111. data/spec/csv_decision/matchers/range_spec.rb +70 -0
  112. data/spec/csv_decision/matchers/symbol_spec.rb +67 -0
  113. data/spec/csv_decision/options_spec.rb +94 -0
  114. data/spec/csv_decision/parse_spec.rb +44 -0
  115. data/spec/csv_decision/table_spec.rb +683 -0
  116. data/spec/csv_decision_spec.rb +7 -0
  117. data/spec/data/invalid/empty.csv +0 -0
  118. data/spec/data/invalid/invalid_header1.csv +4 -0
  119. data/spec/data/invalid/invalid_header2.csv +4 -0
  120. data/spec/data/invalid/invalid_header3.csv +4 -0
  121. data/spec/data/invalid/invalid_header4.csv +4 -0
  122. data/spec/data/valid/benchmark_regexp.csv +10 -0
  123. data/spec/data/valid/index_example.csv +13 -0
  124. data/spec/data/valid/multi_column_index.csv +10 -0
  125. data/spec/data/valid/multi_column_index2.csv +12 -0
  126. data/spec/data/valid/options_in_file1.csv +5 -0
  127. data/spec/data/valid/options_in_file2.csv +5 -0
  128. data/spec/data/valid/options_in_file3.csv +13 -0
  129. data/spec/data/valid/regular_expressions.csv +11 -0
  130. data/spec/data/valid/simple_constants.csv +5 -0
  131. data/spec/data/valid/simple_example.csv +10 -0
  132. data/spec/data/valid/valid.csv +4 -0
  133. data/spec/spec_helper.rb +106 -0
  134. metadata +352 -0
data/doc/index.html ADDED
@@ -0,0 +1,421 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: README
8
+
9
+ &mdash; Documentation by YARD 0.9.12
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
+
17
+ <script type="text/javascript" charset="utf-8">
18
+ pathId = "README";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: README</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <h1 id="label-CSV+Decision">CSV Decision</h1>
62
+
63
+ <p><a href="https://badge.fury.io/rb/csv_decision"><img
64
+ src="https://badge.fury.io/rb/csv_decision.svg"></a> <a
65
+ href="https://travis-ci.org/bpvickers/csv_decision"><img
66
+ src="https://travis-ci.org/bpvickers/csv_decision.svg?branch=master"></a>
67
+ <a
68
+ href="https://coveralls.io/github/bpvickers/csv_decision?branch=master"><img
69
+ src="https://coveralls.io/repos/github/bpvickers/csv_decision/badge.svg?branch=master"></a>
70
+ <a
71
+ href="https://codeclimate.com/github/bpvickers/csv_decision/maintainability"><img
72
+ src="https://api.codeclimate.com/v1/badges/466a6c52e8f6a3840967/maintainability"></a>
73
+ <a href="#license"><img
74
+ src="http://img.shields.io/badge/license-MIT-yellowgreen.svg"></a></p>
75
+
76
+ <h3 id="label-CSV+based+Ruby+decision+tables">CSV based Ruby decision tables</h3>
77
+
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
+ href="https://en.wikipedia.org/wiki/Comma-separated_values">CSV file</a>,
82
+ which can then be used to execute complex conditional logic against an
83
+ input hash, producing a decision as an output hash.</p>
84
+
85
+ <h3 id="label-Why+use+csv_decision-3F">Why use <code>csv_decision</code>?</h3>
86
+
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
+ in a way that comes naturally to subject matter experts, who typically use
90
+ spreadsheet models. Business logic can be encapsulated in a table, avoiding
91
+ the need for tortuous conditional expressions.</p>
92
+
93
+ <p>This gem and the examples below take inspiration from <a
94
+ href="https://github.com/jmettraux/rufus-decision">rufus/decision</a>.
95
+ (That gem is no longer maintained and CSV Decision has better decision-time
96
+ performance, at the expense of slower table parse times and more memory –
97
+ see <code>benchmarks/rufus_decision.rb</code>.)</p>
98
+
99
+ <h3 id="label-Installation">Installation</h3>
100
+
101
+ <p>To get started, just add <code>csv_decision</code> to your
102
+ <code>Gemfile</code>, and then run <code>bundle</code>:</p>
103
+
104
+ <p><code>ruby gem &#39;csv_decision&#39; </code></p>
105
+
106
+ <p>or simply <code>bash gem install csv_decision </code></p>
107
+
108
+ <h3 id="label-Simple+example">Simple example</h3>
109
+
110
+ <p>This table considers two input conditions: <code>topic</code> and
111
+ <code>region</code>, labeled <code>in:</code>. Certain combinations yield
112
+ an output value for <code>team_member</code>, labeled <code>out:</code>.</p>
113
+
114
+ <pre class="code ruby"><code class="ruby">in:topic | in:region | out:team_member
115
+ ---------+------------+----------------
116
+ sports | Europe | Alice
117
+ sports | | Bob
118
+ finance | America | Charlie
119
+ finance | Europe | Donald
120
+ finance | | Ernest
121
+ politics | Asia | Fujio
122
+ politics | America | Gilbert
123
+ politics | | Henry
124
+ | | Zach</code></pre>
125
+
126
+ <p>When the topic is <code>finance</code> and the region is
127
+ <code>Europe</code> the team member <code>Donald</code> is selected. This
128
+ is a “first match” decision table in that as soon as a match is made
129
+ execution stops and a single output row (hash) is returned.</p>
130
+
131
+ <p>The ordering of rows matters. <code>Ernest</code>, who is in charge of
132
+ <code>finance</code> for the rest of the world, except for
133
+ <code>America</code> and <code>Europe</code>, <em>must</em> come after his
134
+ colleagues <code>Charlie</code> and <code>Donald</code>. <code>Zach</code>
135
+ has been placed last, catching all the input combos not matching any other
136
+ row.</p>
137
+
138
+ <p>Here&#39;s the example as code:</p>
139
+
140
+ <p>“`ruby # Valid CSV string data = &lt;&lt;~DATA in :topic, in :region,
141
+ out :team_member sports, Europe, Alice sports, , Bob finance, America,
142
+ Charlie finance, Europe, Donald finance, , Ernest politics, Asia, Fujio
143
+ politics, America, Gilbert politics, , Henry , , Zach DATA</p>
144
+
145
+ <p>table = CSVDecision.parse(data)</p>
146
+
147
+ <p>table.decide(topic: &#39;finance&#39;, region: &#39;Europe&#39;) #=&gt; {
148
+ team_member: &#39;Donald&#39; } table.decide(topic: &#39;sports&#39;,
149
+ region: nil) #=&gt; { team_member: &#39;Bob&#39; } table.decide(topic:
150
+ &#39;culture&#39;, region: &#39;America&#39;) #=&gt; { team_member:
151
+ &#39;Zach&#39; } “`</p>
152
+
153
+ <p>An empty <code>in:</code> cell means “matches any value”.</p>
154
+
155
+ <p>Note that all column header names are symbolized, so it&#39;s actually more
156
+ accurate to write <code>in :topic</code>; however spaces before and after
157
+ the <code>:</code> do not matter.</p>
158
+
159
+ <p>If you have cloned this gem&#39;s git repo, then the example can also be
160
+ run by loading the table from a CSV file:</p>
161
+
162
+ <p><code>ruby table =
163
+ CSVDecision.parse(Pathname(&#39;spec/data/valid/simple_example.csv&#39;))
164
+ </code></p>
165
+
166
+ <p>We can also load this same table using the option: <code>first_match:
167
+ false</code>, which means that <em>all</em> matching rows will be
168
+ accumulated into an array of hashes.</p>
169
+
170
+ <p><code>ruby table = CSVDecision.parse(data, first_match: false)
171
+ table.decide(topic: &#39;finance&#39;, region: &#39;Europe&#39;) #=&gt; {
172
+ team_member: %w[Donald Ernest Zach] } </code></p>
173
+
174
+ <p>For more examples see <code>spec/csv_decision/table_spec.rb</code>.
175
+ Complete documentation of all table parameters is in the code - see
176
+ <code>lib/csv_decision/parse.rb</code> and
177
+ <code>lib/csv_decision/table.rb</code>.</p>
178
+
179
+ <h3 id="label-CSV+Decision+features">CSV Decision features</h3>
180
+ <ul><li>
181
+ <p>Either returns the first matching row as a hash (default), or accumulates
182
+ all matches as an array of hashes (i.e., <code>parse</code> option
183
+ <code>first_match: false</code> or CSV file option
184
+ <code>accumulate</code>).</p>
185
+ </li><li>
186
+ <p>Fast decision-time performance (see <code>benchmarks</code> folder).
187
+ Automatically indexes all constants-only columns that do not contain any
188
+ empty strings.</p>
189
+ </li><li>
190
+ <p>In addition to strings, can match basic Ruby constants (e.g.,
191
+ <code>=nil</code>), regular expressions (e.g., <code>=~ on|off</code>),
192
+ comparisons (e.g., <code>&gt; 100.0</code> ) and Ruby-style ranges (e.g.,
193
+ <code>1..10</code>)</p>
194
+ </li><li>
195
+ <p>Can compare an input column versus another input hash key - e.g.,
196
+ <code>&gt; :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.</p>
200
+ </li><li>
201
+ <p>Column symbol expressions or Ruby methods (0-arity) may be used in input
202
+ columns for matching - e.g., <code>:column.zero?</code> or <code>:column
203
+ == 0</code>.</p>
204
+ </li><li>
205
+ <p>May also use Ruby methods in output columns - e.g.,
206
+ <code>:column.length</code>.</p>
207
+ </li><li>
208
+ <p>Accepts data as a file, CSV string or an array of arrays.</p>
209
+ </li></ul>
210
+
211
+ <h4 id="label-Constants+other+than+strings">Constants other than strings</h4>
212
+
213
+ <p>Although <code>csv_decision</code> is string oriented, it does recognise
214
+ other types of constant present in the input hash. Specifically, the
215
+ following classes are recognized: <code>Integer</code>,
216
+ <code>BigDecimal</code>, <code>NilClass</code>, <code>TrueClass</code> and
217
+ <code>FalseClass</code>.</p>
218
+
219
+ <p>This is accomplished by prefixing the value with one of the operators
220
+ <code>=</code>, <code>==</code> or <code>:=</code>. (The syntax is
221
+ intentionally lax.)</p>
222
+
223
+ <p>For example: “`ruby data = &lt;&lt;~DATA in :constant, out :value
224
+ :=nil, :=nil ==false, ==false =true, =true = 0, = 0 :=100.0, :=100.0
225
+ DATA</p>
226
+
227
+ <p>table = CSVDecision.parse(data) table.decide(constant: nil) # returns
228
+ value: nil<br> table.decide(constant: 0) # returns value: 0<br>
229
+ table.decide(constant: BigDecimal(&#39;100.0&#39;)) # returns value:
230
+ BigDecimal(&#39;100.0&#39;)<br> “`</p>
231
+
232
+ <h4 id="label-Column+header+symbols">Column header symbols</h4>
233
+
234
+ <p>All input and output column names are symbolized, and those symbols may be
235
+ used to form simple expressions that refer to values in the input hash.</p>
236
+
237
+ <p>For example: “`ruby data = &lt;&lt;~DATA in :node, in :parent, out :top?
238
+ , == :node, yes , , no DATA</p>
239
+
240
+ <pre class="code ruby"><code class="ruby"><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>
241
+ <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'>node:</span> <span class='int'>0</span><span class='comma'>,</span> <span class='label'>parent:</span> <span class='int'>0</span><span class='rparen'>)</span> <span class='comment'># returns top?: &#39;yes&#39;
242
+ </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'>node:</span> <span class='int'>1</span><span class='comma'>,</span> <span class='label'>parent:</span> <span class='int'>0</span><span class='rparen'>)</span> <span class='comment'># returns top?: &#39;no&#39;
243
+ </span></code></pre>
244
+
245
+ <p>“`</p>
246
+
247
+ <p>Note that there is no need to include an input column for
248
+ <code>:node</code> in the decision table - it just needs to be present in
249
+ the input hash. The expression, <code>== :node</code> should be read as
250
+ <code>:parent == :node</code>. It can also be shortened to just
251
+ <code>:node</code>, so the above decision table may be simplified to:</p>
252
+
253
+ <p><code>ruby data = &lt;&lt;~DATA in :parent, out :top?
254
+ :node, yes , no DATA </code> These comparison
255
+ operators are also supported: <code>!=</code>, <code>&gt;</code>,
256
+ <code>&gt;=</code>, <code>&lt;</code>, <code>&lt;=</code>. In addition, you
257
+ can also apply a Ruby 0-arity method - e.g., <code>.present?</code> or
258
+ <code>.nil?</code>. Negation is also supported - e.g., <code>!.nil?</code>.
259
+ Note that <code>.nil?</code> can also be written as <code>:= nil?</code>,
260
+ and <code>!.nil?</code> as <code>:= !nil?</code>, depending on preference.</p>
261
+
262
+ <p>For more simple examples see
263
+ <code>spec/csv_decision/examples_spec.rb</code>.</p>
264
+
265
+ <h4 id="label-Input+guard+conditions">Input <code>guard</code> conditions</h4>
266
+
267
+ <p>Sometimes it&#39;s more convenient to write guard expressions in a single
268
+ column specialized for that purpose. For example:</p>
269
+
270
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='heredoc_beg'>&lt;&lt;~DATA</span>
271
+ <span class='tstring_content'> in :country, guard:, out :ID, out :ID_type, out :len
272
+ </span><span class='tstring_content'> US, :CUSIP.present?, :CUSIP, CUSIP, :ID.length
273
+ </span><span class='tstring_content'> GB, :SEDOL.present?, :SEDOL, SEDOL, :ID.length
274
+ </span><span class='tstring_content'> , :ISIN.present?, :ISIN, ISIN, :ID.length
275
+ </span><span class='tstring_content'> , :SEDOL.present?, :SEDOL, SEDOL, :ID.length
276
+ </span><span class='tstring_content'> , :CUSIP.present?, :CUSIP, CUSIP, :ID.length
277
+ </span><span class='tstring_content'> , , := nil, := nil, := nil
278
+ </span><span class='heredoc_end'>DATA
279
+ </span>
280
+ <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>
281
+ <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'>&#39;</span><span class='tstring_content'>US</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>CUSIP:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>123456789</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='comment'>#=&gt; { ID: &#39;123456789&#39;, ID_type: &#39;CUSIP&#39;, len: 9 }
282
+ </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'>country:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>EU</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>CUSIP:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>123456789</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>ISIN:</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>123456789012</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span>
283
+ <span class='comment'>#=&gt; { ID: &#39;123456789012&#39;, ID_type: &#39;ISIN&#39;, len: 12 }
284
+ </span></code></pre>
285
+
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>&gt;</code>, <code>&gt;=</code>, <code>&lt;</code>
290
+ and <code>&lt;=</code>. Also, regular expressions are supported - i.e.,
291
+ <code>=~</code> and <code>!~</code>.</p>
292
+
293
+ <h4 id="label-Output+if+conditions">Output <code>if</code> 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'>&lt;&lt;~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'>&#39;</span><span class='tstring_content'>US</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>CUSIP:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>123456789</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='comment'>#=&gt; {ID: &#39;123456789&#39;, ID_type: &#39;CUSIP9&#39;, 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'>&#39;</span><span class='tstring_content'>12345678</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>ISIN:</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>1234567890</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='comment'>#=&gt; {ID: &#39;1234567890&#39;, ID_type: &#39;DUMMY&#39;, 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>&gt;</code>, <code>&gt;=</code>, <code>&lt;</code>
317
+ and <code>&lt;=</code>. Also, regular expressions are supported - i.e.,
318
+ <code>=~</code> and <code>!~</code>.</p>
319
+
320
+ <h4 id="label-Input+set+columns">Input <code>set</code> columns</h4>
321
+
322
+ <p>If you wish to set default values in the input hash, you can use a
323
+ <code>set</code> column rather than an <code>in</code> column. The data row
324
+ beneath the header is used to specify the default expression. There are
325
+ three variations: <code>set</code> (unconditional default)
326
+ <code>set/nil?</code>(set if <code>nil?</code> true) and
327
+ <code>set/blank?</code> (set if <code>blank?</code> true). Note that the
328
+ <code>decide!</code> method will mutate the input hash.</p>
329
+
330
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='heredoc_beg'>&lt;&lt;~DATA</span>
331
+ <span class='tstring_content'> set/nil? :country, guard:, set: class, out :PAID, out: len, if:
332
+ </span><span class='tstring_content'> US, , :class.upcase,
333
+ </span><span class='tstring_content'> US, :CUSIP.present?, != PRIVATE, :CUSIP, :PAID.length, :len == 9
334
+ </span><span class='tstring_content'> !=US, :ISIN.present?, != PRIVATE, :ISIN, :PAID.length, :len == 12
335
+ </span><span class='tstring_content'> US, :CUSIP.present?, PRIVATE, :CUSIP, :PAID.length,
336
+ </span><span class='tstring_content'> !=US, :ISIN.present?, PRIVATE, :ISIN, :PAID.length,
337
+ </span><span class='heredoc_end'>DATA
338
+ </span>
339
+ <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>
340
+ <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'>&#39;</span><span class='tstring_content'>1234567890</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>class:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>Private</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='comment'>#=&gt; {PAID: &#39;1234567890&#39;, len: 10}
341
+ </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'>ISIN:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>123456789012</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>country:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>GB</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>class:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>private</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='comment'>#=&gt; {PAID: &#39;123456789012&#39;, len: 12}
342
+ </span></code></pre>
343
+
344
+ <h4 id="label-Input+path+columns">Input <code>path</code> columns</h4>
345
+
346
+ <p>For hashes that contain sub-hashes, it&#39;s possible to specify a path for
347
+ the purposes of matching. (Arrays are currently not supported.)</p>
348
+
349
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_data'>data</span> <span class='op'>=</span> <span class='heredoc_beg'>&lt;&lt;~DATA</span>
350
+ <span class='tstring_content'> path:, path:, out :value
351
+ </span><span class='tstring_content'> header, , :source_name
352
+ </span><span class='tstring_content'> header, metrics, :service_name
353
+ </span><span class='tstring_content'> payload, , :amount
354
+ </span><span class='tstring_content'> payload, ref_data, :account_id
355
+ </span><span class='heredoc_end'>DATA
356
+ </span><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='comma'>,</span> <span class='label'>first_match:</span> <span class='kw'>false</span><span class='rparen'>)</span>
357
+
358
+ <span class='id identifier rubyid_input'>input</span> <span class='op'>=</span> <span class='lbrace'>{</span>
359
+ <span class='label'>header:</span> <span class='lbrace'>{</span>
360
+ <span class='label'>id:</span> <span class='int'>1</span><span class='comma'>,</span> <span class='label'>type_cd:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>BUY</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>source_name:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>Client</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>client_name:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>AAPL</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span>
361
+ <span class='label'>metrics:</span> <span class='lbrace'>{</span> <span class='label'>service_name:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>Trading</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>receive_time:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>12:00</span><span class='tstring_end'>&#39;</span></span> <span class='rbrace'>}</span>
362
+ <span class='rbrace'>}</span><span class='comma'>,</span>
363
+ <span class='label'>payload:</span> <span class='lbrace'>{</span>
364
+ <span class='label'>tran_id:</span> <span class='int'>9</span><span class='comma'>,</span> <span class='label'>amount:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>100.00</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span>
365
+ <span class='label'>ref_data:</span> <span class='lbrace'>{</span> <span class='label'>account_id:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>5010</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>type_id:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>BUYL</span><span class='tstring_end'>&#39;</span></span> <span class='rbrace'>}</span>
366
+ <span class='rbrace'>}</span>
367
+ <span class='rbrace'>}</span>
368
+
369
+ <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='id identifier rubyid_input'>input</span><span class='rparen'>)</span> <span class='comment'>#=&gt; { value: %w[Client Trading 100.00 5010] }
370
+ </span></code></pre>
371
+
372
+ <h3 id="label-Testing">Testing</h3>
373
+
374
+ <p><code>csv_decision</code> includes thorough <a
375
+ href="http://rspec.info">RSpec</a> tests:</p>
376
+
377
+ <p><code>bash # Execute within a clone of the csv_decision Git repository:
378
+ bundle install rspec </code></p>
379
+
380
+ <h3 id="label-Planned+features">Planned features</h3>
381
+
382
+ <p><code>csv_decision</code> is still a work in progress, and will be enhanced
383
+ to support the following features: * Supply a pre-defined library of
384
+ functions that may be called within input columns to implement custom
385
+ matching logic, or from the output columns to formulate the final
386
+ decision. * Built-in lookup functions evaluate other decision tables to
387
+ implement guard conditions, or supply output values. * Available
388
+ functions may be extended with a user-supplied library of Ruby methods for
389
+ custom logic. * Output columns may construct interpolated strings
390
+ containing references to column symbols.</p>
391
+
392
+ <h3 id="label-Reasons+for+the+limitations+of+column+expressions">Reasons for the limitations of column expressions</h3>
393
+
394
+ <p>The simple column expressions allowed by <code>csv_decision</code> are
395
+ purposely limited for reasons of understandability and maintainability. The
396
+ whole point of this gem is to make decision rules easier to express and
397
+ comprehend as declarative, tabular logic. While Ruby makes it easy to
398
+ execute arbitrary code embedded within a CSV file, this could easily result
399
+ in hard to debug logic that also poses safety risks.</p>
400
+
401
+ <h2 id="label-Changelog">Changelog</h2>
402
+
403
+ <p>See <a href="./CHANGELOG.md">CHANGELOG.md</a> for a list of changes.</p>
404
+
405
+ <h2 id="label-License">License</h2>
406
+
407
+ <p>CSV Decision © 2017-2018 by <a
408
+ href="mailto:brett@phillips-vickers.com">Brett Vickers</a>. CSV Decision is
409
+ licensed under the MIT license. Please see the <a
410
+ href="./LICENSE">LICENSE</a> document for more information.</p>
411
+ </div></div>
412
+
413
+ <div id="footer">
414
+ Generated on Sun Feb 11 10:26:06 2018 by
415
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
416
+ 0.9.12 (ruby-2.4.0).
417
+ </div>
418
+
419
+ </div>
420
+ </body>
421
+ </html>