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.
@@ -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