csv_decision2 0.5.1

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