rufus-decision 1.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/rufus/hashes.rb CHANGED
@@ -1,6 +1,5 @@
1
- #
2
1
  #--
3
- # Copyright (c) 2008, John Mettraux, jmettraux@gmail.com
2
+ # Copyright (c) 2008-2009, John Mettraux, jmettraux@gmail.com
4
3
  #
5
4
  # Permission is hereby granted, free of charge, to any person obtaining a copy
6
5
  # of this software and associated documentation files (the "Software"), to deal
@@ -19,19 +18,17 @@
19
18
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
19
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
20
  # THE SOFTWARE.
22
- #++
23
21
  #
22
+ # Made in Japan.
23
+ #++
24
24
 
25
- #
26
- # "made in Japan"
27
- #
28
- # John Mettraux at openwfe.org
29
- #
30
25
 
26
+ require 'fileutils'
31
27
  require 'rufus/treechecker'
32
28
 
33
29
 
34
30
  module Rufus
31
+ module Decision
35
32
 
36
33
  #
37
34
  # In the Java world, this class would be considered abstract.
@@ -46,6 +43,8 @@ module Rufus
46
43
  #
47
44
  class HashFilter
48
45
 
46
+ attr_reader :parent_hash
47
+
49
48
  def initialize (parent_hash)
50
49
 
51
50
  @parent_hash = parent_hash
@@ -67,59 +66,59 @@ module Rufus
67
66
 
68
67
  protected
69
68
 
70
- def do_split (element)
69
+ def do_split (element)
71
70
 
72
- return [ nil, element ] unless element.is_a?(String)
71
+ return [ nil, element ] unless element.is_a?(String)
73
72
 
74
- a = element.split(':', 2)
75
- return [ nil ] + a if a.length == 1
76
- a
77
- end
73
+ a = element.split(':', 2)
74
+ return [ nil ] + a if a.length == 1
75
+ a
76
+ end
78
77
 
79
- def handles_prefix? (p)
78
+ def handles_prefix? (p)
80
79
 
81
- false
82
- end
80
+ false
81
+ end
83
82
 
84
- def do_eval (key, p, k)
83
+ def do_eval (key, p, k)
85
84
 
86
- raise NotImplementedError.new(
87
- "missing do_eval(key, p, k) implementation")
88
- end
85
+ raise NotImplementedError.new(
86
+ "missing do_eval(key, p, k) implementation")
87
+ end
89
88
 
90
- def do_lookup (key, p, k)
89
+ def do_lookup (key, p, k)
91
90
 
92
- if handles_prefix?(p)
91
+ if handles_prefix?(p)
93
92
 
94
- do_eval(key, p, k)
93
+ do_eval(key, p, k)
95
94
 
96
- elsif @parent_hash.respond_to?(:do_lookup)
95
+ elsif @parent_hash.respond_to?(:do_lookup)
97
96
 
98
- @parent_hash.do_lookup key, p, k
97
+ @parent_hash.do_lookup(key, p, k)
99
98
 
100
- else
99
+ else
101
100
 
102
- @parent_hash[key]
103
- end
101
+ @parent_hash[key]
104
102
  end
103
+ end
105
104
 
106
- def do_put (key, value, p, v)
105
+ def do_put (key, value, p, v)
107
106
 
108
- val = value
107
+ val = value
109
108
 
110
- if handles_prefix?(p)
109
+ if handles_prefix?(p)
111
110
 
112
- @parent_hash[key] = do_eval(value, p, v)
111
+ @parent_hash[key] = do_eval(value, p, v)
113
112
 
114
- elsif @parent_hash.respond_to?(:do_put)
113
+ elsif @parent_hash.respond_to?(:do_put)
115
114
 
116
- @parent_hash.do_put key, value, p, v
115
+ @parent_hash.do_put key, value, p, v
117
116
 
118
- else
117
+ else
119
118
 
120
- @parent_hash[key] = value
121
- end
119
+ @parent_hash[key] = value
122
120
  end
121
+ end
123
122
  end
124
123
 
125
124
  #
@@ -146,33 +145,33 @@ module Rufus
146
145
 
147
146
  def initialize (parent_hash)
148
147
 
149
- super parent_hash
148
+ super(parent_hash)
150
149
  end
151
150
 
152
151
  protected
153
152
 
154
- RP = [ 'r', 'ruby', 'reval' ]
153
+ RP = %w{ r ruby reval }
155
154
 
156
- def handles_prefix? (prefix)
155
+ def handles_prefix? (prefix)
157
156
 
158
- RP.include?(prefix)
159
- end
157
+ RP.include?(prefix)
158
+ end
160
159
 
161
- #
162
- # Ready for override.
163
- #
164
- def get_binding
160
+ # Ready for override.
161
+ #
162
+ def get_binding
165
163
 
166
- binding()
167
- end
164
+ binding()
165
+ end
168
166
 
169
- def do_eval (key, p, k)
167
+ def do_eval (key, p, k)
170
168
 
171
- #Rufus::eval_safely(k, @safe_level, get_binding)
172
- Rufus::check_and_eval(k, get_binding)
173
- end
169
+ Rufus::Decision.check_and_eval(k, get_binding)
170
+ end
174
171
  end
175
172
 
173
+ # An abstract syntax tree check (prior to any ruby eval)
174
+ #
176
175
  TREECHECKER = Rufus::TreeChecker.new do
177
176
 
178
177
  exclude_fvccall :abort, :exit, :exit!
@@ -200,6 +199,9 @@ module Rufus
200
199
  end
201
200
  TREECHECKER.freeze
202
201
 
202
+ # Given a string (of ruby code), first makes sure it doesn't contain
203
+ # harmful code, then evaluates it.
204
+ #
203
205
  def self.check_and_eval (ruby_code, bndng=binding())
204
206
 
205
207
  TREECHECKER.check(ruby_code)
@@ -209,12 +211,6 @@ module Rufus
209
211
  eval(ruby_code, bndng)
210
212
  end
211
213
 
212
- private
213
-
214
- def Rufus.unescape (text)
215
-
216
- text.gsub("\\\\\\$\\{", "\\${")
217
- end
218
-
214
+ end
219
215
  end
220
216
 
@@ -1,37 +1,28 @@
1
1
 
2
2
  #
3
- # Testing rufus-deciision
4
- #
5
- # John Mettraux at openwfe.org
3
+ # Testing rufus-decision
6
4
  #
7
5
  # Sun Oct 29 15:41:44 JST 2006
8
6
  #
9
7
 
10
8
  require 'test/unit'
11
9
 
12
- require 'dmixin'
10
+ require File.dirname(__FILE__) + '/test_base.rb'
13
11
 
14
12
 
15
- class DecisionTest < Test::Unit::TestCase
13
+ class Decision0Test < Test::Unit::TestCase
16
14
  include DecisionTestMixin
17
15
 
18
- #def setup
19
- #end
20
-
21
- #def teardown
22
- #end
23
-
24
- CSV0 = \
25
- """
16
+ CSV0 = %{
26
17
  ,,
27
18
  in:fx,in:fy,out:fz
28
19
  ,,
29
20
  a,b,0
30
21
  c,d,1
31
22
  e,f,2
32
- """
23
+ }
33
24
 
34
- def test_csv_0
25
+ def test_0
35
26
 
36
27
  wi = {
37
28
  "fx" => "c",
@@ -46,15 +37,13 @@ e,f,2
46
37
  do_test(CSV0, wi, {}, { "fz" => nil }, false)
47
38
  end
48
39
 
49
- CSV0B = \
50
- """
51
-
40
+ CSV0B = %{
52
41
  in:fx,in:fy,out:fz
53
42
 
54
43
  a,b,0
55
44
  c,d,1
56
45
  e,f,2
57
- """
46
+ }
58
47
 
59
48
  def test_0b
60
49
 
@@ -67,32 +56,24 @@ e,f,2
67
56
 
68
57
  # test 1 moved to decision_1_test.rb
69
58
 
70
- CSV2 = \
71
- """
59
+ CSV2 = %{
72
60
  in:fx, in:fy, out:fz
73
61
  ,,
74
62
  a, b, 0
75
63
  c, d, 1
76
64
  e, f, 2
77
- """
65
+ }
78
66
 
79
67
  def test_2
80
68
 
81
- wi = {
82
- "fx" => "c",
83
- "fy" => "d"
84
- }
69
+ wi = { "fx" => "c", "fy" => "d" }
85
70
  do_test(CSV2, wi, {}, { "fz" => "1" }, false)
86
71
 
87
- wi = {
88
- "fx" => "a",
89
- "fy" => "d"
90
- }
72
+ wi = { "fx" => "a", "fy" => "d" }
91
73
  do_test(CSV2, wi, {}, { "fz" => nil }, false)
92
74
  end
93
75
 
94
- CSV3 = \
95
- """
76
+ CSV3 = %{
96
77
  in:weather, in:month, out:take_umbrella?
97
78
  ,,
98
79
  raining, , yes
@@ -100,7 +81,7 @@ sunny, , no
100
81
  cloudy, june, yes
101
82
  cloudy, may, yes
102
83
  cloudy, , no
103
- """
84
+ }
104
85
 
105
86
  def test_3
106
87
 
@@ -143,24 +124,24 @@ cloudy, , no
143
124
 
144
125
  def test_3c
145
126
 
146
- table = Rufus::DecisionTable.new("""
147
- in:topic,in:region,out:team_member
148
- sports,europe,Alice
149
- sports,,Bob
150
- finance,america,Charly
151
- finance,europe,Donald
152
- finance,,Ernest
153
- politics,asia,Fujio
154
- politics,america,Gilbert
155
- politics,,Henry
156
- ,,Zach
157
- """)
158
-
159
- h = {}
160
- h["topic"] = "politics"
161
- table.transform! h
162
-
163
- assert_equal "Henry", h["team_member"]
127
+ table = Rufus::DecisionTable.new(%{
128
+ in:topic,in:region,out:team_member
129
+ sports,europe,Alice
130
+ sports,,Bob
131
+ finance,america,Charly
132
+ finance,europe,Donald
133
+ finance,,Ernest
134
+ politics,asia,Fujio
135
+ politics,america,Gilbert
136
+ politics,,Henry
137
+ ,,Zach
138
+ })
139
+
140
+ h = {}
141
+ h["topic"] = "politics"
142
+ table.transform! h
143
+
144
+ assert_equal "Henry", h["team_member"]
164
145
  end
165
146
 
166
147
  CSV3D = "http://spreadsheets.google.com/pub?key=pCkopoeZwCNsMWOVeDjR1TQ&output=csv&gid=0"
@@ -194,95 +175,83 @@ cloudy, , no
194
175
 
195
176
  def test_3e
196
177
 
197
- table = Rufus::DecisionTable.new("""
198
- in:topic,in:region,out:team_member
199
- sports,europe,Alice
200
- """)
178
+ table = Rufus::DecisionTable.new(%{
179
+ in:topic,in:region,out:team_member
180
+ sports,europe,Alice
181
+ })
201
182
 
202
- h0 = {}
203
- h0["topic"] = "politics"
204
- h1 = table.transform! h0
183
+ h0 = {}
184
+ h0["topic"] = "politics"
185
+ h1 = table.transform! h0
205
186
 
206
- assert_equal h0.object_id, h1.object_id
187
+ assert_equal h0.object_id, h1.object_id
207
188
 
208
- h0 = {}
209
- h0["topic"] = "politics"
210
- h1 = table.transform h0
189
+ h0 = {}
190
+ h0["topic"] = "politics"
191
+ h1 = table.transform h0
211
192
 
212
- assert_not_equal h0.object_id, h1.object_id
193
+ assert_not_equal h0.object_id, h1.object_id
213
194
  end
214
195
 
215
- # CSV4 = \
216
- #'''
217
- #"in:weather", "in:month", "out:take_umbrella?"
218
- #"","",""
219
- #"raining","","yes"
220
- #"sunny","","no"
221
- #"cloudy","june","yes"
222
- #"cloudy","may","yes"
223
- #"cloudy","","no"
224
- #'''
225
-
226
- #def test_4
227
- # h = {}
228
- # h["weather"] = "raining"
229
- # h["month"] = "december"
230
- # do_test(CSV4, h, { "take_umbrella?" => "yes" }, false)
231
- # h = {}
232
- # h["weather"] = "cloudy"
233
- # h["month"] = "june"
234
- # do_test(CSV4, h, { "take_umbrella?" => "yes" }, false)
235
- # h = {}
236
- # h["weather"] = "cloudy"
237
- # h["month"] = "march"
238
- # do_test(CSV4, h, { "take_umbrella?" => "no" }, false)
239
- #end
240
-
241
- CSV5 = \
242
- """
243
- through,ignorecase,,
244
- ,,,
245
- in:fx, in:fy, out:fX, out:fY
246
- ,,,
247
- a, , true,
248
- , a, , true
249
- b, , false,
250
- , b, , false
251
- """
252
196
 
253
- def test_5
197
+ def test_through_and_ignorecase
254
198
 
255
- wi = {
256
- "fx" => "a",
257
- "fy" => "a"
199
+ table = %{
200
+ through,ignorecase,,
201
+ ,,,
202
+ in:fx, in:fy, out:fX, out:fY
203
+ ,,,
204
+ a, , true,
205
+ , a, , true
206
+ b, , false,
207
+ , b, , false
258
208
  }
259
- do_test(CSV5, wi, {}, { "fX" => "true", "fY" => "true" }, false)
260
209
 
261
- wi = {
262
- "fx" => "a",
263
- "fy" => "b"
264
- }
265
- do_test(CSV5, wi, {}, { "fX" => "true", "fY" => "false" }, false)
210
+ wi = { 'fx' => 'a', 'fy' => 'a' }
211
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'true' }, false)
266
212
 
267
- wi = {
268
- "fx" => "A",
269
- "fy" => "b"
213
+ wi = { 'fx' => 'a', 'fy' => 'b' }
214
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'false' }, false)
215
+
216
+ wi = { 'fx' => 'A', 'fy' => 'b' }
217
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'false' }, false)
218
+ end
219
+
220
+ def test_through_and_ignorecase_set_at_table_initialization
221
+
222
+ table = %{
223
+ in:fx, in:fy, out:fX, out:fY
224
+ ,,,
225
+ a, , true,
226
+ , a, , true
227
+ b, , false,
228
+ , b, , false
270
229
  }
271
- do_test(CSV5, wi, {}, { "fX" => "true", "fY" => "false" }, false)
230
+
231
+ table = Rufus::Decision::Table.new(
232
+ table, :through => true, :ignore_case => true)
233
+
234
+ wi = { 'fx' => 'a', 'fy' => 'a' }
235
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'true' }, false)
236
+
237
+ wi = { 'fx' => 'a', 'fy' => 'b' }
238
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'false' }, false)
239
+
240
+ wi = { 'fx' => 'A', 'fy' => 'b' }
241
+ do_test(table, wi, {}, { 'fX' => 'true', 'fY' => 'false' }, false)
272
242
  end
273
243
 
274
244
  #
275
245
  # TEST 6
276
246
 
277
- CSV6 = \
278
- """
247
+ CSV6 = %{
279
248
  ,
280
249
  in:fx, out:fy
281
250
  ,
282
251
  <10,a
283
252
  <=100,b
284
253
  ,c
285
- """
254
+ }
286
255
 
287
256
  def test_6
288
257
 
@@ -300,15 +269,14 @@ in:fx, out:fy
300
269
  #
301
270
  # TEST 7
302
271
 
303
- CSV7 = \
304
- """
272
+ CSV7 = %{
305
273
  ,
306
274
  in:fx, out:fy
307
275
  ,
308
276
  >100,a
309
277
  >=10,b
310
278
  ,c
311
- """
279
+ }
312
280
 
313
281
  def test_7
314
282
 
@@ -328,42 +296,44 @@ in:fx, out:fy
328
296
  do_test(CSV7, wi, {}, { "fy" => "a" }, false)
329
297
  end
330
298
 
331
- CSV8 = \
332
- """
299
+ CSV8 = %{
333
300
  in:efx,in:efy,out:efz
334
301
  a,b,0
335
302
  c,d,1
336
303
  e,f,2
337
- """
304
+ }
338
305
 
339
306
  def test_8
340
307
 
341
308
  assert_equal CSV8.strip, Rufus::DecisionTable.new(CSV8).to_csv.strip
342
309
  end
343
310
 
344
- CSV9 = \
345
- """
346
- in:fx,in:fy,out:fz
347
- a,b,0
348
- c,d,${r: 1 + 2}
349
- e,f,2
350
- """
311
+ CSV9 = %{
312
+ in:fx,in:fy,out:fz
313
+ a,b,0
314
+ c,d,${r: 1 + 2}
315
+ e,f,2
316
+ }
351
317
 
352
- def test_9
318
+ def test_ruby_eval
353
319
 
354
- wi = {
355
- "fx" => "c",
356
- "fy" => "d"
357
- }
358
- do_test(CSV9, wi, { :ruby_eval => true }, { "fz" => "3" }, false)
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
+ table = Rufus::Decision::Table.new(CSV9, :ruby_eval => true)
327
+
328
+ wi = { 'fx' => 'c', 'fy' => 'd' }
329
+ do_test(table, wi, {}, { 'fz' => '3' }, false)
359
330
  end
360
331
 
361
- CSV10 = \
362
- """
332
+ CSV10 = %{
363
333
  in:fx,in:fx,out:fz
364
334
  >90,<92,ok
365
335
  ,,bad
366
- """
336
+ }
367
337
 
368
338
  def test_10
369
339
 
@@ -377,11 +347,7 @@ in:fx,in:fx,out:fz
377
347
  do_test(CSV10, wi, {}, { "fz" => "bad" }, false)
378
348
  end
379
349
 
380
- #
381
- # Fu Zhang's test case
382
-
383
- CSV11 = \
384
- '''
350
+ CSV11 = %{
385
351
  through
386
352
  in:f1,in:f1,in:f2,in:f3,out:o1,out:e1,out:e2
387
353
 
@@ -390,19 +356,15 @@ in:f1,in:f1,in:f2,in:f3,out:o1,out:e1,out:e2
390
356
  <100,>=95,>2.0,,"Expection1",,
391
357
  <100,>=95,,>2.0,,,"Expection2"
392
358
  <100,>=95,,>2.0,"Invalid",,
393
- '''
359
+ }
394
360
 
395
- def test_11
361
+ def test_fu_zhang
396
362
 
397
- wi = { "f1" => 97, "f2" => 5 }
398
- do_test CSV11, wi, {}, { "o1" => "Expection1" }, false
363
+ wi = { 'f1' => 97, 'f2' => 5 }
364
+ do_test CSV11, wi, {}, { 'o1' => 'Expection1' }, false
399
365
  end
400
366
 
401
- #
402
- # 'accumulate'
403
-
404
- CSV12 = \
405
- """
367
+ CSV12 = %{
406
368
  accumulate
407
369
  ,,,
408
370
  in:fx, in:fy, out:fX, out:fY
@@ -411,9 +373,9 @@ a, , red, green
411
373
  , a, blue, purple
412
374
  b, , yellow, beige
413
375
  , b, white, kuro
414
- """
376
+ }
415
377
 
416
- def test_12
378
+ def test_accumulate
417
379
 
418
380
  wi = { "fx" => "a", "fy" => "a" }
419
381
  do_test CSV12, wi, {}, { "fX" => "red;blue", "fY" => "green;purple" }, false
@@ -453,17 +415,13 @@ b, , yellow, beige
453
415
  assert (not r.include?("7"))
454
416
  end
455
417
 
456
- #
457
- # Testing ranges
458
-
459
- CSV13 = \
460
- """
418
+ CSV13 = %{
461
419
  in:fx,out:fz
462
420
  90..92,ok
463
421
  ,bad
464
- """
422
+ }
465
423
 
466
- def test_13
424
+ def test_range
467
425
 
468
426
  wi = { "fx" => "91" }
469
427
  do_test CSV13, wi, {}, { "fz" => "ok" }, false