andromeda 0.1 → 0.1.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.
@@ -0,0 +1,98 @@
1
+ module Andromeda
2
+
3
+ module Guides
4
+
5
+ class Guide
6
+ include Andromeda::Impl::To_S
7
+
8
+ def track(spot, label, suggested_track = nil)
9
+ raise NoMethodError
10
+ end
11
+
12
+ def provision(track, label, tags_in)
13
+ tags_out = Hash.new
14
+ tags_out[:scope] = ::Andromeda::Atom::Region.new unless tags_in[:scope]
15
+ tags_out[:label] = label
16
+ tags_out
17
+ end
18
+
19
+ def pack(plan, track, was_suggested = false)
20
+ if plan.frozen? then plan else plan.identical_copy end
21
+ end
22
+
23
+ end
24
+
25
+ class Track
26
+ include Andromeda::Impl::To_S
27
+
28
+ def follow(scope, *args, &thunk)
29
+ scope.enter
30
+ begin
31
+ thunk.call *args
32
+ ensure
33
+ scope.leave
34
+ end
35
+ end
36
+ end
37
+
38
+ class LocalGuide < Guide
39
+ include Singleton
40
+
41
+ def track(spot, key, suggested_track = nil)
42
+ return suggested_track if suggested_track
43
+ LocalTrack.instance
44
+ end
45
+ end
46
+
47
+ class LocalTrack < Track
48
+ include Singleton
49
+ end
50
+
51
+ module DispatchingTrack
52
+
53
+ def follow(scope, *args, &thunk) ; dispatch(scope, *args, &thunk) end
54
+
55
+ def process(&thunk)
56
+ thunk.call
57
+ end
58
+
59
+ def dispatch(scope, *args, &thunk)
60
+ scope.enter
61
+ begin
62
+ process do
63
+ begin
64
+ thunk.call *args
65
+ ensure
66
+ scope.leave
67
+ end
68
+ end
69
+ rescue
70
+ # In case Thread.new fails
71
+ scope.leave
72
+ raise
73
+ end
74
+ end
75
+
76
+ protected :process
77
+ protected :dispatch
78
+ end
79
+
80
+ class SpawnGuide < Guide
81
+ include Singleton
82
+
83
+ def track(spot, label, suggested_track = nil)
84
+ SpawnTrack.instance
85
+ end
86
+ end
87
+
88
+ class SpawnTrack < Track
89
+ include Singleton
90
+ include DispatchingTrack
91
+
92
+ protected
93
+
94
+ def process(&thunk) ; Thread.new { || Thread.current; thunk.call } end
95
+ end
96
+
97
+ end
98
+ end
@@ -1,92 +1,19 @@
1
1
  module Andromeda
2
2
 
3
- # Generator for random xorable ids (used in marks)
4
- class Id
5
- # Default length if generated ids
6
- NUM_BYTES = 12
3
+ class Id < Impl::XorId
4
+ # Default length of generated ids
5
+ DEFAULT_NUM_BYTES = 8
7
6
 
8
- protected
7
+ # @param [Bool] random
8
+ def initialize(random = true)
9
+ super DEFAULT_NUM_BYTES, random
10
+ end
9
11
 
10
- def initialize(len = NUM_BYTES, random = true, init_data = nil)
11
- raise ArgumentError unless len.kind_of?(Fixnum)
12
- raise ArgumentError unless len >= 0
13
-
14
- @data = if init_data
15
- init_data
16
- else
17
- if random
18
- then len.times.map { Id.rnd_byte }
19
- else len.times.map { 0 } end
20
- end
21
- end
22
-
23
- public
24
-
25
- def length ; @data.length end
26
-
27
- def zero?
28
- each { |b| return false unless b == 0 }
29
- true
30
- end
31
-
32
- def each ; this = self ; 0.upto(length-1).each { |i| yield this[i] } end
33
-
34
- def each_with_index ; this = self ; 0.upto(length-1).each { |i| yield i, this[i] } end
35
-
36
- def zip_bytes(b)
37
- return ArgumentError unless same_id_kind?(b)
38
- a = self
39
- 0.upto(length-1).each { |i| yield a[i], b[i] }
40
- end
41
- def [](key) ; @data[key] end
42
-
43
- def same_id_kind?(obj) ; obj.kind_of?(Id) && obj.length == self.length end
44
-
45
- # Compare self to b
46
- # @param [Id] b
47
- def eq?(b)
48
- zip_bytes(b) { |i,j| return false if i != j }
49
- true
50
- end
51
-
52
- alias_method :==, :eq?
53
-
54
- # xor self and b's ids component-wise
55
- # @param [Array<Fixnum>] b
56
- # @return [Id]
57
- def xor(b)
58
- r = []
59
- zip_bytes(b) { |i,j| r << (i ^ j) }
60
- Id.new r.length, false, r
61
- end
62
-
63
- def to_s
64
- r = "#<#{self.class}:"
65
- each { |b| r << Id.twochars(b.to_s(16)) }
66
- r << '>'
67
- r
68
- end
69
-
70
- # @param [Fixnum] length
71
- # @return [Id] random id
72
- def self.gen(length = NUM_BYTES) ; Id.new length, true end
73
-
74
- # @param [Fixnum] length
75
12
  # @return [Id] empty (zero) id
76
- def self.zero(length = NUM_BYTES) ; Id.new length, false end
77
-
78
- private
79
-
80
- def self.rnd_byte ; Random.rand(256) end
81
-
82
- def self.twochars(s)
83
- case s.length
84
- when 0 then '00'
85
- when 1 then "0#{s}"
86
- else s
87
- end
13
+ def self.zero
14
+ @id = self.new false unless defined? @id
15
+ @id
88
16
  end
89
-
90
17
  end
91
18
 
92
19
  end
@@ -0,0 +1,47 @@
1
+ module Andromeda
2
+
3
+ module Impl
4
+
5
+ class Atom < Atomic
6
+ include To_S
7
+
8
+ def initialize(init_val = nil)
9
+ super init_val
10
+ end
11
+
12
+ def empty? ; value.nil? end
13
+ def full? ; ! value.nil? end
14
+
15
+ def to_short_s ; "(#{To_S.short_s(value)})" end
16
+ alias_method :inspect, :to_s
17
+
18
+ def wait_while(&test)
19
+ while test.call(value) ; Thread::pass end
20
+ end
21
+
22
+ def wait_until_eq(val = nil)
23
+ raise ArgumentError unless val.kind_of?(Fixnum)
24
+ wait_while { |v| v != val }
25
+ end
26
+
27
+ def wait_until_ne(val = nil)
28
+ raise ArgumentError unless val.kind_of?(Fixnum)
29
+ wait_while { |v| v == val }
30
+ end
31
+
32
+ def wait_until_empty?
33
+ wait_until_eq nil
34
+ end
35
+
36
+ def wait_until_full?
37
+ wait_until_ne nil
38
+ end
39
+
40
+ def with_value
41
+ update { |v| yield v ; v }
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,31 @@
1
+ module Andromeda
2
+
3
+ module Impl
4
+
5
+ module ClassAttr
6
+
7
+ protected
8
+
9
+ def get_attr_set(var_name, inherit = true)
10
+ s = if instance_variable_defined?(var_name)
11
+ then instance_variable_get(var_name)
12
+ else Set.new end
13
+ if inherit
14
+ c = self
15
+ while (c = c.superclass)
16
+ s = s.union c.get_attr_set(var_name, false) rescue s
17
+ end
18
+ end
19
+ return s
20
+ end
21
+
22
+ def name_attr_set(var_name, *names)
23
+ name_set = names.to_set
24
+ dest_set = get_attr_set var_name, false
25
+ instance_variable_set var_name, dest_set.union(name_set)
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,219 @@
1
+
2
+ module Andromeda
3
+
4
+ module Impl
5
+
6
+ class ProtoPlan < Impl::ConnectorBase
7
+ include Impl::To_S
8
+ extend Impl::ClassAttr
9
+
10
+ def self.spot_names(inherit = true) ; get_attr_set '@spot_names', inherit end
11
+ def self.attr_spot_names(inherit = true) ; get_attr_set '@attr_spot_names', inherit end
12
+ def self.meth_spot_names(inherit = true) ; get_attr_set '@meth_spot_names', inherit end
13
+ def self.name_spot(*names) ; name_attr_set '@spot_names', *names end
14
+
15
+ def self.meth_spot(name, opts = {})
16
+ name_spot name
17
+ name_attr_set '@meth_spot_names', name
18
+ define_method :"#{name}" do ||
19
+ mk_spot name, opts = {}
20
+ end
21
+ end
22
+
23
+ def self.attr_spot(*names)
24
+ name_spot *names
25
+ name_attr_set '@attr_spot_names', *names
26
+ attr_writer *names
27
+ names.each do |name|
28
+ define_method :"#{name}" do ||
29
+ intern (instance_variable_get("@#{name}"))
30
+ end
31
+ define_method :"#{name}=" do |val|
32
+ if val
33
+ then instance_variable_set "@#{name}", intern(val.entry)
34
+ else instance_variable_set "@#{name}", nil end
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.signal_names(inherit = true) ; get_attr_set '@signal_names', inherit end
40
+ def self.signal_spot(*names) ; name_attr_set '@signal_names', *names end
41
+
42
+ attr_reader :id
43
+ attr_reader :guide
44
+
45
+ def initialize(config = {})
46
+ @id = Id.new
47
+ set_from_config init_from_config, config
48
+ @tags ||= {}
49
+ @guide ||= init_guide
50
+ end
51
+
52
+ def initialize_copy(other)
53
+ @tags = @tags.identical_copy
54
+ @tags ||= {}
55
+ end
56
+
57
+ def init_guide ; Guides::DefaultGuide.instance end
58
+
59
+ def tags ; @tags end
60
+ def to_short_s ; " id=#{id.to_short_s}t" end
61
+ alias_method :inspect, :to_s
62
+
63
+ def guide=(new_guide)
64
+ new_guide = new_guide.instance if new_guide.is_a?(Class) && new_guide.include?(Singleton)
65
+ @guide = new_guide
66
+ end
67
+
68
+ # Overload to map all incoming data, default to data
69
+ def data_map(name, data) ; data end
70
+
71
+ # Overload to extract the data key from mapped, incoming data, defaults to name
72
+ def data_key(name, data) ; name end
73
+
74
+ # Overload to determine the target spot name from the key, defaults to name
75
+ def key_spot(name, key) ; name end
76
+
77
+ # Overload to determine the target track label from the key, defaults to ket
78
+ def key_label(name, key) ; key end
79
+
80
+ # Overload to extract the data value from mapped, incoming data, defaults to data
81
+ def data_val(name, data) ; data end
82
+
83
+ # Overload to compute additional tags
84
+ def data_tag(name, key, val, tags_in) ; { name: name } end
85
+
86
+ # Overload to filter the data events that should be processed, defaults to true
87
+ def selects?(name, key, val, tags_in) ; true end
88
+
89
+ def post_data(spot_, track_in, data, tags_in = {})
90
+ raise ArgumentError, "#{spot_} is not a Spot" unless spot_.is_a?(Spot)
91
+ raise ArgumentError, "#{spot_} is not a Spot of this Plan" unless spot_.plan == self
92
+
93
+ name = spot_.name
94
+ details = { name: name, data: data, tags_in: tags_in, spot: spot_ }
95
+ begin
96
+ data = data_map name, data
97
+ key = data_key name, data
98
+ name = key_spot name, key
99
+
100
+ guide_ = guide
101
+ label = key_label name, key
102
+ details[:label] = label
103
+ track_ = guide_.track spot_, label, track_in
104
+ details[:track] = track_
105
+
106
+ value = data_val name, data
107
+ details[:val] = value
108
+ tags_in.update data_tag name, key, value, tags_in
109
+ tags_in.update guide_.provision track_, label, tags_in
110
+
111
+ pack_ = guide_.pack self, track_, track_.equal?(track_in)
112
+ details[:pack] = pack_
113
+ meth = pack_.method :"on_#{name}"
114
+
115
+ if selects? name, key, value, tags_in
116
+ pack_.transport_data name, track_, meth, key, value, tags_in
117
+ end
118
+ rescue Exception => e
119
+ raise SendError, InfoMsg.str('send_data failed', details, e), e.backtrace
120
+ end
121
+ end
122
+
123
+ # @return [Spot, nil] *public* spot with name name if any, nil otherwise
124
+ #
125
+ def public_spot(name)
126
+ raise ArgumentError, "#{name} is not a Symbol" unless name.is_a? Symbol
127
+ raise ArgumentError, "#{name} is not a known spot name" unless spot_name? name
128
+
129
+ if respond_to?(name)
130
+ then intern public_send(name)
131
+ else nil
132
+ end
133
+ end
134
+
135
+ def spot_name?(name) ; spot_names.include? name end
136
+ def attr_spot_name?(name) ; attr_spot_names.include? name end
137
+ def meth_spot_name?(name) ; meth_spot_names.include? name end
138
+ def signal_name?(name) ; signal_names.include? name end
139
+
140
+ def spot_names ; self.class.spot_names end
141
+ def attr_spot_names ; self.class.attr_spot_names end
142
+ def meth_spot_names ; self.class.meth_spot_names end
143
+ def signal_names ; self.class.signal_names end
144
+
145
+ def current_scope ; tags[:scope] end
146
+ def current_name ; tags[:name] end
147
+
148
+ def >>(spot) ; @emit = spot.entry ; spot.dest end
149
+
150
+ def entry ; enter end
151
+ def dest ; emit end
152
+ def mute ; @emit = nil end
153
+ def via(spot_name) ; entry.via(spot_name) end
154
+
155
+ def post_to(track, data, tags_in = {}) ; start.post_to track, data, tags_in end
156
+
157
+ protected
158
+
159
+ attr_spot :emit
160
+
161
+ def exit ; emit end
162
+
163
+ def init_from_config ; [:readers, :writers] end
164
+
165
+ def set_from_config(what, config = {})
166
+ init_readers = what.include? :readers
167
+ init_writers = what.include? :writers
168
+ config.each_pair do |k, v|
169
+ k = k.to_sym rescue nil
170
+ if init_writers
171
+ writer = :"#{k}=" rescue nil
172
+ if writer && self.respond_to?(writer)
173
+ then self.send writer, v
174
+ else instance_variable_set "@#{k}", v if init_readers && self.respond_to?(k) end
175
+ else
176
+ instance_variable_set "@#{k}", v if init_readers && self.respond_to?(k)
177
+ end
178
+ end
179
+ end
180
+
181
+ # Call local method spot with name spot_name with key and val without any preprocessing
182
+ #
183
+ # Requires that spot_name resolves to a spot of this plan
184
+ #
185
+ def call_local(spot_name, key, val)
186
+ spot_ = spot spot_name
187
+ raise ArgumentError, "#{name} could not be resolved to a Spot" unless spot_
188
+ raise ArgumenError, "Cannot call_local for other Plans" unless spot_.plan == self
189
+ send :"on_#{spot_name}", key, val
190
+ end
191
+
192
+ # @return [Spot, nil] *public* spot with name name if any, nil otherwise
193
+ #
194
+ def spot(name)
195
+ raise ArgumentError, "#{name} is not a Symbol" unless name.is_a? Symbol
196
+ raise ArgumentError, "#{name} is not a known spot name" unless spot_name? name
197
+
198
+ if respond_to?(name)
199
+ then intern send(name)
200
+ else nil
201
+ end
202
+ end
203
+
204
+ def mk_spot(name, opts = {})
205
+ Spot.new self, name, self, opts[:dest]
206
+ end
207
+
208
+ def intern(spot)
209
+ return nil unless spot
210
+ raise ArgumentError unless spot.is_a? Spot
211
+ spot.intern(self)
212
+ end
213
+
214
+ def signal_error(e) ; raise e end
215
+ end
216
+
217
+ end
218
+
219
+ end