-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
delete a reversible stackable values class #1953
delete a reversible stackable values class #1953
Conversation
The reversible stackable values object was initialized for every endpoint, however, it is only needed for keeping rescue handlers. The idea is simple, handlers defined "closer" to an endpoint have higher priority. That test https://github.com/ruby-grape/grape/blob/master/spec/grape/api_spec.rb#L3215-L3232 demonstrates how it works. In our project rescue handlers are defined at the top level, so almost every endpoint keeps the unused object. The mentioned behavior is easy to achieve with the stackable values object and the `reverse_each` method. Thus, endpoints keeps less objects and have less code to be maintained. Besides that, there are a few other simple performance optimizations.
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.
Nice work. See small comment.
@@ -83,7 +83,11 @@ def self.cache_instance(type, method, strict, &_block) | |||
end | |||
|
|||
def self.cache_key(type, method, strict) | |||
[type, method, strict].compact.map(&:to_s).join('_') | |||
[type, method, strict].each_with_object(+'') do |val, memo| |
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.
Should be _
to avoid fo_obar
and foo_bar
type situations.
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 guess joining '_' is unnecessary since the key is meant for "machines" here. Do you think it is necessary? 🙂
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.
Are we 100% sure that type/method/strict don't have the situation like type=fo
+ method=obar
vs. type=foo
+ method=bar
, it would generate the same key. So I think better safe than sorry.
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.
Good point! I've changed it.
@@ -171,7 +168,7 @@ def within_namespace(&_block) | |||
# the superclass's :inheritable_setting. | |||
def build_top_level_setting | |||
Grape::Util::InheritableSetting.new.tap do |setting| | |||
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API | |||
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance |
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.
Is there a bug fix here?
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.
Oh, I forgot to mention this one. Grape::API doesn't make use of Grape::Util::InheritableSetting
, but Grape::API::Instance does.
irb(main):006:0> Grape::API.respond_to?(:inheritable_setting)
=> false
irb(main):007:0> Grape::API::Instance.respond_to?(:inheritable_setting)
=> true
Thus, there is no reason to expect Grape::API
.
The condition was added when Grape::API worked with Grape::Util::InheritableSetting
.
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 understand that part, but without digging into details it's a code change has a side effect. So either we're missing a spec or the entire if
is unnecessary?
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 didn't cause big harm, however, a test for catching the regression makes sense 👍 Please, have a look! 🙂
Merged, 👍 |
@nbulaj Please try writing specs for any regressions? |
Oh, it could be hard, but I'll try to |
Ah, I see. It fails with the test that must check this part: rescue_from ActiveRecord::RecordNotFound do |e|
logger.warn("[#{e.class.name}] #{e.message}")
error!({ message: "Not found" }, 404)
end Instead of rescuing the error it just throws it and I have:
in the test. This UPD 2*: yep, I've analyzed RSpec failed specs and seems like all of them related to |
Ok, so this is the script to reproduce an breaking change: Gemfile source " https://rubygems.org"
# With just:
# gem "grape", "~> 1.2"
# or before this PR merged everything works
gem "grape", git: "https://github.com/ruby-grape/grape"
gem "activerecord"
gem "puma" server.ru require 'bundler/setup'
Bundler.setup
require 'grape'
require 'active_record'
module API
class Endpoint < Grape::API
desc "Test"
get :test do
# Simulate User.find(10)
raise ::ActiveRecord::RecordNotFound.new("test", "name", "key", [])
end
end
class Root < Grape::API
version :v1, using: :path
format :json
rescue_from ::ActiveRecord::RecordNotFound do |e|
error!({ message: "Not found" }, 404)
end
rescue_from StandardError do |e|
raise e
end
mount API::Endpoint
end
end
run API::Root Run the API ( Expected result:
Actual result:
Sorry, can't write a RSpec test right now - don't have a time. But would be great if @dnesteryuk could take a look |
I opened #1957 to track. Please someone take a look. |
added the failing spec(#1959) for testing functional behavior |
* delete a reversible stackable values class The reversible stackable values object was initialized for every endpoint, however, it is only needed for keeping rescue handlers. The idea is simple, handlers defined "closer" to an endpoint have higher priority. That test https://github.com/ruby-grape/grape/blob/master/spec/grape/api_spec.rb#L3215-L3232 demonstrates how it works. In our project rescue handlers are defined at the top level, so almost every endpoint keeps the unused object. The mentioned behavior is easy to achieve with the stackable values object and the `reverse_each` method. Thus, endpoints keeps less objects and have less code to be maintained. Besides that, there are a few other simple performance optimizations.
This reverts commit 9f786ad.
The reversible stackable values object was initialized for every endpoint, however, it is only needed for keeping rescue handlers. The idea is simple, handlers defined "closer" to an endpoint have higher priority. That test demonstrates how it works.
In our project rescue handlers are defined at the top level, so almost every endpoint keeps the unused object.
The mentioned behavior is easy to achieve with the stackable values object and the
reverse_each
method. Thus, endpoints keeps less objects and have less code to be maintained.Besides that, there are a few other simple performance optimizations.