wongi-engine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -26,14 +26,16 @@ Now let's add some facts to the system.
26
26
 
27
27
  ### Facts
28
28
 
29
- All knowledge in Wongi::Engine is represented as triples of { subject, predicate, object }. Predicates usually stand for subjects' properties, and objects for values of those properties. More complex types can always be decomposed into such triples.
29
+ All knowledge in Wongi::Engine is represented by triples of { subject, predicate, object }. Predicates usually stand for subjects' properties, and objects for values of those properties. More complex types can always be decomposed into such triples.
30
30
 
31
31
  Triples can contain any Ruby object that defines the `==` comparison in a meaningful way, but some symbols have special meaning, as we will see.
32
32
 
33
33
  Try this:
34
34
 
35
- engine << [ "Alice", "friend", "Bob" ]
36
- engine << [ "Alice", "age", 35 ]
35
+ ```ruby
36
+ engine << [ "Alice", "friend", "Bob" ]
37
+ engine << [ "Alice", "age", 35 ]
38
+ ```
37
39
 
38
40
  What can we do with this information?
39
41
 
@@ -41,9 +43,11 @@ What can we do with this information?
41
43
 
42
44
  Suppose we want to list all we know about Alice. You could, for instance, do:
43
45
 
44
- engine.each "Alice", :_, :_ do |item|
45
- puts "Alice's #{item.predicate} is #{item.object}"
46
- end
46
+ ```ruby
47
+ engine.each "Alice", :_, :_ do |item|
48
+ puts "Alice's #{item.predicate} is #{item.object}"
49
+ end
50
+ ```
47
51
 
48
52
  `each` takes three arguments for every field of a triple and tries to match the resulting template against the known facts. `:_` is the special value that matches anything. This kind of pattern matching plays a large role in Wongi::Engine; more on that later.
49
53
 
@@ -53,19 +57,23 @@ In a similar way, you can use `select` to get an array of matching facts and `fi
53
57
 
54
58
  It's not very interesting to use the engine like that, though. Rule engines are supposed to be declarative. Let's try this:
55
59
 
56
- friends = engine.rule "friends" do
57
- forall {
58
- has :PersonA, "friend", :PersonB
59
- }
60
- end
60
+ ```ruby
61
+ friends = engine.rule "friends" do
62
+ forall {
63
+ has :PersonA, "friend", :PersonB
64
+ }
65
+ end
66
+ ```
61
67
 
62
68
  Here's your first taste of the engine's DSL. A rule, generally speaking, consists of a number of conditions the dataset needs to meet; those are defined in the `forall` section (also spelled `for_all`, if you prefer that). `has` (or `fact`) specifies that there needs to be a fact that matches the given pattern; in this case, one with the predicate `"friends"`.
63
69
 
64
70
  When a pattern contains a symbol that starts with an uppercase letter, it introduces a variable which will be bound to an actual triple field. Their values can be retrieved from the result set:
65
71
 
66
- friends.tokens.each do |token|
67
- puts "%s and %s are friends" % [ token[ :PersonA ], token[ :PersonB ] ]
68
- end
72
+ ```ruby
73
+ friends.tokens.each do |token|
74
+ puts "%s and %s are friends" % [ token[ :PersonA ], token[ :PersonB ] ]
75
+ end
76
+ ```
69
77
 
70
78
  A **token** represents all facts that passed the rule's conditions. If you think of the dataset as of a long SQL table being joined with itself, then a token is like a row in the resulting table.
71
79
 
@@ -77,16 +85,18 @@ Once a variable is bound, it can be used to match further facts within a rule. L
77
85
 
78
86
  and another rule:
79
87
 
80
- remote = engine.rule "remote friends" do
81
- forall {
82
- has :PersonA, "friend", :PersonB
83
- has :PersonB, "friend", :PersonC
84
- }
85
- end
88
+ ```ruby
89
+ remote = engine.rule "remote friends" do
90
+ forall {
91
+ has :PersonA, "friend", :PersonB
92
+ has :PersonB, "friend", :PersonC
93
+ }
94
+ end
86
95
 
87
- remote.tokens.each do |token|
88
- puts "%s and %s are friends through %s" % [ token[ :PersonA ], token[ :PersonC ], token[ :PersonB ] ]
89
- end
96
+ remote.tokens.each do |token|
97
+ puts "%s and %s are friends through %s" % [ token[ :PersonA ], token[ :PersonC ], token[ :PersonB ] ]
98
+ end
99
+ ```
90
100
 
91
101
  (`engine.rule` returns the created **production node** - an object that accumulates the rule's result set. You don't have to carry it around if you don't want to - it is always possible to retrieve it later as `engine.productions["remote friends"]`.)
92
102
 
@@ -94,17 +104,19 @@ and another rule:
94
104
 
95
105
  Taking the SQL metaphor further, you can use the engine to do fancy searches:
96
106
 
97
- q = engine.query "friends" do
98
- search_on :Name
99
- forall {
100
- has :Name, "friend", :Friend
101
- }
102
- end
107
+ ```ruby
108
+ q = engine.query "friends" do
109
+ search_on :Name
110
+ forall {
111
+ has :Name, "friend", :Friend
112
+ }
113
+ end
103
114
 
104
- engine.execute "friends", { Name: "Alice" }
105
- q.tokens.each do |token|
106
- ... # you know the drill
107
- end
115
+ engine.execute "friends", { Name: "Alice" }
116
+ q.tokens.each do |token|
117
+ ... # you know the drill
118
+ end
119
+ ```
108
120
 
109
121
  Not that this is a particularly fancy search, but you get the idea.
110
122
 
@@ -116,16 +128,18 @@ You can also retrieve the query's production node from `engine.results["friends"
116
128
 
117
129
  There's more to rules than passive accumulation:
118
130
 
119
- engine.rule "self-printer" do
120
- forall {
121
- has :PersonA, "friend", :PersonB
122
- }
123
- make {
124
- action { |token|
125
- puts "%s and %s are friends" % [ token[ :PersonA ], token[ :PersonB ] ]
126
- }
131
+ ```ruby
132
+ engine.rule "self-printer" do
133
+ forall {
134
+ has :PersonA, "friend", :PersonB
135
+ }
136
+ make {
137
+ action { |token|
138
+ puts "%s and %s are friends" % [ token[ :PersonA ], token[ :PersonB ] ]
127
139
  }
128
- end
140
+ }
141
+ end
142
+ ```
129
143
 
130
144
  The `make` section (also spelled `do!`, if you find it more agreeable English, because `do` is a keyword in Ruby) lists everything that happens when a rule's conditions are fully matched (we say the production node is activated). Wongi::Engine provides only a small amount of build-in actions, but you can define define your own ones, and the simplest one is just `action` with a block.
131
145
 
@@ -133,17 +147,19 @@ The `make` section (also spelled `do!`, if you find it more agreeable English, b
133
147
 
134
148
  Note how our facts define relations that always go from subject to object - they form a directed graph. In a perfect world, friendships go both ways, but to specify this in out model, we need to have two facts for each couple. Instead of duplicating everything by hand, let's automate that:
135
149
 
136
- engine.rule "symmetric predicate" do
137
- forall {
138
- has :Predicate, "symmetric", true
139
- has :X, :Predicate, :Y
140
- }
141
- make {
142
- gen :Y, :Predicate, :X
143
- }
144
- end
150
+ ```ruby
151
+ engine.rule "symmetric predicate" do
152
+ forall {
153
+ has :Predicate, "symmetric", true
154
+ has :X, :Predicate, :Y
155
+ }
156
+ make {
157
+ gen :Y, :Predicate, :X
158
+ }
159
+ end
145
160
 
146
- engine << ["friend", "symmetric", true]
161
+ engine << ["friend", "symmetric", true]
162
+ ```
147
163
 
148
164
  If you still have the "self-printer" rule installed, you will see some new friendships pop up immediately!
149
165
 
@@ -165,9 +181,9 @@ Passes whether or not the template matches anything. It's only useful if it intr
165
181
 
166
182
  The `none` block contains other matchers and passes if that *entire subchain* returns an empty set. In other words, it corresponds to an expression `not ( a and b and ... )`.
167
183
 
168
- #### `any { variant { ... } ... }`
184
+ #### `any { option { ... } ... }`
169
185
 
170
- The `any` block contains several `variant` blocks, each of them containing other matchers. It passes if any of the `variant` subchains matches. It's a shame that disjunction has to be so much more verbose than conjunction, but life is cruel.
186
+ The `any` block contains several `option` blocks, each of them containing other matchers. It passes if any of the `option` subchains matches. It's a shame that disjunction has to be so much more verbose than conjunction, but life is cruel.
171
187
 
172
188
  #### `same x, y`
173
189
 
@@ -195,8 +211,10 @@ The following matchers are nothing but syntactic sugar for a combination of prim
195
211
 
196
212
  Short for:
197
213
 
198
- neg subject, predicate, object, -1
199
- has subject, predicate, object, 0
214
+ ```ruby
215
+ neg subject, predicate, object, -1
216
+ has subject, predicate, object, 0
217
+ ```
200
218
 
201
219
  That is, it passes if the fact was missing in the previous state but exists in the current one. Alias: `added`.
202
220
 
@@ -204,8 +222,10 @@ That is, it passes if the fact was missing in the previous state but exists in t
204
222
 
205
223
  Short for:
206
224
 
207
- has subject, predicate, object, -1
208
- neg subject, predicate, object, 0
225
+ ```ruby
226
+ has subject, predicate, object, -1
227
+ neg subject, predicate, object, 0
228
+ ```
209
229
 
210
230
  The reverse of `asserted`. Alias: `removed`.
211
231
 
@@ -213,8 +233,10 @@ The reverse of `asserted`. Alias: `removed`.
213
233
 
214
234
  Short for:
215
235
 
216
- has subject, predicate, object, -1
217
- has subject, predicate, object, 0
236
+ ```ruby
237
+ has subject, predicate, object, -1
238
+ has subject, predicate, object, 0
239
+ ```
218
240
 
219
241
  Alias: `still_has`.
220
242
 
@@ -222,8 +244,10 @@ Alias: `still_has`.
222
244
 
223
245
  Short for:
224
246
 
225
- neg subject, predicate, object, -1
226
- neg subject, predicate, object, 0
247
+ ```ruby
248
+ neg subject, predicate, object, -1
249
+ neg subject, predicate, object, 0
250
+ ```
227
251
 
228
252
  Alias: `still_missing`.
229
253
 
@@ -250,9 +274,11 @@ The debugging action that will print a message every time it's activated. Possib
250
274
 
251
275
  We've seen one way to specify custom actions: using `action` with a block. Another way to use it is to say:
252
276
 
253
- action class, ... do
254
- ...
255
- end
277
+ ```ruby
278
+ action class, ... do
279
+ ...
280
+ end
281
+ ```
256
282
 
257
283
  Any additional arguments or blocks will be given to `initialize`, and the class must define an `execute` method taking a token. Passing any object with an `execute` method also works.
258
284
 
@@ -260,7 +286,7 @@ If your action class inherits from `Wongi::Engine::Action`, you'll have the foll
260
286
 
261
287
  * `rete`: the engine instance
262
288
  * `rule`: the rule object that is using this action
263
- * `name`: the extension clause used to define this action (read more under [DSL extensions])
289
+ * `name`: the extension clause used to define this action (read more under [DSL extensions](#dsl-extensions))
264
290
  * `production`: the production node
265
291
 
266
292
  If you can't or don't want to inherit, you can define the accessors yourself. Having just the ones you need is fine.
@@ -269,48 +295,58 @@ If you can't or don't want to inherit, you can define the accessors yourself. Ha
269
295
 
270
296
  Using `engine.rule` and `engine.query` is fine if you want to experiment, but to make rules and queries more manageable, you will probably want to keep them separate from the engine instance. One way to do that is to just say:
271
297
 
272
- my_rule = rule "name" do
273
- ...
274
- end
298
+ ```ruby
299
+ my_rule = rule "name" do
300
+ ...
301
+ end
275
302
 
276
- engine << my_rule
303
+ engine << my_rule
304
+ ```
277
305
 
278
306
  For even more convenience, why not group rules together:
279
307
 
280
- my_ruleset = ruleset {
281
- rule "rule 1" do
282
- ...
283
- end
284
- rule "rule 2" do
285
- ...
286
- end
287
- }
308
+ ```ruby
309
+ my_ruleset = ruleset {
310
+ rule "rule 1" do
311
+ ...
312
+ end
313
+ rule "rule 2" do
314
+ ...
315
+ end
316
+ }
288
317
 
289
- engine << my_ruleset
318
+ engine << my_ruleset
319
+ ```
290
320
 
291
321
  Again, you don't need to hold on to object references if you don't want to:
292
322
 
293
- ruleset "my set" do
294
- ...
295
- end
323
+ ```ruby
324
+ ruleset "my set" do
325
+ ...
326
+ end
296
327
 
297
- engine << Wongi::Engine::Ruleset[ "my set" ]
328
+ engine << Wongi::Engine::Ruleset[ "my set" ]
329
+ ```
298
330
 
299
331
  ### DSL extensions
300
332
 
301
333
  This is a more advanced method of customising. In general, DSL extensions have the form:
302
334
 
303
- dsl {
304
- section [ :forall | :make ]
305
- clause :my_action
306
- [ action | accept | body ] ...
307
- }
335
+ ```ruby
336
+ dsl {
337
+ section [ :forall | :make ]
338
+ clause :my_action
339
+ [ action | accept | body ] ...
340
+ }
341
+ ```
308
342
 
309
343
  which is then used in a rule like this:
310
344
 
311
- make {
312
- my_action ...
313
- }
345
+ ```ruby
346
+ make {
347
+ my_action ...
348
+ }
349
+ ```
314
350
 
315
351
  DSL extensions are globally visible to all engine instances.
316
352
 
@@ -322,7 +358,34 @@ This simply allows you to group several other actions or matchers. It is perhaps
322
358
 
323
359
  #### `action class`, `action do |token| ... end`
324
360
 
325
- This works almost exactly like using the `action` action directly in a rule, but gives it a more meaningful alias. Arguments to `initialize`, however, are taken from the action's invocation in `make`, not definition.
361
+ This works almost exactly like using the `action` action directly in a rule, but gives it a more meaningful alias. Arguments to `initialize`, however, are taken from the action's invocation in `make`, not the definition.
362
+
363
+ A useful pattern is having specialised named collectors, defined like this:
364
+
365
+ ```ruby
366
+ dsl {
367
+ section :make
368
+ clause :my_collection
369
+ action Wongi::Engine::SimpleCollector.collector
370
+ }
371
+ ```
372
+
373
+ installed like this:
374
+
375
+ ```ruby
376
+ rule('collecting') {
377
+ ...
378
+ make {
379
+ my_collection :X
380
+ }
381
+ }
382
+ ```
383
+
384
+ and accessed like this:
385
+
386
+ ```ruby
387
+ collection = engine.collection :my_collection
388
+ ```
326
389
 
327
390
  #### `accept class`
328
391
 
@@ -10,7 +10,7 @@ module Wongi::Engine
10
10
  end
11
11
  end
12
12
 
13
- def variant &block
13
+ def option &block
14
14
  var = VariantRule.new
15
15
  var.instance_eval &block
16
16
  variants << var
@@ -1,5 +1,5 @@
1
1
  module Wongi
2
2
  module Engine
3
- VERSION = "0.0.1"
3
+ VERSION = "0.0.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wongi-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-13 00:00:00.000000000 Z
12
+ date: 2012-07-16 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A rule engine.
15
15
  email: