rider-server 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.build.yml +3 -3
  3. data/CHANGELOG.md +17 -1
  4. data/README.rdoc +39 -0
  5. data/Rakefile +5 -0
  6. data/exe/rider-server +3 -3
  7. data/lib/rider_server/config.rb +76 -0
  8. data/lib/rider_server/core_ext/array.rb +3 -1
  9. data/lib/rider_server/core_ext/class.rb +5 -0
  10. data/lib/rider_server/core_ext/env.rb +14 -0
  11. data/lib/rider_server/core_ext/hash.rb +3 -1
  12. data/lib/rider_server/core_ext/kernel.rb +5 -0
  13. data/lib/rider_server/core_ext/module.rb +18 -0
  14. data/lib/rider_server/core_ext/object.rb +7 -1
  15. data/lib/rider_server/core_ext/string.rb +3 -1
  16. data/lib/rider_server/core_ext/symbol.rb +3 -1
  17. data/lib/rider_server/exception_extension.rb +2 -0
  18. data/lib/rider_server/inspect.rb +108 -32
  19. data/lib/rider_server/logger.rb +11 -4
  20. data/lib/rider_server/operation.rb +39 -20
  21. data/lib/rider_server/{ops → operations}/clone.rb +3 -2
  22. data/lib/rider_server/{ops → operations}/close.rb +3 -2
  23. data/lib/rider_server/operations/completions.rb +146 -0
  24. data/lib/rider_server/operations/lookup.rb +102 -0
  25. data/lib/rider_server/operations/ls_sessions.rb +51 -0
  26. data/lib/rider_server/operations/toggle_catch_all_exceptions.rb +24 -0
  27. data/lib/rider_server/operations.rb +42 -68
  28. data/lib/rider_server/request.rb +61 -0
  29. data/lib/rider_server/response.rb +10 -2
  30. data/lib/rider_server/server.rb +28 -16
  31. data/lib/rider_server/services/capture_exceptions.rb +1 -1
  32. data/lib/rider_server/services/rails.rb +1 -1
  33. data/lib/rider_server/session.rb +75 -44
  34. data/lib/rider_server/session_operation.rb +17 -0
  35. data/lib/rider_server/session_operations/eval.rb +61 -0
  36. data/lib/rider_server/session_operations/inspect.rb +123 -0
  37. data/lib/rider_server/session_operations/inspect_exception.rb +46 -0
  38. data/lib/rider_server/session_operations/interrupt.rb +29 -0
  39. data/lib/rider_server/session_operations/load_path.rb +19 -0
  40. data/lib/rider_server/session_operations/ls_exceptions.rb +28 -0
  41. data/lib/rider_server/session_operations/ls_services.rb +18 -0
  42. data/lib/rider_server/session_operations/service.rb +42 -0
  43. data/lib/rider_server/session_operations/set_namespace.rb +82 -0
  44. data/lib/rider_server/session_operations/set_namespace_variable.rb +81 -0
  45. data/lib/rider_server/session_operations/stdin.rb +19 -0
  46. data/lib/rider_server/utils.rb +7 -7
  47. data/lib/rider_server/validate/array.rb +32 -0
  48. data/lib/rider_server/validate/base.rb +28 -0
  49. data/lib/rider_server/validate/boolean.rb +47 -0
  50. data/lib/rider_server/validate/hash.rb +32 -0
  51. data/lib/rider_server/validate/integer.rb +56 -0
  52. data/lib/rider_server/validate/predicates.rb +30 -0
  53. data/lib/rider_server/validate/string.rb +60 -0
  54. data/lib/rider_server/validate/symbol.rb +90 -0
  55. data/lib/rider_server/validate.rb +15 -0
  56. data/lib/rider_server/version.rb +1 -1
  57. data/lib/rider_server/workspace.rb +1 -1
  58. data/lib/rider_server.rb +3 -1
  59. metadata +54 -24
  60. data/README.md +0 -46
  61. data/lib/rider_server/ops/completions.rb +0 -145
  62. data/lib/rider_server/ops/eval.rb +0 -62
  63. data/lib/rider_server/ops/inspect.rb +0 -121
  64. data/lib/rider_server/ops/inspect_exception.rb +0 -47
  65. data/lib/rider_server/ops/interrupt.rb +0 -30
  66. data/lib/rider_server/ops/load_path.rb +0 -20
  67. data/lib/rider_server/ops/lookup.rb +0 -104
  68. data/lib/rider_server/ops/ls_exceptions.rb +0 -29
  69. data/lib/rider_server/ops/ls_services.rb +0 -19
  70. data/lib/rider_server/ops/ls_sessions.rb +0 -52
  71. data/lib/rider_server/ops/service.rb +0 -43
  72. data/lib/rider_server/ops/set_namespace.rb +0 -79
  73. data/lib/rider_server/ops/set_namespace_variable.rb +0 -80
  74. data/lib/rider_server/ops/stdin.rb +0 -20
  75. data/lib/rider_server/ops/toggle_catch_all_exceptions.rb +0 -27
@@ -51,7 +51,7 @@ module RiderServer
51
51
  end
52
52
  end
53
53
  rescue => e
54
- @session.push_exception(stream_id, e)
54
+ @session.add_exception(stream_id, e)
55
55
  nputs "Error starting Rails Server: #{e.message}"
56
56
  end
57
57
  end
@@ -15,8 +15,28 @@ require "rider_server/services/rails"
15
15
  require "rider_server/services/capture_io"
16
16
  require "rider_server/services/capture_exceptions"
17
17
  require "rider_server/logger"
18
+ require "rider_server/session_operations/eval"
19
+ require "rider_server/session_operations/inspect"
20
+ require "rider_server/session_operations/inspect_exception"
21
+ require "rider_server/session_operations/interrupt"
22
+ require "rider_server/session_operations/load_path"
23
+ require "rider_server/session_operations/ls_exceptions"
24
+ require "rider_server/session_operations/ls_services"
25
+ require "rider_server/session_operations/service"
26
+ require "rider_server/session_operations/set_namespace"
27
+ require "rider_server/session_operations/set_namespace_variable"
18
28
 
19
29
  module RiderServer
30
+ def self.create_session(config, response_queue, history: [])
31
+ session = Session.new(config, response_queue, history: history)
32
+
33
+ ObjectSpace.each_object(Class).select { |klass| klass < ::RiderServer::Service }.map do |klass, h|
34
+ session.add_service klass
35
+ end
36
+
37
+ session
38
+ end
39
+
20
40
  class Session
21
41
  include RiderServer::Logger
22
42
 
@@ -27,24 +47,17 @@ module RiderServer
27
47
  attr_reader :response_queue
28
48
  attr_reader :history
29
49
 
30
- SERVICES = [
31
- Services::Rails,
32
- Services::CaptureIO,
33
- Services::CaptureExceptions
34
- ]
35
-
36
- def initialize(response_queue, history: [])
50
+ def initialize(config, response_queue, history: [])
37
51
  @id = SecureRandom.uuid
52
+ @config = config
38
53
  @history = history
39
54
  @workspace = Workspace.new
40
55
  @queue = Thread::Queue.new
41
- @exceptions = Utils::FixedArray.new
56
+ @exceptions = Utils::FixedArray.new(max_size: config.exception_history_size)
42
57
  @response_queue = response_queue
43
58
  @exception_queue = Thread::Queue.new
44
59
  @evaluations = {}
45
- @services = SERVICES.each_with_object({}) do |klass, h|
46
- h[klass.service_name] = klass.new(self)
47
- end
60
+ @services = {}
48
61
 
49
62
  # XXX Side effects in initializer, :()
50
63
  start_exception_processing
@@ -56,34 +69,40 @@ module RiderServer
56
69
  end
57
70
  end
58
71
 
72
+ def stdin
73
+ ::STDIN # rubocop:disable Style/GlobalStdStream
74
+ end
75
+
59
76
  def clone
60
- Session.new(@responses_queue, history: @history.clone)
77
+ RiderServer.create_session(@responses_queue, history: @history.clone)
61
78
  end
62
79
 
63
80
  #
64
81
  # Input History
65
82
  #
66
83
 
84
+ # Push an event onto the history stack.
67
85
  def push_history(event)
68
86
  @history.push(event)
69
87
  end
70
88
 
71
- def last_history
72
- @history.last
73
- end
74
-
75
89
  #
76
90
  # Historical Results
77
91
  #
78
92
 
93
+ # Return a Hash containing all historical evaluations.
79
94
  def result_history
80
95
  @result_history ||= {}
81
96
  end
82
97
 
98
+ # Add an execution result to the history. The +evaluation_id+ is
99
+ # the operation id of the evaluation and the +value+ is the result
100
+ # of the evaluation.
83
101
  def add_result(evaluation_id, value)
84
102
  result_history[evaluation_id] = value
85
103
  end
86
104
 
105
+ # Return a historical result of an evaluation by +evaluation_id+.
87
106
  def get_result(evaluation_id)
88
107
  raise "Missing history item #{evaluation_id}." unless result_history.key?(evaluation_id)
89
108
  result_history[evaluation_id]
@@ -93,41 +112,33 @@ module RiderServer
93
112
  # Historical Exceptions
94
113
  #
95
114
 
96
- def wrap_exception(operation_id, exception, metadata = {})
97
- id = SecureRandom.uuid
98
- {
99
- "id" => id,
100
- "operation_id" => operation_id,
101
- "created_at" => DateTime.now,
102
- "exception" => exception,
103
- "metadata" => metadata
104
- }
115
+ # Add an exception to the exception history. +operation_id+ is the
116
+ # id of the request. +exception+ is the exception
117
+ # object. +metadata+ is a Hash containing additional information
118
+ # about the exception.
119
+ def add_exception(operation_id, exception, metadata = {})
120
+ ex = wrap_exception(operation_id, exception, metadata)
121
+ @exception_queue.push(ex)
122
+ ex
105
123
  end
106
124
 
107
- def add_exception(operation_id, exception, metadata = {})
108
- id = SecureRandom.uuid
109
- @exceptions << wrap_exception(operation_id, exception, metadata)
110
- id
125
+ # Add an exception that has already been wrapped in metadata.
126
+ # +wrapped_exception+ should be a hash, that has the keys "id",
127
+ # "created_at", "exception", and "metadata"
128
+ def add_wrapped_exception(wrapped_exception)
129
+ @exception_queue.push(wrapped_exception)
130
+ exception
111
131
  end
112
132
 
133
+ # Return an exception by +exception_id+. Raises an exception if
134
+ # the exception doesn't exist.
113
135
  def get_exception(exception_id)
114
136
  exception = @exceptions.find { |item| item["id"] == exception_id }
115
137
  raise "Missing exception #{exception_id}." unless exception
116
138
  exception
117
139
  end
118
140
 
119
- # A threadsafe way to add exceptions
120
- def push_exception(operation_id, exception, metadata = {})
121
- ex = wrap_exception(operation_id, exception, metadata)
122
- @exception_queue.push(ex)
123
- ex
124
- end
125
-
126
- def push_anonymous_exception(exception)
127
- @exception_queue.push(exception)
128
- exception
129
- end
130
-
141
+ # Start a thread to process exceptions from the exception queue.
131
142
  def start_exception_processing
132
143
  return if @exception_processing_thread
133
144
 
@@ -138,6 +149,7 @@ module RiderServer
138
149
  end
139
150
  end
140
151
 
152
+ # Stop the exception processing thread.
141
153
  def stop_exception_processing
142
154
  @exception_processing_thread.exit
143
155
  @exception_processing_thread = nil
@@ -168,11 +180,17 @@ module RiderServer
168
180
  #
169
181
  # Service control
170
182
  #
183
+
184
+ def add_service(service_class)
185
+ cls = service_class.new(self)
186
+ @services[service_class.service_name] = cls
187
+ end
188
+
171
189
  def list_services
172
- SERVICES.map do |klass|
190
+ @services.map do |name, _|
173
191
  {
174
- "name" => klass.service_name,
175
- "state" => service_state(klass.service_name).to_s
192
+ "name" => name,
193
+ "state" => service_state(name).to_s
176
194
  }
177
195
  end
178
196
  end
@@ -198,5 +216,18 @@ module RiderServer
198
216
  :stopped
199
217
  end
200
218
  end
219
+
220
+ private
221
+
222
+ def wrap_exception(operation_id, exception, metadata = {})
223
+ id = SecureRandom.uuid
224
+ {
225
+ "id" => id,
226
+ "operation_id" => operation_id,
227
+ "created_at" => DateTime.now,
228
+ "exception" => exception,
229
+ "metadata" => metadata
230
+ }
231
+ end
201
232
  end
202
233
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # session_operation.rb -- Base class for operations
5
+ #
6
+ # Author: Russell Sim
7
+ # Copyright (c) 2024 Russell Sim
8
+ # SPDX-License-Identifier: MIT
9
+
10
+ require "rider_server/operation"
11
+
12
+ module RiderServer
13
+ class SessionOperation < Operation
14
+ # Operations registry
15
+ @operations = {}
16
+ end
17
+ end
@@ -0,0 +1,61 @@
1
+ require "rider_server/session_operation"
2
+ require "rider_server/response"
3
+ require "rider_server/utils"
4
+
5
+ module RiderServer
6
+ SessionOperation.define do
7
+ name "eval"
8
+ documentation "Evaluate a string of code"
9
+
10
+ argument :id, :string, "The request id", required: true
11
+ argument :code, :string, "The code to evaluate"
12
+ argument :ns, :string, "The namespace to evaluate the code in"
13
+ argument :file, :string, "The file the code is from"
14
+ argument :line, :integer, "The line number the code is from"
15
+
16
+ def handle(session, operation)
17
+ # TODO should do something if the session is nil
18
+
19
+ response = Response.new(operation)
20
+
21
+ # Abort if there is an evaluation with the same id
22
+ if session.running_evaluation?(operation["id"])
23
+ msg = "Evaluation already in progress for #{operation["id"]}"
24
+ log.warn(msg)
25
+ response.set("value", msg)
26
+ response.status("eval-error", "done")
27
+ send_response(response)
28
+ return
29
+ end
30
+
31
+ session.push_history(operation)
32
+ code = operation["code"]
33
+ ns = operation["ns"]
34
+ file = operation["file"] || ""
35
+ line = operation["line"] || 0
36
+
37
+ eval_thread = Thread.new do
38
+ begin
39
+ value = session.workspace.evaluate(code, ns, file, line)
40
+ response.set("value", Utils.rider_display(value))
41
+ response.set("ns", session.workspace.evaluate("inspect"))
42
+ response.status("done")
43
+ session.add_result(operation["id"], value)
44
+ session.send_response(response)
45
+ rescue EvalInterrupt, ScriptError, StandardError => e
46
+ response.set("ex", e.inspect)
47
+ response.set("ns", session.workspace.evaluate("inspect"))
48
+ response.status("eval-error", "done")
49
+ exception = session.add_exception(operation["id"], e)
50
+ response.set("rider/exception-id", exception["id"])
51
+ session.send_response(response)
52
+ end
53
+
54
+ session.remove_evaluation(operation["id"])
55
+ end
56
+
57
+ session.add_evaluation(operation["id"], eval_thread)
58
+ nil
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,123 @@
1
+ require "rider_server/inspect"
2
+ require "rider_server/operation"
3
+ require "rider_server/response"
4
+ require "rider_server/utils"
5
+
6
+ module RiderServer
7
+ SessionOperation.define do
8
+ name "inspect"
9
+ documentation "Inspect an object"
10
+
11
+ argument :id, :string, "The request id", required: true
12
+ argument :location, :array, "The location of the object to inspect"
13
+
14
+ def handle(session, operation)
15
+ response = Response.new(operation)
16
+ location = operation["location"]
17
+ raise "Location is required" if location.nil? || location.empty?
18
+
19
+ value = traverse_location(location, nil, session)
20
+ response.set("name", Utils.rider_display(value))
21
+
22
+ if value.is_a?(Array)
23
+ value = value.map.with_index do |item, index|
24
+ {
25
+ "name" => item.to_s,
26
+ "value" => Utils.rider_display(item),
27
+ "inspect-location" => "rider_array_item:#{index}"
28
+ }
29
+ end
30
+ response.set("value-array", value)
31
+ elsif value.is_a?(Hash) || value.eql?(::ENV)
32
+ value = value.map do |key, value|
33
+ [
34
+ {
35
+ "name" => key.to_s,
36
+ "value" => Utils.rider_display(key),
37
+ "inspect-location" => "rider_hash_key:#{key.hash}"
38
+ },
39
+ {
40
+ "name" => value.to_s,
41
+ "value" => Utils.rider_display(value),
42
+ "inspect-location" => "rider_hash_value:#{key.hash}"
43
+ }
44
+ ]
45
+ end
46
+ response.set("value-hash", value)
47
+ elsif value.is_a?(String)
48
+ response.set("value", value.inspect)
49
+ elsif value.is_a?(Numeric)
50
+ response.set("value", value.to_s)
51
+ end
52
+
53
+ response.set("inspect-location", location)
54
+
55
+ response.set("class", RiderServer::Inspect.inspect_class(value))
56
+ response.set("singleton-class", RiderServer::Inspect.inspect_singleton_class(value))
57
+ response.set("ancestors", RiderServer::Inspect.inspect_ancestors(value))
58
+ response.set("constants", RiderServer::Inspect.inspect_constants(value))
59
+ response.set("methods", RiderServer::Inspect.inspect_methods(value))
60
+ response.set("instance-variables", RiderServer::Inspect.inspect_instance_variables(value))
61
+ response.set("instance-methods", RiderServer::Inspect.inspect_instance_methods(value))
62
+ response.set("class-variables", RiderServer::Inspect.inspect_class_variables(value))
63
+ response.status("done")
64
+ response
65
+ end
66
+
67
+ def traverse_location(location, ctx, session)
68
+ loc = location.first
69
+ locs = location.drop(1)
70
+
71
+ if locs.empty?
72
+ lookup_object(loc, ctx, session)
73
+ else
74
+ traverse_location(locs, lookup_object(loc, ctx, session), session)
75
+ end
76
+ end
77
+
78
+ def lookup_object(loc, ctx, session)
79
+ lookup, identifier = loc.split(":", 2)
80
+
81
+ case lookup
82
+ when "rider_main"
83
+ "main"
84
+ when "rider_history"
85
+ rider_history(identifier, nil, session)
86
+ when "rider_exception"
87
+ rider_exception(identifier, nil, session)
88
+ when "rider_stackframe_variable"
89
+ frame_id, local_var = identifier.split(":", 2)
90
+ ctx.__rider_bindings_stack[frame_id.to_i].local_variable_get(local_var)
91
+ when "rider_array_item"
92
+ ctx[identifier.to_i]
93
+ when "rider_hash_key"
94
+ hash = identifier.to_i
95
+ ctx.keys.find { |key| key.hash == hash }
96
+ when "rider_hash_value"
97
+ hash = identifier.to_i
98
+ key = ctx.keys.find { |key| key.hash == hash }
99
+ ctx.fetch(key)
100
+ when "toplevel_const_get"
101
+ Object.const_get(identifier)
102
+ when "instance_variable_get"
103
+ ctx.instance_variable_get(identifier)
104
+ when "singleton_class"
105
+ ctx.singleton_class
106
+ when "const_get"
107
+ ctx.const_get(identifier)
108
+ when "ancestor_find"
109
+ ctx.class.ancestors.find { |item| item.to_s == identifier }
110
+ else
111
+ raise "Unknown inspect function: #{lookup}"
112
+ end
113
+ end
114
+
115
+ def rider_history(id, ctx, session)
116
+ session.get_result(id)
117
+ end
118
+
119
+ def rider_exception(id, ctx, session)
120
+ session.get_exception(id)["exception"]
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,46 @@
1
+ require "rider_server/operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "inspect-exception"
7
+ documentation "Inspect an exception."
8
+
9
+ argument :id, :string, "The request id", required: true
10
+ argument :"exception-id", :string, "The exception id"
11
+
12
+ def handle(session, operation)
13
+ response = Response.new(operation)
14
+ exception_id = operation["exception-id"]
15
+
16
+ value = session.get_exception(exception_id)
17
+ response.set("inspect-location", value["id"])
18
+ response.set("exception-id", value["id"])
19
+ response.set("source-operation-id", value["operation_id"])
20
+ response.set("created-at", value["created_at"].to_s)
21
+
22
+ exception = value["exception"]
23
+ response.set("value", exception.inspect)
24
+ response.set("stacktrace", encode_stacktrace(exception, exception_id))
25
+
26
+ response.status("done")
27
+ response
28
+ end
29
+
30
+ def encode_stacktrace(exception, exception_id)
31
+ exception.backtrace.zip(exception.__rider_bindings_stack).map.with_index do |(line, frame), frame_id|
32
+ {
33
+ "line" => line,
34
+ "inspect-location" => ["rider_exception:#{exception_id}", "rider_stackframe:#{frame_id}"],
35
+ "frame" => (frame&.local_variables || []).map do |var|
36
+ {
37
+ "name" => var.to_s,
38
+ "value" => Utils.rider_display(frame.local_variable_get(var)),
39
+ "inspect-location" => ["rider_exception:#{exception_id}", "rider_stackframe_variable:#{frame_id}:#{var}"]
40
+ }
41
+ end
42
+ }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ require "rider_server/operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "interrupt"
7
+ documentation "Interrupts the evaluation of a session."
8
+
9
+ argument :id, :string, "The request id", required: true
10
+ argument :"interrupt-id", :string, "The ID of the evaluation to interrupt.", required: true
11
+
12
+ def handle(session, operation)
13
+ # TODO should do something if the session is nil
14
+
15
+ if operation["interrupt-id"].nil? || operation["interrupt-id"].empty?
16
+ if session.evaluations.empty?
17
+ raise ArgumentError, "No evaluations to interrupt"
18
+ end
19
+ session.interrupt_evaluation(session.evaluations.keys.max)
20
+ else
21
+ session.interrupt_evaluation(operation["interrupt-id"])
22
+ end
23
+
24
+ response = Response.new(operation)
25
+ response.status("interrupted", "done")
26
+ response
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ require "rider_server/operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "load-path"
7
+ documentation "Return the current load path."
8
+
9
+ argument :id, :string, "The request id", required: true
10
+
11
+ def handle(session, operation)
12
+ response = Response.new(operation)
13
+ load_path = session.workspace.evaluate("$LOAD_PATH", "main")
14
+ response.set("load-path", load_path)
15
+ response.status("done")
16
+ response
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ require "rider_server/session_operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "ls-exceptions"
7
+ documentation "List all exceptions that exist in the sessions cache."
8
+
9
+ argument :id, :string, "The request id", required: true
10
+
11
+ def handle(session, operation)
12
+ response = Response.new(operation)
13
+
14
+ exceptions = session.exceptions.map do |item|
15
+ {
16
+ "id" => item["id"],
17
+ "operation-id" => item["operation_id"],
18
+ "created-at" => item["created_at"].iso8601,
19
+ "exception" => item["exception"].inspect
20
+ }
21
+ end
22
+
23
+ response.set("exceptions", exceptions)
24
+ response.status("done")
25
+ response
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ require "rider_server/operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "ls-services"
7
+ documentation "List all services."
8
+
9
+ argument :id, :string, "The request id", required: true
10
+
11
+ def handle(session, operation)
12
+ response = Response.new(operation)
13
+ response.set("services", session.list_services)
14
+ response.status("done")
15
+ response
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,42 @@
1
+ require "rider_server/operation"
2
+ require "rider_server/response"
3
+
4
+ module RiderServer
5
+ SessionOperation.define do
6
+ name "service"
7
+ documentation "Control a Ruby service integration"
8
+
9
+ argument :id, :string, "The request id", required: true
10
+ argument :service, :string, "The name of the Ruby service to control", required: true
11
+ argument :state, :string, "The desired state of the service", required: true
12
+
13
+ def handle(session, operation)
14
+ response = Response.new(operation)
15
+
16
+ service = operation["service"]
17
+ state = operation["state"]
18
+
19
+ current_state = session.service_state(service)
20
+
21
+ case state
22
+ when "start"
23
+ raise "Service already running" if current_state == "running"
24
+ session.start_service(service, response.id)
25
+ response.set("rider/stream", "true")
26
+ when "stop"
27
+ raise "Can't stop service #{service}. It's not running." if current_state == "stopped"
28
+ session.stop_service(service)
29
+ response.status("done")
30
+ else
31
+ # TODO: throwing this exception will caues a "done" response
32
+ # to be sent, which will implicitly close the stream. It
33
+ # might make sense to handle this more gracefully here.
34
+ raise "Unknown state #{state}"
35
+ end
36
+
37
+ response.set("service", service)
38
+ response.set("state", session.service_state(service).to_s)
39
+ response
40
+ end
41
+ end
42
+ end