-
Notifications
You must be signed in to change notification settings - Fork 30
Use middleware instead Rails controller for model query #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
60107fa
to
9416b36
Compare
9416b36
to
e39cff3
Compare
@@ -206,6 +206,7 @@ GEM | |||
sorbet (0.5.10782) | |||
sorbet-static (= 0.5.10782) | |||
sorbet-runtime (0.5.10782) | |||
sorbet-static (0.5.10782-universal-darwin-21) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why the Gemfile.lock
was 'out of sync' but I was seeing these same changes locally so 👍 to this.
match = request.path.match(PATH_REGEXP) | ||
|
||
if match | ||
resolve_database_info_from_model(match[:model_name]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: We could return early here, then change rescue
to ensure
, that way we only need @app.call(env)
once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think ensure
rescues exceptions:
def foo
1 / 0
ensure
"bar"
end
puts foo
$ ruby test.rb
test.rb:5:in `/': divided by 0 (ZeroDivisionError)
from test.rb:5:in `foo'
from test.rb:10:in `<main>'
Even if it works, I think it only reduces one @app.call(env)
but sacrifices explicitness of rescue
. So I prefer not making such change yet.
But I will see if I can reduce the number of conditions here.
|
||
module RubyLsp | ||
module Rails | ||
class Middleware |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might later want to consider extracting some of this into ruby-lsp
, so there's less 'boilerplate' necessary for extension authors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMO, we don't want to encourage extension authors doing this kind of work themselves. It's the same as we don't want them to implement their own visitor, but a listener to a specific request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that's what I mean. Things like the /ruby_lsp_rails/
path shouldn't be something for the extension developer to be concerned with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if we won't want them to do this, what's the point of extracting them to ruby-lsp
?
This is Rack/Rails specific pattern and not what ruby-lsp
should have provided?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with Stan here, ruby-lsp-rails
will be quite a unique extension since it will be communicating with a live server. I suspect most other extensions wouldn't need it - and we shouldn't create incentive for people to do so as getting this wrong can be detrimental to the overall experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there could be other neat extensions powered by a similar mechanism, but I agree with not trying to optimize for that until we actually have more real-world examples.
sig { params(env: T::Hash[T.untyped, T.untyped]).returns(T::Array[T.untyped]) } | ||
def call(env) | ||
request = ActionDispatch::Request.new(env) | ||
# TODO: improve the model name regex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine for now. Once we add support for namespaces it may need adjusted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we can keep it for the time being. In the future, we may need a simple router just to make the codebase maintainable. Something like
return @app.call(env) unless request.path.start_with?("/ruby_lsp_rails/")
endpoint, argument = request.path.delete_prefix("/ruby_lsp_rails/").split("/")
case endpoint
when "models"
# ...
when "jobs"
# ...
end
I'll try it out shortly, but I assume that since this is done at the rack level, we'll no longer see any |
The middleware is not injected that early in the stack, so you should still see it in logs, like:
|
- By using middleware instead of a Rails controller, it skips many middlewares that aren't needed for our model query request. In big Rails apps, this could be a big performance improvement. - Although Rails engine also supports being used as a middleware, it still requires registering routes to the application. That doesn't always work depends on the app's own rules on how to register routes. - Compare to Rails engine, middleware is a more flexible way to integrate with Rails apps and also a lot lighter.
e39cff3
to
637b911
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works really well and greatly simplifies the gem. Amazing work
module Rails | ||
class Railtie < ::Rails::Railtie | ||
initializer "ruby_lsp_rails.setup" do |app| | ||
app.config.middleware.insert_after(ActionDispatch::ShowExceptions, RubyLsp::Rails::Middleware) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, what happens if the application doesn't have ActionDispatch::ShowExceptions
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll raise an exception saying there's no ActionDispatch::ShowExceptions
to insert after.
But this is one of the key middlewares in Rails. If someone removes it, that's probably not something we'd want to support.
sig { params(env: T::Hash[T.untyped, T.untyped]).returns(T::Array[T.untyped]) } | ||
def call(env) | ||
request = ActionDispatch::Request.new(env) | ||
# TODO: improve the model name regex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that we can keep it for the time being. In the future, we may need a simple router just to make the codebase maintainable. Something like
return @app.call(env) unless request.path.start_with?("/ruby_lsp_rails/")
endpoint, argument = request.path.delete_prefix("/ruby_lsp_rails/").split("/")
case endpoint
when "models"
# ...
when "jobs"
# ...
end
Closes https://github.com/Shopify/ruby-dev-exp-issues/issues/898