rufus-decision 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.txt +4 -1
- data/lib/rufus/decision.rb +68 -20
- data/test/decision_0_test.rb +34 -70
- data/test/decision_1_test.rb +13 -9
- data/test/decision_2_test.rb +45 -0
- data/test/decision_3_test.rb +56 -0
- data/test/test_base.rb +2 -2
- metadata +7 -6
data/README.txt
CHANGED
@@ -10,10 +10,13 @@ or at
|
|
10
10
|
|
11
11
|
http://rubyforge.org/frs/?group_id=4812
|
12
12
|
|
13
|
+
== intro blog post
|
14
|
+
|
15
|
+
http://jmettraux.wordpress.com/2009/04/25/rufus-decision-11-ruby-decision-tables/
|
13
16
|
|
14
17
|
== usage
|
15
18
|
|
16
|
-
|
19
|
+
More info at http://rufus.rubyforge.org/rufus-decision/classes/Rufus/Decision/Table.html but here is a recap.
|
17
20
|
|
18
21
|
An example where a few rules determine which salesperson should interact with a customer with given characteristics.
|
19
22
|
|
data/lib/rufus/decision.rb
CHANGED
@@ -35,7 +35,7 @@ require 'rufus/hashes'
|
|
35
35
|
module Rufus
|
36
36
|
module Decision
|
37
37
|
|
38
|
-
VERSION = '1.
|
38
|
+
VERSION = '1.2.0'
|
39
39
|
|
40
40
|
#
|
41
41
|
# A decision table is a description of a set of rules as a CSV (comma
|
@@ -171,6 +171,11 @@ module Decision
|
|
171
171
|
#
|
172
172
|
# will yield { result => [ 'normal', 'large' ]} for f0 => 56
|
173
173
|
#
|
174
|
+
# * "unbounded", by default, string matching is 'bounded', "apple" will match
|
175
|
+
# 'apple', but not 'greenapple'. When "unbounded" is set, 'greenapple' will
|
176
|
+
# match. ('bounded', in reality, means the target value is surrounded
|
177
|
+
# by ^ and $)
|
178
|
+
#
|
174
179
|
# === Setting options at table initialization
|
175
180
|
#
|
176
181
|
# It's OK to set the options at initialization time :
|
@@ -250,33 +255,58 @@ module Decision
|
|
250
255
|
#
|
251
256
|
attr_accessor :accumulate
|
252
257
|
|
258
|
+
# when set to true, evaluation of ruby code for output is allowed. False
|
259
|
+
# by default.
|
260
|
+
#
|
261
|
+
attr_accessor :ruby_eval
|
262
|
+
|
263
|
+
# false (bounded) by default : exact matches for string matching. When
|
264
|
+
# 'unbounded', target 'apple' will match for values like 'greenapples' or
|
265
|
+
# 'apple seed'.
|
266
|
+
#
|
267
|
+
attr_accessor :unbound
|
268
|
+
|
253
269
|
# The constructor for DecisionTable, you can pass a String, an Array
|
254
270
|
# (of arrays), a File object. The CSV parser coming with Ruby will take
|
255
271
|
# care of it and a DecisionTable instance will be built.
|
256
272
|
#
|
257
|
-
#
|
273
|
+
# Options are :through, :ignore_case, :accumulate (which
|
258
274
|
# forces :through to true when set) and :ruby_eval. See
|
259
275
|
# Rufus::Decision::Table for more details.
|
260
276
|
#
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
277
|
+
# Options passed to this method do override the options defined
|
278
|
+
# in the CSV itself.
|
279
|
+
#
|
280
|
+
# == options
|
281
|
+
#
|
282
|
+
# * :through : when set, all the rows of the decision table are considered
|
283
|
+
# * :ignore_case : case is ignored (not ignored by default)
|
284
|
+
# * :accumulate : gather instead of overriding (implies :through)
|
285
|
+
# * :ruby_eval : ruby code evaluation is OK
|
286
|
+
#
|
287
|
+
def initialize (csv, options={})
|
269
288
|
|
270
289
|
@rows = Rufus::Decision.csv_to_a(csv)
|
271
290
|
|
272
291
|
extract_options
|
292
|
+
|
273
293
|
parse_header_row
|
294
|
+
|
295
|
+
@first_match = false if options[:through] == true
|
296
|
+
@first_match = true if @first_match.nil?
|
297
|
+
|
298
|
+
set_opt(options, :ignore_case, :ignorecase)
|
299
|
+
set_opt(options, :accumulate)
|
300
|
+
set_opt(options, :ruby_eval)
|
301
|
+
set_opt(options, :unbounded)
|
302
|
+
|
303
|
+
@first_match = false if @accumulate
|
274
304
|
end
|
275
305
|
|
276
306
|
# Like transform, but the original hash doesn't get touched,
|
277
307
|
# a copy of it gets transformed and finally returned.
|
278
308
|
#
|
279
|
-
def transform (hash
|
309
|
+
def transform (hash)
|
280
310
|
|
281
311
|
transform!(hash.dup)
|
282
312
|
end
|
@@ -284,10 +314,9 @@ module Decision
|
|
284
314
|
# Passes the hash through the decision table and returns it,
|
285
315
|
# transformed.
|
286
316
|
#
|
287
|
-
def transform! (hash
|
317
|
+
def transform! (hash)
|
288
318
|
|
289
|
-
hash = Rufus::Decision::EvalHashFilter.new(hash)
|
290
|
-
if @ruby_eval || options[:ruby_eval] == true
|
319
|
+
hash = Rufus::Decision::EvalHashFilter.new(hash) if @ruby_eval
|
291
320
|
|
292
321
|
@rows.each do |row|
|
293
322
|
next unless matches?(row, hash)
|
@@ -304,11 +333,24 @@ module Decision
|
|
304
333
|
#
|
305
334
|
def to_csv
|
306
335
|
|
307
|
-
|
308
|
-
|
336
|
+
@rows.inject([ @header.to_csv ]) { |a, row|
|
337
|
+
a << row.join(',')
|
338
|
+
}.join("\n")
|
339
|
+
end
|
340
|
+
|
341
|
+
protected
|
342
|
+
|
343
|
+
def set_opt (options, *optnames)
|
344
|
+
|
345
|
+
optnames.each do |oname|
|
346
|
+
|
347
|
+
v = options[oname]
|
348
|
+
next unless v != nil
|
349
|
+
instance_variable_set("@#{optnames.first.to_s}", v)
|
350
|
+
return
|
351
|
+
end
|
309
352
|
end
|
310
353
|
|
311
|
-
private
|
312
354
|
|
313
355
|
# Returns true if the hash matches the in: values for this row
|
314
356
|
#
|
@@ -332,7 +374,7 @@ module Decision
|
|
332
374
|
else
|
333
375
|
|
334
376
|
range = to_ruby_range(cell)
|
335
|
-
range ? range.include?(value) :
|
377
|
+
range ? range.include?(value) : string_compare(value, cell)
|
336
378
|
end
|
337
379
|
|
338
380
|
return false unless b
|
@@ -341,12 +383,13 @@ module Decision
|
|
341
383
|
true
|
342
384
|
end
|
343
385
|
|
344
|
-
def
|
386
|
+
def string_compare (value, cell)
|
345
387
|
|
346
388
|
modifiers = 0
|
347
389
|
modifiers += Regexp::IGNORECASE if @ignore_case
|
348
390
|
|
349
|
-
rcell =
|
391
|
+
rcell = @unbounded ?
|
392
|
+
Regexp.new(cell, modifiers) : Regexp.new("^#{cell}$", modifiers)
|
350
393
|
|
351
394
|
rcell.match(value)
|
352
395
|
end
|
@@ -423,6 +466,8 @@ module Decision
|
|
423
466
|
elsif cell == 'accumulate'
|
424
467
|
@first_match = false
|
425
468
|
@accumulate = true
|
469
|
+
elsif cell == 'unbounded'
|
470
|
+
@unbounded = true
|
426
471
|
end
|
427
472
|
end
|
428
473
|
|
@@ -435,13 +480,16 @@ module Decision
|
|
435
480
|
# an "out:"
|
436
481
|
#
|
437
482
|
def is_vertical_table? (first_row)
|
483
|
+
|
438
484
|
bin = false
|
439
485
|
bout = false
|
486
|
+
|
440
487
|
first_row.each do |cell|
|
441
488
|
bin ||= cell.match(IN)
|
442
489
|
bout ||= cell.match(OUT)
|
443
490
|
return false if bin and bout
|
444
491
|
end
|
492
|
+
|
445
493
|
true
|
446
494
|
end
|
447
495
|
|
data/test/decision_0_test.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
require 'test/unit'
|
9
9
|
|
10
|
-
require File.dirname(__FILE__)
|
10
|
+
require File.join(File.dirname(__FILE__), 'test_base.rb')
|
11
11
|
|
12
12
|
|
13
13
|
class Decision0Test < Test::Unit::TestCase
|
@@ -28,13 +28,13 @@ e,f,2
|
|
28
28
|
"fx" => "c",
|
29
29
|
"fy" => "d"
|
30
30
|
}
|
31
|
-
do_test(CSV0, wi, {
|
31
|
+
do_test(CSV0, wi, { "fz" => "1" }, false)
|
32
32
|
|
33
33
|
wi = {
|
34
34
|
"fx" => "a",
|
35
35
|
"fy" => "d"
|
36
36
|
}
|
37
|
-
do_test(CSV0, wi, {
|
37
|
+
do_test(CSV0, wi, { "fz" => nil }, false)
|
38
38
|
end
|
39
39
|
|
40
40
|
CSV0B = %{
|
@@ -51,7 +51,7 @@ e,f,2
|
|
51
51
|
"fx" => "c",
|
52
52
|
"fy" => "d"
|
53
53
|
}
|
54
|
-
do_test(CSV0B, wi, {
|
54
|
+
do_test(CSV0B, wi, { "fz" => "1" }, false)
|
55
55
|
end
|
56
56
|
|
57
57
|
# test 1 moved to decision_1_test.rb
|
@@ -67,10 +67,10 @@ e, f, 2
|
|
67
67
|
def test_2
|
68
68
|
|
69
69
|
wi = { "fx" => "c", "fy" => "d" }
|
70
|
-
do_test(CSV2, wi, {
|
70
|
+
do_test(CSV2, wi, { "fz" => "1" }, false)
|
71
71
|
|
72
72
|
wi = { "fx" => "a", "fy" => "d" }
|
73
|
-
do_test(CSV2, wi, {
|
73
|
+
do_test(CSV2, wi, { "fz" => nil }, false)
|
74
74
|
end
|
75
75
|
|
76
76
|
CSV3 = %{
|
@@ -89,19 +89,19 @@ cloudy, , no
|
|
89
89
|
"weather" => "raining",
|
90
90
|
"month" => "december"
|
91
91
|
}
|
92
|
-
do_test(CSV3, wi, {
|
92
|
+
do_test(CSV3, wi, { "take_umbrella?" => "yes" }, false)
|
93
93
|
|
94
94
|
wi = {
|
95
95
|
"weather" => "cloudy",
|
96
96
|
"month" => "june"
|
97
97
|
}
|
98
|
-
do_test(CSV3, wi, {
|
98
|
+
do_test(CSV3, wi, { "take_umbrella?" => "yes" }, false)
|
99
99
|
|
100
100
|
wi = {
|
101
101
|
"weather" => "cloudy",
|
102
102
|
"month" => "march"
|
103
103
|
}
|
104
|
-
do_test(CSV3, wi, {
|
104
|
+
do_test(CSV3, wi, { "take_umbrella?" => "no" }, false)
|
105
105
|
end
|
106
106
|
|
107
107
|
def test_3b
|
@@ -109,17 +109,17 @@ cloudy, , no
|
|
109
109
|
h = {}
|
110
110
|
h["weather"] = "raining"
|
111
111
|
h["month"] = "december"
|
112
|
-
do_test(CSV3, h, {
|
112
|
+
do_test(CSV3, h, { "take_umbrella?" => "yes" }, false)
|
113
113
|
|
114
114
|
h = {}
|
115
115
|
h["weather"] = "cloudy"
|
116
116
|
h["month"] = "june"
|
117
|
-
do_test(CSV3, h, {
|
117
|
+
do_test(CSV3, h, { "take_umbrella?" => "yes" }, false)
|
118
118
|
|
119
119
|
h = {}
|
120
120
|
h["weather"] = "cloudy"
|
121
121
|
h["month"] = "march"
|
122
|
-
do_test(CSV3, h, {
|
122
|
+
do_test(CSV3, h, { "take_umbrella?" => "no" }, false)
|
123
123
|
end
|
124
124
|
|
125
125
|
def test_3c
|
@@ -144,35 +144,6 @@ cloudy, , no
|
|
144
144
|
assert_equal "Henry", h["team_member"]
|
145
145
|
end
|
146
146
|
|
147
|
-
CSV3D = "http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ&output=csv&gid=0"
|
148
|
-
|
149
|
-
def test_3d
|
150
|
-
|
151
|
-
#return unless online?
|
152
|
-
|
153
|
-
h = {}
|
154
|
-
h["weather"] = "raining"
|
155
|
-
h["month"] = "december"
|
156
|
-
|
157
|
-
do_test(CSV3D, h, {}, { "take_umbrella?" => "yes" }, false)
|
158
|
-
|
159
|
-
h = {}
|
160
|
-
h["weather"] = "cloudy"
|
161
|
-
h["month"] = "june"
|
162
|
-
|
163
|
-
sleep 0.5 # don't request the doc too often
|
164
|
-
|
165
|
-
do_test(CSV3D, h, {}, { "take_umbrella?" => "yes" }, false)
|
166
|
-
|
167
|
-
h = {}
|
168
|
-
h["weather"] = "cloudy"
|
169
|
-
h["month"] = "march"
|
170
|
-
|
171
|
-
sleep 0.5 # don't request the doc too often
|
172
|
-
|
173
|
-
do_test(CSV3D, h, {}, { "take_umbrella?" => "no" }, false)
|
174
|
-
end
|
175
|
-
|
176
147
|
def test_3e
|
177
148
|
|
178
149
|
table = Rufus::DecisionTable.new(%{
|
@@ -208,13 +179,13 @@ cloudy, , no
|
|
208
179
|
}
|
209
180
|
|
210
181
|
wi = { 'fx' => 'a', 'fy' => 'a' }
|
211
|
-
do_test(table, wi, {
|
182
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'true' }, false)
|
212
183
|
|
213
184
|
wi = { 'fx' => 'a', 'fy' => 'b' }
|
214
|
-
do_test(table, wi, {
|
185
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'false' }, false)
|
215
186
|
|
216
187
|
wi = { 'fx' => 'A', 'fy' => 'b' }
|
217
|
-
do_test(table, wi, {
|
188
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'false' }, false)
|
218
189
|
end
|
219
190
|
|
220
191
|
def test_through_and_ignorecase_set_at_table_initialization
|
@@ -232,13 +203,13 @@ cloudy, , no
|
|
232
203
|
table, :through => true, :ignore_case => true)
|
233
204
|
|
234
205
|
wi = { 'fx' => 'a', 'fy' => 'a' }
|
235
|
-
do_test(table, wi, {
|
206
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'true' }, false)
|
236
207
|
|
237
208
|
wi = { 'fx' => 'a', 'fy' => 'b' }
|
238
|
-
do_test(table, wi, {
|
209
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'false' }, false)
|
239
210
|
|
240
211
|
wi = { 'fx' => 'A', 'fy' => 'b' }
|
241
|
-
do_test(table, wi, {
|
212
|
+
do_test(table, wi, { 'fX' => 'true', 'fY' => 'false' }, false)
|
242
213
|
end
|
243
214
|
|
244
215
|
#
|
@@ -258,12 +229,12 @@ in:fx, out:fy
|
|
258
229
|
wi = {
|
259
230
|
"fx" => "5"
|
260
231
|
}
|
261
|
-
do_test(CSV6, wi, {
|
232
|
+
do_test(CSV6, wi, { "fy" => "a" }, false)
|
262
233
|
|
263
234
|
wi = {
|
264
235
|
"fx" => "100.0001"
|
265
236
|
}
|
266
|
-
do_test(CSV6, wi, {
|
237
|
+
do_test(CSV6, wi, { "fy" => "c" }, false)
|
267
238
|
end
|
268
239
|
|
269
240
|
#
|
@@ -283,17 +254,17 @@ in:fx, out:fy
|
|
283
254
|
wi = {
|
284
255
|
"fx" => "5"
|
285
256
|
}
|
286
|
-
do_test(CSV7, wi, {
|
257
|
+
do_test(CSV7, wi, { "fy" => "c" }, false)
|
287
258
|
|
288
259
|
wi = {
|
289
260
|
"fx" => "10"
|
290
261
|
}
|
291
|
-
do_test(CSV7, wi, {
|
262
|
+
do_test(CSV7, wi, { "fy" => "b" }, false)
|
292
263
|
|
293
264
|
wi = {
|
294
265
|
"fx" => "10a"
|
295
266
|
}
|
296
|
-
do_test(CSV7, wi, {
|
267
|
+
do_test(CSV7, wi, { "fy" => "a" }, false)
|
297
268
|
end
|
298
269
|
|
299
270
|
CSV8 = %{
|
@@ -317,16 +288,10 @@ e,f,2
|
|
317
288
|
|
318
289
|
def test_ruby_eval
|
319
290
|
|
320
|
-
wi = { 'fx' => 'c', 'fy' => 'd' }
|
321
|
-
do_test(CSV9, wi, { :ruby_eval => true }, { 'fz' => '3' }, false)
|
322
|
-
end
|
323
|
-
|
324
|
-
def test_ruby_eval_set_at_table_initialization
|
325
|
-
|
326
291
|
table = Rufus::Decision::Table.new(CSV9, :ruby_eval => true)
|
327
292
|
|
328
293
|
wi = { 'fx' => 'c', 'fy' => 'd' }
|
329
|
-
do_test(table, wi, {
|
294
|
+
do_test(table, wi, { 'fz' => '3' }, false)
|
330
295
|
end
|
331
296
|
|
332
297
|
CSV10 = %{
|
@@ -338,13 +303,13 @@ in:fx,in:fx,out:fz
|
|
338
303
|
def test_10
|
339
304
|
|
340
305
|
wi = { "fx" => "91" }
|
341
|
-
do_test(CSV10, wi, {
|
306
|
+
do_test(CSV10, wi, { "fz" => "ok" }, false)
|
342
307
|
|
343
308
|
wi = { "fx" => "95" }
|
344
|
-
do_test(CSV10, wi, {
|
309
|
+
do_test(CSV10, wi, { "fz" => "bad" }, false)
|
345
310
|
|
346
311
|
wi = { "fx" => "81" }
|
347
|
-
do_test(CSV10, wi, {
|
312
|
+
do_test(CSV10, wi, { "fz" => "bad" }, false)
|
348
313
|
end
|
349
314
|
|
350
315
|
CSV11 = %{
|
@@ -361,7 +326,7 @@ in:f1,in:f1,in:f2,in:f3,out:o1,out:e1,out:e2
|
|
361
326
|
def test_fu_zhang
|
362
327
|
|
363
328
|
wi = { 'f1' => 97, 'f2' => 5 }
|
364
|
-
do_test CSV11, wi, {
|
329
|
+
do_test CSV11, wi, { 'o1' => 'Expection1' }, false
|
365
330
|
end
|
366
331
|
|
367
332
|
CSV12 = %{
|
@@ -378,14 +343,14 @@ b, , yellow, beige
|
|
378
343
|
def test_accumulate
|
379
344
|
|
380
345
|
wi = { "fx" => "a", "fy" => "a" }
|
381
|
-
do_test CSV12, wi, {
|
346
|
+
do_test CSV12, wi, { "fX" => "red;blue", "fY" => "green;purple" }, false
|
382
347
|
|
383
348
|
wi = { "fx" => "a", "fy" => "a", "fX" => "BLACK" }
|
384
|
-
do_test CSV12, wi, {
|
349
|
+
do_test CSV12, wi, {
|
385
350
|
"fX" => "BLACK;red;blue", "fY" => "green;purple" }, false
|
386
351
|
|
387
352
|
wi = { "fx" => "a", "fy" => "a", "fX" => [ "BLACK", "BLUE" ] }
|
388
|
-
do_test CSV12, wi, {
|
353
|
+
do_test CSV12, wi, {
|
389
354
|
"fX" => "BLACK;BLUE;red;blue", "fY" => "green;purple" }, false
|
390
355
|
end
|
391
356
|
|
@@ -424,14 +389,13 @@ in:fx,out:fz
|
|
424
389
|
def test_range
|
425
390
|
|
426
391
|
wi = { "fx" => "91" }
|
427
|
-
do_test CSV13, wi, {
|
392
|
+
do_test CSV13, wi, { "fz" => "ok" }, false
|
428
393
|
|
429
394
|
wi = { "fx" => "95" }
|
430
|
-
do_test CSV13, wi, {
|
395
|
+
do_test CSV13, wi, { "fz" => "bad" }, false
|
431
396
|
|
432
397
|
wi = { "fx" => "81" }
|
433
|
-
do_test CSV13, wi, {
|
398
|
+
do_test CSV13, wi, { "fz" => "bad" }, false
|
434
399
|
end
|
435
|
-
|
436
400
|
end
|
437
401
|
|
data/test/decision_1_test.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
require 'test/unit'
|
9
9
|
|
10
|
-
require File.dirname(__FILE__)
|
10
|
+
require File.join(File.dirname(__FILE__), 'test_base.rb')
|
11
11
|
|
12
12
|
|
13
13
|
class Decision1Test < Test::Unit::TestCase
|
@@ -26,22 +26,26 @@ g,h,${r:'${fx}' + '${fy}'}
|
|
26
26
|
def test_1
|
27
27
|
|
28
28
|
wi = { 'fx' => 'c', 'fy' => 'd' }
|
29
|
-
do_test(CSV1, wi, {
|
29
|
+
do_test(CSV1, wi, { 'fz' => 'c' }, false)
|
30
30
|
|
31
31
|
wi = { 'fx' => 'a', 'fy' => 'a' }
|
32
|
-
do_test(CSV1, wi, {
|
32
|
+
do_test(CSV1, wi, { 'fz' => '0' }, false)
|
33
33
|
end
|
34
34
|
|
35
35
|
def test_1b
|
36
36
|
|
37
|
+
table = Rufus::Decision::Table.new(CSV1, :ruby_eval => true)
|
38
|
+
|
37
39
|
h = { 'fx' => 'e', 'fy' => 'f' }
|
38
|
-
do_test(
|
40
|
+
do_test(table, h, { 'fz' => '7' }, false)
|
39
41
|
end
|
40
42
|
|
41
43
|
def test_1c
|
42
44
|
|
45
|
+
table = Rufus::Decision::Table.new(CSV1, :ruby_eval => true)
|
46
|
+
|
43
47
|
h = { 'fx' => 'g', 'fy' => 'h' }
|
44
|
-
do_test(
|
48
|
+
do_test(table, h, { 'fz' => 'gh' }, false)
|
45
49
|
end
|
46
50
|
|
47
51
|
CSV2 = [
|
@@ -55,7 +59,7 @@ g,h,${r:'${fx}' + '${fy}'}
|
|
55
59
|
def test_with_array_table
|
56
60
|
|
57
61
|
wi = { 'fx' => 'c', 'fy' => 'd' }
|
58
|
-
do_test(CSV2, wi, {
|
62
|
+
do_test(CSV2, wi, { 'fz' => 'c' }, false)
|
59
63
|
end
|
60
64
|
|
61
65
|
def test_empty_string_to_float
|
@@ -70,7 +74,7 @@ g,h,${r:'${fx}' + '${fy}'}
|
|
70
74
|
[ '', 'maniac', 'Korolev' ]
|
71
75
|
]
|
72
76
|
|
73
|
-
do_test(table, wi, {
|
77
|
+
do_test(table, wi, { 'salesperson' => 'Korolev' }, false)
|
74
78
|
end
|
75
79
|
|
76
80
|
def test_vertical_rules
|
@@ -78,7 +82,7 @@ g,h,${r:'${fx}' + '${fy}'}
|
|
78
82
|
table = CSV2.transpose
|
79
83
|
|
80
84
|
wi = { 'fx' => 'c', 'fy' => 'd' }
|
81
|
-
do_test(table, wi, {
|
85
|
+
do_test(table, wi, { 'fz' => 'c' }, false)
|
82
86
|
end
|
83
87
|
|
84
88
|
def test_vertical_rules_2
|
@@ -90,7 +94,7 @@ out:team_member,Alice,Bob,Charly,Donald,Ernest,Fujio,Gilbert,Henry,Zach
|
|
90
94
|
}
|
91
95
|
|
92
96
|
h = { 'topic' => 'politics', 'region' => 'america' }
|
93
|
-
do_test(table, h, {
|
97
|
+
do_test(table, h, { 'team_member' => 'Gilbert' }, false)
|
94
98
|
end
|
95
99
|
|
96
100
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-decision
|
4
|
+
#
|
5
|
+
# Sun Oct 29 15:41:44 JST 2006
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), 'test_base.rb')
|
11
|
+
|
12
|
+
|
13
|
+
class Decision2Test < Test::Unit::TestCase
|
14
|
+
include DecisionTestMixin
|
15
|
+
|
16
|
+
CSV3D = "http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ&output=csv&gid=0"
|
17
|
+
|
18
|
+
def test_3d
|
19
|
+
|
20
|
+
#return unless online?
|
21
|
+
|
22
|
+
h = {}
|
23
|
+
h["weather"] = "raining"
|
24
|
+
h["month"] = "december"
|
25
|
+
|
26
|
+
do_test(CSV3D, h, { "take_umbrella?" => "yes" }, false)
|
27
|
+
|
28
|
+
h = {}
|
29
|
+
h["weather"] = "cloudy"
|
30
|
+
h["month"] = "june"
|
31
|
+
|
32
|
+
sleep 0.3 # don't request the doc too often
|
33
|
+
|
34
|
+
do_test(CSV3D, h, { "take_umbrella?" => "yes" }, false)
|
35
|
+
|
36
|
+
h = {}
|
37
|
+
h["weather"] = "cloudy"
|
38
|
+
h["month"] = "march"
|
39
|
+
|
40
|
+
sleep 0.3 # don't request the doc too often
|
41
|
+
|
42
|
+
do_test(CSV3D, h, { "take_umbrella?" => "no" }, false)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-decision
|
4
|
+
#
|
5
|
+
# Mon Sep 7 13:42:09 JST 2009
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'test/unit'
|
9
|
+
|
10
|
+
require File.join(File.dirname(__FILE__), 'test_base.rb')
|
11
|
+
|
12
|
+
|
13
|
+
class Decision3Test < Test::Unit::TestCase
|
14
|
+
include DecisionTestMixin
|
15
|
+
|
16
|
+
CSV14 = %{
|
17
|
+
in:fstate,out:result
|
18
|
+
apple,1
|
19
|
+
orange,2
|
20
|
+
}
|
21
|
+
|
22
|
+
def test_bounded_match
|
23
|
+
|
24
|
+
dt = Rufus::DecisionTable.new(CSV14)
|
25
|
+
|
26
|
+
assert_equal(
|
27
|
+
{ 'fstate' => 'greenapples' },
|
28
|
+
dt.transform({ 'fstate' => 'greenapples' }))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_unbounded_match
|
32
|
+
|
33
|
+
dt = Rufus::DecisionTable.new(CSV14, :unbounded => true)
|
34
|
+
|
35
|
+
assert_equal(
|
36
|
+
{ 'fstate' => 'apples', 'result' => '1' },
|
37
|
+
dt.transform({ 'fstate' => 'apples' }))
|
38
|
+
end
|
39
|
+
|
40
|
+
CSV14b = %{
|
41
|
+
unbounded,
|
42
|
+
in:fstate,out:result
|
43
|
+
apple,1
|
44
|
+
orange,2
|
45
|
+
}
|
46
|
+
|
47
|
+
def test_unbounded_match_set_in_table
|
48
|
+
|
49
|
+
dt = Rufus::DecisionTable.new(CSV14b)
|
50
|
+
|
51
|
+
assert_equal(
|
52
|
+
{ 'fstate' => 'apples', 'result' => '1' },
|
53
|
+
dt.transform({ 'fstate' => 'apples' }))
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
data/test/test_base.rb
CHANGED
@@ -14,7 +14,7 @@ module DecisionTestMixin
|
|
14
14
|
|
15
15
|
protected
|
16
16
|
|
17
|
-
def do_test (table_data, h,
|
17
|
+
def do_test (table_data, h, expected_result, verbose=false)
|
18
18
|
|
19
19
|
table = table_data.is_a?(Rufus::Decision::Table) ?
|
20
20
|
table_data :
|
@@ -29,7 +29,7 @@ module DecisionTestMixin
|
|
29
29
|
p h
|
30
30
|
end
|
31
31
|
|
32
|
-
h = table.transform!(h
|
32
|
+
h = table.transform!(h)
|
33
33
|
|
34
34
|
if verbose
|
35
35
|
puts
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rufus-decision
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Mettraux
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-09-07 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -44,11 +44,14 @@ extra_rdoc_files:
|
|
44
44
|
- CREDITS.txt
|
45
45
|
files:
|
46
46
|
- bin/rufus_decide
|
47
|
+
- lib/rufus
|
47
48
|
- lib/rufus/decision.rb
|
48
49
|
- lib/rufus/hashes.rb
|
49
50
|
- lib/rufus-decision.rb
|
50
51
|
- test/decision_0_test.rb
|
51
52
|
- test/decision_1_test.rb
|
53
|
+
- test/decision_2_test.rb
|
54
|
+
- test/decision_3_test.rb
|
52
55
|
- test/eval_test.rb
|
53
56
|
- test/goal.csv
|
54
57
|
- test/input.csv
|
@@ -61,8 +64,6 @@ files:
|
|
61
64
|
- CREDITS.txt
|
62
65
|
has_rdoc: true
|
63
66
|
homepage: http://rufus.rubyforge.org/rufus-decision
|
64
|
-
licenses: []
|
65
|
-
|
66
67
|
post_install_message:
|
67
68
|
rdoc_options: []
|
68
69
|
|
@@ -84,9 +85,9 @@ requirements:
|
|
84
85
|
- rufus-dollar
|
85
86
|
- rufus-treechecker
|
86
87
|
rubyforge_project: rufus
|
87
|
-
rubygems_version: 1.3.
|
88
|
+
rubygems_version: 1.3.1
|
88
89
|
signing_key:
|
89
|
-
specification_version:
|
90
|
+
specification_version: 2
|
90
91
|
summary: CSV based Ruby decision tables
|
91
92
|
test_files:
|
92
93
|
- test/test.rb
|