omf_rete 0.6.1 → 0.6.2

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