andromeda 0.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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