rtext 0.9.0 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +108 -98
- data/RText_Protocol +1 -1
- data/Rakefile +39 -46
- data/lib/rtext/default_completer.rb +212 -208
- data/lib/rtext/frontend/connector.rb +120 -53
- data/lib/rtext/instantiator.rb +11 -3
- data/lib/rtext/service.rb +264 -260
- data/test/completer_test.rb +606 -607
- data/test/context_builder_test.rb +952 -949
- data/test/frontend/context_test.rb +301 -302
- data/test/instantiator_test.rb +1773 -1733
- data/test/integration/test.rb +974 -968
- data/test/link_detector_test.rb +287 -288
- data/test/message_helper_test.rb +116 -117
- data/test/serializer_test.rb +1023 -1024
- data/test/tokenizer_test.rb +173 -174
- metadata +17 -19
- data/test/integration/backend.out +0 -16
- data/test/integration/frontend.log +0 -40058
@@ -21,15 +21,21 @@ def initialize(config, options={})
|
|
21
21
|
@outfile_provider = options[:outfile_provider]
|
22
22
|
@keep_outfile = options[:keep_outfile]
|
23
23
|
@connection_timeout = options[:connection_timeout] || 10
|
24
|
+
@process_id = nil
|
24
25
|
end
|
25
26
|
|
26
27
|
def execute_command(obj, options={})
|
27
|
-
timeout = options[:timeout] ||
|
28
|
+
timeout = options[:timeout] || 10
|
28
29
|
@busy = false if @busy_start_time && (Time.now > @busy_start_time + timeout)
|
29
30
|
if @busy
|
30
31
|
do_work
|
31
|
-
:backend_busy
|
32
|
-
|
32
|
+
return :backend_busy
|
33
|
+
end
|
34
|
+
unless connected?
|
35
|
+
connect unless connecting?
|
36
|
+
do_work
|
37
|
+
end
|
38
|
+
if connected?
|
33
39
|
obj["invocation_id"] = @invocation_id
|
34
40
|
obj["type"] = "request"
|
35
41
|
@socket.send(serialize_message(obj), 0)
|
@@ -66,9 +72,7 @@ def execute_command(obj, options={})
|
|
66
72
|
result
|
67
73
|
end
|
68
74
|
else
|
69
|
-
|
70
|
-
do_work
|
71
|
-
:connecting
|
75
|
+
:connecting
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
@@ -87,23 +91,68 @@ def stop
|
|
87
91
|
sleep(0.1)
|
88
92
|
end
|
89
93
|
end
|
94
|
+
ensure_process_cleanup(@process_id, @keep_outfile ? nil : @out_file, 10)
|
95
|
+
@process_id = nil
|
90
96
|
end
|
91
97
|
|
92
98
|
private
|
93
99
|
|
100
|
+
def wait_for_process_to_exit(process_id, timeout)
|
101
|
+
with_timeout timeout do
|
102
|
+
begin
|
103
|
+
waitpid(process_id, Process::WNOHANG)
|
104
|
+
process_id = nil
|
105
|
+
true
|
106
|
+
rescue Errno::ECHILD => _
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def ensure_process_cleanup(process_id, out_file, timeout)
|
113
|
+
Thread.new do
|
114
|
+
begin
|
115
|
+
unless process_id.nil?
|
116
|
+
process_id = nil if wait_for_process_to_exit(process_id, timeout)
|
117
|
+
end
|
118
|
+
ensure
|
119
|
+
unless process_id.nil?
|
120
|
+
begin
|
121
|
+
Process.kill('QUIT', process_id)
|
122
|
+
rescue Errno::ESRCH => _
|
123
|
+
end
|
124
|
+
end
|
125
|
+
File.unlink(out_file) if !out_file.nil? && File.exist?(out_file)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def with_timeout(timeout, sleep_time = 0.1, &block)
|
131
|
+
started = Time.now
|
132
|
+
while true do
|
133
|
+
return true if block.call
|
134
|
+
if Time.now > started + timeout
|
135
|
+
return false
|
136
|
+
end
|
137
|
+
sleep(sleep_time)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
94
142
|
def connected?
|
95
|
-
@state == :
|
143
|
+
!@process_id.nil? && @state == :read_from_socket && backend_running?
|
96
144
|
end
|
97
145
|
|
98
146
|
def connecting?
|
99
|
-
@state == :
|
147
|
+
!@process_id.nil? && (@state == :wait_for_file || @state == :wait_for_port)
|
100
148
|
end
|
101
149
|
|
102
150
|
def backend_running?
|
103
151
|
if @process_id
|
104
152
|
begin
|
105
|
-
|
106
|
-
|
153
|
+
waitpid(@process_id, Process::WNOHANG)
|
154
|
+
return true
|
155
|
+
rescue Errno::ECHILD => _
|
107
156
|
end
|
108
157
|
end
|
109
158
|
false
|
@@ -121,7 +170,7 @@ def tempfile_name
|
|
121
170
|
end
|
122
171
|
|
123
172
|
def connect
|
124
|
-
|
173
|
+
return if connected?
|
125
174
|
@connect_start_time = Time.now
|
126
175
|
|
127
176
|
@logger.info @config.command if @logger
|
@@ -131,56 +180,75 @@ def connect
|
|
131
180
|
else
|
132
181
|
@out_file = tempfile_name
|
133
182
|
end
|
134
|
-
File.unlink(@out_file) if File.exist?(@out_file)
|
135
183
|
|
136
|
-
|
137
|
-
@
|
184
|
+
if @process_id.nil?
|
185
|
+
File.unlink(@out_file) if File.exist?(@out_file)
|
186
|
+
Dir.chdir(File.dirname(@config.file)) do
|
187
|
+
@process_id = spawn(@config.command.strip + " > #{@out_file} 2>&1")
|
188
|
+
@state = :wait_for_file
|
189
|
+
end
|
138
190
|
end
|
139
|
-
@work_state = :wait_for_file
|
140
191
|
end
|
141
192
|
|
142
193
|
def do_work
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
194
|
+
if @process_id.nil?
|
195
|
+
@state = :off
|
196
|
+
return false
|
197
|
+
end
|
198
|
+
if @state == :wait_for_port && !File.exist?(@out_file)
|
199
|
+
@state = :wait_for_file
|
200
|
+
end
|
201
|
+
if @state == :wait_for_file && File.exist?(@out_file)
|
202
|
+
@state = :wait_for_port
|
203
|
+
end
|
204
|
+
if @state == :wait_for_file
|
205
|
+
while true
|
206
|
+
if Time.now > @connect_start_time + @connection_timeout
|
207
|
+
cleanup
|
208
|
+
@connection_listener.call(:timeout) if @connection_listener
|
209
|
+
@state = :off
|
210
|
+
@logger.warn "process didn't startup (connection timeout)" if @logger
|
211
|
+
return false
|
212
|
+
end
|
213
|
+
sleep(0.1)
|
214
|
+
if File.exist?(@out_file)
|
215
|
+
@state = :wait_for_port
|
216
|
+
break
|
217
|
+
end
|
154
218
|
end
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
@
|
163
|
-
|
164
|
-
|
219
|
+
end
|
220
|
+
if @state == :wait_for_port
|
221
|
+
while true
|
222
|
+
break unless File.exist?(@out_file)
|
223
|
+
output = File.read(@out_file)
|
224
|
+
if output =~ /^RText service, listening on port (\d+)/
|
225
|
+
port = $1.to_i
|
226
|
+
@logger.info "connecting to #{port}" if @logger
|
227
|
+
begin
|
228
|
+
@socket = TCPSocket.new("127.0.0.1", port)
|
229
|
+
@socket.setsockopt(:SOCKET, :RCVBUF, 1000000)
|
230
|
+
rescue Errno::ECONNREFUSED
|
231
|
+
cleanup
|
232
|
+
@connection_listener.call(:timeout) if @connection_listener
|
233
|
+
@state = :off
|
234
|
+
@logger.warn "could not connect socket (connection timeout)" if @logger
|
235
|
+
return false
|
236
|
+
end
|
237
|
+
@state = :read_from_socket
|
238
|
+
@connection_listener.call(:connected) if @connection_listener
|
239
|
+
break
|
240
|
+
end
|
241
|
+
if Time.now > @connect_start_time + @connection_timeout
|
165
242
|
cleanup
|
166
243
|
@connection_listener.call(:timeout) if @connection_listener
|
167
|
-
@work_state = :done
|
168
244
|
@state = :off
|
169
245
|
@logger.warn "could not connect socket (connection timeout)" if @logger
|
246
|
+
return false
|
170
247
|
end
|
171
|
-
|
172
|
-
@work_state = :read_from_socket
|
173
|
-
@connection_listener.call(:connected) if @connection_listener
|
174
|
-
end
|
175
|
-
if Time.now > @connect_start_time + @connection_timeout
|
176
|
-
cleanup
|
177
|
-
@connection_listener.call(:timeout) if @connection_listener
|
178
|
-
@work_state = :done
|
179
|
-
@state = :off
|
180
|
-
@logger.warn "could not connect socket (connection timeout)" if @logger
|
248
|
+
sleep(0.1)
|
181
249
|
end
|
182
|
-
|
183
|
-
|
250
|
+
end
|
251
|
+
if @state == :read_from_socket
|
184
252
|
repeat = true
|
185
253
|
socket_closed = false
|
186
254
|
response_data = ""
|
@@ -208,13 +276,12 @@ def do_work
|
|
208
276
|
end
|
209
277
|
elsif !backend_running? || socket_closed
|
210
278
|
cleanup
|
211
|
-
@
|
279
|
+
@state = :off
|
212
280
|
return false
|
213
281
|
end
|
214
282
|
end
|
215
|
-
true
|
216
283
|
end
|
217
|
-
|
284
|
+
true
|
218
285
|
end
|
219
286
|
|
220
287
|
def cleanup
|
@@ -224,7 +291,7 @@ def cleanup
|
|
224
291
|
break unless backend_running?
|
225
292
|
sleep(0.1)
|
226
293
|
end
|
227
|
-
|
294
|
+
ensure_process_cleanup(@process_id, @keep_outfile ? @out_file : nil, 10)
|
228
295
|
end
|
229
296
|
|
230
297
|
end
|
data/lib/rtext/instantiator.rb
CHANGED
@@ -247,7 +247,15 @@ class Instantiator
|
|
247
247
|
element.setOrAddGeneric(feature.name, proxy)
|
248
248
|
else
|
249
249
|
begin
|
250
|
-
|
250
|
+
v_value = v.value
|
251
|
+
feature_instance_class = feature.eType.instanceClass
|
252
|
+
if feature_instance_class == String && (v_value.is_a?(Float) || v_value.is_a?(Fixnum))
|
253
|
+
element.setOrAddGeneric(feature.name, v_value.to_s)
|
254
|
+
elsif feature_instance_class == Float && v_value.is_a?(Fixnum)
|
255
|
+
element.setOrAddGeneric(feature.name, v_value.to_f)
|
256
|
+
else
|
257
|
+
element.setOrAddGeneric(feature.name, v_value)
|
258
|
+
end
|
251
259
|
rescue StandardError
|
252
260
|
# backward compatibility for RGen versions not supporting BigDecimal
|
253
261
|
if v.value.is_a?(BigDecimal)
|
@@ -302,9 +310,9 @@ class Instantiator
|
|
302
310
|
elsif feature.eType.is_a?(RGen::ECore::EEnum)
|
303
311
|
[:identifier, :string]
|
304
312
|
else
|
305
|
-
expected = { String => [:string, :identifier],
|
313
|
+
expected = { String => [:string, :identifier, :integer, :float],
|
306
314
|
Integer => [:integer],
|
307
|
-
Float => [:float],
|
315
|
+
Float => [:float, :integer],
|
308
316
|
RGen::MetamodelBuilder::DataTypes::Boolean => [:boolean],
|
309
317
|
Object => [:string, :identifier, :integer, :float, :boolean]
|
310
318
|
}[feature.eType.instanceClass]
|