rider-server 0.1.0
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.
- checksums.yaml +7 -0
- data/.build.yml +23 -0
- data/.ruby-version +1 -0
- data/.standard.yml +3 -0
- data/CHANGELOG.md +5 -0
- data/README.md +44 -0
- data/Rakefile +12 -0
- data/exe/rider-server +11 -0
- data/lib/rider_server/core_ext/array.rb +32 -0
- data/lib/rider_server/core_ext/hash.rb +14 -0
- data/lib/rider_server/core_ext/object.rb +18 -0
- data/lib/rider_server/core_ext/string.rb +18 -0
- data/lib/rider_server/core_ext/symbol.rb +14 -0
- data/lib/rider_server/errors.rb +16 -0
- data/lib/rider_server/exception_extension.rb +34 -0
- data/lib/rider_server/inspect.rb +148 -0
- data/lib/rider_server/logger.rb +13 -0
- data/lib/rider_server/operation.rb +69 -0
- data/lib/rider_server/operations.rb +136 -0
- data/lib/rider_server/ops/clone.rb +32 -0
- data/lib/rider_server/ops/close.rb +25 -0
- data/lib/rider_server/ops/completions.rb +100 -0
- data/lib/rider_server/ops/eval.rb +62 -0
- data/lib/rider_server/ops/inspect.rb +121 -0
- data/lib/rider_server/ops/inspect_exception.rb +47 -0
- data/lib/rider_server/ops/interrupt.rb +30 -0
- data/lib/rider_server/ops/load_path.rb +20 -0
- data/lib/rider_server/ops/lookup.rb +83 -0
- data/lib/rider_server/ops/ls_exceptions.rb +29 -0
- data/lib/rider_server/ops/ls_services.rb +19 -0
- data/lib/rider_server/ops/ls_sessions.rb +52 -0
- data/lib/rider_server/ops/service.rb +43 -0
- data/lib/rider_server/ops/set_namespace.rb +79 -0
- data/lib/rider_server/ops/set_namespace_variable.rb +80 -0
- data/lib/rider_server/ops/stdin.rb +20 -0
- data/lib/rider_server/ops/toggle_catch_all_exceptions.rb +27 -0
- data/lib/rider_server/response.rb +69 -0
- data/lib/rider_server/server.rb +104 -0
- data/lib/rider_server/service.rb +20 -0
- data/lib/rider_server/services/capture_exceptions.rb +62 -0
- data/lib/rider_server/services/capture_io.rb +302 -0
- data/lib/rider_server/services/rails.rb +129 -0
- data/lib/rider_server/session.rb +190 -0
- data/lib/rider_server/transports/bencode.rb +0 -0
- data/lib/rider_server/utils.rb +63 -0
- data/lib/rider_server/version.rb +12 -0
- data/lib/rider_server/workspace.rb +111 -0
- data/lib/rider_server.rb +5 -0
- metadata +122 -0
@@ -0,0 +1,302 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# capture_io.rb -- Handle IO
|
5
|
+
#
|
6
|
+
# Author: Russell Sim
|
7
|
+
# Copyright (c) 2024 Russell Sim
|
8
|
+
# SPDX-License-Identifier: MIT
|
9
|
+
|
10
|
+
require "rider_server/service"
|
11
|
+
|
12
|
+
module RiderServer
|
13
|
+
module Services
|
14
|
+
class CaptureIO < Service
|
15
|
+
attr_reader :stdin, :stdout, :stderr
|
16
|
+
|
17
|
+
@sessions = []
|
18
|
+
@stdin_stream, @stdin = ::IO.pipe
|
19
|
+
|
20
|
+
def self.stdin
|
21
|
+
@stdin
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.sessions
|
25
|
+
@sessions
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.nwrite(stream_name, *string)
|
29
|
+
str = string.join
|
30
|
+
send_response(stream_name, str)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.create_response(id, stream_name, str)
|
34
|
+
{
|
35
|
+
"id" => id,
|
36
|
+
stream_name => str,
|
37
|
+
"time-stamp" => Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.send_response(io_stream, string)
|
42
|
+
@sessions.each do |session, stream_id|
|
43
|
+
response = create_response(stream_id, io_stream, string)
|
44
|
+
session.response_queue.push(response)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(session)
|
49
|
+
@session = session
|
50
|
+
@stdout = ::STDOUT # rubocop:disable Style/GlobalStdStream
|
51
|
+
@stderr = ::STDERR # rubocop:disable Style/GlobalStdStream
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def start(stream_id)
|
56
|
+
unless @stdin == ::STDIN && @stdout == ::STDOUT && @stderr == ::STDERR # rubocop:disable Style/GlobalStdStream
|
57
|
+
$stdin = self.class.stdin
|
58
|
+
$stdout = @stdout = Services::IO.new(::STDOUT, "out") # rubocop:disable Style/GlobalStdStream
|
59
|
+
$stderr = @stderr = Services::IO.new(::STDERR, "err") # rubocop:disable Style/GlobalStdStream
|
60
|
+
end
|
61
|
+
self.class.sessions << [@session, stream_id]
|
62
|
+
:running
|
63
|
+
end
|
64
|
+
|
65
|
+
def stop
|
66
|
+
self.class.sessions.delete_if { |s| s[0] == @session }
|
67
|
+
|
68
|
+
if self.class.sessions.empty?
|
69
|
+
$stdin = @stdin = ::STDIN # rubocop:disable Style/GlobalStdStream
|
70
|
+
$stdout = @stdout = ::STDOUT # rubocop:disable Style/GlobalStdStream
|
71
|
+
$stderr = @stderr = ::STDERR # rubocop:disable Style/GlobalStdStream
|
72
|
+
end
|
73
|
+
:stopped
|
74
|
+
end
|
75
|
+
|
76
|
+
def status
|
77
|
+
if self.class.sessions.find { |s| s[0] == @session }
|
78
|
+
:running
|
79
|
+
else
|
80
|
+
:stopped
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class IO
|
86
|
+
def initialize(io, stream_name)
|
87
|
+
@stream_name = stream_name
|
88
|
+
@io = io
|
89
|
+
end
|
90
|
+
|
91
|
+
def write(*string)
|
92
|
+
strings = string.map(&:to_s)
|
93
|
+
Services::CaptureIO.nwrite(@stream_name, *strings)
|
94
|
+
@io.write(*strings)
|
95
|
+
end
|
96
|
+
|
97
|
+
def write_nonblock(string, **kwargs)
|
98
|
+
Services::CaptureIO.nwrite(@stream_name, string.to_s)
|
99
|
+
@io.write_nonblock(strings, **kwargs)
|
100
|
+
end
|
101
|
+
|
102
|
+
def syswrite(string)
|
103
|
+
Services::CaptureIO.nwrite(@stream_name, string.to_s)
|
104
|
+
@io.syswrite(string)
|
105
|
+
end
|
106
|
+
|
107
|
+
def <<(obj)
|
108
|
+
write(obj.to_s)
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
def print(*args)
|
113
|
+
if args.length == 0
|
114
|
+
write($_)
|
115
|
+
else
|
116
|
+
args.each_with_index do |arg, index|
|
117
|
+
write(arg.to_s)
|
118
|
+
if $, && index < args.length - 1
|
119
|
+
write($,)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def printf(format_string, *args)
|
127
|
+
write(sprintf(format_string, *args))
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def putc(obj)
|
132
|
+
if obj.is_a?(String)
|
133
|
+
write(obj[0])
|
134
|
+
else
|
135
|
+
# The real version triggers a "TypeError: no implicit
|
136
|
+
# conversion of IO into Integer", when called on objects that
|
137
|
+
# are not numbers or strings and can't be coerced to a number.
|
138
|
+
write(obj.to_int.chr)
|
139
|
+
end
|
140
|
+
obj
|
141
|
+
end
|
142
|
+
|
143
|
+
def puts(*args)
|
144
|
+
args.each do |arg|
|
145
|
+
if arg.is_a?(Array)
|
146
|
+
arg.each do |a|
|
147
|
+
write(a.to_s, "\n")
|
148
|
+
end
|
149
|
+
else
|
150
|
+
write(arg.to_s, "\n")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
write("\n") if args.length == 0
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
|
158
|
+
def to_io
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
def pwrite(string, offset)
|
163
|
+
raise NotImplementedError
|
164
|
+
end
|
165
|
+
|
166
|
+
%i[
|
167
|
+
advise
|
168
|
+
all?
|
169
|
+
any?
|
170
|
+
autoclose?
|
171
|
+
bdecode
|
172
|
+
binmode
|
173
|
+
binmode?
|
174
|
+
chain
|
175
|
+
chunk
|
176
|
+
chunk_while
|
177
|
+
close
|
178
|
+
close_on_exec=
|
179
|
+
close_on_exec?
|
180
|
+
close_read
|
181
|
+
close_write
|
182
|
+
closed?
|
183
|
+
collect
|
184
|
+
collect_concat
|
185
|
+
compact
|
186
|
+
count
|
187
|
+
cycle
|
188
|
+
detect
|
189
|
+
drop
|
190
|
+
drop_while
|
191
|
+
each
|
192
|
+
each_byte
|
193
|
+
each_char
|
194
|
+
each_codepoint
|
195
|
+
each_cons
|
196
|
+
each_entry
|
197
|
+
each_line
|
198
|
+
each_slice
|
199
|
+
each_with_index
|
200
|
+
each_with_object
|
201
|
+
entries
|
202
|
+
eof
|
203
|
+
eof?
|
204
|
+
external_encoding
|
205
|
+
fcntl
|
206
|
+
fdatasync
|
207
|
+
fileno
|
208
|
+
filter
|
209
|
+
filter_map
|
210
|
+
find
|
211
|
+
find_all
|
212
|
+
find_index
|
213
|
+
first
|
214
|
+
flat_map
|
215
|
+
flush
|
216
|
+
fsync
|
217
|
+
getbyte
|
218
|
+
getc
|
219
|
+
gets
|
220
|
+
grep
|
221
|
+
grep_v
|
222
|
+
group_by
|
223
|
+
include?
|
224
|
+
inject
|
225
|
+
internal_encoding
|
226
|
+
ioctl
|
227
|
+
isatty
|
228
|
+
lazy
|
229
|
+
lineno
|
230
|
+
lineno=
|
231
|
+
map
|
232
|
+
max
|
233
|
+
max_by
|
234
|
+
member?
|
235
|
+
min
|
236
|
+
min_by
|
237
|
+
minmax
|
238
|
+
minmax_by
|
239
|
+
nonblock
|
240
|
+
nonblock=
|
241
|
+
nonblock?
|
242
|
+
none?
|
243
|
+
nread
|
244
|
+
one?
|
245
|
+
partition
|
246
|
+
pid
|
247
|
+
pos
|
248
|
+
pos=
|
249
|
+
pread
|
250
|
+
read
|
251
|
+
read_nonblock
|
252
|
+
readbyte
|
253
|
+
readchar
|
254
|
+
readline
|
255
|
+
readlines
|
256
|
+
readpartial
|
257
|
+
ready?
|
258
|
+
reduce
|
259
|
+
reject
|
260
|
+
reopen
|
261
|
+
reverse_each
|
262
|
+
rewind
|
263
|
+
seek
|
264
|
+
select
|
265
|
+
set_encoding
|
266
|
+
set_encoding_by_bom
|
267
|
+
slice_after
|
268
|
+
slice_before
|
269
|
+
slice_when
|
270
|
+
sort
|
271
|
+
sort_by
|
272
|
+
stat
|
273
|
+
sum
|
274
|
+
sync
|
275
|
+
sync=
|
276
|
+
sysread
|
277
|
+
sysseek
|
278
|
+
take
|
279
|
+
take_while
|
280
|
+
tally
|
281
|
+
tell
|
282
|
+
to_a
|
283
|
+
to_h
|
284
|
+
to_i
|
285
|
+
tty?
|
286
|
+
ungetbyte
|
287
|
+
ungetc
|
288
|
+
uniq
|
289
|
+
wait
|
290
|
+
wait_priority
|
291
|
+
wait_readable
|
292
|
+
wait_writable
|
293
|
+
zip
|
294
|
+
autoclose=
|
295
|
+
].each do |method|
|
296
|
+
define_method(method) do |*args, **kwargs, &block|
|
297
|
+
@io.send(method, *args, **kwargs, &block)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# rails.rb -- Rails service for Rider
|
5
|
+
#
|
6
|
+
# Author: Russell Sim
|
7
|
+
# Copyright (c) 2024 Russell Sim
|
8
|
+
# SPDX-License-Identifier: MIT
|
9
|
+
|
10
|
+
require "rider_server/service"
|
11
|
+
|
12
|
+
module RiderServer
|
13
|
+
module Services
|
14
|
+
class Rails < Service
|
15
|
+
def initialize(session)
|
16
|
+
@session = session
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def start(stream_id)
|
21
|
+
@stream_id = stream_id
|
22
|
+
|
23
|
+
# Suck it ruby, i can define constants at runtime
|
24
|
+
eval('::APP_PATH = File.expand_path("./config/application", Dir.pwd)', TOPLEVEL_BINDING, __FILE__, __LINE__)
|
25
|
+
nputs "Starting Rails Server..."
|
26
|
+
|
27
|
+
@thread = Thread.new do
|
28
|
+
require File.expand_path("./config/boot", Dir.pwd)
|
29
|
+
require "rails/command"
|
30
|
+
require "rails/commands/server/server_command"
|
31
|
+
|
32
|
+
set_application_directory!
|
33
|
+
|
34
|
+
::Rails::Server.new(server_options).tap do |server|
|
35
|
+
require APP_PATH
|
36
|
+
|
37
|
+
Dir.chdir(::Rails.application.root)
|
38
|
+
|
39
|
+
unless Rails.application.config.middleware.include?(Services::RailsExceptionMiddleware)
|
40
|
+
::Rails.application.config.middleware.insert_after(
|
41
|
+
ActionDispatch::DebugExceptions, Services::RailsExceptionMiddleware, @stream_id, @session
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
if server.serveable?
|
46
|
+
nputs_boot_information(server.server, server.served_url)
|
47
|
+
after_stop_callback = -> { nputs "Rails Server Exiting..." }
|
48
|
+
server.start(after_stop_callback)
|
49
|
+
else
|
50
|
+
nputs rack_server_suggestion(options[:using])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue => e
|
54
|
+
@session.push_exception(stream_id, e)
|
55
|
+
nputs "Error starting Rails Server: #{e.message}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def stop
|
60
|
+
@thread.raise(Interrupt)
|
61
|
+
end
|
62
|
+
|
63
|
+
def status
|
64
|
+
if @thread&.alive?
|
65
|
+
:running
|
66
|
+
else
|
67
|
+
:stopped
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def set_application_directory!
|
74
|
+
Dir.chdir(File.expand_path("../..", APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
|
75
|
+
end
|
76
|
+
|
77
|
+
def server_options
|
78
|
+
{
|
79
|
+
server: nil,
|
80
|
+
log_stdout: true,
|
81
|
+
Port: nil,
|
82
|
+
Host: nil,
|
83
|
+
DoNotReverseLookup: true,
|
84
|
+
config: "config.ru",
|
85
|
+
environment: "development",
|
86
|
+
daemonize: false,
|
87
|
+
pid: nil,
|
88
|
+
caching: nil,
|
89
|
+
restart_cmd: nil,
|
90
|
+
early_hints: nil
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
def nputs_boot_information(server, url)
|
95
|
+
nputs <<~MSG
|
96
|
+
=> Booting #{ActiveSupport::Inflector.demodulize(server)}
|
97
|
+
=> Rails #{::Rails.version} application starting in #{::Rails.env} #{url}
|
98
|
+
MSG
|
99
|
+
end
|
100
|
+
|
101
|
+
def nputs(string)
|
102
|
+
response = create_response
|
103
|
+
response["out"] = string + "\n"
|
104
|
+
@session.response_queue.push(response)
|
105
|
+
nil
|
106
|
+
end
|
107
|
+
|
108
|
+
def create_response
|
109
|
+
{
|
110
|
+
"id" => @stream_id,
|
111
|
+
"time-stamp" => Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
112
|
+
}
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class RailsExceptionMiddleware
|
117
|
+
def initialize(app)
|
118
|
+
@app = app
|
119
|
+
end
|
120
|
+
|
121
|
+
def call(env)
|
122
|
+
@app.call(env)
|
123
|
+
rescue => e
|
124
|
+
Services::CaptureExceptions.handle_exception(e, env)
|
125
|
+
raise
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# session.rb -- Represents a session in the Rider REPL
|
5
|
+
#
|
6
|
+
# Author: Russell Sim
|
7
|
+
# Copyright (c) 2024 Russell Sim
|
8
|
+
# SPDX-License-Identifier: MIT
|
9
|
+
|
10
|
+
require "date"
|
11
|
+
require "securerandom"
|
12
|
+
require "rider_server/workspace"
|
13
|
+
require "rider_server/errors"
|
14
|
+
require "rider_server/services/rails"
|
15
|
+
require "rider_server/services/capture_io"
|
16
|
+
require "rider_server/services/capture_exceptions"
|
17
|
+
require "rider_server/logger"
|
18
|
+
|
19
|
+
module RiderServer
|
20
|
+
class Session
|
21
|
+
include RiderServer::Logger
|
22
|
+
|
23
|
+
attr_reader :id
|
24
|
+
attr_reader :workspace
|
25
|
+
attr_reader :exceptions
|
26
|
+
attr_reader :evaluations
|
27
|
+
attr_reader :response_queue
|
28
|
+
attr_reader :history
|
29
|
+
|
30
|
+
SERVICES = [
|
31
|
+
Services::Rails,
|
32
|
+
Services::CaptureIO,
|
33
|
+
Services::CaptureExceptions
|
34
|
+
]
|
35
|
+
|
36
|
+
def initialize(response_queue, history: [])
|
37
|
+
@id = SecureRandom.uuid
|
38
|
+
@history = history
|
39
|
+
@workspace = Workspace.new
|
40
|
+
@queue = Thread::Queue.new
|
41
|
+
@exceptions = Utils::FixedArray.new
|
42
|
+
@response_queue = response_queue
|
43
|
+
@exception_queue = Thread::Queue.new
|
44
|
+
@evaluations = {}
|
45
|
+
@services = SERVICES.each_with_object({}) do |klass, h|
|
46
|
+
h[klass.service_name] = klass.new(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# XXX Side effects in initializer, :()
|
50
|
+
start_exception_processing
|
51
|
+
end
|
52
|
+
|
53
|
+
def send_response(response)
|
54
|
+
if response
|
55
|
+
@response_queue.push(response)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def clone
|
60
|
+
Session.new(@responses_queue, history: @history.clone)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Input History
|
65
|
+
#
|
66
|
+
|
67
|
+
def push_history(event)
|
68
|
+
@history.push(event)
|
69
|
+
end
|
70
|
+
|
71
|
+
def last_history
|
72
|
+
@history.last
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# Historical Results
|
77
|
+
#
|
78
|
+
|
79
|
+
def result_history
|
80
|
+
@result_history ||= {}
|
81
|
+
end
|
82
|
+
|
83
|
+
def add_result(evaluation_id, value)
|
84
|
+
result_history[evaluation_id] = value
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_result(evaluation_id)
|
88
|
+
raise "Missing history item #{evaluation_id}." unless result_history.key?(evaluation_id)
|
89
|
+
result_history[evaluation_id]
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Historical Exceptions
|
94
|
+
#
|
95
|
+
|
96
|
+
def add_exception(operation_id, exception, metadata = {})
|
97
|
+
id = SecureRandom.uuid
|
98
|
+
@exceptions << {
|
99
|
+
"id" => id,
|
100
|
+
"operation_id" => operation_id,
|
101
|
+
"created_at" => DateTime.now,
|
102
|
+
"exception" => exception,
|
103
|
+
"metadata" => metadata
|
104
|
+
}
|
105
|
+
id
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_exception(exception_id)
|
109
|
+
exception = @exceptions.find { |item| item["id"] == exception_id }
|
110
|
+
raise "Missing exception #{exception_id}." unless exception
|
111
|
+
exception
|
112
|
+
end
|
113
|
+
|
114
|
+
# A threadsafe way to add exceptions
|
115
|
+
def push_exception(operation_id, exception, metadata = {})
|
116
|
+
@exception_queue.push([operation_id, exception, metadata])
|
117
|
+
end
|
118
|
+
|
119
|
+
def start_exception_processing
|
120
|
+
return if @exception_processing_thread
|
121
|
+
|
122
|
+
@exception_processing_thread = Thread.new do
|
123
|
+
loop do
|
124
|
+
add_exception(*@exception_queue.pop)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def stop_exception_processing
|
130
|
+
@exception_processing_thread.exit
|
131
|
+
@exception_processing_thread = nil
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Historical Evaluations
|
136
|
+
#
|
137
|
+
|
138
|
+
def add_evaluation(evaluation_id, eval_thread)
|
139
|
+
@evaluations[evaluation_id] = eval_thread
|
140
|
+
end
|
141
|
+
|
142
|
+
def remove_evaluation(evaluation_id)
|
143
|
+
@evaluations.delete(evaluation_id)
|
144
|
+
end
|
145
|
+
|
146
|
+
def running_evaluation?(evaluation_id)
|
147
|
+
@evaluations[evaluation_id]
|
148
|
+
end
|
149
|
+
|
150
|
+
def interrupt_evaluation(evaluation_id)
|
151
|
+
log.info "Interrupting eval thread #{evaluation_id}"
|
152
|
+
# Signal the eval thread to raise an exception
|
153
|
+
@evaluations[evaluation_id].raise EvalInterrupt.new
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Service control
|
158
|
+
#
|
159
|
+
def list_services
|
160
|
+
SERVICES.map do |klass|
|
161
|
+
{
|
162
|
+
"name" => klass.service_name,
|
163
|
+
"state" => service_state(klass.service_name).to_s
|
164
|
+
}
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def start_service(service_type, stream_id)
|
169
|
+
raise "Service #{service_type} is already running." \
|
170
|
+
if @services[service_type].status == :running
|
171
|
+
|
172
|
+
@services[service_type].start(stream_id)
|
173
|
+
end
|
174
|
+
|
175
|
+
def stop_service(service_type)
|
176
|
+
raise "Service #{service_type} is not running." \
|
177
|
+
if @services[service_type].status == :stopped
|
178
|
+
|
179
|
+
@services[service_type].stop
|
180
|
+
end
|
181
|
+
|
182
|
+
def service_state(service_type)
|
183
|
+
if @services.key?(service_type)
|
184
|
+
@services[service_type].status
|
185
|
+
else
|
186
|
+
:stopped
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
File without changes
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# utils.rb -- Miscellaneous Utilities
|
5
|
+
#
|
6
|
+
# Author: Russell Sim
|
7
|
+
# Copyright (c) 2024 Russell Sim
|
8
|
+
# SPDX-License-Identifier: MIT
|
9
|
+
|
10
|
+
require "socket"
|
11
|
+
require "io/nonblock"
|
12
|
+
|
13
|
+
module RiderServer
|
14
|
+
module Utils
|
15
|
+
##
|
16
|
+
# Creates TCP server sockets bound to +address+:+port+ and returns them.
|
17
|
+
#
|
18
|
+
# It will create IPV4 and IPV6 sockets on all interfaces.
|
19
|
+
def create_listeners(address, port)
|
20
|
+
unless port
|
21
|
+
raise ArgumentError, "must specify port"
|
22
|
+
end
|
23
|
+
sockets = Socket.tcp_server_sockets(address, port)
|
24
|
+
sockets.map { |s|
|
25
|
+
s.autoclose = false
|
26
|
+
ts = TCPServer.for_fd(s.fileno)
|
27
|
+
s.close
|
28
|
+
ts
|
29
|
+
}
|
30
|
+
end
|
31
|
+
module_function :create_listeners
|
32
|
+
|
33
|
+
def rider_inspect(obj)
|
34
|
+
if obj.respond_to?(:rider_inspect)
|
35
|
+
obj.rider_inspect
|
36
|
+
else
|
37
|
+
obj.inspect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
module_function :rider_inspect
|
41
|
+
|
42
|
+
class FixedArray < Array
|
43
|
+
def initialize(*args)
|
44
|
+
@max_size = args[0] || 10
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def <<(element)
|
49
|
+
super
|
50
|
+
shift while size > @max_size
|
51
|
+
end
|
52
|
+
|
53
|
+
alias_method :push, :<<
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_response(operation)
|
58
|
+
{
|
59
|
+
"id" => operation["id"],
|
60
|
+
"time-stamp" => Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|