andromeda 0.1.2 → 0.1.3
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/Gemfile +1 -29
- data/Gemfile.lock +27 -71
- data/README.md +4 -4
- data/ROADMAP.md +7 -8
- data/andromeda.gemspec +17 -1
- data/lib/andromeda.rb +34 -34
- data/lib/andromeda/atom.rb +76 -76
- data/lib/andromeda/cmd.rb +239 -239
- data/lib/andromeda/error.rb +1 -1
- data/lib/andromeda/guide.rb +2 -2
- data/lib/andromeda/guide_track.rb +2 -3
- data/lib/andromeda/id.rb +13 -13
- data/lib/andromeda/impl/class_attr.rb +2 -2
- data/lib/andromeda/impl/id.rb +89 -0
- data/lib/andromeda/impl/proto_plan.rb +34 -25
- data/lib/andromeda/impl_base.rb +48 -0
- data/lib/andromeda/kit.rb +169 -169
- data/lib/andromeda/map_reduce.rb +1 -1
- data/lib/andromeda/patch/array_bin_search.rb +83 -0
- data/lib/andromeda/plan.rb +4 -4
- data/lib/andromeda/spot.rb +3 -3
- data/lib/andromeda/sugar.rb +13 -13
- data/lib/andromeda/sync.rb +5 -3
- data/lib/andromeda/version.rb +1 -1
- data/yard_extensions/andromeda.rb +2 -2
- metadata +119 -6
data/lib/andromeda/cmd.rb
CHANGED
@@ -1,242 +1,242 @@
|
|
1
1
|
module Andromeda
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
3
|
+
module Cmd
|
4
|
+
|
5
|
+
class Cmd
|
6
|
+
attr_reader :cmd
|
7
|
+
attr_reader :data
|
8
|
+
attr_reader :time
|
9
|
+
|
10
|
+
def initialize(cmd, data = {}, cmd_time = nil)
|
11
|
+
raise ArgumentError unless cmd.kind_of?(Symbol)
|
12
|
+
@cmd = cmd
|
13
|
+
@data = data
|
14
|
+
@time = if cmd_time then cmd_time else Time.now.to_i end
|
15
|
+
@comment = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def comment ; @comment || '' end
|
19
|
+
def comment=(str = nil) ; @comment = str end
|
20
|
+
|
21
|
+
def if_cmd(sym) ; if sym == cmd then yield data else seld end end
|
22
|
+
|
23
|
+
def as_json
|
24
|
+
h = { cmd: cmd, data: (data.as_json rescue data), time: time }
|
25
|
+
c = @comment
|
26
|
+
h[:comment] = c if c
|
27
|
+
h
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s ; as_json.to_json end
|
31
|
+
|
32
|
+
def with_comment(str = nil)
|
33
|
+
@comment = str
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.new_input(cmd, data = {}, cmd_time = nil)
|
38
|
+
inner = Cmd.new cmd, data, cmd_time
|
39
|
+
Cmd.new :input, inner, cmd_time
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.from_json(json)
|
43
|
+
Cmd.new json['cmd'].to_sym, json['data'], (json['time'] rescue nil)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class FileCmdPlan < Kit::InlineKeyRouter
|
48
|
+
attr_reader :path
|
49
|
+
attr_reader :mode
|
50
|
+
attr_reader :file
|
51
|
+
|
52
|
+
spot_meth :open
|
53
|
+
spot_meth :sync
|
54
|
+
spot_meth :close
|
55
|
+
spot_meth :input
|
56
|
+
|
57
|
+
signal_spot :open
|
58
|
+
signal_spot :close
|
59
|
+
signal_spot :sync
|
60
|
+
|
61
|
+
def data_map(name, data)
|
62
|
+
if data.is_a?(Cmd) then data else Cmd.new(data) end
|
63
|
+
end
|
64
|
+
|
65
|
+
def data_key(name, data) ; data.cmd end
|
66
|
+
def data_val(name, data) ; data.data end
|
67
|
+
|
68
|
+
def data_tag(name, key, val, tags_in)
|
69
|
+
tags_out = super
|
70
|
+
if name == :input
|
71
|
+
tags_out[:time] = val.time
|
72
|
+
tags_out[:comment] = val.comment
|
73
|
+
end
|
74
|
+
tags_out
|
75
|
+
end
|
76
|
+
|
77
|
+
def on_open(key, val)
|
78
|
+
if @file
|
79
|
+
signal_error ArgumentError.new("associated file already open")
|
80
|
+
else
|
81
|
+
@path = val[:path] if val[:path]
|
82
|
+
@mode = val[:mode] if val[:mode]
|
83
|
+
@mode ||= init_mode
|
84
|
+
@file = File.open @path, @mode
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def on_input(key, val)
|
89
|
+
exit << val if exit
|
90
|
+
end
|
91
|
+
|
92
|
+
def on_sync(key, val)
|
93
|
+
f = @file ; sync_file f if f
|
94
|
+
end
|
95
|
+
|
96
|
+
def on_close(key, val)
|
97
|
+
if @file
|
98
|
+
begin
|
99
|
+
close_file(@file)
|
100
|
+
ensure
|
101
|
+
@file = nil
|
102
|
+
end
|
103
|
+
else
|
104
|
+
signal_error ArgumentError.new("associated file not open")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
protected
|
109
|
+
|
110
|
+
def close_file(f)
|
111
|
+
begin
|
112
|
+
sync_file f
|
113
|
+
ensure
|
114
|
+
f.close
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def sync_file(f) ; end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Writer < FileCmdPlan
|
122
|
+
|
123
|
+
def init_mode ; 'w' end
|
124
|
+
|
125
|
+
def on_input(key, val)
|
126
|
+
signal_error ArgumentError.new("associated filed not open") unless file
|
127
|
+
cmd = val.cmd
|
128
|
+
raise ArgumentError, "invalid cmd" unless cmd.kind_of?(Symbol)
|
129
|
+
data = val.data
|
130
|
+
str = if data then data.to_json else '' end
|
131
|
+
len = str.length + 1
|
132
|
+
tim = val.time if val.time
|
133
|
+
tim = Time.now unless tim
|
134
|
+
tim = tim.to_i unless tim.kind_of?(Fixnum)
|
135
|
+
new_str = ''
|
136
|
+
str.each_line { |line| new_str << "... #{line}\n" }
|
137
|
+
str = nil
|
138
|
+
len = new_str.length
|
139
|
+
val = tags[:comment]
|
140
|
+
val.each_line { |line| file.write "\# #{line}\n" } if val
|
141
|
+
file.write "<<< ANDROMEDA START :#{cmd} TIME #{tim} LEN #{len} >>>\n"
|
142
|
+
file.write new_str
|
143
|
+
file.write "<<< ANDROMEDA END :#{cmd} >>>\n"
|
144
|
+
super key, val
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
|
149
|
+
def sync_file(f)
|
150
|
+
f.sync
|
151
|
+
f.fsync rescue nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class Reader < Kit::FileReader
|
156
|
+
attr_reader :start_matcher, :end_matcher, :comment_matcher, :line_matcher
|
157
|
+
|
158
|
+
def initialize(config = {})
|
159
|
+
super config
|
160
|
+
@start_matcher = /<<< ANDROMEDA START :(\w+) TIME (\d+) LEN (\d+) >>>/
|
161
|
+
@end_matcher = /<<< ANDROMEDA END :(\w+) >>>/
|
162
|
+
@comment_matcher = /#(.*)/
|
163
|
+
# remember to change the offset for :line, too whenever you change this
|
164
|
+
@line_matcher = /... (.*)/
|
165
|
+
end
|
166
|
+
|
167
|
+
def match_line(state, line)
|
168
|
+
m = @start_matcher.match line
|
169
|
+
return yield :start, state, ({ cmd: m[1].to_sym, tim: m[2].to_i, len: m[3].to_i }) if m
|
170
|
+
m = @end_matcher.match line
|
171
|
+
return yield :end, state, m[1].to_sym if m
|
172
|
+
m = @comment_matcher.match line
|
173
|
+
return yield :comment, state, m[1] if m
|
174
|
+
m = @line_matcher.match line
|
175
|
+
return yield :line, state, m[1] if m
|
176
|
+
yield :garbage, state, line
|
177
|
+
end
|
178
|
+
|
179
|
+
def on_enter(key, val)
|
180
|
+
super key, val do |file|
|
181
|
+
fst = tags[:first]
|
182
|
+
lst = tags[:last]
|
183
|
+
|
184
|
+
state = { :comment => true, :start => true, :cont => true }
|
185
|
+
while (line = file.gets) && state[:cont]
|
186
|
+
line = line.chomp
|
187
|
+
match_line(state, line) do |token, state, parts|
|
188
|
+
signal_error ArgumentError.new("Skipping unexpected token #{token} in line '#{line}' (state: #{state})") unless state[token]
|
189
|
+
case token
|
190
|
+
when :comment
|
191
|
+
if state[:comment_str]
|
192
|
+
then state[:comment_str] << parts
|
193
|
+
else state[:comment_str] = parts end
|
194
|
+
when :start
|
195
|
+
state.delete :comment
|
196
|
+
state.delete :start
|
197
|
+
state[:line] = true
|
198
|
+
state[:end] = true
|
199
|
+
parts[:data] = ''
|
200
|
+
state[:len] = 0
|
201
|
+
state[:cur] = parts
|
202
|
+
parts[:comment] = state[:comment_str]
|
203
|
+
state.delete :comment_str
|
204
|
+
when :line
|
205
|
+
state[:len] += parts.length + 5
|
206
|
+
state[:cur][:data] << "#{parts}\n"
|
207
|
+
when :end
|
208
|
+
state.delete :line
|
209
|
+
state.delete :end
|
210
|
+
state[:start] = true
|
211
|
+
state[:comment] = true
|
212
|
+
cur = state[:cur]
|
213
|
+
data = cur[:data]
|
214
|
+
signal_error ArgumentError.new("Start (#{cur[:cmd]}) and end (#{parts})cmd mismatch") unless cur[:cmd] == parts
|
215
|
+
signal_error ArgumentError.new("Length mismatch (expected: #{cur[:len]}, found: #{state[:len]})") unless cur[:len] == state[:len]
|
216
|
+
exit << cur if exit
|
217
|
+
state[:cont] = false unless file.pos <= lst
|
218
|
+
else
|
219
|
+
signal_error ArgumentError.new("Garbage encountered (line: '#{line}')")
|
220
|
+
return
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
class Parser < Plan
|
230
|
+
|
231
|
+
def on_enter(key, val)
|
232
|
+
data = val[:data].chomp
|
233
|
+
data = JSON::parse(data)
|
234
|
+
cmd = Cmd.new val[:cmd], data, val[:time]
|
235
|
+
rem = val[:comment] rescue nil
|
236
|
+
cmd.with_comment rem if rem
|
237
|
+
exit << cmd if cmd
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
242
|
end
|