rools 0.2 → 0.3

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/CHANGELOG CHANGED
@@ -1,5 +1,12 @@
1
1
  = Rools CHANGELOG
2
2
 
3
+ == Rools - 0.3
4
+ * facts can now have a defined namespace
5
+ * fixed csv_test
6
+ * added load_xml_rules_as_string and load_rb_rules_as_string
7
+ * 100% test coverage
8
+ * Applied doc patch from John Mettraux
9
+
3
10
  == Rools - 0.2 released 2007/05/22
4
11
  * specifications have been created using RSpec
5
12
  * fix bug #10985 regarding the support of rule extension
data/RAKEFILE CHANGED
@@ -144,20 +144,30 @@ end
144
144
  task :upload_website => [:doc] do
145
145
  sh """
146
146
  rsync -azv -e ssh \
147
+ --exclude='.svn' --delete-excluded \
147
148
  html/ \
148
- ENV['RUBYFORGE_USER']:ENV['RUBYFORGE_PROJECT']
149
+ cappelaere@rubyforge.org:/var/www/gforge-projects/rools \
149
150
  """
150
151
  sh """
151
152
  rsync -azv -e ssh \
152
153
  --exclude='.svn' --delete-excluded \
153
154
  doc/res/defs \
154
- ENV['RUBYFORGE_USER']:ENV['RUBYFORGE_PROJECT']
155
+ cappelaere@rubyforge.org:/var/www/gforge-projects/rools \
155
156
  """
156
157
  sh """
157
158
  rsync -azv -e ssh \
158
159
  --exclude='.svn' --delete-excluded \
159
160
  examples \
160
- ENV['RUBYFORGE_USER']:ENV['RUBYFORGE_PROJECT']
161
+ cappelaere@rubyforge.org:/var/www/gforge-projects/rools \
162
+ """
163
+ end
164
+
165
+ task :upload_html do
166
+ sh """
167
+ rsync -azv -e ssh \
168
+ --exclude='.svn' \
169
+ html \
170
+ cappelaere@rubyforge.org:/var/www/gforge-projects/rools \
161
171
  """
162
172
  end
163
173
 
data/README CHANGED
@@ -1,4 +1,4 @@
1
- :nodoc: YOU SHOULD NOT BE VIEWING THIS FILE DIRECTLY. PLEASE USE: gem_server
1
+
2
2
  = Rools -- A pure ruby rules-engine
3
3
 
4
4
  Rools is a rules engine for abstracting business logic and program-flow. It's ideally suited to processing applications where the business logic undergoes frequent modification.
@@ -25,15 +25,15 @@ module Rools
25
25
  raise ArgumentError.new('The "rule" parameter must respond to an :assert method') unless rule.respond_to?(:assert)
26
26
  @rule = rule
27
27
  @proc = b
28
- @working_object = nil
28
+ #@working_object = nil
29
29
  end
30
30
 
31
31
  # Call the bound block and set the working object so that it
32
32
  # can be referred to by method_missing
33
33
  def call(obj)
34
- @working_object = obj
34
+ #@working_object = obj
35
35
  status = instance_eval(&@proc)
36
- @working_object = nil
36
+ #@working_object = nil
37
37
  return status
38
38
  end
39
39
 
@@ -45,22 +45,20 @@ module Rools
45
45
  # Parameterless method calls by the attached block are assumed to
46
46
  # be references to the working object
47
47
  def method_missing(sym, *args)
48
- #puts "method missing: #{sym}"
48
+ # puts "method missing: #{sym} args:#{args.inspect}"
49
49
  # check if it is a fact first
50
- begin
50
+ #begin
51
51
  facts = @rule.rule_set.get_facts
52
52
  if facts.has_key?( sym.to_s )
53
53
  #puts "return fact #{facts[sym.to_s].value}"
54
54
  return facts[sym.to_s].value
55
- #else
56
- #puts "#{sym} not in facts"
55
+ else
56
+ raise Exception, "symbol: #{sym} not found in facts"
57
57
  end
58
- rescue Exception => e
59
- #logger.error "miss exception #{e} #{e.backtrace.join("\n")}" if logger
60
- #puts "miss exception #{e} #{e.backtrace.join("\n")}"
61
- end
62
- #return @working_object if @working_object && args.size == 0
63
- #return nil
58
+ #rescue Exception => e
59
+ # puts "miss exception #{e} #{e.backtrace.join("\n")}"
60
+ # return nil
61
+ #end
64
62
  end
65
63
 
66
64
  # Stops the current assertion. Does not indicate failure.
@@ -60,8 +60,16 @@ module Rools
60
60
  # To verify that the asserted object is an Employee, that inherits from
61
61
  # Person, and responds to :department
62
62
  def parameters(*matches)
63
- logger.debug( "Adding parameters: #{matches}") if logger
64
- @parameters += matches
63
+ logger.debug( "Adding parameters: #{matches.inspect}") if logger
64
+ # @parameters += matches
65
+ @parameters << matches
66
+ end
67
+
68
+ #
69
+ # returns parameters of rule
70
+ #
71
+ def get_parameters
72
+ @parameters
65
73
  end
66
74
 
67
75
  # parameters is aliased to aid in readability if you decide
@@ -70,25 +78,31 @@ module Rools
70
78
 
71
79
  # Checks to see if this Rule's parameters match the asserted object
72
80
  def parameters_match?(obj)
73
- @parameters.each do |p|
74
- logger.debug( "#{self} match p:#{p} obj:#{obj} sym:#{Symbol}") if logger
75
- if p.is_a?(Symbol)
76
- #return false unless obj.respond_to?(p)
77
- return true if obj.respond_to?(p)
78
- else
79
- logger.debug( "#{self} is_a p:#{p} obj:#{obj} #{obj.is_a?(p)}") if logger
80
- #return false unless obj.is_a?(p)
81
- return true if obj.is_a?(p)
82
- end
83
- end
84
-
85
81
  # if parameters are not specified, let's assume that the rule is always relevant
86
82
  if @parameters.size == 0
87
83
  logger.debug "no parameters defined for rule: #{self}" if logger
88
84
  return true
89
85
  end
90
86
 
91
- logger.debug( "no parameter match") if logger
87
+ @parameters.each do |params|
88
+ match = false
89
+
90
+ params.each do |p|
91
+ logger.debug( "#{self} match p:#{p} obj:#{obj}") if logger
92
+
93
+ if p.is_a?(Symbol)
94
+ if obj.respond_to?(p)
95
+ match = true
96
+ else
97
+ return false
98
+ end
99
+ elsif obj.is_a?(p)
100
+ match = true
101
+ end
102
+ end
103
+ return true if match
104
+ end
105
+
92
106
  return false
93
107
  end
94
108
 
@@ -98,7 +112,7 @@ module Rools
98
112
  begin
99
113
  @conditions.each { |c| return false unless c.call(obj) }
100
114
  rescue StandardError => e
101
- logger.error( "rule StandardError #{e} #{e.backtrace.join("\n")}") if logger
115
+ logger.error( "conditions_match? StandardError #{e} #{e.backtrace.join("\n")}") if logger
102
116
  raise RuleCheckError.new(self, e)
103
117
  end
104
118
 
@@ -118,10 +132,10 @@ module Rools
118
132
  @consequences.each do |c|
119
133
  c.call(obj)
120
134
  end
121
- rescue StandardError => e
135
+ rescue Exception => e
122
136
  # discontinue the Rools::RuleSet#assert if any consequence fails
123
137
  logger.error( "rule RuleConsequenceError #{e.to_s} #{e.backtrace.join("\n")}") if logger
124
- raise RuleConsequenceError.new(rule, e)
138
+ raise RuleConsequenceError.new(self, e)
125
139
  end
126
140
  end
127
141
 
@@ -61,8 +61,17 @@ module Rools
61
61
  #
62
62
  def load_xml( fileName )
63
63
  begin
64
- file = File.new( fileName )
65
- doc = REXML::Document.new file
64
+ str = IO.read(fileName)
65
+ load_xml_rules_as_string(str)
66
+ rescue Exception => e
67
+ raise RuleLoadingError, "loading xml file"
68
+ end
69
+ end
70
+
71
+ # load xml rules as a string
72
+ def load_xml_rules_as_string( str )
73
+ begin
74
+ doc = REXML::Document.new str
66
75
  doc.elements.each( "rule-set") { |rs|
67
76
  facts = rs.elements.each( "facts") { |f|
68
77
  facts( f.attributes["name"] ) do f.text.strip end
@@ -100,10 +109,20 @@ module Rools
100
109
  end
101
110
 
102
111
  #
103
- # Ruby File format
112
+ # Ruby File format loading
104
113
  #
105
114
  def load_rb( file )
106
- instance_eval(File::open(file).read)
115
+ begin
116
+ str = IO.read(file)
117
+ load_rb_rules_as_string(str)
118
+ rescue Exception => e
119
+ raise RuleLoadingError, "loading ruby file"
120
+ end
121
+ end
122
+
123
+ # load ruby rules as a string
124
+ def load_rb_rules_as_string( str )
125
+ instance_eval(str)
107
126
  end
108
127
 
109
128
  #
@@ -136,25 +155,28 @@ module Rools
136
155
  # facts can be created in a similar manner to rules
137
156
  # all names are converted to strings and downcased.
138
157
  # Facts name is equivalent to a Class Name
158
+ #
139
159
  # ==Example
140
- # require 'rools'
141
- #
142
- # rules = Rools::RuleSet.new do
143
- #
144
- # facts 'Countries' do
145
- # ["China", "USSR", "France", "Great Britain", "USA"]
146
- # end
147
- #
148
- # rule 'Is it on Security Council?' do
149
- # parameter String
150
- # condition { countries.include?(string) }
151
- # consequence { puts "Yes, #{string} is in the country list"}
152
- # end
153
- # end
160
+ #
161
+ # require 'rools'
162
+ #
163
+ # rules = Rools::RuleSet.new do
164
+ #
165
+ # facts 'Countries' do
166
+ # ["China", "USSR", "France", "Great Britain", "USA"]
167
+ # end
168
+ #
169
+ # rule 'Is it on Security Council?' do
170
+ # parameter String
171
+ # condition { countries.include?(string) }
172
+ # consequence { puts "Yes, #{string} is in the country list"}
173
+ # end
174
+ # end
154
175
  #
155
- # rules.assert 'France'
176
+ # rules.assert 'France'
156
177
  #
157
178
  def facts(name, &b)
179
+ name.gsub!(/:/, '_')
158
180
  name.to_s.downcase!
159
181
  @facts[name] = Facts.new(self, name, b)
160
182
  logger.debug( "created facts: #{name}") if logger
@@ -163,11 +185,11 @@ module Rools
163
185
  # A single fact can be an single object of a particular class type
164
186
  # or a collection of objects of a particular type
165
187
  def fact( obj )
166
- begin
188
+ #begin
167
189
  # check if facts already exist for that class
168
190
  # if so, we need to add it to the existing list
169
191
  cls = obj.class.to_s.downcase
170
-
192
+ cls.gsub!(/:/, '_')
171
193
  if @facts.key? cls
172
194
  logger.debug( "adding to facts: #{cls}") if logger
173
195
  @facts[cls].fact_value << obj
@@ -178,9 +200,9 @@ module Rools
178
200
  proc = Proc.new { arr }
179
201
  @facts[cls] = Facts.new(self, cls, proc )
180
202
  end
181
- rescue Exception=> e
182
- logger.error e if logger
183
- end
203
+ #rescue Exception=> e
204
+ # logger.error e if logger
205
+ #end
184
206
  end
185
207
 
186
208
  # Delete all existing facts
@@ -289,7 +311,7 @@ module Rools
289
311
  get_relevant_rules()
290
312
  logger.debug("no relevant rules") if logger && @relevant_rules.size==0
291
313
 
292
- begin #rescue
314
+ #begin #rescue
293
315
 
294
316
  # loop through the available_rules, evaluating each one,
295
317
  # until there are no more matching rules available
@@ -331,23 +353,22 @@ module Rools
331
353
  break
332
354
  end # if rule.conditions_match?(obj)
333
355
 
356
+ rescue RuleConsequenceError
357
+ fail
334
358
  rescue RuleCheckError => e
335
- #puts "evaluate RuleCheckError: #{e.to_s}"
336
- logger.error( "RuleCheckError") if logger
337
- @relevant_rules.delete(e.rule)
338
- @status = fail
359
+ fail
339
360
  end # begin/rescue
340
361
 
341
362
  end # available_rules.each
342
363
 
343
364
  end while(matches && @assert)
344
365
 
345
- rescue RuleConsequenceError => rce
366
+ #rescue RuleConsequenceError => rce
346
367
  # RuleConsequenceErrors are allowed to break out of the current assertion,
347
368
  # then the inner error is bubbled-up to the asserting code.
348
- @status = fail
349
- raise rce.inner_error
350
- end
369
+ # @status = FAIL
370
+ # raise rce.inner_error
371
+ #end
351
372
 
352
373
  @assert = false
353
374
 
@@ -355,4 +376,4 @@ module Rools
355
376
  end
356
377
 
357
378
  end # class RuleSet
358
- end # module Rools
379
+ end # module Rools
@@ -1,4 +1,4 @@
1
1
  module Rools
2
- ROOLS_VERSION = '0.2'
3
- ROOLS_COVERAGE = 95.1
2
+ ROOLS_VERSION = '0.3'
3
+ ROOLS_COVERAGE = 100.0
4
4
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: rools
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.2"
7
- date: 2007-05-22 00:00:00 -04:00
6
+ version: "0.3"
7
+ date: 2007-07-18 00:00:00 -04:00
8
8
  summary: A Rules Engine written in Ruby
9
9
  require_paths:
10
10
  - lib