Skip to content

Commit 9f786ad

Browse files
dnesteryukdblock
authored andcommitted
delete a reversible stackable values class (#1953)
* 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.
1 parent d9cf755 commit 9f786ad

16 files changed

+40
-219
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#### Features
44

55
* Your contribution here.
6+
* [#1953](https://github.com/ruby-grape/grape/pull/1953): Delete a reversible stackable values class - [@dnesteryuk](https://github.com/dnesteryuk).
67
* [#1949](https://github.com/ruby-grape/grape/pull/1949): Add support for Ruby 2.7 - [@nbulaj](https://github.com/nbulaj).
78
* [#1948](https://github.com/ruby-grape/grape/pull/1948): Relax `dry-types` dependency version - [@nbulaj](https://github.com/nbulaj).
89
* [#1944](https://github.com/ruby-grape/grape/pull/1944): Reduces `attribute_translator` string allocations - [@ericproulx](https://github.com/ericproulx).

lib/grape.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,6 @@ module Util
140140
eager_autoload do
141141
autoload :InheritableValues
142142
autoload :StackableValues
143-
autoload :ReverseStackableValues
144143
autoload :InheritableSetting
145144
autoload :StrictHashConfiguration
146145
autoload :Registrable

lib/grape/dsl/request_response.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def rescue_from(*args, &block)
127127
:base_only_rescue_handlers
128128
end
129129

130-
namespace_reverse_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
130+
namespace_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
131131
end
132132

133133
namespace_stackable(:rescue_options, options)

lib/grape/dsl/settings.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,18 @@ def namespace_stackable(key, value = nil)
9696
get_or_set :namespace_stackable, key, value
9797
end
9898

99-
def namespace_reverse_stackable(key, value = nil)
100-
get_or_set :namespace_reverse_stackable, key, value
101-
end
102-
10399
def namespace_stackable_with_hash(key)
104100
settings = get_or_set :namespace_stackable, key, nil
105101
return if settings.blank?
106102
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
107103
end
108104

109105
def namespace_reverse_stackable_with_hash(key)
110-
settings = get_or_set :namespace_reverse_stackable, key, nil
106+
settings = get_or_set :namespace_stackable, key, nil
111107
return if settings.blank?
108+
112109
result = {}
113-
settings.each do |setting|
110+
settings.reverse_each do |setting|
114111
setting.each do |field, value|
115112
result[field] ||= value
116113
end
@@ -171,7 +168,11 @@ def within_namespace(&_block)
171168
# the superclass's :inheritable_setting.
172169
def build_top_level_setting
173170
Grape::Util::InheritableSetting.new.tap do |setting|
174-
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API
171+
# Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
172+
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
173+
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
174+
# in the chain for every endpoint.
175+
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
175176
setting.inherit_from superclass.inheritable_setting
176177
end
177178
end

lib/grape/endpoint.rb

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -386,24 +386,8 @@ def run_filters(filters, type = :other)
386386
extend post_extension if post_extension
387387
end
388388

389-
def befores
390-
namespace_stackable(:befores) || []
391-
end
392-
393-
def before_validations
394-
namespace_stackable(:before_validations) || []
395-
end
396-
397-
def after_validations
398-
namespace_stackable(:after_validations) || []
399-
end
400-
401-
def afters
402-
namespace_stackable(:afters) || []
403-
end
404-
405-
def finallies
406-
namespace_stackable(:finallies) || []
389+
%i[befores before_validations after_validations afters finallies].each do |meth_id|
390+
define_method(meth_id) { namespace_stackable(meth_id) }
407391
end
408392

409393
def validations

lib/grape/util/inheritable_setting.rb

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ module Util
66
# and inheritable values (see InheritableValues and StackableValues).
77
class InheritableSetting
88
attr_accessor :route, :api_class, :namespace
9-
attr_accessor :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable
9+
attr_accessor :namespace_inheritable, :namespace_stackable
1010
attr_accessor :parent, :point_in_time_copies
1111

1212
# Retrieve global settings.
@@ -31,7 +31,6 @@ def initialize
3131
# used with a mount, or should every API::Class be a separate namespace by default?
3232
self.namespace_inheritable = InheritableValues.new
3333
self.namespace_stackable = StackableValues.new
34-
self.namespace_reverse_stackable = ReverseStackableValues.new
3534

3635
self.point_in_time_copies = []
3736

@@ -54,7 +53,6 @@ def inherit_from(parent)
5453

5554
namespace_inheritable.inherited_values = parent.namespace_inheritable
5655
namespace_stackable.inherited_values = parent.namespace_stackable
57-
namespace_reverse_stackable.inherited_values = parent.namespace_reverse_stackable
5856
self.route = parent.route.merge(route)
5957

6058
point_in_time_copies.map { |cloned_one| cloned_one.inherit_from parent }
@@ -72,7 +70,6 @@ def point_in_time_copy
7270
new_setting.namespace = namespace.clone
7371
new_setting.namespace_inheritable = namespace_inheritable.clone
7472
new_setting.namespace_stackable = namespace_stackable.clone
75-
new_setting.namespace_reverse_stackable = namespace_reverse_stackable.clone
7673
new_setting.route = route.clone
7774
new_setting.api_class = api_class
7875

@@ -93,8 +90,7 @@ def to_hash
9390
route: route.clone,
9491
namespace: namespace.to_hash,
9592
namespace_inheritable: namespace_inheritable.to_hash,
96-
namespace_stackable: namespace_stackable.to_hash,
97-
namespace_reverse_stackable: namespace_reverse_stackable.to_hash
93+
namespace_stackable: namespace_stackable.to_hash
9894
}
9995
end
10096
end

lib/grape/util/reverse_stackable_values.rb

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

lib/grape/util/stackable_values.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ def initialize(*_args)
1313
@frozen_values = {}
1414
end
1515

16+
# Even if there is no value, an empty array will be returned.
1617
def [](name)
1718
return @frozen_values[name] if @frozen_values.key? name
1819

lib/grape/validations/attributes_iterator.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ def initialize(validator, scope, params)
1111
@scope = scope
1212
@attrs = validator.attrs
1313
@original_params = scope.params(params)
14+
@array_given = @original_params.is_a?(Array)
1415
@params = Array.wrap(@original_params)
1516
end
1617

@@ -30,7 +31,7 @@ def do_each(params_to_process, parent_indicies = [], &block)
3031
end
3132

3233
if @scope.type == Array
33-
next unless @original_params.is_a?(Array) # do not validate content of array if it isn't array
34+
next unless @array_given # do not validate content of array if it isn't array
3435

3536
# fill current and parent scopes with correct array indicies
3637
parent_scope = @scope.parent

lib/grape/validations/params_scope.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ def configuration
4545
# @return [Boolean] whether or not this entire scope needs to be
4646
# validated
4747
def should_validate?(parameters)
48-
return false if @optional && (params(parameters).blank? || all_element_blank?(parameters))
49-
return false unless meets_dependency?(params(parameters), parameters)
48+
scoped_params = params(parameters)
49+
50+
return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
51+
return false unless meets_dependency?(scoped_params, parameters)
5052
return true if parent.nil?
5153
parent.should_validate?(parameters)
5254
end
@@ -446,8 +448,8 @@ def options_key?(type, key, validations)
446448
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
447449
end
448450

449-
def all_element_blank?(parameters)
450-
params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
451+
def all_element_blank?(scoped_params)
452+
scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
451453
end
452454

453455
# Validators don't have access to each other and they don't need, however,

0 commit comments

Comments
 (0)