ruler 1.0.0 → 1.2.0

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/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