tupelo 0.20 → 0.21

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -3
  3. data/bench/{tuplespace.rb → tuplestore.rb} +3 -3
  4. data/bin/tspy +2 -2
  5. data/bin/tup +20 -3
  6. data/example/multi-tier/{kvspace.rb → kvstore.rb} +2 -2
  7. data/example/multi-tier/memo2.rb +4 -4
  8. data/example/riemann/v2/event-sql.rb +56 -0
  9. data/example/riemann/v2/expirer.rb +4 -1
  10. data/example/riemann/v2/hash-store.rb +6 -6
  11. data/example/riemann/v2/ordered-event-store.rb +1 -1
  12. data/example/riemann/v2/riemann.rb +7 -4
  13. data/example/riemann/v2/sqlite-event-store.rb +174 -0
  14. data/example/sqlite/poi-client.rb +11 -0
  15. data/example/sqlite/poi-store.rb +38 -56
  16. data/example/sqlite/poi-template.rb +61 -0
  17. data/example/sqlite/poi-v2.rb +63 -33
  18. data/example/sqlite/poi.rb +13 -25
  19. data/example/sqlite/tmp/poi-sqlite.rb +10 -8
  20. data/example/subspaces/addr-book.rb +2 -2
  21. data/example/subspaces/{sorted-set-space.rb → sorted-set-store.rb} +4 -4
  22. data/example/wip/complex-tags.rb +45 -0
  23. data/lib/tupelo/app/trace.rb +1 -1
  24. data/lib/tupelo/app.rb +3 -3
  25. data/lib/tupelo/archiver/{persistent-tuplespace.rb → persistent-tuplestore.rb} +1 -1
  26. data/lib/tupelo/archiver/{tuplespace.rb → tuplestore.rb} +2 -2
  27. data/lib/tupelo/archiver/worker.rb +12 -12
  28. data/lib/tupelo/archiver.rb +7 -5
  29. data/lib/tupelo/client/reader.rb +13 -10
  30. data/lib/tupelo/client/subspace.rb +12 -4
  31. data/lib/tupelo/client/transaction.rb +98 -95
  32. data/lib/tupelo/client/{tuplespace.rb → tuplestore.rb} +4 -4
  33. data/lib/tupelo/client/worker.rb +44 -35
  34. data/lib/tupelo/client.rb +4 -4
  35. data/lib/tupelo/version.rb +1 -1
  36. data/test/lib/mock-client.rb +1 -1
  37. data/test/lib/testable-worker.rb +1 -1
  38. data/test/unit/test-ops.rb +1 -1
  39. metadata +13 -9
  40. data/example/map-reduce/ex.rb +0 -32
@@ -0,0 +1,61 @@
1
+ # Hard-coded to work with tuples belonging to the "poi" subspace
2
+ # and with the PoiStore table.
3
+ class PoiTemplate
4
+ attr_reader :lat, :lng
5
+
6
+ # Template for matching all POI tuples.
7
+ attr_reader :poi_template
8
+
9
+ # lat and lng can be intervals or single values or nil to match any value
10
+ def initialize lat: nil, lng: nil, poi_template: nil
11
+ @lat = lat
12
+ @lng = lng
13
+ @poi_template = poi_template
14
+ end
15
+
16
+ # we only need to define this method if we plan to wait for poi tuples
17
+ # locally using this template, i.e. read(template) or take(template)
18
+ def === tuple
19
+ @poi_template === tuple and
20
+ !@lat || @lat === tuple[:lat] and
21
+ !@lng || @lng === tuple[:lng]
22
+ end
23
+
24
+ def find_in table, distinct_from: []
25
+ where_terms = {}
26
+ where_terms[:lat] = @lat if @lat
27
+ where_terms[:lng] = @lng if @lng
28
+
29
+ matches = table.
30
+ select(:lat, :lng, :desc).
31
+ where(where_terms).
32
+ limit(distinct_from.size + 1).all
33
+
34
+ # Note: everything in this method up to and including where(...) can
35
+ # be called once and reused as a sequel dataset.
36
+
37
+ distinct_from.each do |tuple|
38
+ if i=matches.index(tuple)
39
+ matches.delete_at i
40
+ end
41
+ end
42
+
43
+ matches.first
44
+ end
45
+
46
+ def find_all_in table, &bl
47
+ where_terms = {}
48
+ where_terms[:lat] = @lat if @lat
49
+ where_terms[:lng] = @lng if @lng
50
+
51
+ matches = table.
52
+ select(:lat, :lng, :desc).
53
+ where(where_terms)
54
+
55
+ if bl
56
+ matches.each(&bl)
57
+ else
58
+ matches.all
59
+ end
60
+ end
61
+ end
@@ -1,58 +1,88 @@
1
- # POI -- Points Of Interest
2
- #
3
- # This example creates a sqlite db in memory with a table of locations and
4
- # descriptions of points of interest, and attaches the db to a subspace of the
5
- # tuplespace. The process which manages that subspace can now do two things:
6
- #
7
- # 1. accept inserts (via write)
8
- #
9
- # 2. custom queries, accessed by write to a different subspace
10
- #
11
- # You can have redundant instances of this, and that will distribute load
12
- # in #2 above.
13
- #
14
- # See example/subspaces/addr-book.rb for more comments on the command/response
15
- # subspace pattern.
16
- #
17
- # gem install sequel sqlite3
18
-
19
1
  require 'tupelo/app'
20
- require_relative 'poi-store'
2
+ require_relative 'poi-client'
21
3
 
22
4
  Tupelo.application do
23
5
  local do
24
6
  POISPACE = PoiStore.define_poispace(self)
25
7
  define_subspace("cmd", {id: nil, cmd: String, arg: nil})
26
8
  define_subspace("rsp", {id: nil, result: nil})
9
+ # Note: this id is request id, not related to sqlite table id.
27
10
  end
28
11
 
29
- child tuplespace: [PoiStore, POISPACE],
30
- subscribe: ["poi", "cmd"], passive: true do
12
+ child PoiClient, poispace: POISPACE, subscribe: "cmd", passive: true do
31
13
  log.progname = "poi-store #{client_id}"
14
+
15
+ poispace = subspace("poi")
16
+ # get local copy to replace poispace argument, which has wrong kind of
17
+ # keys (string, not symbol)
32
18
 
33
19
  # handle custom queries here, using poi template
34
20
  loop do
35
21
  req = take subspace("cmd")
36
- case req["cmd"]
22
+ case req[:cmd]
37
23
  when "find box"
38
- arg = req["arg"] ## validate this
39
- lat = arg["lat"]; lng = arg["lng"]
40
- template = PoiTemplate.new(poispace: subspace("poi"),
24
+ arg = req[:arg] ## should validate args
25
+ lat = arg[:lat]; lng = arg[:lng]
26
+ template = PoiTemplate.new(poi_template: poispace,
27
+ lat: lat[0]..lat[1], lng: lng[0]..lng[1])
28
+ write id: req[:id], result: read_all(template)
29
+
30
+ when "delete box"
31
+ arg = req[:arg]
32
+ lat = arg[:lat]; lng = arg[:lng]
33
+ template = PoiTemplate.new(poi_template: poispace,
41
34
  lat: lat[0]..lat[1], lng: lng[0]..lng[1])
42
- write id: req["id"], result: read_all(template)
35
+
36
+ deleted = []
37
+ transaction do
38
+ while poi = take_nowait(template)
39
+ log "preparing to delete: #{poi}"
40
+ deleted << poi
41
+ end
42
+ # Wrapping this in a transaction not really necessary, but more
43
+ # efficient (only one round-trip to network). Watch out for huge sets
44
+ # of tuples, though.
45
+ end
46
+
47
+ write id: req[:id], result: deleted
43
48
  end
44
49
  end
45
50
  end
46
51
 
47
52
  child subscribe: "rsp" do
48
- write lat: 12, lng: 34, desc: "foo"
49
- write lat: 56, lng: 78, desc: "bar"
50
- write lat: 12, lng: 34, desc: "foo" # dup is ok
51
- write lat: 13, lng: 35, desc: "baz"
53
+ write lat: 1.2, lng: 3.4, desc: "foo"
54
+ write lat: 5.6, lng: 7.8, desc: "bar"
55
+ write lat: 1.2, lng: 3.4, desc: "foo" # dup is ok
56
+ write lat: 1.3, lng: 3.5, desc: "baz"
52
57
 
53
- req_id = [client_id, 1]
54
- write id: req_id, cmd: "find box", arg: {lat: [10, 14], lng: [30, 40]}
58
+ cmd_id = 0 # manually managing cmd id is a pain!
59
+
60
+ log "finding in box"
61
+ cmd_id += 1
62
+ req_id = [client_id, cmd_id]
63
+ write id: req_id, cmd: "find box", arg: {lat: [1.0, 1.4], lng: [3.0, 4.0]}
64
+ rsp = take id: req_id, result: nil
65
+ log "result: #{rsp["result"]}"
66
+
67
+ log "deleting in box"
68
+ cmd_id += 1
69
+ req_id = [client_id, cmd_id]
70
+ write id: req_id, cmd: "delete box", arg: {lat: [1.0, 1.4], lng: [3.0, 4.0]}
71
+ rsp = take id: req_id, result: nil
72
+ log "result: #{rsp["result"]}"
73
+
74
+ log "finding in box"
75
+ cmd_id += 1
76
+ req_id = [client_id, cmd_id]
77
+ write id: req_id, cmd: "find box", arg: {lat: [1.0, 1.4], lng: [3.0, 4.0]}
78
+ rsp = take id: req_id, result: nil
79
+ log "result: #{rsp["result"]}"
80
+
81
+ log "finding in BIGGER box"
82
+ cmd_id += 1
83
+ req_id = [client_id, cmd_id]
84
+ write id: req_id, cmd: "find box", arg: {lat: [0, 100], lng: [0, 100]}
55
85
  rsp = take id: req_id, result: nil
56
- log rsp["result"]
86
+ log "result: #{rsp["result"]}"
57
87
  end
58
88
  end
@@ -1,40 +1,28 @@
1
- # POI -- Points Of Interest
2
- #
3
- # This example creates a sqlite db in memory with a table of locations and
4
- # descriptions of points of interest, and attaches the db to a subspace of the
5
- # tuplespace. The process which manages that subspace can now do two things:
6
- #
7
- # 1. accept inserts (via write)
8
- #
9
- # 2. custom queries, accessed by write to a different subspace
10
- #
11
- # You can have redundant instances of this, and that will distribute load
12
- # in #2 above.
13
- #
14
- # gem install sequel sqlite3
15
-
16
1
  require 'tupelo/app'
17
- require_relative 'poi-store'
2
+ require_relative 'poi-client'
18
3
 
19
4
  Tupelo.application do
20
5
  local do
21
6
  POISPACE = PoiStore.define_poispace(self)
22
7
  end
23
8
 
24
- child tuplespace: [PoiStore, POISPACE], subscribe: "poi", passive: true do
9
+ child PoiClient, poispace: POISPACE, passive: true do
25
10
  log.progname = "poi-store"
26
- # handle custom queries here, using poi template
11
+
12
+ # At this point, the client already accepts writes in the space and stores
13
+ # them in a sqlite table. For the sake of a simple example, we add
14
+ # one feature to this mix: just show everything for each new tuple
27
15
  read do
28
- log read_all # just show everything for each new tuple
16
+ log read_all
29
17
  end
30
18
  end
31
19
 
32
20
  child subscribe: nil do
33
- write_wait lat: 12, lng: 34, desc: "foo"
34
- sleep 0.5 # give poi store time to store and log
35
- write_wait lat: 56, lng: 78, desc: "bar"
36
- sleep 0.5 # give poi store time to store and log
37
- write_wait lat: 12, lng: 34, desc: "foo" # dup is ok
38
- sleep 0.5 # give poi store time to store and log
21
+ write lat: 1.2, lng: 3.4, desc: "foo"
22
+ sleep 0.5 # delay to make the demo interesting
23
+ write lat: 5.6, lng: 7.8, desc: "bar"
24
+ sleep 0.5
25
+ write lat: 1.2, lng: 3.4, desc: "foo" # dup is ok
26
+ sleep 0.5
39
27
  end
40
28
  end
@@ -13,13 +13,13 @@ end
13
13
 
14
14
  poi = db[:poi]
15
15
 
16
- #10.times do |i|
17
- # poi << {
18
- # lat: rand,
19
- # lng: rand,
20
- # desc: "point #{i}"
21
- # }
22
- #end
16
+ 10.times do |i|
17
+ poi << {
18
+ lat: rand,
19
+ lng: rand,
20
+ desc: "point #{i}"
21
+ }
22
+ end
23
23
 
24
24
  #p poi.all
25
25
  #exit
@@ -28,6 +28,8 @@ poi << {lat: 1, lng: 2, desc: "a"}
28
28
  poi << {lat: 1, lng: 2, desc: "b"}
29
29
 
30
30
  #p poi.count
31
-
31
+ p poi.select(:lat, :lng, :desc).
32
+ where(lat: 0..1, lng: 0..1)
33
+
32
34
  p poi.select(:lat, :lng, :desc).
33
35
  where(lat: 0..1, lng: 0..1).limit(5).all
@@ -20,7 +20,7 @@
20
20
  # clients can each use their own data structure for these tuples.
21
21
 
22
22
  require 'tupelo/app'
23
- require_relative 'sorted-set-space'
23
+ require_relative 'sorted-set-store'
24
24
 
25
25
  SHOW_HANDLERS = ARGV.delete("--show-handlers")
26
26
 
@@ -66,7 +66,7 @@ Tupelo.application do
66
66
  N_REPLICAS.times do |i|
67
67
  # Inserts are just writes, which are handled by Worker and SortedSetSpace,
68
68
  # so this child's app loop only needs to handle the special commands.
69
- child tuplespace: [SortedSetSpace, ab_tag],
69
+ child tuplestore: [SortedSetStore, ab_tag],
70
70
  subscribe: [ab_tag, cmd_tag], passive: true do
71
71
 
72
72
  log.progname = "replica ##{i}"
@@ -2,10 +2,10 @@ require 'rbtree'
2
2
 
3
3
  ## TODO
4
4
  ##
5
- ## generalize SortedSetSpace to accept params that indicate which fields
5
+ ## generalize SortedSetStore to accept params that indicate which fields
6
6
  ## are key and value
7
7
  ##
8
- ## unify with space used in ../riemann v2, generalize
8
+ ## unify with store used in ../riemann v2, generalize
9
9
 
10
10
  # This is a template class, but it doesn't just match tuples. It can
11
11
  # be used to find the *next* tuple after a given one, using the rbtree
@@ -74,8 +74,8 @@ end
74
74
  # (See memo2.rb.)
75
75
  #
76
76
  # This store also manages command and meta tuples, which it keeps in an array,
77
- # just like the default Tuplespace class does.
78
- class SortedSetSpace
77
+ # just like the default Tuplestore class does.
78
+ class SortedSetStore
79
79
  include Enumerable
80
80
 
81
81
  attr_reader :tag, :tree, :metas
@@ -0,0 +1,45 @@
1
+ # Tags can be any object that can be serialized by msgpack. Strings and
2
+ # numbers are typical, but it can be useful to encode a bit more structure.
3
+ # In this case we can use a complex tag to limit communication to particular
4
+ # client groups (use groups of clients, so
5
+ # that the lifespan of one process does not affect the system as a whole).
6
+
7
+ require 'tupelo/app'
8
+
9
+ Tupelo.application do
10
+ num_tag = "input numbers"#["input numbers", 1]
11
+ str_tag = "input strings"#["input strings", 2]
12
+
13
+ local do
14
+ define_subspace num_tag, [Numeric]
15
+ define_subspace str_tag, [String]
16
+ end
17
+
18
+ child subscribe: num_tag, passive: true do
19
+ read subspace(num_tag) do |num, _|
20
+ write [num.to_s]
21
+ end
22
+ end
23
+
24
+ child subscribe: str_tag, passive: true do
25
+ read subspace(str_tag) do |str, _|
26
+ begin
27
+ num = Integer(str) rescue Float(str)
28
+ write [num]
29
+ rescue
30
+ end
31
+ end
32
+ end
33
+
34
+ local do # subscribed to everything
35
+ write [42]
36
+ write ["17.5"]
37
+
38
+ count = 0
39
+ read do |tuple|
40
+ log tuple
41
+ count += 1
42
+ break if count == 4
43
+ end
44
+ end
45
+ end
@@ -18,7 +18,7 @@ class Tupelo::Client
18
18
  end
19
19
 
20
20
  def tracing?
21
- !!@trace_thread
21
+ defined?(@trace_thread) && !!@trace_thread
22
22
  end
23
23
 
24
24
  private
data/lib/tupelo/app.rb CHANGED
@@ -105,14 +105,14 @@ module Tupelo
105
105
  ez.service :arcd, **arcd_addr do |sv|
106
106
  require 'tupelo/archiver'
107
107
  if persist_dir
108
- require 'tupelo/archiver/persistent-tuplespace'
108
+ require 'tupelo/archiver/persistent-tuplestore'
109
109
  arc = Archiver.new sv, seq: arc_to_seq_sock,
110
- tuplespace: Archiver::PersistentTuplespace,
110
+ tuplestore: Archiver::PersistentTupleStore,
111
111
  persist_dir: persist_dir,
112
112
  cseq: arc_to_cseq_sock, log: log
113
113
  else
114
114
  arc = Archiver.new sv, seq: arc_to_seq_sock,
115
- tuplespace: Archiver::Tuplespace,
115
+ tuplestore: Archiver::TupleStore,
116
116
  cseq: arc_to_cseq_sock, log: log
117
117
  end
118
118
  arc.start
@@ -1,5 +1,5 @@
1
1
  class Tupelo::Archiver
2
- class PersistentTuplespace
2
+ class PersistentTupleStore
3
3
  include Enumerable
4
4
 
5
5
  attr_reader :zero_tolerance
@@ -1,7 +1,7 @@
1
1
  class Tupelo::Archiver
2
- # Faster than the default tuplespace, but does not support some things
2
+ # Faster than the default tuplestore, but does not support some things
3
3
  # that are not needed in the Archiver: template searches, for example.
4
- class Tuplespace
4
+ class TupleStore
5
5
  include Enumerable
6
6
 
7
7
  attr_reader :zero_tolerance
@@ -10,19 +10,19 @@ class Tupelo::Archiver
10
10
  @opts = opts
11
11
  end
12
12
 
13
- def tuplespace
14
- @tuplespace ||= begin
15
- if client.tuplespace.respond_to? :new
16
- client.tuplespace.new **@opts
13
+ def tuplestore
14
+ @tuplestore ||= begin
15
+ if client.tuplestore.respond_to? :new
16
+ client.tuplestore.new **@opts
17
17
  else
18
- client.tuplespace
18
+ client.tuplestore
19
19
  end
20
20
  end
21
21
  end
22
22
 
23
23
  def stop
24
24
  super
25
- tuplespace.flush global_tick
25
+ tuplestore.flush global_tick
26
26
  end
27
27
 
28
28
  def handle_client_request req
@@ -67,8 +67,8 @@ class Tupelo::Archiver
67
67
  raise "Unimplemented" ###
68
68
  when "get range" ### handle this in Funl::HistoryWorker
69
69
  raise "Unimplemented" ###
70
- when GET_TUPLESPACE
71
- send_tuplespace stream, sub_delta
70
+ when GET_TUPLESTORE
71
+ send_tuplestore stream, sub_delta
72
72
  else
73
73
  raise "Unknown operation: #{op.inspect}"
74
74
  end
@@ -94,9 +94,9 @@ class Tupelo::Archiver
94
94
  end
95
95
  end
96
96
 
97
- def send_tuplespace stream, sub_delta
97
+ def send_tuplestore stream, sub_delta
98
98
  log.info {
99
- "send_tuplespace to #{stream.peer_name} " +
99
+ "send_tuplestore to #{stream.peer_name} " +
100
100
  "at tick #{global_tick.inspect} " +
101
101
  (sub_delta ? " with sub_delta #{sub_delta.inspect}" : "")}
102
102
 
@@ -106,7 +106,7 @@ class Tupelo::Archiver
106
106
  ## has to be sent.
107
107
 
108
108
  if sub_delta["request_all"]
109
- tuplespace.each do |tuple, count|
109
+ tuplestore.each do |tuple, count|
110
110
  count.times do ## just dump and send str * count?
111
111
  stream << tuple ## optimize this, and cache the serial
112
112
  ## optimization: use stream.write_to_buffer
@@ -117,7 +117,7 @@ class Tupelo::Archiver
117
117
  tags = sub_delta["request_tags"] ## use set
118
118
  subs = subspaces.select {|sub| tags.include? sub.tag}
119
119
 
120
- tuplespace.each do |tuple, count|
120
+ tuplestore.each do |tuple, count|
121
121
  ## alternately, store tags with tuples (risk if dynamic spaces)
122
122
  if subs.any? {|sub| sub === tuple}
123
123
  count.times do
@@ -9,7 +9,7 @@ require 'funl/history-client'
9
9
  class Tupelo::Archiver < Tupelo::Client; end
10
10
 
11
11
  require 'tupelo/archiver/worker'
12
- require 'tupelo/archiver/tuplespace' ## unless persistent?
12
+ require 'tupelo/archiver/tuplestore' ## unless persistent?
13
13
 
14
14
  module Tupelo
15
15
  class Archiver
@@ -23,11 +23,13 @@ module Tupelo
23
23
  ZERO_TOLERANCE = 1000
24
24
 
25
25
  def initialize server,
26
- tuplespace: Tupelo::Archiver::Tuplespace,
26
+ tuplestore: Tupelo::Archiver::TupleStore,
27
27
  persist_dir: nil, **opts
28
+ ## when ruby does GC symbols, add this:
29
+ ## symbolize_keys: true
28
30
  @server = server
29
31
  @persist_dir = persist_dir
30
- super arc: nil, tuplespace: tuplespace, **opts
32
+ super arc: nil, tuplestore: tuplestore, **opts
31
33
  end
32
34
 
33
35
  # three kinds of requests:
@@ -37,12 +39,12 @@ module Tupelo
37
39
  #
38
40
  # 2. accept tcp/unix socket connection and fork, and then:
39
41
  #
40
- # a. dump subspace matching given templates OR
42
+ # a. dump tuples matching given templates OR
41
43
  #
42
44
  # b. dump all ops in a given range of the global sequence
43
45
  # matching given templates
44
46
  #
45
- # the fork happens when tuplespace is consistent; we
47
+ # the fork happens when tuplestore is consistent; we
46
48
  # do this by passing cmd to worker thread, with conn
47
49
  class ForkRequest
48
50
  attr_reader :io
@@ -3,18 +3,21 @@ require 'tupelo/client/common'
3
3
  class Tupelo::Client
4
4
  # Include into class that defines #worker and #log.
5
5
  module Api
6
- NOT_META = proc {|t| not defined? t.key? or not t.key? TUPELO_META_KEY}
6
+ def not_meta
7
+ k = tupelo_meta_key
8
+ @not_meta ||= proc {|t| not defined? t.key? or not t.key? k}
9
+ end
7
10
 
8
11
  # If no block given, return one matching tuple, blocking if necessary.
9
12
  # If block given, yield each matching tuple that is found
10
- # locally and then yield each new match as it is written to the space.
13
+ # locally and then yield each new match as it is written to the store.
11
14
  # Guaranteed not to miss tuples, even if they arrive and are immediately
12
15
  # taken. (Note that simply doing read(template) in a loop would not
13
16
  # have this guarantee.)
14
- # The template defaults to NOT_META, which matches any tuple except metas.
17
+ # The template defaults to not_meta, which matches any tuple except metas.
15
18
  # The first phase of this method, reading existing tuples, is essentially
16
19
  # the same as read_all, and subject to the same warnings.
17
- def read_wait template = NOT_META
20
+ def read_wait template = not_meta
18
21
  waiter = Waiter.new(worker.make_template(template), self, !block_given?)
19
22
  worker << waiter
20
23
  if block_given?
@@ -31,15 +34,15 @@ class Tupelo::Client
31
34
  end
32
35
  alias read read_wait
33
36
 
34
- # The template defaults to NOT_META, which matches any tuple except metas.
35
- def read_nowait template = NOT_META
37
+ # The template defaults to not_meta, which matches any tuple except metas.
38
+ def read_nowait template = not_meta
36
39
  matcher = Matcher.new(worker.make_template(template), self)
37
40
  worker << matcher
38
41
  matcher.wait
39
42
  end
40
43
 
41
- # Returns all matching tuples currently in the space. Does not wait for more
42
- # tuples to arrive. The template defaults to NOT_META, which matches any
44
+ # Returns all matching tuples currently in the store. Does not wait for more
45
+ # tuples to arrive. The template defaults to not_meta, which matches any
43
46
  # tuple except metas. To read all matches of more than one template, use
44
47
  # the #or method from util/boolean.rb.
45
48
  # Matches are guaranteed to exist at the same tick (even if they no longer
@@ -47,11 +50,11 @@ class Tupelo::Client
47
50
  # blocked from all other activity for some time, so be careful about
48
51
  # using read_all when large numbers of tuples match. If a block is given,
49
52
  # it runs after the worker has unblocked.
50
- def read_all template = NOT_META
53
+ def read_all template = not_meta
51
54
  matcher = Matcher.new(worker.make_template(template), self, :all => true)
52
55
  worker << matcher
53
56
  a = []
54
- while tuple = matcher.wait ## inefficient?
57
+ while tuple = matcher.wait ## inefficient to wait one at a time?
55
58
  yield tuple if block_given?
56
59
  a << tuple
57
60
  end
@@ -1,11 +1,19 @@
1
1
  class Tupelo::Client
2
2
  module Api
3
3
  TUPELO_SUBSPACE_TAG = "tupelo subspace".freeze
4
- TUPELO_META_KEY = "__tupelo__".freeze
4
+
5
+ def tupelo_subspace_tag
6
+ @tupelo_subspace_tag ||=
7
+ symbolize_keys ? TUPELO_SUBSPACE_TAG.to_sym : TUPELO_SUBSPACE_TAG
8
+ end
9
+
10
+ def tupelo_meta_key
11
+ @tupelo_meta_key ||= symbolize_keys ? :__tupelo__ : "__tupelo__".freeze
12
+ end
5
13
 
6
14
  def define_subspace tag, template, addr: nil
7
15
  metatuple = {
8
- TUPELO_META_KEY => "subspace",
16
+ tupelo_meta_key => "subspace",
9
17
  tag: tag,
10
18
  template: PortableObjectTemplate.spec_from(template),
11
19
  addr: addr
@@ -19,7 +27,7 @@ class Tupelo::Client
19
27
  def use_subspaces!
20
28
  return if find_subspace_by_tag(TUPELO_SUBSPACE_TAG)
21
29
  define_subspace(TUPELO_SUBSPACE_TAG, {
22
- TUPELO_META_KEY => "subspace",
30
+ tupelo_meta_key => "subspace",
23
31
  tag: nil,
24
32
  template: nil,
25
33
  addr: nil
@@ -30,7 +38,7 @@ class Tupelo::Client
30
38
  tag = tag.to_s
31
39
  find_subspace_by_tag(tag) or begin
32
40
  if subscribed_tags.include? tag
33
- read TUPELO_META_KEY => "subspace",
41
+ read tupelo_meta_key => "subspace",
34
42
  tag: tag,
35
43
  template: nil,
36
44
  addr: nil