omf_rete 0.6.1 → 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  .project
2
2
  Rakefile-back
3
3
  examples/of.rb
4
- lib/omf_rete/UNUSED/tuple_set.rb
4
+ lib/omf_rete/UNUSED
5
+ pkg/
@@ -6,37 +6,51 @@ module OMF::Rete
6
6
  # being called whenever a tuple is added or
7
7
  # removed.
8
8
  #
9
- # The TupleSet is defined by a +description+.
9
+ # The TupleSet is defined by a +description+.
10
10
  #
11
11
  # The +description+ is an array of the
12
12
  # same length as the tuples maintained. Each element,
13
13
  # if not nil, names the binding variable associated with it.
14
- # The position of a binding can be retrieved with
14
+ # The position of a binding can be retrieved with
15
15
  # +index_for_binding+.
16
16
  #
17
17
  class AbstractTupleSet
18
-
18
+
19
19
  attr_reader :description
20
20
  attr_accessor :source
21
-
21
+
22
22
  def initialize(description, source = nil)
23
23
  @description = description
24
24
  @source = source
25
25
  end
26
-
26
+
27
+ def register_with_store(store, description)
28
+ @store = store
29
+ raise "BUG ALERT" unless description == @description
30
+ store.registerTSet(self, description)
31
+ end
32
+
33
+ # Detach all streams from each other as they are no longer in use
34
+ #
35
+ def detach()
36
+ @source.detach if @source
37
+ puts ">>> UNREGISTER"
38
+ @store.unregisterTSet(self) if @store
39
+ end
40
+
27
41
  def addTuple(tuple)
28
42
  raise 'Abstract class'
29
43
  end
30
-
44
+
31
45
  def removeTuple(tuple)
32
46
  raise 'Abstract class'
33
47
  end
34
-
48
+
35
49
  # Call block for every tuple stored in this set currently and
36
50
  # in the future. In other words, the block may be called even after this
37
- # method returns.
51
+ # method returns.
38
52
  #
39
- # The block will be called with one parameters, the
53
+ # The block will be called with one parameters, the
40
54
  # tuple added.
41
55
  #
42
56
  def on_add(&block)
@@ -47,7 +61,7 @@ module OMF::Rete
47
61
  def to_a
48
62
  raise 'Abstract class'
49
63
  end
50
-
64
+
51
65
  # Retunr the index into the tuple for the binding variable +bname+.
52
66
  #
53
67
  # Note: This index is different to the set index used in +IndexedTupleSet+
@@ -57,7 +71,7 @@ module OMF::Rete
57
71
  el == bname
58
72
  end
59
73
  end
60
-
74
+
61
75
  def binding_at(index)
62
76
  @description[index]
63
77
  end
@@ -65,8 +79,8 @@ module OMF::Rete
65
79
  def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
66
80
  raise 'Abstract class'
67
81
  end
68
-
69
-
70
- end # class
82
+
83
+
84
+ end # class
71
85
  end # module
72
86
 
@@ -85,6 +85,8 @@ module OMF::Rete
85
85
  @index = {}
86
86
  end
87
87
 
88
+
89
+
88
90
  # Call block for every tuple stored in this set currently and
89
91
  # in the future. In other words, the block may be called even after this
90
92
  # method returns.
@@ -182,6 +184,8 @@ module OMF::Rete
182
184
  a
183
185
  end
184
186
 
187
+
188
+
185
189
  def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
186
190
  out.write(" " * offset)
187
191
  desc = @description.collect do |e| e || '*' end
@@ -101,6 +101,15 @@ module OMF::Rete
101
101
  return false
102
102
  end
103
103
 
104
+ # Detach all streams from each other as they are no longer in use
105
+ #
106
+ def detach()
107
+ @left.detach
108
+ @right.detach
109
+ @results.clear
110
+ end
111
+
112
+
104
113
  def description()
105
114
  @resultSet.description
106
115
  end
@@ -0,0 +1,27 @@
1
+
2
+ module OMF::Rete::Planner
3
+
4
+ # This plan holds the final result stream from which all other streams can be
5
+ # discovered and if necessary, freed.
6
+ #
7
+ #
8
+ class FinalPlan
9
+
10
+ def initialize(result_stream)
11
+ @result_stream = result_stream
12
+ end
13
+
14
+ def materialize(indexPattern, resultSet, opts, &block)
15
+ raise "Shouldn't be called - THis is just a place holder"
16
+ end
17
+
18
+ def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
19
+ @result_stream.describe(out, offset, incr, sep)
20
+ end
21
+
22
+ def detach()
23
+ @result_stream.detach()
24
+ end
25
+ end
26
+
27
+ end # module
@@ -18,6 +18,7 @@ module OMF::Rete
18
18
  class PlannerException < Exception; end
19
19
 
20
20
  require 'omf_rete/planner/source_plan'
21
+ require 'omf_rete/planner/final_plan'
21
22
  require 'omf_rete/planner/plan_level_builder'
22
23
  require 'omf_rete/planner/plan_set'
23
24
  require 'omf_rete/planner/filter_plan'
@@ -88,6 +89,9 @@ module OMF::Rete
88
89
  # set.
89
90
  #
90
91
  def materialize(projectPattern = nil, plan = nil, opts = nil, &block)
92
+ unless block
93
+ raise PlannerException.new("Missing block to process result tuples with")
94
+ end
91
95
  unless plan
92
96
  plan = best_plan()
93
97
  end
@@ -96,7 +100,9 @@ module OMF::Rete
96
100
  end
97
101
  if (plan.is_a?(SourcePlan))
98
102
  # This is really just a simple pattern on the store
99
- _materialize_simple_plan(projectPattern, plan, opts, &block)
103
+ #plan = SimpleSourcePlan.new(plan)
104
+ #plan.materialize(projectPattern, opts, &block)
105
+ endS = _materialize_simple_plan(projectPattern, plan, opts, &block)
100
106
  else
101
107
  # this is the root of the plan
102
108
  if projectPattern
@@ -106,8 +112,9 @@ module OMF::Rete
106
112
  end
107
113
  frontS, endS = _materialize_result_stream(plan, projectPattern, opts, &block)
108
114
  plan.materialize(nil, frontS, opts, &block)
109
- endS
115
+ #endS
110
116
  end
117
+ FinalPlan.new(endS)
111
118
  end
112
119
 
113
120
 
@@ -267,7 +274,6 @@ module OMF::Rete
267
274
  frontS.source = src
268
275
 
269
276
  @store.registerTSet(src, plan.description) if @store
270
-
271
277
  endS
272
278
  end
273
279
 
@@ -51,7 +51,8 @@ module OMF::Rete
51
51
  else
52
52
  @source_set = OMF::Rete::IndexedTupleSet.new(@description, indexPattern)
53
53
  end
54
- @store.registerTSet(@source_set, @description) if @store
54
+ #@store.registerTSet(@source_set, @description) if @store
55
+ @source_set.register_with_store(@store, @description)
55
56
  end
56
57
 
57
58
  # Return the cost of this plan.
@@ -69,11 +70,7 @@ module OMF::Rete
69
70
 
70
71
 
71
72
  def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
72
- out.write(" " * offset)
73
- desc = @description.collect do |e| e || '*' end
74
- # index = @result_set.to_a.sort
75
- # out.write("src: [#{desc.join(', ')}] index: [#{index.join(', ')}] cost: #{cost}#{sep}")
76
- out.write("src: [#{desc.join(', ')}] cost: #{cost}#{sep}")
73
+ @source_set.describe(out, offset, incr, sep) if @source_set
77
74
  end
78
75
 
79
76
  end # SourcePlan
@@ -7,6 +7,7 @@ module OMF::Rete::Store
7
7
  class StoreException < Exception; end
8
8
 
9
9
  class NotImplementedException < StoreException; end
10
+ class UnknownSubscriptionException < StoreException; end
10
11
 
11
12
  DEF_TYPE = :alpha
12
13
 
@@ -36,14 +37,46 @@ module OMF::Rete::Store
36
37
  # end
37
38
 
38
39
  def subscribe(name, query, out_pattern = nil, &block)
40
+ if name && @plans[name]
41
+ raise StoreException.new "Already have subscription '#{name}'."
42
+ end
43
+
39
44
  require 'omf_rete/planner/plan_builder'
40
45
 
41
46
  pb = OMF::Rete::Planner::PlanBuilder.new(query, self)
42
47
  pb.build
43
- pb.materialize(out_pattern, &block)
48
+ plan = pb.materialize(out_pattern, &block)
49
+ if name
50
+ @plans[name] = plan
51
+ end
52
+ plan
44
53
  end
45
54
  alias :add_rule :subscribe
46
55
 
56
+ def unsubscribe(name_or_plan)
57
+ if name_or_plan.is_a? OMF::Rete::Planner::FinalPlan
58
+ plan = name_or_plan
59
+ else
60
+ plan = @plans.delete(name_or_plan)
61
+ end
62
+ unless plan
63
+ raise UnknownSubscriptionException.new("Unknown subscription '#{name_or_plan}'")
64
+ end
65
+ plan.detach
66
+ end
67
+
68
+ # Run a query against the store. This is essentially a short lived subscription
69
+ # may not be catch everything if there are inserts at the same time.
70
+ #
71
+ def query(query, out_pattern = nil)
72
+ result = []
73
+ plan = subscribe(null, query, out_pattern) do |t|
74
+ result << t
75
+ end
76
+ unsubscribe(plan)
77
+ result
78
+ end
79
+
47
80
  def addTuple(tarray)
48
81
  raise NotImplementedException.new
49
82
  end
@@ -98,5 +131,10 @@ module OMF::Rete::Store
98
131
  tuple.is_a?(Array) && tuple.length == @length
99
132
  end
100
133
 
134
+ protected
135
+ def store_initialize()
136
+ @plans = {}
137
+ end
138
+
101
139
 
102
140
  end
@@ -5,17 +5,18 @@ module OMF::Rete::Store::Alpha
5
5
  #
6
6
  class AlphaElement
7
7
 
8
- def self.create(level, length)
8
+ def self.create(level, length, store)
9
9
  rem = length - level
10
10
  if (rem > 1)
11
- AlphaInnerElement.new(level, length)
11
+ AlphaInnerElement.new(level, length, store)
12
12
  else
13
- AlphaLeafElement.new(level)
13
+ AlphaLeafElement.new(level, store)
14
14
  end
15
15
  end
16
16
 
17
- def initialize(level)
17
+ def initialize(level, store)
18
18
  @level = level
19
+ @store = store
19
20
  end
20
21
 
21
22
  end
@@ -4,12 +4,12 @@ module OMF::Rete::Store::Alpha
4
4
 
5
5
  class AlphaInnerElement < AlphaElement
6
6
 
7
- def initialize(level, length)
8
- super(level)
7
+ def initialize(level, length, store)
8
+ super(level, store)
9
9
  @length = length
10
10
  @children = {}
11
11
  if (level < length)
12
- @wildChild = AlphaElement.create(level + 1, length)
12
+ @wildChild = AlphaElement.create(level + 1, length, store)
13
13
  end
14
14
  end
15
15
 
@@ -18,7 +18,7 @@ module OMF::Rete::Store::Alpha
18
18
  def registerTSet(tset, pattern)
19
19
  pitem = pattern[@level]
20
20
  if (pitem) # not nil
21
- child = (@children[pitem] ||= AlphaElement.create(@level + 1, @length))
21
+ child = (@children[pitem] ||= AlphaElement.create(@level + 1, @length, @store))
22
22
  child.registerTSet(tset, pattern)
23
23
  else # wildcard
24
24
  @wildChild.registerTSet(tset, pattern)
@@ -7,7 +7,7 @@ module OMF::Rete::Store::Alpha
7
7
  #
8
8
  class AlphaLeafElement < AlphaElement
9
9
 
10
- def initialize(level)
10
+ def initialize(level, store)
11
11
  super
12
12
  @tsetIndex = {}
13
13
  @tsetWildcards = []
@@ -18,11 +18,16 @@ module OMF::Rete::Store::Alpha
18
18
  def registerTSet(tset, pattern)
19
19
  pitem = pattern[@level]
20
20
  leaf = (@level == @length)
21
- if (pitem) # not nil
22
- (@tsetIndex[pitem] ||= []) << tset
23
- else # wildcard
24
- @tsetWildcards << tset
21
+ a = pitem ? (@tsetIndex[pitem] ||= []) : @tsetWildcards
22
+ a << tset
23
+ @store.onUnregisterTSet(tset) do
24
+ a.delete(tset)
25
25
  end
26
+ # if (pitem) # not nil
27
+ # (@tsetIndex[pitem] ||= []) << tset
28
+ # else # wildcard
29
+ # @tsetWildcards << tset
30
+ # end
26
31
  end
27
32
 
28
33
  def addTuple(tarray)
@@ -16,8 +16,10 @@ module OMF::Rete::Store
16
16
  # fixed length +length+.
17
17
  #
18
18
  def initialize(length, opts = {})
19
+ store_initialize()
19
20
  @length = length
20
- @root = Alpha::AlphaInnerElement.new(0, length)
21
+ @root = Alpha::AlphaInnerElement.new(0, length, self)
22
+ @unregisterHandler = {}
21
23
  @index = []
22
24
  length.times do @index << {} end
23
25
  end
@@ -39,6 +41,14 @@ module OMF::Rete::Store
39
41
  tset
40
42
  end
41
43
 
44
+ def unregisterTSet(tset)
45
+ (@unregisterHandler[tset] || []).each do |proc|
46
+ proc.call
47
+ end
48
+ @unregisterHandler.delete(tset)
49
+ end
50
+
51
+
42
52
  def createTSet(description, indexPattern)
43
53
  tset = Moana::Filter::IndexedTupleSet.new(description, indexPattern)
44
54
  registerTSet(tset, description)
@@ -114,5 +124,12 @@ module OMF::Rete::Store
114
124
  def to_s()
115
125
  "Store"
116
126
  end
127
+
128
+ # Register a block to call whenever a TSet is being unregistered
129
+ #
130
+ def onUnregisterTSet(tset, &block)
131
+ (@unregisterHandler[tset] ||= []) << block
132
+ end
133
+
117
134
  end # Store
118
135
  end # Moana
@@ -20,6 +20,7 @@ module OMF::Rete::Store
20
20
  # is always 'name' (included in length)
21
21
  #
22
22
  def initialize(name, length, opts = {})
23
+ super(length, opts)
23
24
  @name = name
24
25
  super length, opts
25
26
  #@store = AlphaStore.new(length - 1)
@@ -23,6 +23,7 @@ module OMF::Rete::Store
23
23
 
24
24
  # @param opts :include_object Make the fist element the object
25
25
  def initialize(opts = {})
26
+ store_initialize()
26
27
  @include_object = opts[:name] || (opts[:include_object] == true ? self : nil)
27
28
  @object = nil
28
29
  @tsets = {}
@@ -30,6 +30,7 @@ module OMF::Rete::Store
30
30
  include OMF::Rete::Store
31
31
 
32
32
  def initialize(opts = {})
33
+ store_initialize()
33
34
  @stores = {}
34
35
  end
35
36
 
@@ -28,6 +28,13 @@ module OMF::Rete
28
28
  raise "Method 'check_for_tuple' is not implemented"
29
29
  end
30
30
 
31
+ # Detach all streams from each other as they are no longer in use
32
+ #
33
+ def detach()
34
+ @source.detach if @source
35
+ @source = nil
36
+ end
37
+
31
38
  def describe(out = STDOUT, offset = 0, incr = 2, sep = "\n")
32
39
  out.write(" " * offset)
33
40
  _describe(out, sep)
@@ -1,7 +1,7 @@
1
1
 
2
2
  module OMF
3
3
  module Rete
4
- VERSION = '0.6.1'
4
+ VERSION = '0.6.2'
5
5
  # Used for finding the example directory
6
6
  TOP_DIR = File.dirname(File.dirname(File.dirname(__FILE__)))
7
7
  end
data/tests/test_filter.rb CHANGED
@@ -9,7 +9,7 @@ include OMF::Rete
9
9
  include OMF::Rete::Planner
10
10
 
11
11
  class TestFilter < Test::Unit::TestCase
12
-
12
+
13
13
 
14
14
  def _test_plan(plan, storeSize, expected = nil, inTuples = nil, outTuples = nil, outPattern = nil)
15
15
  store = Store.create(storeSize)
@@ -22,12 +22,11 @@ class TestFilter < Test::Unit::TestCase
22
22
  result = pb.materialize(outPattern) do |t|
23
23
  resT << t.to_a
24
24
  end
25
-
26
25
  out = StringIO.new
27
26
  #result.describe(out, 0, 0, '|')
28
27
  result.describe(out)
29
28
  assert_equal(expected, out.string) if expected
30
-
29
+
31
30
  if (inTuples)
32
31
  inTuples.each do |t|
33
32
  store.addTuple(t)
@@ -36,10 +35,10 @@ class TestFilter < Test::Unit::TestCase
36
35
  end
37
36
  result
38
37
  end
39
-
38
+
40
39
  def test_theshold_test
41
40
  plan = [
42
- [:x?],
41
+ [:x?],
43
42
  OMF::Rete.filter(:x?) do |x|
44
43
  x > 2
45
44
  end
@@ -53,10 +52,10 @@ out: [x?]
53
52
  resT = [[3], [4]]
54
53
  _test_plan plan, 1, exp, inT, resT
55
54
  end
56
-
55
+
57
56
  def test_theshold_test2
58
57
  plan = [
59
- [:x?, :y?],
58
+ [:x?, :y?],
60
59
  OMF::Rete::filter(:x?) do |x|
61
60
  x > 2
62
61
  end,
@@ -20,7 +20,7 @@ class TestPlanner < Test::Unit::TestCase
20
20
 
21
21
  # with empty store
22
22
  resT = []
23
- result = store.subscribe(:test, plan, outPattern) do |t|
23
+ result = store.subscribe(nil, plan, outPattern) do |t|
24
24
  resT << t.to_a
25
25
  end
26
26
 
@@ -29,6 +29,8 @@ class TestPlanner < Test::Unit::TestCase
29
29
  result.describe(out)
30
30
  assert_equal(expected, out.string) if expected
31
31
 
32
+ #store.unsubscribe(result)
33
+
32
34
  if (inTuples)
33
35
 
34
36
  inTuples.each do |t|
metadata CHANGED
@@ -1,39 +1,44 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: omf_rete
3
- version: !ruby/object:Gem::Version
4
- version: 0.6.1
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 6
8
+ - 2
9
+ version: 0.6.2
6
10
  platform: ruby
7
- authors:
11
+ authors:
8
12
  - NICTA
9
13
  autorequire:
10
14
  bindir: bin
11
15
  cert_chain: []
12
- date: 2013-12-22 00:00:00.000000000 Z
13
- dependencies:
14
- - !ruby/object:Gem::Dependency
16
+
17
+ date: 2014-01-02 00:00:00 +11:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
15
21
  name: omf_base
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :runtime
23
22
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
30
32
  description: Tuple store with query and filter functionality.
31
- email:
33
+ email:
32
34
  - omf-user@lists.nicta.com.au
33
35
  executables: []
36
+
34
37
  extensions: []
38
+
35
39
  extra_rdoc_files: []
36
- files:
40
+
41
+ files:
37
42
  - .gitignore
38
43
  - README.md
39
44
  - Rakefile
@@ -43,6 +48,7 @@ files:
43
48
  - lib/omf_rete/join_op.rb
44
49
  - lib/omf_rete/planner/abstract_plan.rb
45
50
  - lib/omf_rete/planner/filter_plan.rb
51
+ - lib/omf_rete/planner/final_plan.rb
46
52
  - lib/omf_rete/planner/join_plan.rb
47
53
  - lib/omf_rete/planner/plan_builder.rb
48
54
  - lib/omf_rete/planner/plan_level_builder.rb
@@ -71,29 +77,35 @@ files:
71
77
  - tests/test_predicate_store.rb
72
78
  - tests/test_readme.rb
73
79
  - tests/test_store.rb
80
+ has_rdoc: true
74
81
  homepage: https://www.mytestbed.net
75
82
  licenses: []
83
+
76
84
  post_install_message:
77
85
  rdoc_options: []
78
- require_paths:
86
+
87
+ require_paths:
79
88
  - lib
80
- required_ruby_version: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ! '>='
84
- - !ruby/object:Gem::Version
85
- version: '0'
86
- required_rubygems_version: !ruby/object:Gem::Requirement
87
- none: false
88
- requirements:
89
- - - ! '>='
90
- - !ruby/object:Gem::Version
91
- version: '0'
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ segments:
101
+ - 0
102
+ version: "0"
92
103
  requirements: []
104
+
93
105
  rubyforge_project: omf_rete
94
- rubygems_version: 1.8.23
106
+ rubygems_version: 1.3.6
95
107
  signing_key:
96
108
  specification_version: 3
97
109
  summary: A Rete implementation.
98
110
  test_files: []
99
- has_rdoc:
111
+