Skip to content

Commit fc99832

Browse files
authored
Avoid need for special VOID return (#453)
* Avoid need for special VOID return * Update tests
1 parent 77dc960 commit fc99832

File tree

2 files changed

+43
-39
lines changed

2 files changed

+43
-39
lines changed

lib/ruby_lsp/ruby_lsp_rails/server.rb

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
module RubyLsp
1010
module Rails
1111
class Server
12-
VOID = Object.new
13-
14-
def initialize
12+
def initialize(stdout: $stdout, override_default_output_device: true)
1513
# Grab references to the original pipes so that we can change the default output device further down
1614
@stdin = $stdin
17-
@stdout = $stdout
15+
@stdout = stdout
1816
@stderr = $stderr
1917
@stdin.sync = true
2018
@stdout.sync = true
@@ -26,7 +24,7 @@ def initialize
2624
# # Set the default output device to be $stderr. This means that using `puts` by itself will default to printing
2725
# # to $stderr and only explicit `$stdout.puts` will go to $stdout. This reduces the chance that output coming
2826
# # from the Rails app will be accidentally sent to the client
29-
$> = $stderr
27+
$> = $stderr if override_default_output_device
3028

3129
@running = true
3230
end
@@ -44,39 +42,36 @@ def start
4442
json = @stdin.read(headers[/Content-Length: (\d+)/i, 1].to_i)
4543

4644
request = JSON.parse(json, symbolize_names: true)
47-
response = execute(request.fetch(:method), request[:params])
48-
next if response == VOID
49-
50-
json_response = response.to_json
51-
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
45+
execute(request.fetch(:method), request[:params])
5246
end
5347
end
5448

5549
def execute(request, params)
5650
case request
5751
when "shutdown"
5852
@running = false
59-
VOID
6053
when "model"
61-
resolve_database_info_from_model(params.fetch(:name))
54+
write_response(resolve_database_info_from_model(params.fetch(:name)))
6255
when "association_target_location"
63-
resolve_association_target(params)
56+
write_response(resolve_association_target(params))
6457
when "reload"
6558
::Rails.application.reloader.reload!
66-
VOID
6759
when "route_location"
68-
route_location(params.fetch(:name))
60+
write_response(route_location(params.fetch(:name)))
6961
when "route_info"
70-
resolve_route_info(params)
71-
else
72-
VOID
62+
write_response(resolve_route_info(params))
7363
end
7464
rescue => e
75-
{ error: e.full_message(highlight: false) }
65+
write_response({ error: e.full_message(highlight: false) })
7666
end
7767

7868
private
7969

70+
def write_response(response)
71+
json_response = response.to_json
72+
@stdout.write("Content-Length: #{json_response.length}\r\n\r\n#{json_response}")
73+
end
74+
8075
def resolve_route_info(requirements)
8176
if requirements[:controller]
8277
requirements[:controller] = requirements.fetch(:controller).underscore.delete_suffix("_controller")

test/ruby_lsp_rails/server_test.rb

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,27 @@
66

77
class ServerTest < ActiveSupport::TestCase
88
setup do
9-
@server = RubyLsp::Rails::Server.new
9+
@stdout = StringIO.new
10+
@server = RubyLsp::Rails::Server.new(stdout: @stdout, override_default_output_device: false)
1011
end
1112

1213
test "returns nil if model doesn't exist" do
13-
response = @server.execute("model", { name: "Foo" })
14+
@server.execute("model", { name: "Foo" })
1415
assert_nil(response.fetch(:result))
1516
end
1617

1718
test "returns nil if class is not a model" do
18-
response = @server.execute("model", { name: "Time" })
19+
@server.execute("model", { name: "Time" })
1920
assert_nil(response.fetch(:result))
2021
end
2122

2223
test "returns nil if class is an abstract model" do
23-
response = @server.execute("model", { name: "ApplicationRecord" })
24+
@server.execute("model", { name: "ApplicationRecord" })
2425
assert_nil(response.fetch(:result))
2526
end
2627

2728
test "returns nil if constant is not a class" do
28-
response = @server.execute("model", { name: "RUBY_VERSION" })
29+
@server.execute("model", { name: "RUBY_VERSION" })
2930
assert_nil(response.fetch(:result))
3031
end
3132

@@ -38,21 +39,21 @@ def <(other)
3839
end
3940
end
4041

41-
response = @server.execute("model", { name: "TestClassWithOverwrittenLessThan" })
42+
@server.execute("model", { name: "TestClassWithOverwrittenLessThan" })
4243
assert_nil(response.fetch(:result))
4344
end
4445

4546
test "handles older Rails version which don't have `schema_dump_path`" do
4647
ActiveRecord::Tasks::DatabaseTasks.send(:alias_method, :old_schema_dump_path, :schema_dump_path)
4748
ActiveRecord::Tasks::DatabaseTasks.undef_method(:schema_dump_path)
48-
response = @server.execute("model", { name: "User" })
49+
@server.execute("model", { name: "User" })
4950
assert_nil(response.fetch(:result)[:schema_file])
5051
ensure
5152
ActiveRecord::Tasks::DatabaseTasks.send(:alias_method, :schema_dump_path, :old_schema_dump_path)
5253
end
5354

5455
test "resolve association returns the location of the target class of a has_many association" do
55-
response = @server.execute(
56+
@server.execute(
5657
"association_target_location",
5758
{ model_name: "Organization", association_name: :memberships },
5859
)
@@ -61,7 +62,7 @@ def <(other)
6162
end
6263

6364
test "resolve association returns the location of the target class of a belongs_to association" do
64-
response = @server.execute(
65+
@server.execute(
6566
"association_target_location",
6667
{ model_name: "Membership", association_name: :organization },
6768
)
@@ -70,7 +71,7 @@ def <(other)
7071
end
7172

7273
test "resolve association returns the location of the target class of a has_one association" do
73-
response = @server.execute(
74+
@server.execute(
7475
"association_target_location",
7576
{ model_name: "User", association_name: :profile },
7677
)
@@ -79,7 +80,7 @@ def <(other)
7980
end
8081

8182
test "resolve association returns the location of the target class of a has_and_belongs_to_many association" do
82-
response = @server.execute(
83+
@server.execute(
8384
"association_target_location",
8485
{ model_name: "Profile", association_name: :labels },
8586
)
@@ -88,23 +89,23 @@ def <(other)
8889
end
8990

9091
test "resolve association handles invalid model name" do
91-
response = @server.execute(
92+
@server.execute(
9293
"association_target_location",
9394
{ model_name: "NotHere", association_name: :labels },
9495
)
9596
assert_nil(response.fetch(:result))
9697
end
9798

9899
test "resolve association handles invalid association name" do
99-
response = @server.execute(
100+
@server.execute(
100101
"association_target_location",
101102
{ model_name: "Membership", association_name: :labels },
102103
)
103104
assert_nil(response.fetch(:result))
104105
end
105106

106107
test "resolve association handles class_name option" do
107-
response = @server.execute(
108+
@server.execute(
108109
"association_target_location",
109110
{ model_name: "User", association_name: :location },
110111
)
@@ -113,18 +114,18 @@ def <(other)
113114
end
114115

115116
test "route location returns the location for a valid route" do
116-
response = @server.execute("route_location", { name: "user_path" })
117+
@server.execute("route_location", { name: "user_path" })
117118
location = response[:result][:location]
118119
assert_match %r{test/dummy/config/routes.rb:4$}, location
119120
end
120121

121122
test "route location returns nil for invalid routes" do
122-
response = @server.execute("route_location", { name: "invalid_path" })
123+
@server.execute("route_location", { name: "invalid_path" })
123124
assert_nil response[:result]
124125
end
125126

126127
test "route info" do
127-
response = @server.execute("route_info", { controller: "UsersController", action: "index" })
128+
@server.execute("route_info", { controller: "UsersController", action: "index" })
128129

129130
result = response[:result]
130131

@@ -136,7 +137,8 @@ def <(other)
136137
end
137138

138139
test "prints in the Rails application or server are automatically redirected to stderr" do
139-
server = RubyLsp::Rails::Server.new
140+
stdout = StringIO.new
141+
server = RubyLsp::Rails::Server.new(stdout: stdout)
140142

141143
server.instance_eval do
142144
def resolve_route_info(requirements)
@@ -145,11 +147,18 @@ def resolve_route_info(requirements)
145147
end
146148
end
147149

148-
stdout, stderr = capture_subprocess_io do
150+
_, stderr = capture_subprocess_io do
149151
server.execute("route_info", { controller: "UsersController", action: "index" })
150152
end
151153

152-
assert_empty(stdout)
154+
refute_match("Hello", stdout.string)
153155
assert_equal("Hello\n", stderr)
154156
end
157+
158+
private
159+
160+
def response
161+
_headers, content = @stdout.string.split("\r\n\r\n")
162+
JSON.parse(content, symbolize_names: true)
163+
end
155164
end

0 commit comments

Comments
 (0)