andromeda 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,242 +1,242 @@
1
1
  module Andromeda
2
2
 
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
- meth_spot :open
53
- meth_spot :sync
54
- meth_spot :close
55
- meth_spot :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
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