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.
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +17 -9
- data/Gemfile.lock +5 -0
- data/LICENSE.txt +1 -2
- data/README.md +150 -15
- data/ROADMAP.md +73 -0
- data/Rakefile +4 -6
- data/andromeda.gemspec +2 -2
- data/lib/andromeda.rb +52 -9
- data/lib/andromeda/atom.rb +105 -0
- data/lib/andromeda/cmd.rb +242 -0
- data/lib/andromeda/common.rb +0 -0
- data/lib/andromeda/copy_clone.rb +44 -0
- data/lib/andromeda/error.rb +42 -0
- data/lib/andromeda/guide.rb +50 -0
- data/lib/andromeda/guide_track.rb +98 -0
- data/lib/andromeda/id.rb +10 -83
- data/lib/andromeda/impl/atom.rb +47 -0
- data/lib/andromeda/impl/class_attr.rb +31 -0
- data/lib/andromeda/impl/proto_plan.rb +219 -0
- data/lib/andromeda/impl/to_s.rb +48 -0
- data/lib/andromeda/impl/xor_id.rb +89 -0
- data/lib/andromeda/kit.rb +172 -0
- data/lib/andromeda/map_reduce.rb +3 -0
- data/lib/andromeda/plan.rb +130 -0
- data/lib/andromeda/pool_guide.rb +70 -0
- data/lib/andromeda/spot.rb +132 -0
- data/lib/andromeda/sugar.rb +41 -0
- data/lib/andromeda/sync.rb +68 -0
- data/lib/andromeda/version.rb +1 -1
- data/yard_extensions/andromeda.rb +28 -0
- metadata +30 -13
- data/lib/andromeda/andromeda.rb +0 -225
- data/lib/andromeda/commando.rb +0 -106
- data/lib/andromeda/helpers.rb +0 -134
- data/lib/andromeda/join.rb +0 -48
- data/lib/andromeda/pools.rb +0 -69
- data/lib/andromeda/scope.rb +0 -38
@@ -0,0 +1,48 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
module To_S
|
6
|
+
|
7
|
+
def to_s(short = false)
|
8
|
+
if short
|
9
|
+
to_short_s
|
10
|
+
else
|
11
|
+
super_str = super()
|
12
|
+
class_name = self.class.name.split('::')[-1]
|
13
|
+
obj_id = object_id.to_s(16)
|
14
|
+
"\#<#{class_name}:0x#{obj_id}#{to_s(true)}>"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_short_s ; '' end
|
19
|
+
|
20
|
+
def self.short_s(v = value)
|
21
|
+
return ":#{v}" if v.is_a?(Symbol)
|
22
|
+
return "'#{v}'" if v.is_a?(String)
|
23
|
+
return 'nil' unless v
|
24
|
+
"#{v}"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
# Shared base class of Spot and Impl::PrePlan
|
30
|
+
class ConnectorBase
|
31
|
+
# @return [Spot] entry.intern nil
|
32
|
+
def start ; entry.intern(nil) end
|
33
|
+
|
34
|
+
# post_to nil, data, tags_in
|
35
|
+
#
|
36
|
+
# @return [self]
|
37
|
+
def post(data, tags_in = {}) ; post_to nil, data, tags_in end
|
38
|
+
|
39
|
+
alias_method :<<, :post
|
40
|
+
|
41
|
+
# post_to LocalTrack.instance, data, tags_in
|
42
|
+
#
|
43
|
+
# @return [self]
|
44
|
+
def post_local(data, tags_in = {}) ; post_to Guides::LocalTrack.instance, data, tags_in end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
module Impl
|
4
|
+
|
5
|
+
# Generator for random xorable ids (used for markers)
|
6
|
+
class XorId
|
7
|
+
include To_S
|
8
|
+
|
9
|
+
protected
|
10
|
+
|
11
|
+
def initialize(len, random = true, init_data = nil)
|
12
|
+
raise ArgumentError unless len.kind_of?(Fixnum)
|
13
|
+
raise ArgumentError unless len >= 0
|
14
|
+
|
15
|
+
@data = if init_data
|
16
|
+
init_data
|
17
|
+
else
|
18
|
+
if random
|
19
|
+
then len.times.map { Id.rnd_byte }
|
20
|
+
else len.times.map { 0 } end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
public
|
25
|
+
|
26
|
+
def clone_to_copy? ; false end
|
27
|
+
def identical_copy ; self end
|
28
|
+
|
29
|
+
def length ; @data.length end
|
30
|
+
def zero? ; each { |b| return false unless b == 0 } ; true end
|
31
|
+
|
32
|
+
def [](key) ; @data[key] end
|
33
|
+
|
34
|
+
def same_length?(obj) ; self.length == obj.length end
|
35
|
+
|
36
|
+
def each ; this = self ; 0.upto(length-1).each { |i| yield this[i] } end
|
37
|
+
def each_with_index ; this = self ; 0.upto(length-1).each { |i| yield i, this[i] } end
|
38
|
+
|
39
|
+
# Compare self to b
|
40
|
+
# @param [Id] b
|
41
|
+
def ==(b)
|
42
|
+
return true if self.equal? b
|
43
|
+
return false if b.nil?
|
44
|
+
return false unless b.class.equal? self.class
|
45
|
+
return false unless same_length? b
|
46
|
+
zip_bytes(b) { |i, j| return false if i != j }
|
47
|
+
true
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash ; @data.hash end
|
51
|
+
|
52
|
+
# xor self and b's ids component-wise
|
53
|
+
# @param [Array<Fixnum>] b
|
54
|
+
# @return [Id]
|
55
|
+
def xor(b)
|
56
|
+
r = []
|
57
|
+
zip_bytes(b) { |i,j| r << (i ^ j) }
|
58
|
+
Id.new r.length, false, r
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_short_s
|
62
|
+
r = ''
|
63
|
+
each { |b| r << Id.two_char_hex_str(b.to_s(16)) }
|
64
|
+
r
|
65
|
+
end
|
66
|
+
|
67
|
+
def inspect ; to_s end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def zip_bytes(b)
|
72
|
+
a = self
|
73
|
+
0.upto(length-1).each { |i| yield a[i], b[i] }
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.rnd_byte ; Random.rand(256) end
|
77
|
+
|
78
|
+
def self.two_char_hex_str(s)
|
79
|
+
case s.length
|
80
|
+
when 0 then '00'
|
81
|
+
when 1 then "0#{s}"
|
82
|
+
else s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
module Kit
|
4
|
+
|
5
|
+
module Transf
|
6
|
+
attr_accessor :filter
|
7
|
+
attr_accessor :mapper
|
8
|
+
|
9
|
+
def deliver_data(name, meth, key, val, tags_in)
|
10
|
+
if signal_name?(name)
|
11
|
+
super name, meth, key, val, tags_in
|
12
|
+
else
|
13
|
+
filter_ = filter
|
14
|
+
mapper_ = mapper
|
15
|
+
if !(filter_ && filter_.call(val))
|
16
|
+
super name, meth, key, (if mapper_ then mapper_.call(val) else val end), tags_in
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Tee < Plan
|
23
|
+
attr_accessor :level
|
24
|
+
attr_accessor :other
|
25
|
+
attr_accessor :delay
|
26
|
+
|
27
|
+
def initialize(config = {})
|
28
|
+
config = { nick: config } unless config.is_a? Hash || config.is_a?(Spot)
|
29
|
+
config = { other: config } unless config.is_a? Hash
|
30
|
+
super config
|
31
|
+
|
32
|
+
@level ||= :info
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_copy(other)
|
36
|
+
@level = other.level.identical_copy
|
37
|
+
@delay = other.delay.identical_copy
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_enter(key, val)
|
41
|
+
log_ = log
|
42
|
+
level_ = level
|
43
|
+
sleep delay.to_i if delay
|
44
|
+
if log_ && level_
|
45
|
+
cur_name = current_name
|
46
|
+
key_str = Andromeda::Impl::To_S.short_s key
|
47
|
+
val_str = Andromeda::Impl::To_S.short_s val
|
48
|
+
log_str = "#{to_s}.#{cur_name}(#{key_str}, #{val_str})"
|
49
|
+
tags.each_pair { |k, v| log_str << " #{k}=#{Andromeda::Impl::To_S.short_s(v)}" }
|
50
|
+
log_str << " tid=0x#{Thread.current.object_id.to_s(16)}"
|
51
|
+
log_.send level, log_str
|
52
|
+
end
|
53
|
+
other_ = other
|
54
|
+
other_ << val if other_
|
55
|
+
super key, val
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Targeting < Plan
|
60
|
+
attr_accessor :targets
|
61
|
+
|
62
|
+
def initialize(config = {})
|
63
|
+
super config
|
64
|
+
@targets ||= {}
|
65
|
+
end
|
66
|
+
|
67
|
+
def target_values ; t = targets ; t.values rescue t end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Broadc < Targeting
|
71
|
+
def on_enter(key, val)
|
72
|
+
target_values { |t| intern(t) << val rescue nil }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class Switch < Targeting
|
77
|
+
def on_enter(key, val)
|
78
|
+
(intern(key) rescue exit) << val rescue nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class SinglePlan < Plan
|
83
|
+
def init_guide ; Guides::SinglePoolGuide.new end
|
84
|
+
end
|
85
|
+
|
86
|
+
class InlineKeyRouter < SinglePlan
|
87
|
+
def key_spot(name, key) ; key end
|
88
|
+
end
|
89
|
+
|
90
|
+
class Gatherer < SinglePlan
|
91
|
+
include Transf
|
92
|
+
end
|
93
|
+
|
94
|
+
class Reducer < Gatherer
|
95
|
+
attr_accessor :state
|
96
|
+
attr_accessor :reducer
|
97
|
+
|
98
|
+
attr_spot :new_state
|
99
|
+
|
100
|
+
def on_enter(key, val)
|
101
|
+
reducer_ = reducer
|
102
|
+
|
103
|
+
state_ = state
|
104
|
+
new_ = reducer_.call state_, key, val
|
105
|
+
unless new_ == state_
|
106
|
+
state = new_
|
107
|
+
new_state << state if new_state
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class FileReader < Plan
|
113
|
+
attr_reader :path
|
114
|
+
attr_reader :mode
|
115
|
+
|
116
|
+
def initialize(config = {})
|
117
|
+
super config
|
118
|
+
@mode ||= init_mode
|
119
|
+
end
|
120
|
+
|
121
|
+
def data_tag(name, key, val, tags_in)
|
122
|
+
tags_out = super
|
123
|
+
tags_out[:first] = val.first rescue 0
|
124
|
+
tags_out[:last] = val.last rescue -1
|
125
|
+
tags_out
|
126
|
+
end
|
127
|
+
|
128
|
+
def init_mode ; 'r' end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
def on_enter(key, val)
|
133
|
+
file = File.open path, mode
|
134
|
+
begin
|
135
|
+
file.seek tags[:first]
|
136
|
+
tags[:last] = file.size - 1 if tags[:last] < 0
|
137
|
+
tags[:num] = tags[:last] - tags[:first]
|
138
|
+
if block_given? then yield file else super key, val end
|
139
|
+
ensure
|
140
|
+
file.close
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
class FileChunker < FileReader
|
147
|
+
attr_reader :num_chunks
|
148
|
+
|
149
|
+
def initialize(config = {})
|
150
|
+
super config
|
151
|
+
@num_chunks ||= Guides::PoolGuide.num_procs
|
152
|
+
end
|
153
|
+
|
154
|
+
def on_enter(key, val)
|
155
|
+
num_c = num_chunks
|
156
|
+
super key, val do |f|
|
157
|
+
fst = tags[:first]
|
158
|
+
lst = tags[:last]
|
159
|
+
sz = tags[:num] / num_c rescue 1
|
160
|
+
sz = 1 if sz < 0
|
161
|
+
while fst <= lst
|
162
|
+
nxt = fst + sz
|
163
|
+
nxt = lst if nxt > lst
|
164
|
+
exit << Range.new(fst, nxt)
|
165
|
+
fst = nxt + 1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
class Plan < Impl::ProtoPlan
|
4
|
+
|
5
|
+
meth_spot :enter
|
6
|
+
attr_spot :errors
|
7
|
+
|
8
|
+
signal_spot :errors
|
9
|
+
|
10
|
+
attr_accessor :log
|
11
|
+
attr_accessor :marker
|
12
|
+
attr_accessor :nick
|
13
|
+
|
14
|
+
attr_accessor :error_level
|
15
|
+
|
16
|
+
attr_accessor :trace_enter
|
17
|
+
attr_accessor :trace_exit
|
18
|
+
|
19
|
+
|
20
|
+
def initialize(config = {})
|
21
|
+
super config
|
22
|
+
@trace_enter ||= init_trace_hash :enter
|
23
|
+
@trace_exit ||= init_trace_hash :emit
|
24
|
+
@error_level ||= :error
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_copy(other)
|
28
|
+
super other
|
29
|
+
@trace_enter = other.trace_enter.identical_copy
|
30
|
+
@trace_exit = other.trace_exit.identical_copy
|
31
|
+
@error_level = other.error_level.identical_copy
|
32
|
+
@nick = other.nick.identical_copy
|
33
|
+
end
|
34
|
+
|
35
|
+
def tap ; yield self end
|
36
|
+
|
37
|
+
def log ; @log = DefaultLogger.instance end
|
38
|
+
def mark ; @mark = Id.zero unless @mark ; @mark end
|
39
|
+
|
40
|
+
def on_enter(k, v)
|
41
|
+
exit_ = exit
|
42
|
+
exit_ << v if exit_
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_short_s
|
46
|
+
super_ = super()
|
47
|
+
nick_ = nick
|
48
|
+
if nick_
|
49
|
+
then "#{super_} aka: #{Impl::To_S.short_s(nick_)}"
|
50
|
+
else super_ end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def transport_data name, track, meth, key, val, tags_in
|
56
|
+
scope = tags_in[:scope]
|
57
|
+
enter_level = trace_level trace_enter, name
|
58
|
+
exit_level = trace_level trace_exit, name
|
59
|
+
details = { name: name, plan: self, track: track, key: key, val: val }
|
60
|
+
track.follow(scope) do
|
61
|
+
begin
|
62
|
+
trace :enter, enter_level, name, details if enter_level
|
63
|
+
deliver_data name, meth, key, val, tags_in
|
64
|
+
trace :exit, exit_level, name, details if exit_level
|
65
|
+
rescue Exception => e
|
66
|
+
uncaught_exception name, key, val, e
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def init_trace_hash(kind) ; {} end
|
72
|
+
|
73
|
+
def trace_level(h, name) ; if h.is_a?(Symbol) then h else (h[name] rescue nil) end end
|
74
|
+
|
75
|
+
def trace(kind, level, name, details)
|
76
|
+
log.send level, InfoMsg.str("TRACE :#{kind} :#{name}", details) if log
|
77
|
+
end
|
78
|
+
|
79
|
+
def deliver_data(name, meth, k, v, tags_in)
|
80
|
+
update_mark
|
81
|
+
tags.update tags_in
|
82
|
+
meth.call k, v
|
83
|
+
end
|
84
|
+
|
85
|
+
def signal_error(e)
|
86
|
+
errors_ = errors
|
87
|
+
errors_ << e if errors_
|
88
|
+
end
|
89
|
+
|
90
|
+
def signal_uncaught(e)
|
91
|
+
log.send error_level, e rescue nil
|
92
|
+
signal_error e
|
93
|
+
end
|
94
|
+
|
95
|
+
def reset_mark(new_mark)
|
96
|
+
tags[:mark] = if new_mark then new_mark else Id.zero end
|
97
|
+
end
|
98
|
+
|
99
|
+
def mark ; tags[:mark] end
|
100
|
+
|
101
|
+
def check_mark
|
102
|
+
mark_ = mark
|
103
|
+
raise RuntimeError, 'Invalid mark' if mark_ && !mark_.zero?
|
104
|
+
end
|
105
|
+
|
106
|
+
def update_mark
|
107
|
+
marker_ = marker
|
108
|
+
if marker_ && !marker_.zero?
|
109
|
+
mark_ = mark
|
110
|
+
if mark_
|
111
|
+
then reset_mark mark_.xor(marker_)
|
112
|
+
else reset_mark mark_ end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def uncaught_exception(name, key, value, e)
|
117
|
+
begin
|
118
|
+
details = { name: name, key: key, val: value, cause: e }
|
119
|
+
info = InfoMsg.str 'Uncaught exception', details
|
120
|
+
err = ExecError.new(info)
|
121
|
+
err.set_backtrace e.backtrace
|
122
|
+
signal_uncaught e
|
123
|
+
rescue Exception => e2
|
124
|
+
details[:cause] = e2
|
125
|
+
log.error InfoMsg.str('Failure handling uncaught exception', details) rescue nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Andromeda
|
2
|
+
|
3
|
+
module Guides
|
4
|
+
|
5
|
+
class PoolGuide < Guide
|
6
|
+
attr_reader :max_procs
|
7
|
+
attr_reader :pool_track
|
8
|
+
|
9
|
+
# @return [Fixnum] number of processors as determined by Facter
|
10
|
+
def self.num_procs
|
11
|
+
@num_procs = Facter.sp_number_processors.strip.to_i unless defined?(@num_procs)
|
12
|
+
@num_procs
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(num_procs = nil)
|
16
|
+
num_procs = PoolGuide.num_procs unless num_procs
|
17
|
+
raise ArgumentError unless num_procs.is_a?(Fixnum)
|
18
|
+
raise ArgumentError unless num_procs > 0
|
19
|
+
@max_procs = num_procs
|
20
|
+
@pool_track = PoolTrack.new ThreadPool.new(@max_procs)
|
21
|
+
end
|
22
|
+
|
23
|
+
def track(spot, label, suggested_track = nil)
|
24
|
+
return suggested_track if suggested_track
|
25
|
+
return @pool_track
|
26
|
+
end
|
27
|
+
|
28
|
+
def pack(plan, track, was_suggested = false)
|
29
|
+
return plan if plan.frozen?
|
30
|
+
return plan.identical_copy if was_suggested
|
31
|
+
if max_procs > 1 then plan.identical_copy else plan end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class PoolTrack
|
36
|
+
include DispatchingTrack
|
37
|
+
|
38
|
+
attr_reader :pool
|
39
|
+
|
40
|
+
def initialize(pool)
|
41
|
+
@pool = pool
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def process(&thunk)
|
47
|
+
# DefaultLogger.instance.info ":enter #{pool.inspect}"
|
48
|
+
pool.process &thunk
|
49
|
+
# DefaultLogger.instance.info ":exit #{pool.inspect}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class SharedPoolGuide < PoolGuide
|
54
|
+
include Singleton
|
55
|
+
|
56
|
+
def initialize
|
57
|
+
super PoolGuide.num_procs
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class SinglePoolGuide < PoolGuide
|
62
|
+
def initialize ; super 1 end
|
63
|
+
end
|
64
|
+
|
65
|
+
class SharedSinglePoolGuide < SinglePoolGuide
|
66
|
+
include Singleton
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|