ruler 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -16,7 +16,7 @@ Jeweler::Tasks.new do |gem|
16
16
  gem.homepage = "http://github.com/BlueFrogGaming/ruler"
17
17
  gem.license = "MIT"
18
18
  gem.summary = %Q{Ruby DSL to help with rules and facts}
19
- gem.description = %Q{Longer description of your gem}
19
+ gem.description = %Q{Ruler module implements a DSL that makes it easy to write a set of facts and rules. If you have some tricky conditional logic, Ruler can help clear it up.}
20
20
  gem.email = "kognate@gmail.com"
21
21
  gem.authors = ["Joshua Smith"]
22
22
  # Include your dependencies below. Runtime dependencies are required when using your gem,
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.2.0
@@ -1,4 +1,4 @@
1
-
1
+ #!/usr/bin/env ruby
2
2
  require 'rubygems'
3
3
  require 'lib/ruler'
4
4
 
@@ -8,7 +8,13 @@
8
8
  # Copyright:: (c) 2011 Blue Frog Gaming, All Rights Reserved
9
9
  # License:: This file has no public license.
10
10
 
11
- class BadDefaultRule < StandardError
11
+ class RulerError < StandardError
12
+ end
13
+
14
+ class BadDefaultRule < RulerError
15
+ end
16
+
17
+ class BadNotCall < RulerError
12
18
  end
13
19
 
14
20
  module Ruler
@@ -73,19 +79,28 @@ module Ruler
73
79
  # will never be used.
74
80
  def fact name, dval = nil, &blk
75
81
  if dval.nil?
76
- Thread.current[:working_memory][name] = yield
82
+ Thread.current[:working_memory][name] = {:value => yield }
77
83
  else
78
- Thread.current[:working_memory][name] = dval
84
+ Thread.current[:working_memory][name] = {:value => dval }
79
85
  end
80
86
  end
81
87
 
88
+ # a dynamic_fact is evaulated every time the fact is checked. Unlike a normal fact, which
89
+ # is only evaluated once, dynamic facts are evaluated once for each rule they appear in
90
+ def dynamic_fact name, &blk
91
+ Thread.current[:working_memory][name] = {:transient => true, :block => blk }
92
+ end
82
93
 
83
- # allows for a fact to be NOT another fact.
94
+ # allows for a fact to be NOT another fact. notf cannot be used with a dynamic_fact
84
95
  # for example:
85
96
  # fact :one, 10 == 10
86
97
  # fact :notfone, not(:one)
87
98
  def notf name
88
- not(Thread.current[:working_memory][name])
99
+ if Thread.current[:working_memory][name][:transient]
100
+ raise BadNotCall.new("Cannot call notf on dynamic fact")
101
+ else
102
+ not(Thread.current[:working_memory][name][:value])
103
+ end
89
104
  end
90
105
 
91
106
  # a rule takes a list of fact names and a block. Rules are evaluated in the order
@@ -101,7 +116,7 @@ module Ruler
101
116
  # there is no check to see if fact names are valid, and facts can be (re)defined
102
117
  #inside of rules. Fact names are false if they are not defined.
103
118
  def rule vlist,docstr = nil,&blk
104
- dbg = lambda {|va| puts "|=-\t#{va} = #{Thread.current[:working_memory][va]}" }
119
+ dbg = lambda {|va| puts Thread.current[:working_memory][va][:transient].nil? ? "|=-\t#{va} = #{Thread.current[:working_memory][va][:value]}" : "|=-\t#{va} = #{Thread.current[:working_memory][va][:block].call()}" }
105
120
  if @DEBUG
106
121
  puts "---------------------------------------"
107
122
  puts vlist.join(" & ")
@@ -112,8 +127,8 @@ module Ruler
112
127
  if Thread.current[:singletary] && Thread.current[:rulematched]
113
128
  Thread.current[:rulematched]
114
129
  else
115
-
116
- Thread.current[:rulematched] = if vlist.inject(true) {|k,v| k ? k && Thread.current[:working_memory][v] : false }
130
+ conditional_call = lambda {|n| Thread.current[:working_memory][n][:transient].nil? ? Thread.current[:working_memory][n][:value] : Thread.current[:working_memory][n][:block].call() }
131
+ Thread.current[:rulematched] = if vlist.inject(true) {|k,v| k ? k && conditional_call.call(v) : false }
117
132
  yield
118
133
  end
119
134
  end
@@ -137,6 +137,39 @@ I think.
137
137
  end
138
138
  end
139
139
  end
140
+
141
+ def test_ten
142
+ #@DEBUG = true
143
+ ruleset do
144
+ dynamic_fact :one do
145
+ self.checking_method
146
+ end
147
+
148
+ fact :wrong, false
149
+
150
+ rule [:one, :wrong] do
151
+ false
152
+ end
153
+
154
+ rule [:one, :one, :wrong] do
155
+ false
156
+ end
157
+ end
158
+ end
159
+
160
+ def test_eleven
161
+ ruleset do
162
+ dynamic_fact :one do
163
+ true
164
+ end
165
+
166
+ fact :two, notf(:one)
167
+
168
+ default_rule do
169
+ true
170
+ end
171
+ end
172
+ end
140
173
  end
141
174
 
142
175
  describe Rules do
@@ -187,5 +220,16 @@ describe Rules do
187
220
  lambda { r.test_nine.should}.should raise_error
188
221
  end
189
222
 
223
+ it "should call dynamic rules each time they are evaluated" do
224
+ r = Rules.new
225
+ r.expects(:checking_method).times(3).returns(true)
226
+ r.test_ten.should be(nil)
227
+ end
228
+
229
+ it "should throw an exception if a dyanmic fact is declared in a notf" do
230
+ r = Rules.new
231
+ lambda { r.test_eleven }.should raise_error
232
+ end
233
+
190
234
  end
191
235
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruler
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
+ - 2
8
9
  - 0
9
- - 0
10
- version: 1.0.0
10
+ version: 1.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Joshua Smith
@@ -94,7 +94,7 @@ dependencies:
94
94
  name: mocha
95
95
  requirement: *id005
96
96
  type: :development
97
- description: Longer description of your gem
97
+ description: Ruler module implements a DSL that makes it easy to write a set of facts and rules. If you have some tricky conditional logic, Ruler can help clear it up.
98
98
  email: kognate@gmail.com
99
99
  executables:
100
100
  - example.rb