rider-server 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|