Skip to content

Commit c57ca34

Browse files
authored
Merge pull request #256 from Shopify/andyw8/implement-lsp-runner
Switch to `rails runner` approach instead of Rack app
2 parents 7db4883 + 758ca85 commit c57ca34

File tree

12 files changed

+33
-353
lines changed

12 files changed

+33
-353
lines changed

README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,15 @@ See the [documentation](https://shopify.github.io/ruby-lsp-rails) for more in-de
5252
5353
## How It Works
5454

55-
This gem consists of two components that enable enhanced Rails functionality in the editor:
55+
When Ruby LSP Rails starts, it spawns a `rails runner` instance which runs
56+
`[server.rb](https://github.com/Shopify/ruby-lsp-rails/blob/main/lib/ruby_lsp/ruby_lsp_rails/server.rb)`.
57+
The addon communicates with this process over a pipe (i.e. `stdin` and `stdout`) to fetch runtime information about the application.
5658

57-
1. A Rack app that automatically exposes APIs when Rails server is running
58-
1. A Ruby LSP addon that connects to the exposed APIs to fetch runtime information from the Rails server
59-
60-
This is why the Rails server needs to be running for some features to work.
59+
When extension is stopped (e.g. by quitting the editor), the server instance is shut down.
6160

6261
> [!NOTE]
63-
> There is no need to restart the Ruby LSP every time the Rails server is booted.
64-
> If the server is shut down, the extra features will temporarily disappear and reappear once the server is running again.
62+
> Prior to v0.3, `ruby-lsp-rails` used a different approach which involved mounting a Rack application within the Rails app.
63+
> That approach was brittle and susceptible to the application's configuration, such as routing and middleware.
6564
6665
## Contributing
6766

lib/ruby-lsp-rails.rb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
# typed: strict
22
# frozen_string_literal: true
33

4-
require "sorbet-runtime"
5-
require "pathname"
6-
74
require "ruby_lsp_rails/version"
85
require "ruby_lsp_rails/railtie"
96

lib/ruby_lsp/ruby_lsp_rails/addon.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
require "ruby_lsp/addon"
55

6-
require_relative "rails_client"
6+
require_relative "runner_client"
77
require_relative "hover"
88
require_relative "code_lens"
99

@@ -12,18 +12,18 @@ module Rails
1212
class Addon < ::RubyLsp::Addon
1313
extend T::Sig
1414

15-
sig { returns(RailsClient) }
15+
sig { returns(RunnerClient) }
1616
def client
17-
@client ||= T.let(RailsClient.new, T.nilable(RailsClient))
17+
@client ||= T.let(RunnerClient.new, T.nilable(RunnerClient))
1818
end
1919

2020
sig { override.params(message_queue: Thread::Queue).void }
21-
def activate(message_queue)
22-
client.check_if_server_is_running!
23-
end
21+
def activate(message_queue); end
2422

2523
sig { override.void }
26-
def deactivate; end
24+
def deactivate
25+
client.shutdown
26+
end
2727

2828
# Creates a new CodeLens listener. This method is invoked on every CodeLens request
2929
sig do

lib/ruby_lsp/ruby_lsp_rails/hover.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class Hover
2222

2323
sig do
2424
params(
25-
client: RailsClient,
25+
client: RunnerClient,
2626
response_builder: ResponseBuilders::Hover,
2727
nesting: T::Array[String],
2828
index: RubyIndexer::Index,

lib/ruby_lsp/ruby_lsp_rails/rails_client.rb

Lines changed: 0 additions & 77 deletions
This file was deleted.

lib/ruby_lsp_rails/rack_app.rb

Lines changed: 0 additions & 58 deletions
This file was deleted.

lib/ruby_lsp_rails/railtie.rb

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,17 @@
22
# frozen_string_literal: true
33

44
require "rails"
5-
require "ruby_lsp_rails/rack_app"
65

76
module RubyLsp
87
module Rails
98
class Railtie < ::Rails::Railtie
109
config.ruby_lsp_rails = ActiveSupport::OrderedOptions.new
11-
config.ruby_lsp_rails.server = true
1210

1311
initializer "ruby_lsp_rails.setup" do |_app|
14-
config.after_initialize do |app|
15-
# If we start the app with `bin/rails console` then `Rails::Server` is not defined.
16-
if defined?(::Rails::Server) && config.ruby_lsp_rails.server
17-
app.routes.prepend do
18-
T.bind(self, ActionDispatch::Routing::Mapper)
19-
mount(RackApp.new => RackApp::BASE_PATH)
20-
end
21-
22-
ssl_enable, host, port = ::Rails::Server::Options.new.parse!(ARGV).values_at(:SSLEnable, :Host, :Port)
23-
app_uri = "#{ssl_enable ? "https" : "http"}://#{host}:#{port}"
24-
app_uri_path = ::Rails.root.join("tmp", "app_uri.txt")
25-
app_uri_path.write(app_uri)
26-
27-
at_exit do
28-
# The app_uri.txt file should only exist when the server is running. The addon uses its presence to
29-
# report if the server is running or not. If the server is not running, some of the addon features
30-
# will not be available.
31-
File.delete(app_uri_path) if File.exist?(app_uri_path)
32-
end
12+
config.after_initialize do |_app|
13+
unless config.ruby_lsp_rails.server.nil?
14+
ActiveSupport::Deprecation.new.warn("The `ruby_lsp_rails.server` configuration option is no longer " \
15+
"needed and will be removed in a future release.")
3316
end
3417
end
3518
end

test/ruby_lsp_rails/addon_test.rb

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,6 @@ class AddonTest < ActiveSupport::TestCase
1010
addon = Addon.new
1111
assert_equal("Ruby LSP Rails", addon.name)
1212
end
13-
14-
test "activate checks if Rails server is running" do
15-
rails_client = stub("rails_client", check_if_server_is_running!: true)
16-
17-
RubyLsp::Rails::RailsClient.stubs(instance: rails_client)
18-
19-
addon = Addon.new
20-
queue = Thread::Queue.new
21-
22-
capture_io { assert(addon.activate(queue)) }
23-
ensure
24-
T.must(queue).close
25-
end
2613
end
2714
end
2815
end

test/ruby_lsp_rails/hover_test.rb

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ module RubyLsp
77
module Rails
88
class HoverTest < ActiveSupport::TestCase
99
setup do
10-
File.write("#{Dir.pwd}/test/dummy/tmp/app_uri.txt", "http://localhost:3000")
11-
@client = RailsClient.new
1210
@message_queue = Thread::Queue.new
1311

1412
# Build the Rails documents index ahead of time
@@ -23,7 +21,7 @@ class HoverTest < ActiveSupport::TestCase
2321

2422
test "hook returns model column information" do
2523
expected_response = {
26-
schema_file: "#{@client.root}/db/schema.rb",
24+
schema_file: "#{dummy_root}/db/schema.rb",
2725
columns: [
2826
["id", "integer"],
2927
["first_name", "string"],
@@ -34,8 +32,7 @@ class HoverTest < ActiveSupport::TestCase
3432
],
3533
}
3634

37-
stub_http_request("200", expected_response.to_json)
38-
@client.stubs(:check_if_server_is_running!)
35+
RunnerClient.any_instance.stubs(model: expected_response)
3936

4037
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
4138
class User < ApplicationRecord
@@ -50,7 +47,7 @@ class User < ApplicationRecord
5047
```
5148
5249
**Definitions**: [fake.rb](file:///fake.rb#L1,1-2,4)
53-
[Schema](file://#{@client.root}/db/schema.rb)
50+
[Schema](file://#{dummy_root}/db/schema.rb)
5451
5552
5653
**id**: integer
@@ -69,7 +66,7 @@ class User < ApplicationRecord
6966

7067
test "return column information for namespaced models" do
7168
expected_response = {
72-
schema_file: "#{@client.root}/db/schema.rb",
69+
schema_file: "#{dummy_root}/db/schema.rb",
7370
columns: [
7471
["id", "integer"],
7572
["first_name", "string"],
@@ -80,8 +77,7 @@ class User < ApplicationRecord
8077
],
8178
}
8279

83-
stub_http_request("200", expected_response.to_json)
84-
@client.stubs(:check_if_server_is_running!)
80+
RunnerClient.any_instance.stubs(model: expected_response)
8581

8682
response = hover_on_source(<<~RUBY, { line: 4, character: 6 })
8783
module Blog
@@ -93,7 +89,7 @@ class User < ApplicationRecord
9389
RUBY
9490

9591
assert_equal(<<~CONTENT.chomp, response.contents.value)
96-
[Schema](file://#{@client.root}/db/schema.rb)
92+
[Schema](file://#{dummy_root}/db/schema.rb)
9793
9894
**id**: integer
9995
@@ -111,12 +107,11 @@ class User < ApplicationRecord
111107

112108
test "handles `db/structure.sql` instead of `db/schema.rb`" do
113109
expected_response = {
114-
schema_file: "#{@client.root}/db/structure.sql",
110+
schema_file: "#{dummy_root}/db/structure.sql",
115111
columns: [],
116112
}
117113

118-
stub_http_request("200", expected_response.to_json)
119-
@client.stubs(:check_if_server_is_running!)
114+
RunnerClient.any_instance.stubs(model: expected_response)
120115

121116
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
122117
class User < ApplicationRecord
@@ -127,7 +122,7 @@ class User < ApplicationRecord
127122

128123
assert_includes(
129124
response.contents.value,
130-
"[Schema](file://#{@client.root}/db/structure.sql)",
125+
"[Schema](file://#{dummy_root}/db/structure.sql)",
131126
)
132127
end
133128

@@ -137,8 +132,7 @@ class User < ApplicationRecord
137132
columns: [],
138133
}
139134

140-
stub_http_request("200", expected_response.to_json)
141-
@client.stubs(:check_if_server_is_running!)
135+
RunnerClient.any_instance.stubs(model: expected_response)
142136

143137
response = hover_on_source(<<~RUBY, { line: 3, character: 0 })
144138
class User < ApplicationRecord
@@ -213,6 +207,10 @@ def hover_on_source(source, position)
213207
assert_nil(response.error)
214208
response.response
215209
end
210+
211+
def dummy_root
212+
File.expand_path("#{__dir__}/../../test/dummy")
213+
end
216214
end
217215
end
218216
end

0 commit comments

Comments
 (0)