ruby-lsp-rails 0.2.10 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41272ca8c9212dc08dc09beeef481c282656946e182e0c62c72585ecc6cfe658
4
- data.tar.gz: 0dc3c80ab252123291ab7b77a02b22fff1f27dd5bc07eb49ea51dfdbff4310f2
3
+ metadata.gz: ecc8c1b4cd4569bdb9c816f842a90f0dfb1122931adcd9c96d1e2eb8a3799471
4
+ data.tar.gz: 3c1bcc4f69a361ae6a26b635331f6475413fed38bac9a88c7ed45ffea02129eb
5
5
  SHA512:
6
- metadata.gz: fb753c4c3a4282faeefbd254840be186969179e6eaff83320a79e6fd5d2149e4d35acd68f7c38d8bd436e07b233f5e3cebf553c53317576333b83c4b81582dc0
7
- data.tar.gz: 881b3d77f931f890ed68a12b478b7762ea491165e70c4cd61af078eccdeb139c28d9f9eed83406b7fa01927e6b0ea3a07993e0d73ae4c0eede146c6fb124a4aa
6
+ metadata.gz: eff545775dfa463c99b301a94ab450070f683d3f70a3c974683ac1b311a53d10da57acbd051a6fd0e4c8b005629c1d18bd8d61e33c94548e2fcf2b22b70df606
7
+ data.tar.gz: 927c991dc2d499be19c626b4a64cfcc11064f2bbadad41abb71ebea13e1e33592810c7fe6135393d7a1c2140d760e8518eee085d3a2d596942af5e031ff8f272
data/README.md CHANGED
@@ -52,16 +52,15 @@ See the [documentation](https://shopify.github.io/ruby-lsp-rails) for more in-de
52
52
 
53
53
  ## How It Works
54
54
 
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.
56
58
 
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.
61
60
 
62
61
  > [!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.
65
64
 
66
65
  ## Contributing
67
66
 
@@ -1,9 +1,6 @@
1
1
  # typed: strict
2
2
  # frozen_string_literal: true
3
3
 
4
- require "sorbet-runtime"
5
- require "pathname"
6
-
7
4
  require "ruby_lsp_rails/version"
8
5
  require "ruby_lsp_rails/railtie"
9
6
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  require "ruby_lsp/addon"
5
5
 
6
- require_relative "rails_client"
6
+ require_relative "runner_client"
7
7
  require_relative "hover"
8
8
  require_relative "code_lens"
9
9
 
@@ -12,18 +12,18 @@ module RubyLsp
12
12
  class Addon < ::RubyLsp::Addon
13
13
  extend T::Sig
14
14
 
15
- sig { returns(RailsClient) }
15
+ sig { returns(RunnerClient) }
16
16
  def client
17
- @client ||= T.let(RailsClient.new, T.nilable(RailsClient))
17
+ @client ||= T.let(RunnerClient.new, T.nilable(RunnerClient))
18
18
  end
19
19
 
20
20
  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
24
22
 
25
23
  sig { override.void }
26
- def deactivate; end
24
+ def deactivate
25
+ client.shutdown
26
+ end
27
27
 
28
28
  # Creates a new CodeLens listener. This method is invoked on every CodeLens request
29
29
  sig do
@@ -22,7 +22,7 @@ module RubyLsp
22
22
 
23
23
  sig do
24
24
  params(
25
- client: RailsClient,
25
+ client: RunnerClient,
26
26
  response_builder: ResponseBuilders::Hover,
27
27
  nesting: T::Array[String],
28
28
  index: RubyIndexer::Index,
@@ -2,34 +2,17 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require "rails"
5
- require "ruby_lsp_rails/rack_app"
6
5
 
7
6
  module RubyLsp
8
7
  module Rails
9
8
  class Railtie < ::Rails::Railtie
10
9
  config.ruby_lsp_rails = ActiveSupport::OrderedOptions.new
11
- config.ruby_lsp_rails.server = true
12
10
 
13
11
  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.")
33
16
  end
34
17
  end
35
18
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module RubyLsp
5
5
  module Rails
6
- VERSION = "0.2.10"
6
+ VERSION = "0.3.0"
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-lsp-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-02-13 00:00:00.000000000 Z
11
+ date: 2024-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -100,11 +100,9 @@ files:
100
100
  - lib/ruby_lsp/ruby_lsp_rails/addon.rb
101
101
  - lib/ruby_lsp/ruby_lsp_rails/code_lens.rb
102
102
  - lib/ruby_lsp/ruby_lsp_rails/hover.rb
103
- - lib/ruby_lsp/ruby_lsp_rails/rails_client.rb
104
103
  - lib/ruby_lsp/ruby_lsp_rails/runner_client.rb
105
104
  - lib/ruby_lsp/ruby_lsp_rails/server.rb
106
105
  - lib/ruby_lsp/ruby_lsp_rails/support/rails_document_client.rb
107
- - lib/ruby_lsp_rails/rack_app.rb
108
106
  - lib/ruby_lsp_rails/railtie.rb
109
107
  - lib/ruby_lsp_rails/version.rb
110
108
  - lib/tasks/ruby_lsp_rails_tasks.rake
@@ -1,77 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- require "net/http"
5
-
6
- module RubyLsp
7
- module Rails
8
- class RailsClient
9
- class ServerAddressUnknown < StandardError; end
10
-
11
- extend T::Sig
12
-
13
- SERVER_NOT_RUNNING_MESSAGE = "Rails server is not running. " \
14
- "To get Rails features in the editor, boot the Rails server"
15
-
16
- sig { returns(Pathname) }
17
- attr_reader :root
18
-
19
- sig { void }
20
- def initialize
21
- project_root = T.let(Bundler.with_unbundled_env { Bundler.default_gemfile }.dirname, Pathname)
22
- dummy_path = project_root.join("test", "dummy")
23
-
24
- @root = T.let(dummy_path.exist? ? dummy_path : project_root, Pathname)
25
- app_uri_path = @root.join("tmp", "app_uri.txt")
26
-
27
- if app_uri_path.exist?
28
- url = URI(app_uri_path.read.chomp)
29
-
30
- @ssl = T.let(url.scheme == "https", T::Boolean)
31
- @address = T.let(
32
- [url.host, url.path].reject { |component| component.nil? || component.empty? }.join("/"),
33
- T.nilable(String),
34
- )
35
- @port = T.let(T.must(url.port).to_i, Integer)
36
- end
37
- end
38
-
39
- sig { params(name: String).returns(T.nilable(T::Hash[Symbol, T.untyped])) }
40
- def model(name)
41
- response = request("models/#{name}")
42
- return unless response.code == "200"
43
-
44
- JSON.parse(response.body.chomp, symbolize_names: true)
45
- rescue Errno::ECONNREFUSED,
46
- Errno::EADDRNOTAVAIL,
47
- Errno::ECONNRESET,
48
- Net::ReadTimeout,
49
- Net::OpenTimeout,
50
- ServerAddressUnknown
51
- nil
52
- end
53
-
54
- sig { void }
55
- def check_if_server_is_running!
56
- request("activate", 0.2)
57
- rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL, Errno::ECONNRESET, ServerAddressUnknown
58
- warn(SERVER_NOT_RUNNING_MESSAGE)
59
- rescue Net::ReadTimeout, Net::OpenTimeout
60
- # If the server is running, but the initial request is taking too long, we don't want to block the
61
- # initialization of the Ruby LSP
62
- end
63
-
64
- private
65
-
66
- sig { params(path: String, timeout: T.nilable(Float)).returns(Net::HTTPResponse) }
67
- def request(path, timeout = nil)
68
- raise ServerAddressUnknown unless @address
69
-
70
- http = Net::HTTP.new(@address, @port)
71
- http.use_ssl = @ssl
72
- http.read_timeout = timeout if timeout
73
- http.get("/ruby_lsp_rails/#{path}")
74
- end
75
- end
76
- end
77
- end
@@ -1,58 +0,0 @@
1
- # typed: strict
2
- # frozen_string_literal: true
3
-
4
- module RubyLsp
5
- module Rails
6
- class RackApp
7
- extend T::Sig
8
-
9
- BASE_PATH = "/ruby_lsp_rails/"
10
-
11
- sig { params(env: T::Hash[T.untyped, T.untyped]).returns(T::Array[T.untyped]) }
12
- def call(env)
13
- request = ActionDispatch::Request.new(env)
14
- path = request.path
15
-
16
- route, argument = path.delete_prefix(BASE_PATH).split("/")
17
-
18
- case route
19
- when "activate"
20
- [200, { "Content-Type" => "application/json" }, []]
21
- when "models"
22
- resolve_database_info_from_model(argument)
23
- else
24
- not_found
25
- end
26
- end
27
-
28
- private
29
-
30
- sig { params(model_name: String).returns(T::Array[T.untyped]) }
31
- def resolve_database_info_from_model(model_name)
32
- const = ActiveSupport::Inflector.safe_constantize(model_name)
33
-
34
- if const && const < ActiveRecord::Base && !const.abstract_class?
35
- begin
36
- schema_file = ActiveRecord::Tasks::DatabaseTasks.schema_dump_path(const.connection.pool.db_config)
37
- rescue => e
38
- warn("Could not locate schema: #{e.message}")
39
- end
40
-
41
- body = JSON.dump({
42
- columns: const.columns.map { |column| [column.name, column.type] },
43
- schema_file: schema_file,
44
- })
45
-
46
- [200, { "Content-Type" => "application/json" }, [body]]
47
- else
48
- not_found
49
- end
50
- end
51
-
52
- sig { returns(T::Array[T.untyped]) }
53
- def not_found
54
- [404, { "Content-Type" => "text/plain" }, ["Not Found"]]
55
- end
56
- end
57
- end
58
- end