ruleby 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,285 +1,102 @@
1
- require 'ruleby'
2
-
3
- class Rulebook
4
- include Ruleby
5
- def initialize(engine)
6
- @engine = engine
7
- end
8
-
9
- attr_reader :engine
10
-
11
- def rule(name, &block)
12
- r = Core::Rule.new name
13
- yield r if block_given?
14
- @engine.assert_rule r
15
- r
16
- end
17
-
18
- def action(&block)
19
- return Core::Action.new(&block)
20
- end
21
-
22
- end
1
+ # This file is part of the Ruleby project (http://ruleby.org)
2
+ #
3
+ # This application is free software; you can redistribute it and/or
4
+ # modify it under the terms of the Ruby license defined in the
5
+ # LICENSE.txt file.
6
+ #
7
+ # Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
8
+ #
9
+ # * Authors: Matt Smith, Joe Kutner
10
+ #
23
11
 
24
- class WhenBuilder < Rulebook
25
- def initialize()
26
- @pattern_hash = Hash.new
27
- @pattern_keys = []
28
- end
29
-
30
- def method_missing(method_id, *args, &block)
31
- method = method_id.to_sym
32
- wi = nil
33
- if @pattern_hash.key? method
34
- wi = @pattern_hash[method]
35
- elsif :not == method
36
- @pattern_keys.push method
37
- return self
38
- else
39
- wi = WhenInternal.new method, args[0]
40
- @pattern_hash[method] = wi
41
- @pattern_keys.push method
42
- end
43
- return wi
44
- end
45
- def pattern
46
- operands = []
47
- nt = false
48
- @pattern_keys.each do |key|
49
- if :not != key
50
- wi = @pattern_hash[key]
51
- tag = wi.tag
52
- type = wi.type
53
- atoms = wi.to_atoms
54
- p = nil
55
- if nt
56
- p = Core::NotPattern.new(tag, type, atoms)
57
- nt = false
58
- else
59
- p = Core::ObjectPattern.new(tag, type, atoms)
60
- end
61
- operands = operands + [p]
62
- else
63
- nt = true
64
- end
65
- end
66
- return and_pattern(operands)
67
- end
68
- end
12
+ require 'ruleby'
13
+ require 'dsl/ferrari'
14
+ require 'dsl/letigre'
15
+ require 'dsl/steel'
69
16
 
70
- class WhenInternal
71
- public_instance_methods.each do |m|
72
- a = [:method_missing, :new, :public_instance_methods, :__send__, :__id__]
73
- undef_method m.to_sym unless a.include? m.to_sym
74
- end
75
-
76
- attr_reader :tag, :type
77
- def initialize(tag, type)
78
- @tag = tag
79
- @type = type
80
- @builder = WhenPropertyBuilder.new self
81
- end
82
-
83
- def to_atoms
84
- atoms = []
85
- tags = [@tag]
86
- @builder.property_hash.each_value do |wp|
87
- tags.push wp.tag if wp.tag
88
- end
89
- @builder.property_keys.each do |key|
90
- wp = @builder.property_hash[key]
91
- atoms = atoms + [wp.to_atom(tags)]
17
+ module Ruleby
18
+ class Rulebook
19
+ include Ruleby
20
+ def initialize(engine)
21
+ @engine = engine
92
22
  end
93
- return atoms
94
- end
95
23
 
96
- def &
97
- return self
98
- end
24
+ attr_reader :engine
99
25
 
100
- def method_missing(method_id, *args, &block)
101
- m = method_id.to_s
102
- suffix = m.to_s[-1..-1]
103
- if suffix == '='
104
- new_m = m[0,m.size-1]
105
- if args[0].class == Array && args[0].size > 1 && args[0][1] == :%
106
- wp = @builder.create new_m do |x,y| x == y end
107
- wp.references args[0][0]
108
- return wp
109
- else
110
- wp = @builder.create new_m do |x| x == args[0] end
111
- return wp
26
+ def rule(*args, &block)
27
+ unless args.empty?
28
+ name = args[0].kind_of?(Symbol) ? args.shift : GeneratedTag.new
112
29
  end
113
- else
114
- wp = @builder.create(m, &block)
115
- if args.size > 0 && args[0]
116
- if block_given?
117
- wp.references args[0]
30
+
31
+ if args.empty?
32
+ # use steel DSL
33
+ r = Steel::RulebookHelper.new @engine
34
+ r.rule name, &block
35
+ else
36
+ i = args[0].kind_of?(Hash) ? 1 : 0
37
+ if args[i].kind_of? Array
38
+ # use ferrari DSL
39
+ r = Ferrari::RulebookHelper.new @engine
40
+ r.rule name, *args, &block
41
+ elsif args[i].kind_of? String
42
+ # use letigre DSL
43
+ r = LeTigre::RulebookHelper.new @engine
44
+ r.rule name, *args, &block
118
45
  else
119
- wp.tag = args[0]
46
+ raise 'Rule format not recognized.'
120
47
  end
121
48
  end
122
- return wp
123
- end
124
- end
125
- end
126
-
127
- class WhenPropertyBuilder
128
- attr_reader:property_hash
129
- attr_reader:property_keys
130
-
131
- def initialize(parent)
132
- @parent = parent
133
- @property_hash = Hash.new
134
- @property_keys = []
135
- end
136
-
137
- def create(method_id,&block)
138
- method = method_id.to_sym
139
- wp = nil
140
- if @property_hash.key? method
141
- wp = @property_hash[method]
142
- else
143
- wp = WhenProperty.new @parent, method do |p| true end
144
- @property_hash[method] = wp
145
- @property_keys.push method
146
49
  end
147
- if block_given?
148
- wp.block = block
149
- end
150
- return wp
151
- end
152
- end
153
-
154
- class WhenProperty
155
- attr_accessor :block
156
- def initialize(parent,name, &block)
157
- @tag = nil
158
- @name = name
159
- @references = nil
160
- @block = block
161
- @parent = parent
162
- end
163
- attr:tag,true
164
- attr:type,true
165
- attr:value,true
166
-
167
- def &
168
- return @parent
169
- end
170
-
171
- def bind(n)
172
- @tag = n
173
- end
174
-
175
- def not=(value,ref=nil)
176
- if ref && ref == :%
177
- raise 'Using \'not=\' for references is not yet supported'
178
- set_block do |x,y| x != y end
179
- references value
180
- else
181
- set_block do |s| s != value end
50
+
51
+ def m
52
+ Ruleby::Ferrari::MethodBuilder.new
182
53
  end
183
54
 
184
- end
185
- def set_block(&block)
186
- @block = block
187
- end
188
- private:set_block
189
-
190
- def references(refs)
191
- @references = refs
192
- end
193
-
194
- def to_atom(pattern_tags)
195
- unless @tag
196
- @tag = GeneratedTag.new
55
+ def method
56
+ m
197
57
  end
198
- if @references
199
- @references = [@references] unless @references.kind_of?(Array)
200
- i = includes_how_many(@references, pattern_tags)
201
- if i == 0
202
- return Core::ReferenceAtom.new(@tag, @name, @references, &@block)
203
- elsif i == @references.size
204
- return Core::SelfReferenceAtom.new(@tag, @name, @references, &@block)
205
- else
206
- raise 'Referencing self AND other patterns in the same atom is not yet supported'
207
- end
208
- else
209
- return Core::PropertyAtom.new(@tag, @name, &@block)
58
+
59
+ def b(variable_name)
60
+ Ruleby::Ferrari::BindingBuilder.new(variable_name)
210
61
  end
211
- end
212
-
213
- private
214
- def includes_how_many(list1, list2)
215
- i = 0
216
- list2.each do |a|
217
- i += 1 if list1.include?(a)
62
+
63
+ def binding(variable_name)
64
+ b
218
65
  end
219
- return i
220
- end
221
- end
222
-
223
-
224
-
225
- private
226
-
227
- def or_pattern(operands)
228
- # TODO raise exception if referenceAtoms from the right do not
229
- # have the values they referenece in the left
230
- # TODO raise exception if there are repeated tags?
231
- left = nil
232
- operands.each do |operand|
233
- if left.nil?
234
- left = operand
235
- else
236
- right = operand
237
- left = Core::OrPattern.new(left, right)
66
+
67
+ def c(&block)
68
+ return lambda(&block)
238
69
  end
239
- end
240
- left
241
- end
242
-
243
- def and_pattern(operands)
244
- # TODO raise exception if referenceAtoms from the right do not
245
- # have the values they referenece in the left
246
- # TODO raise exception if there are repeated tags?
247
- left = nil
248
- operands.each do |operand|
249
- if left.nil?
250
- left = operand
251
- else
252
- right = operand
253
- left = Core::AndPattern.new(left, right)
70
+
71
+ def condition(&block)
72
+ return lambda(&block)
254
73
  end
255
- end
256
- left
257
- end
258
-
259
- class GeneratedTag
260
-
261
- # this counter is incremented for each UniqueTag created, and is
262
- # appended to the end of the unique_seed in order to create a
263
- # string that is unique for each instance of this class.
264
- @@tag_counter = 0
265
-
266
- # every generated tag will be prefixed with this string
267
- @@unique_seed = 'unique_seed'
268
-
269
- def initialize()
270
- @@tag_counter += 1
271
- @tag = @@unique_seed + @@tag_counter.to_s
272
- end
74
+ end
273
75
 
274
- attr_reader:tag_counter
275
- attr_reader:unique_seed
276
- attr_reader:tag
277
-
278
- def ==(ut)
279
- return ut && ut.kind_of?(GeneratedTag) && @tag == ut.tag
280
- end
76
+ class GeneratedTag
77
+ # this counter is incremented for each UniqueTag created, and is
78
+ # appended to the end of the unique_seed in order to create a
79
+ # string that is unique for each instance of this class.
80
+ @@tag_counter = 0
81
+
82
+ # every generated tag will be prefixed with this string
83
+ @@unique_seed = 'unique_seed'
281
84
 
282
- def to_s
283
- return @tag.to_s
85
+ def initialize()
86
+ @@tag_counter += 1
87
+ @tag = @@unique_seed + @@tag_counter.to_s
88
+ end
89
+
90
+ attr_reader:tag_counter
91
+ attr_reader:unique_seed
92
+ attr_reader:tag
93
+
94
+ def ==(ut)
95
+ return ut && ut.kind_of?(GeneratedTag) && @tag == ut.tag
96
+ end
97
+
98
+ def to_s
99
+ return @tag.to_s
100
+ end
284
101
  end
285
- end
102
+ end
@@ -1,4 +1,17 @@
1
+ # This file is part of the Ruleby project (http://ruleby.org)
2
+ #
3
+ # This application is free software; you can redistribute it and/or
4
+ # modify it under the terms of the Ruby license defined in the
5
+ # LICENSE.txt file.
6
+ #
7
+ # Copyright (c) 2007 Joe Kutner and Matt Smith. All rights reserved.
8
+ #
9
+ # * Authors: Joe Kutner, Matt Smith
10
+ #
11
+
1
12
  require 'core/engine'
13
+ require 'rulebook'
14
+
2
15
  module Ruleby
3
16
  #helper classes for using ruleby go here
4
17
  def engine(name, &block)
metadata CHANGED
@@ -1,10 +1,10 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.3
2
+ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ruleby
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.2"
7
- date: 2007-07-10 00:00:00 -05:00
6
+ version: "0.3"
7
+ date: 2007-09-03 00:00:00 -05:00
8
8
  summary: the Rule Engine for Ruby
9
9
  require_paths:
10
10
  - lib
@@ -29,29 +29,31 @@ post_install_message:
29
29
  authors:
30
30
  - Joe Kutner, Matt Smith
31
31
  files:
32
- - ./benchmarks/50_joined_rules.rb
33
- - ./benchmarks/50_rules.rb
34
- - ./benchmarks/5_joined_rules.rb
35
- - ./benchmarks/5_rules.rb
36
- - ./benchmarks/miss_manners/data.rb
32
+ - ./lib/ruleby.rb
33
+ - ./lib/rulebook.rb
34
+ - ./lib/dsl/ferrari.rb
35
+ - ./lib/dsl/letigre.rb
36
+ - ./lib/dsl/steel.rb
37
+ - ./lib/core/patterns.rb
38
+ - ./lib/core/utils.rb
39
+ - ./lib/core/atoms.rb
40
+ - ./lib/core/engine.rb
41
+ - ./lib/core/nodes.rb
42
+ - ./benchmarks/model.rb
43
+ - ./benchmarks/joined_rules.rb
44
+ - ./benchmarks/basic_rules.rb
37
45
  - ./benchmarks/miss_manners/miss_manners.rb
38
46
  - ./benchmarks/miss_manners/model.rb
47
+ - ./benchmarks/miss_manners/data.rb
39
48
  - ./benchmarks/miss_manners/rules.rb
40
- - ./examples/example_diagnosis.rb
41
- - ./examples/example_hello.rb
42
- - ./examples/example_politician.rb
43
- - ./examples/example_ticket.rb
44
49
  - ./examples/fibonacci_example1.rb
45
50
  - ./examples/fibonacci_example2.rb
51
+ - ./examples/example_hello.rb
52
+ - ./examples/example_ticket.rb
46
53
  - ./examples/fibonacci_rulebook.rb
54
+ - ./examples/example_politician.rb
47
55
  - ./examples/test_self_reference.rb
48
- - ./lib/rulebook.rb
49
- - ./lib/ruleby.rb
50
- - ./lib/core/atoms.rb
51
- - ./lib/core/engine.rb
52
- - ./lib/core/nodes.rb
53
- - ./lib/core/patterns.rb
54
- - ./lib/core/utils.rb
56
+ - ./examples/example_diagnosis.rb
55
57
  test_files: []
56
58
 
57
59
  rdoc_options: []
@@ -1,78 +0,0 @@
1
- $LOAD_PATH << File.join(File.dirname(__FILE__), '../lib/')
2
- require 'ruleby'
3
- require 'rulebook'
4
-
5
- class Account
6
- def initialize(status, title, account_id)
7
- @status = status
8
- @title = title
9
- @account_id = account_id
10
- end
11
-
12
- attr :status, true
13
- attr :title, true
14
- attr :account_id, true
15
- end
16
-
17
- class Address
18
- def initialize(addr_id, city, state, zip)
19
- @addr_id = addr_id
20
- @city = city
21
- @state = state
22
- @zip = zip
23
- end
24
-
25
- attr :addr_id, true
26
- attr :city, true
27
- attr :state, true
28
- attr :zip, true
29
- end
30
-
31
- class TestRulebook < Rulebook
32
- def rules
33
- (0..50).each do |index|
34
- rule "Rule-#{index}" do |r|
35
- r.when do |has|
36
- has.a Account
37
- has.a.status = 'standard'
38
-
39
- has.addr Address
40
- has.addr.addr_id = "acc#{index}"
41
- has.addr.city = 'Foobar'
42
- has.addr.state = 'FB'
43
- has.addr.zip = '12345'
44
- end
45
-
46
- r.then do |e,vars|
47
- puts "rule #{index} fired"
48
- end
49
- end
50
- end
51
- end
52
- end
53
-
54
- include Ruleby
55
-
56
- t1 = Time.new
57
- engine :engine do |e|
58
- TestRulebook.new(e).rules
59
-
60
- t2 = Time.new
61
- diff = t2.to_f - t1.to_f
62
- puts 'time to create rule set: ' + diff.to_s
63
-
64
- e.assert Account.new('standard', nil, nil)
65
- for k in (0..500)
66
- e.assert Address.new(('acc'+k.to_s),'Foobar', 'FB', '12345')
67
- end
68
-
69
- t3 = Time.new
70
- diff = t3.to_f - t2.to_f
71
- puts 'time to assert facts: ' + diff.to_s
72
-
73
- e.match
74
-
75
- t4 = Time.new
76
- diff = t4.to_f - t3.to_f
77
- puts 'time to match rules: ' + diff.to_s
78
- end