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