rider-server 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.build.yml +3 -3
- data/CHANGELOG.md +23 -1
- data/COPYING +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +5 -0
- data/exe/rider-server +3 -3
- data/lib/rider_server/config.rb +76 -0
- data/lib/rider_server/core_ext/array.rb +3 -1
- data/lib/rider_server/core_ext/class.rb +5 -0
- data/lib/rider_server/core_ext/env.rb +14 -0
- data/lib/rider_server/core_ext/hash.rb +3 -1
- data/lib/rider_server/core_ext/kernel.rb +5 -0
- data/lib/rider_server/core_ext/module.rb +18 -0
- data/lib/rider_server/core_ext/object.rb +7 -1
- data/lib/rider_server/core_ext/string.rb +3 -1
- data/lib/rider_server/core_ext/symbol.rb +3 -1
- data/lib/rider_server/exception_extension.rb +2 -0
- data/lib/rider_server/inspect.rb +115 -43
- data/lib/rider_server/logger.rb +11 -4
- data/lib/rider_server/operation.rb +39 -20
- data/lib/rider_server/{ops → operations}/clone.rb +3 -2
- data/lib/rider_server/{ops → operations}/close.rb +3 -2
- data/lib/rider_server/operations/completions.rb +146 -0
- data/lib/rider_server/operations/lookup.rb +102 -0
- data/lib/rider_server/operations/ls_sessions.rb +51 -0
- data/lib/rider_server/operations/toggle_catch_all_exceptions.rb +24 -0
- data/lib/rider_server/operations.rb +43 -69
- data/lib/rider_server/request.rb +61 -0
- data/lib/rider_server/response.rb +10 -2
- data/lib/rider_server/server.rb +29 -17
- data/lib/rider_server/services/capture_exceptions.rb +18 -2
- data/lib/rider_server/services/rails.rb +1 -1
- data/lib/rider_server/session.rb +77 -34
- data/lib/rider_server/session_operation.rb +17 -0
- data/lib/rider_server/session_operations/eval.rb +61 -0
- data/lib/rider_server/session_operations/inspect.rb +123 -0
- data/lib/rider_server/session_operations/inspect_exception.rb +46 -0
- data/lib/rider_server/session_operations/interrupt.rb +29 -0
- data/lib/rider_server/session_operations/load_path.rb +19 -0
- data/lib/rider_server/session_operations/ls_exceptions.rb +28 -0
- data/lib/rider_server/session_operations/ls_services.rb +18 -0
- data/lib/rider_server/session_operations/service.rb +42 -0
- data/lib/rider_server/session_operations/set_namespace.rb +82 -0
- data/lib/rider_server/session_operations/set_namespace_variable.rb +81 -0
- data/lib/rider_server/session_operations/stdin.rb +19 -0
- data/lib/rider_server/utils.rb +7 -7
- data/lib/rider_server/validate/array.rb +32 -0
- data/lib/rider_server/validate/base.rb +28 -0
- data/lib/rider_server/validate/boolean.rb +47 -0
- data/lib/rider_server/validate/hash.rb +32 -0
- data/lib/rider_server/validate/integer.rb +56 -0
- data/lib/rider_server/validate/predicates.rb +30 -0
- data/lib/rider_server/validate/string.rb +60 -0
- data/lib/rider_server/validate/symbol.rb +90 -0
- data/lib/rider_server/validate.rb +15 -0
- data/lib/rider_server/version.rb +1 -1
- data/lib/rider_server/workspace.rb +1 -1
- data/lib/rider_server.rb +3 -1
- metadata +55 -24
- data/README.md +0 -44
- data/lib/rider_server/ops/completions.rb +0 -100
- data/lib/rider_server/ops/eval.rb +0 -62
- data/lib/rider_server/ops/inspect.rb +0 -121
- data/lib/rider_server/ops/inspect_exception.rb +0 -47
- data/lib/rider_server/ops/interrupt.rb +0 -30
- data/lib/rider_server/ops/load_path.rb +0 -20
- data/lib/rider_server/ops/lookup.rb +0 -83
- data/lib/rider_server/ops/ls_exceptions.rb +0 -29
- data/lib/rider_server/ops/ls_services.rb +0 -19
- data/lib/rider_server/ops/ls_sessions.rb +0 -52
- data/lib/rider_server/ops/service.rb +0 -43
- data/lib/rider_server/ops/set_namespace.rb +0 -79
- data/lib/rider_server/ops/set_namespace_variable.rb +0 -80
- data/lib/rider_server/ops/stdin.rb +0 -20
- data/lib/rider_server/ops/toggle_catch_all_exceptions.rb +0 -27
@@ -13,38 +13,57 @@ module RiderServer
|
|
13
13
|
class Operation
|
14
14
|
include RiderServer::Logger
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
# Operations registry
|
17
|
+
@operations = {}
|
18
|
+
class << self
|
19
|
+
attr_reader :operations
|
19
20
|
end
|
20
21
|
|
21
|
-
def self.
|
22
|
-
@
|
22
|
+
def self.handles?(request)
|
23
|
+
@operations.key? request.op
|
23
24
|
end
|
24
25
|
|
25
|
-
def self.
|
26
|
-
|
26
|
+
def self.handle(context, request)
|
27
|
+
operation = @operations[request.op]
|
28
|
+
operation.validate_request!(request)
|
29
|
+
operation.handle(context, request)
|
30
|
+
end
|
27
31
|
|
28
|
-
|
29
|
-
|
32
|
+
def self.define(&block)
|
33
|
+
instance = new
|
34
|
+
instance.instance_eval(&block)
|
35
|
+
raise ArgumentError, "Operation #{self} must have a name" if instance.name.empty?
|
36
|
+
raise ArgumentError, "Operation #{self} must have a documentation string" if instance.documentation.empty?
|
37
|
+
raise ArgumentError, "Operation #{self} must have at least one argument" if instance.arguments.empty?
|
38
|
+
@operations[instance.name] = instance
|
39
|
+
instance
|
40
|
+
end
|
30
41
|
|
31
|
-
|
42
|
+
attr_reader :arguments
|
43
|
+
|
44
|
+
def documentation(value = nil)
|
45
|
+
@documentation = value if value
|
46
|
+
@documentation
|
32
47
|
end
|
33
48
|
|
34
|
-
def
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@arguments = self.class.instance_variable_get(:@arguments) || []
|
38
|
-
raise ArgumentError, "Operation must have a documentation string" if @documentation.empty?
|
39
|
-
raise ArgumentError, "Operation must have at least one argument" if @arguments.empty?
|
49
|
+
def name(value = nil)
|
50
|
+
@name = value if value
|
51
|
+
@name
|
40
52
|
end
|
41
53
|
|
42
|
-
def
|
43
|
-
|
54
|
+
def initialize
|
55
|
+
@name = ""
|
56
|
+
@arguments = []
|
57
|
+
@documentation = ""
|
44
58
|
end
|
45
59
|
|
46
|
-
def
|
47
|
-
|
60
|
+
def argument(name, type, description, required: false)
|
61
|
+
@arguments ||= []
|
62
|
+
|
63
|
+
raise ArgumentError, "Invalid argument type #{type}" \
|
64
|
+
unless [:string, :integer, :array].include?(type)
|
65
|
+
|
66
|
+
@arguments << {name: name, type: type, required: required, description: description}
|
48
67
|
end
|
49
68
|
|
50
69
|
def validate_request!(request)
|
@@ -3,14 +3,15 @@ require "rider_server/response"
|
|
3
3
|
|
4
4
|
module RiderServer
|
5
5
|
module Ops
|
6
|
-
|
6
|
+
Operation.define do
|
7
|
+
name "clone"
|
7
8
|
documentation "Clone a session"
|
8
9
|
|
9
10
|
argument :id, :string, "The request id", required: true
|
10
11
|
argument :session, :string, "The session to clone"
|
11
12
|
|
12
13
|
# Handle the clone operation, session will be nil
|
13
|
-
def handle(
|
14
|
+
def handle(controller, operation)
|
14
15
|
response = Response.new(operation)
|
15
16
|
|
16
17
|
# Clone a specific session if specified
|
@@ -3,14 +3,15 @@ require "rider_server/response"
|
|
3
3
|
|
4
4
|
module RiderServer
|
5
5
|
module Ops
|
6
|
-
|
6
|
+
Close = Operation.define do
|
7
|
+
name "close"
|
7
8
|
documentation "Close a specific session."
|
8
9
|
|
9
10
|
argument :id, :string, "The request id", required: true
|
10
11
|
argument :session, :string, "The session to close", required: true
|
11
12
|
|
12
13
|
# Handle the close operation, session will be nil
|
13
|
-
def handle(
|
14
|
+
def handle(controller, operation)
|
14
15
|
response = Response.new(operation)
|
15
16
|
|
16
17
|
session_id = operation["session"]
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require "rider_server/operation"
|
2
|
+
require "rider_server/response"
|
3
|
+
require "rider_server/inspect"
|
4
|
+
require "rider_server/workspace"
|
5
|
+
|
6
|
+
module RiderServer
|
7
|
+
Operation.define do
|
8
|
+
name "completions"
|
9
|
+
documentation "Get completions for a given prefix"
|
10
|
+
|
11
|
+
argument :id, :string, "The request id", required: true
|
12
|
+
argument :prefix, :string, "The prefix to complete", required: true
|
13
|
+
argument :complete_fn, :string, "What completion function to use, top-level, module, method or singleton-method"
|
14
|
+
argument :ns, :string, "The namespace to search for completions"
|
15
|
+
|
16
|
+
@workspace = Workspace.new
|
17
|
+
|
18
|
+
def handle(controller, operation)
|
19
|
+
response = Response.new(operation)
|
20
|
+
|
21
|
+
prefix = operation["prefix"]
|
22
|
+
ns = operation["ns"]
|
23
|
+
complete_fn = operation.fetch("complete-fn", "top-level")
|
24
|
+
|
25
|
+
if prefix.nil? || parse_string(prefix).nil?
|
26
|
+
response.set("completions", [])
|
27
|
+
else
|
28
|
+
ns_object = if ns.empty?
|
29
|
+
::Object
|
30
|
+
else
|
31
|
+
@workspace.lookup_module(ns)
|
32
|
+
end
|
33
|
+
|
34
|
+
# If the prefix isn't parseable, return an empty list
|
35
|
+
response.set("completions", lookup_module(prefix, klass: ns_object, completion_fn: complete_fn))
|
36
|
+
end
|
37
|
+
response.status("done")
|
38
|
+
response
|
39
|
+
end
|
40
|
+
|
41
|
+
def lookup_module(module_name, klass: ::Object, completion_fn: "top-level")
|
42
|
+
node = parse_string(module_name)
|
43
|
+
case node.type
|
44
|
+
when :const
|
45
|
+
ns = lookup_module_ast(node.children.first, klass)
|
46
|
+
prefix = node.children.last
|
47
|
+
all_constants(ns, prefix)
|
48
|
+
when :send
|
49
|
+
ns = lookup_module_ast(node.children.first, klass)
|
50
|
+
prefix = node.children.last
|
51
|
+
if completion_fn == "method"
|
52
|
+
all_constants(ns, prefix) + encode_methods(class_instance_methods(ns, prefix) + class_singleton_methods(::Kernel, prefix))
|
53
|
+
else
|
54
|
+
all_constants(ns, prefix) + encode_methods(class_methods(ns, prefix) + class_singleton_methods(::Kernel, prefix))
|
55
|
+
end
|
56
|
+
else
|
57
|
+
[]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_string(string)
|
62
|
+
buffer = Parser::Source::Buffer.new("(string)")
|
63
|
+
buffer.source = string
|
64
|
+
Parser::CurrentRuby.new.parse(buffer)
|
65
|
+
end
|
66
|
+
|
67
|
+
def lookup_module_ast(node, klass = ::Object)
|
68
|
+
return klass if node.nil?
|
69
|
+
|
70
|
+
case node.type
|
71
|
+
when :cbase
|
72
|
+
::Object
|
73
|
+
when :const
|
74
|
+
if node.children.first.nil?
|
75
|
+
klass.const_get(node.children.last)
|
76
|
+
else
|
77
|
+
lookup_module_ast(node.children.first, klass).const_get(node.children.last)
|
78
|
+
end
|
79
|
+
else
|
80
|
+
raise ModuleLookupError, "Unknown node type #{node.type}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def parent_constant(ns_object)
|
85
|
+
ns_object_name = ns_object.to_s
|
86
|
+
parent_name, _ = ns_object_name.rpartition("::")
|
87
|
+
parent_name.empty? ? nil : ::Object.const_get(parent_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def all_constants(ns_object, prefix)
|
91
|
+
consts = []
|
92
|
+
ns_object.constants.grep(/^#{prefix}/).each do |constant|
|
93
|
+
consts << constant.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
# Search up the tree to find all the other constants
|
97
|
+
constant = ns_object
|
98
|
+
loop do
|
99
|
+
constant = parent_constant(constant)
|
100
|
+
break if constant.nil?
|
101
|
+
|
102
|
+
constant.constants.grep(/^#{prefix}/).each do |constant|
|
103
|
+
consts << constant.to_s
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
::Object.constants.grep(/^#{prefix}/).each do |constant|
|
108
|
+
consts << constant.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
consts.map do |constant|
|
112
|
+
{
|
113
|
+
"candidate" => constant,
|
114
|
+
"type" => "constant"
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
##
|
120
|
+
# Get all instance methods that match a +prefix+ for a given object +obj+
|
121
|
+
#
|
122
|
+
# This method should be called when in the context of an
|
123
|
+
# instance method, as all other instance methods will be
|
124
|
+
# acessable.
|
125
|
+
def class_instance_methods(obj, prefix)
|
126
|
+
Set.new(obj.instance_methods.grep(/^#{prefix}/))
|
127
|
+
end
|
128
|
+
|
129
|
+
def class_methods(obj, prefix)
|
130
|
+
Set.new(obj.methods.grep(/^#{prefix}/))
|
131
|
+
end
|
132
|
+
|
133
|
+
def class_singleton_methods(obj, prefix)
|
134
|
+
Set.new(obj.singleton_methods.grep(/^#{prefix}/))
|
135
|
+
end
|
136
|
+
|
137
|
+
def encode_methods(methods)
|
138
|
+
methods.map do |method|
|
139
|
+
{
|
140
|
+
"candidate" => method.to_s,
|
141
|
+
"type" => "method"
|
142
|
+
}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require "rider_server/operation"
|
2
|
+
require "rider_server/response"
|
3
|
+
require "rider_server/inspect"
|
4
|
+
|
5
|
+
module RiderServer
|
6
|
+
Operation.define do
|
7
|
+
name "lookup"
|
8
|
+
documentation "Get info about a symbol."
|
9
|
+
|
10
|
+
argument :id, :string, "The request id", required: true
|
11
|
+
argument :sym, :string, "The symbol to lookup", required: true
|
12
|
+
argument :lookup_fn, :string, "What lookup function to use, top-level, module, method or singleton-method"
|
13
|
+
argument :ns, :string, "The namespace to search for completions"
|
14
|
+
|
15
|
+
def handle(controller, operation)
|
16
|
+
response = Response.new(operation)
|
17
|
+
ns = operation["ns"]
|
18
|
+
sym = operation["sym"]
|
19
|
+
lookup_fn = operation.fetch("lookup-fn", "top-level")
|
20
|
+
|
21
|
+
if sym.nil? || parse_string(sym).nil?
|
22
|
+
response.set("info", {})
|
23
|
+
else
|
24
|
+
ns_object = if ns.empty?
|
25
|
+
::Object
|
26
|
+
else
|
27
|
+
workspace.lookup_module(ns)
|
28
|
+
end
|
29
|
+
|
30
|
+
# If the prefix isn't parseable, return an empty list
|
31
|
+
response.set("info", lookup_module(sym, klass: ns_object, lookup_fn: lookup_fn))
|
32
|
+
end
|
33
|
+
response.status("done")
|
34
|
+
response
|
35
|
+
end
|
36
|
+
|
37
|
+
def lookup_module(module_name, klass: ::Object, lookup_fn: "top-level")
|
38
|
+
node = parse_string(module_name)
|
39
|
+
case node.type
|
40
|
+
when :const
|
41
|
+
ns = lookup_module_ast(node.children.first, klass)
|
42
|
+
name node.children.last
|
43
|
+
encode_const(ns, name)
|
44
|
+
when :send
|
45
|
+
ns = lookup_module_ast(node.children.first, klass)
|
46
|
+
prefix = node.children.last
|
47
|
+
if lookup_fn == "method"
|
48
|
+
encode_method(ns.instance_method(prefix))
|
49
|
+
else
|
50
|
+
encode_method(ns.method(prefix))
|
51
|
+
end
|
52
|
+
else
|
53
|
+
[]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_string(string)
|
58
|
+
buffer = Parser::Source::Buffer.new("(string)")
|
59
|
+
buffer.source = string
|
60
|
+
Parser::CurrentRuby.new.parse(buffer)
|
61
|
+
end
|
62
|
+
|
63
|
+
def lookup_module_ast(node, klass = ::Object)
|
64
|
+
return klass if node.nil?
|
65
|
+
|
66
|
+
case node.type
|
67
|
+
when :cbase
|
68
|
+
::Object
|
69
|
+
when :const
|
70
|
+
if node.children.first.nil?
|
71
|
+
klass.const_get(node.children.last)
|
72
|
+
else
|
73
|
+
lookup_module_ast(node.children.first, klass).const_get(node.children.last)
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise ModuleLookupError, "Unknown node type #{node.type}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def encode_const(ns, const_name)
|
81
|
+
if ns.const_defined? const_name
|
82
|
+
{
|
83
|
+
"name" => "#{ns}::#{const_name}",
|
84
|
+
"source_location" => ns.const_source_location(const_name)
|
85
|
+
}
|
86
|
+
else
|
87
|
+
{}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def encode_method(thing)
|
92
|
+
{
|
93
|
+
"name" => thing.name.to_s,
|
94
|
+
"source_location" => thing.source_location
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def workspace
|
99
|
+
@workspace ||= Workspace.new
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "rider_server/operation"
|
2
|
+
require "rider_server/response"
|
3
|
+
|
4
|
+
module RiderServer
|
5
|
+
Operation.define do
|
6
|
+
name "ls-sessions"
|
7
|
+
documentation "List all sessions"
|
8
|
+
|
9
|
+
argument :id, :string, "The request id", required: true
|
10
|
+
|
11
|
+
def handle(controller, operation)
|
12
|
+
response = Response.new(operation)
|
13
|
+
response.set("sessions", controller.sessions.map { |k, v| k })
|
14
|
+
response.set("rider/session-headings",
|
15
|
+
[
|
16
|
+
{
|
17
|
+
"id" => "id",
|
18
|
+
"name" => "ID",
|
19
|
+
"length" => 36
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"id" => "namespace_name",
|
23
|
+
"name" => "Namespace",
|
24
|
+
"length" => 25
|
25
|
+
},
|
26
|
+
{
|
27
|
+
"id" => "history_items",
|
28
|
+
"name" => "History Items",
|
29
|
+
"length" => 5
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"id" => "exceptions",
|
33
|
+
"name" => "Exceptions",
|
34
|
+
"length" => 5
|
35
|
+
}
|
36
|
+
])
|
37
|
+
response.set("rider/sessions", controller.sessions.map { |k, v| session_summary(v) })
|
38
|
+
response.status("done")
|
39
|
+
response
|
40
|
+
end
|
41
|
+
|
42
|
+
def session_summary(session)
|
43
|
+
{
|
44
|
+
"id" => session.id,
|
45
|
+
"namespace_name" => session.workspace.namespace_name,
|
46
|
+
"history_items" => session.history.length.to_s,
|
47
|
+
"exceptions" => session.exceptions.length.to_s
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rider_server/operation"
|
2
|
+
require "rider_server/response"
|
3
|
+
|
4
|
+
module RiderServer
|
5
|
+
Operation.define do
|
6
|
+
name "toggle-catch-all-exceptions"
|
7
|
+
documentation "Enable catching all exceptions. Not just the ones with a session."
|
8
|
+
|
9
|
+
argument :id, :string, "The request id", required: true
|
10
|
+
|
11
|
+
def handle(controller, operation)
|
12
|
+
value = if controller.toggle_capture_exceptions
|
13
|
+
"enabled"
|
14
|
+
else
|
15
|
+
"disabled"
|
16
|
+
end
|
17
|
+
|
18
|
+
response = Response.new(operation)
|
19
|
+
response.status("done")
|
20
|
+
response.set("value", value)
|
21
|
+
response
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -12,112 +12,78 @@ require "rider_server/session"
|
|
12
12
|
require "rider_server/errors"
|
13
13
|
require "rider_server/response"
|
14
14
|
require "rider_server/core_ext/array"
|
15
|
+
require "rider_server/core_ext/class"
|
16
|
+
require "rider_server/core_ext/env"
|
15
17
|
require "rider_server/core_ext/hash"
|
18
|
+
require "rider_server/core_ext/kernel"
|
19
|
+
require "rider_server/core_ext/module"
|
16
20
|
require "rider_server/core_ext/object"
|
17
21
|
require "rider_server/core_ext/string"
|
18
22
|
require "rider_server/core_ext/symbol"
|
19
|
-
require "rider_server/
|
20
|
-
require "rider_server/
|
21
|
-
require "rider_server/
|
22
|
-
require "rider_server/
|
23
|
-
require "rider_server/
|
24
|
-
require "rider_server/
|
25
|
-
require "rider_server/ops/interrupt"
|
26
|
-
require "rider_server/ops/load_path"
|
27
|
-
require "rider_server/ops/lookup"
|
28
|
-
require "rider_server/ops/ls_exceptions"
|
29
|
-
require "rider_server/ops/ls_services"
|
30
|
-
require "rider_server/ops/ls_sessions"
|
31
|
-
require "rider_server/ops/service"
|
32
|
-
require "rider_server/ops/set_namespace"
|
33
|
-
require "rider_server/ops/set_namespace_variable"
|
34
|
-
require "rider_server/ops/toggle_catch_all_exceptions"
|
23
|
+
require "rider_server/operations/clone"
|
24
|
+
require "rider_server/operations/close"
|
25
|
+
require "rider_server/operations/completions"
|
26
|
+
require "rider_server/operations/lookup"
|
27
|
+
require "rider_server/operations/ls_sessions"
|
28
|
+
require "rider_server/operations/toggle_catch_all_exceptions"
|
35
29
|
|
36
30
|
module RiderServer
|
31
|
+
def self.create_operation_handler(config, response_queue)
|
32
|
+
Operations.new(config, response_queue)
|
33
|
+
end
|
34
|
+
|
37
35
|
class Operations
|
38
36
|
include RiderServer::Logger
|
39
37
|
|
40
|
-
attr_reader :executing_requests
|
41
38
|
attr_reader :sessions
|
42
|
-
attr_reader :stdin, :stdout, :stderr
|
43
39
|
attr_reader :response_queue
|
44
|
-
attr_accessor :sessions_catching_exceptions
|
45
|
-
|
46
|
-
OPERATIONS = [
|
47
|
-
Ops::Clone,
|
48
|
-
Ops::Close,
|
49
|
-
Ops::Completions,
|
50
|
-
Ops::Eval,
|
51
|
-
Ops::Inspect,
|
52
|
-
Ops::InspectException,
|
53
|
-
Ops::Interrupt,
|
54
|
-
Ops::LoadPath,
|
55
|
-
Ops::Lookup,
|
56
|
-
Ops::LsExceptions,
|
57
|
-
Ops::LsServices,
|
58
|
-
Ops::LsSessions,
|
59
|
-
Ops::Service,
|
60
|
-
Ops::SetNamespace,
|
61
|
-
Ops::SetNamespaceVariable,
|
62
|
-
Ops::ToggleCatchAllExceptions
|
63
|
-
]
|
64
40
|
|
65
|
-
def initialize(response_queue)
|
41
|
+
def initialize(config, response_queue)
|
42
|
+
@config = config
|
66
43
|
@sessions = {}
|
67
44
|
@response_queue = response_queue
|
68
|
-
@executing_requests = []
|
69
|
-
@operations = OPERATIONS.each_with_object({}) do |klass, h|
|
70
|
-
h[klass.operation_name] = klass.new(self)
|
71
|
-
end
|
72
|
-
@sessions_catching_exceptions = []
|
73
45
|
end
|
74
46
|
|
75
47
|
def send_response(response)
|
76
48
|
if response
|
77
49
|
@response_queue.push(response)
|
78
|
-
if response.done?
|
79
|
-
@executing_requests.delete(response.id)
|
80
|
-
end
|
81
50
|
end
|
82
51
|
end
|
83
52
|
|
84
|
-
def handle(
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
session
|
91
|
-
|
92
|
-
if @operations.key? op
|
93
|
-
@operations[op].validate_request!(operation)
|
94
|
-
send_response(@operations[op].handle(session, operation))
|
53
|
+
def handle(request)
|
54
|
+
op = request.op
|
55
|
+
log.info("Handling operation '#{request.inspect}'")
|
56
|
+
session = get_session(request.session)
|
57
|
+
if Operation.handles?(request)
|
58
|
+
send_response(Operation.handle(self, request))
|
59
|
+
elsif !session.nil? && SessionOperation.handles?(request)
|
60
|
+
send_response(SessionOperation.handle(session, request))
|
95
61
|
else
|
96
62
|
log.warn("Unknown operation '#{op}' requested")
|
97
|
-
response = Response.new(
|
63
|
+
response = Response.new(request)
|
98
64
|
response.status "unknown-op", "done"
|
99
65
|
send_response(response)
|
100
66
|
end
|
101
67
|
rescue ScriptError, StandardError => e
|
102
|
-
response = Response.new(
|
68
|
+
response = Response.new(request)
|
103
69
|
response.set("ex", e.inspect)
|
104
70
|
response.set("out", e.full_message)
|
105
71
|
response.status("eval-error", "done")
|
72
|
+
log.error "Error handling request: #{e}\n #{e.backtrace.join("\n")}"
|
106
73
|
if session
|
107
|
-
|
108
|
-
response.set("rider/exception-id",
|
109
|
-
end
|
110
|
-
|
111
|
-
# Broadcast the exception to sessions that are listening
|
112
|
-
@sessions_catching_exceptions.each do |s|
|
113
|
-
next if session == s
|
114
|
-
@sessions[s]&.add_exception(operation["id"], e)
|
74
|
+
exception = session.add_exception(request.id, e)
|
75
|
+
response.set("rider/exception-id", exception["id"])
|
115
76
|
end
|
77
|
+
RiderServer::Services::CaptureExceptions.handle_exception(e)
|
116
78
|
send_response(response)
|
117
79
|
end
|
118
80
|
|
81
|
+
#
|
82
|
+
# Sessions
|
83
|
+
#
|
84
|
+
|
119
85
|
def new_session
|
120
|
-
|
86
|
+
RiderServer.create_session(@config, @response_queue)
|
121
87
|
end
|
122
88
|
|
123
89
|
def get_session(id)
|
@@ -132,5 +98,13 @@ module RiderServer
|
|
132
98
|
def delete_session(session_id)
|
133
99
|
@sessions.delete(session_id)
|
134
100
|
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Exceptions
|
104
|
+
#
|
105
|
+
|
106
|
+
def toggle_capture_exceptions
|
107
|
+
@config.capture_exceptions(!@config.capture_exceptions)
|
108
|
+
end
|
135
109
|
end
|
136
110
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# request.rb -- A simple wrapper around a request
|
5
|
+
#
|
6
|
+
# Author: Russell Sim
|
7
|
+
# Copyright (c) 2024 Russell Sim
|
8
|
+
# SPDX-License-Identifier: MIT
|
9
|
+
|
10
|
+
require "bencode"
|
11
|
+
|
12
|
+
module RiderServer
|
13
|
+
class Request
|
14
|
+
def initialize(operation)
|
15
|
+
raise "Operation ID cannot be nil" if operation["id"].nil?
|
16
|
+
raise "Operation name cannot be nil" if operation["op"].nil?
|
17
|
+
|
18
|
+
@data = operation
|
19
|
+
end
|
20
|
+
|
21
|
+
def op
|
22
|
+
@data["op"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def id
|
26
|
+
@data["id"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def session
|
30
|
+
@data["session"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def set(key, value)
|
34
|
+
@data[key] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch(*args)
|
38
|
+
@data.fetch(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def key?(name)
|
42
|
+
@data.key? name
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](name)
|
46
|
+
@data[name]
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=(name, value)
|
50
|
+
@data[name] = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def inspect
|
54
|
+
"#<RiderServer::Request:#{object_id} id=#{id} session=#{session} op=#{op}}>"
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_h
|
58
|
+
@data
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -42,8 +42,16 @@ module RiderServer
|
|
42
42
|
@data.fetch(*args)
|
43
43
|
end
|
44
44
|
|
45
|
-
def
|
46
|
-
@data
|
45
|
+
def key?(name)
|
46
|
+
@data.key? name
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](name)
|
50
|
+
@data[name]
|
51
|
+
end
|
52
|
+
|
53
|
+
def []=(name, value)
|
54
|
+
@data[name] = value
|
47
55
|
end
|
48
56
|
|
49
57
|
def status(*value)
|