Skip to content

Commit

Permalink
Revert "delete a reversible stackable values class (#1953)"
Browse files Browse the repository at this point in the history
This reverts commit 9f786ad.
  • Loading branch information
dblock committed Jan 9, 2020
1 parent 3e9c32d commit 38c6bc0
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 40 deletions.
1 change: 0 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
#### Features

* Your contribution here.
* [#1953](https://github.com/ruby-grape/grape/pull/1953): Delete a reversible stackable values class - [@dnesteryuk](https://github.com/dnesteryuk).
* [#1949](https://github.com/ruby-grape/grape/pull/1949): Add support for Ruby 2.7 - [@nbulaj](https://github.com/nbulaj).
* [#1948](https://github.com/ruby-grape/grape/pull/1948): Relax `dry-types` dependency version - [@nbulaj](https://github.com/nbulaj).
* [#1944](https://github.com/ruby-grape/grape/pull/1944): Reduces `attribute_translator` string allocations - [@ericproulx](https://github.com/ericproulx).
Expand Down
1 change: 1 addition & 0 deletions lib/grape.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ module Util
eager_autoload do
autoload :InheritableValues
autoload :StackableValues
autoload :ReverseStackableValues
autoload :InheritableSetting
autoload :StrictHashConfiguration
autoload :Registrable
Expand Down
2 changes: 1 addition & 1 deletion lib/grape/dsl/request_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def rescue_from(*args, &block)
:base_only_rescue_handlers
end

namespace_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
namespace_reverse_stackable handler_type, Hash[args.map { |arg| [arg, handler] }]
end

namespace_stackable(:rescue_options, options)
Expand Down
15 changes: 7 additions & 8 deletions lib/grape/dsl/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,21 @@ def namespace_stackable(key, value = nil)
get_or_set :namespace_stackable, key, value
end

def namespace_reverse_stackable(key, value = nil)
get_or_set :namespace_reverse_stackable, key, value
end

def namespace_stackable_with_hash(key)
settings = get_or_set :namespace_stackable, key, nil
return if settings.blank?
settings.each_with_object({}) { |value, result| result.deep_merge!(value) }
end

def namespace_reverse_stackable_with_hash(key)
settings = get_or_set :namespace_stackable, key, nil
settings = get_or_set :namespace_reverse_stackable, key, nil
return if settings.blank?

result = {}
settings.reverse_each do |setting|
settings.each do |setting|
setting.each do |field, value|
result[field] ||= value
end
Expand Down Expand Up @@ -168,11 +171,7 @@ def within_namespace(&_block)
# the superclass's :inheritable_setting.
def build_top_level_setting
Grape::Util::InheritableSetting.new.tap do |setting|
# Doesn't try to inherit settings from +Grape::API::Instance+ which also responds to
# +inheritable_setting+, however, it doesn't contain any user-defined settings.
# Otherwise, it would lead to an extra instance of +Grape::Util::InheritableSetting+
# in the chain for every endpoint.
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API::Instance
if defined?(superclass) && superclass.respond_to?(:inheritable_setting) && superclass != Grape::API
setting.inherit_from superclass.inheritable_setting
end
end
Expand Down
20 changes: 18 additions & 2 deletions lib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,24 @@ def run_filters(filters, type = :other)
extend post_extension if post_extension
end

%i[befores before_validations after_validations afters finallies].each do |meth_id|
define_method(meth_id) { namespace_stackable(meth_id) }
def befores
namespace_stackable(:befores) || []
end

def before_validations
namespace_stackable(:before_validations) || []
end

def after_validations
namespace_stackable(:after_validations) || []
end

def afters
namespace_stackable(:afters) || []
end

def finallies
namespace_stackable(:finallies) || []
end

def validations
Expand Down
8 changes: 6 additions & 2 deletions lib/grape/util/inheritable_setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module Util
# and inheritable values (see InheritableValues and StackableValues).
class InheritableSetting
attr_accessor :route, :api_class, :namespace
attr_accessor :namespace_inheritable, :namespace_stackable
attr_accessor :namespace_inheritable, :namespace_stackable, :namespace_reverse_stackable
attr_accessor :parent, :point_in_time_copies

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

self.point_in_time_copies = []

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

namespace_inheritable.inherited_values = parent.namespace_inheritable
namespace_stackable.inherited_values = parent.namespace_stackable
namespace_reverse_stackable.inherited_values = parent.namespace_reverse_stackable
self.route = parent.route.merge(route)

point_in_time_copies.map { |cloned_one| cloned_one.inherit_from parent }
Expand All @@ -70,6 +72,7 @@ def point_in_time_copy
new_setting.namespace = namespace.clone
new_setting.namespace_inheritable = namespace_inheritable.clone
new_setting.namespace_stackable = namespace_stackable.clone
new_setting.namespace_reverse_stackable = namespace_reverse_stackable.clone
new_setting.route = route.clone
new_setting.api_class = api_class

Expand All @@ -90,7 +93,8 @@ def to_hash
route: route.clone,
namespace: namespace.to_hash,
namespace_inheritable: namespace_inheritable.to_hash,
namespace_stackable: namespace_stackable.to_hash
namespace_stackable: namespace_stackable.to_hash,
namespace_reverse_stackable: namespace_reverse_stackable.to_hash
}
end
end
Expand Down
18 changes: 18 additions & 0 deletions lib/grape/util/reverse_stackable_values.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

require_relative 'stackable_values'

module Grape
module Util
class ReverseStackableValues < StackableValues
protected

def concat_values(inherited_value, new_value)
[].tap do |value|
value.concat(new_value)
value.concat(inherited_value)
end
end
end
end
end
1 change: 0 additions & 1 deletion lib/grape/util/stackable_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def initialize(*_args)
@frozen_values = {}
end

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

Expand Down
3 changes: 1 addition & 2 deletions lib/grape/validations/attributes_iterator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ def initialize(validator, scope, params)
@scope = scope
@attrs = validator.attrs
@original_params = scope.params(params)
@array_given = @original_params.is_a?(Array)
@params = Array.wrap(@original_params)
end

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

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

# fill current and parent scopes with correct array indicies
parent_scope = @scope.parent
Expand Down
10 changes: 4 additions & 6 deletions lib/grape/validations/params_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ def configuration
# @return [Boolean] whether or not this entire scope needs to be
# validated
def should_validate?(parameters)
scoped_params = params(parameters)

return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
return false unless meets_dependency?(scoped_params, parameters)
return false if @optional && (params(parameters).blank? || all_element_blank?(parameters))
return false unless meets_dependency?(params(parameters), parameters)
return true if parent.nil?
parent.should_validate?(parameters)
end
Expand Down Expand Up @@ -448,8 +446,8 @@ def options_key?(type, key, validations)
validations[type].respond_to?(:key?) && validations[type].key?(key) && !validations[type][key].nil?
end

def all_element_blank?(scoped_params)
scoped_params.respond_to?(:all?) && scoped_params.all?(&:blank?)
def all_element_blank?(parameters)
params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
end

# Validators don't have access to each other and they don't need, however,
Expand Down
6 changes: 1 addition & 5 deletions lib/grape/validations/types/build_coercer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ def self.cache_instance(type, method, strict, &_block)
end

def self.cache_key(type, method, strict)
[type, method, strict].each_with_object(+'') do |val, memo|
next if val.nil?

memo << '_' << val.to_s
end
[type, method, strict].compact.map(&:to_s).join('_')
end

instance_variable_set(:@__cache, {})
Expand Down
6 changes: 0 additions & 6 deletions spec/grape/api/instance_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,4 @@ def app
expect(last_response.body).to eq 'success'
end
end

context 'top level setting' do
it 'does not inherit settings from the superclass (Grape::API::Instance)' do
expect(an_instance.top_level_setting.parent).to be_nil
end
end
end
10 changes: 5 additions & 5 deletions spec/grape/dsl/request_response_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,34 +161,34 @@ def self.imbue(key, value)

describe 'list of exceptions is passed' do
it 'sets hash of exceptions as rescue handlers' do
expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => nil)
expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => nil)
expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
subject.rescue_from StandardError
end

it 'rescues only base handlers if rescue_subclasses: false option is passed' do
expect(subject).to receive(:namespace_stackable).with(:base_only_rescue_handlers, StandardError => nil)
expect(subject).to receive(:namespace_reverse_stackable).with(:base_only_rescue_handlers, StandardError => nil)
expect(subject).to receive(:namespace_stackable).with(:rescue_options, rescue_subclasses: false)
subject.rescue_from StandardError, rescue_subclasses: false
end

it 'sets given proc as rescue handler for each key in hash' do
rescue_handler_proc = proc {}
expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
subject.rescue_from StandardError, rescue_handler_proc
end

it 'sets given block as rescue handler for each key in hash' do
rescue_handler_proc = proc {}
expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => rescue_handler_proc)
expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
subject.rescue_from StandardError, &rescue_handler_proc
end

it 'sets a rescue handler declared through :with option for each key in hash' do
with_block = -> { 'hello' }
expect(subject).to receive(:namespace_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
expect(subject).to receive(:namespace_reverse_stackable).with(:rescue_handlers, StandardError => an_instance_of(Proc))
expect(subject).to receive(:namespace_stackable).with(:rescue_options, {})
subject.rescue_from StandardError, with: with_block
end
Expand Down
23 changes: 23 additions & 0 deletions spec/grape/util/inheritable_setting_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Util
settings.namespace[:namespace_thing] = :namespace_foo_bar
settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar
settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar
settings.route[:route_thing] = :route_foo_bar
end
end
Expand All @@ -23,6 +24,7 @@ module Util
settings.namespace[:namespace_thing] = :namespace_foo_bar_other
settings.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar_other
settings.namespace_stackable[:namespace_stackable_thing] = :namespace_stackable_foo_bar_other
settings.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :namespace_reverse_stackable_foo_bar_other
settings.route[:route_thing] = :route_foo_bar_other
end
end
Expand Down Expand Up @@ -120,6 +122,16 @@ module Util
end
end

describe '#namespace_reverse_stackable' do
it 'works with reverse stackable values' do
expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]

subject.inherit_from other_parent

expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar_other]
end
end

describe '#route' do
it 'sets a value until the next route' do
subject.route[:some_thing] = :foo_bar
Expand Down Expand Up @@ -186,6 +198,14 @@ module Util
expect(cloned_obj.namespace_stackable[:namespace_stackable_thing]).to eq [:namespace_stackable_foo_bar]
end

it 'decouples namespace reverse stackable values' do
expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]

subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = :other_thing
expect(subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq %i[other_thing namespace_reverse_stackable_foo_bar]
expect(cloned_obj.namespace_reverse_stackable[:namespace_reverse_stackable_thing]).to eq [:namespace_reverse_stackable_foo_bar]
end

it 'decouples route values' do
expect(cloned_obj.route[:route_thing]).to eq :route_foo_bar

Expand All @@ -204,6 +224,7 @@ module Util
subject.namespace[:namespace_thing] = :namespace_foo_bar
subject.namespace_inheritable[:namespace_inheritable_thing] = :namespace_inheritable_foo_bar
subject.namespace_stackable[:namespace_stackable_thing] = [:namespace_stackable_foo_bar]
subject.namespace_reverse_stackable[:namespace_reverse_stackable_thing] = [:namespace_reverse_stackable_foo_bar]
subject.route[:route_thing] = :route_foo_bar

expect(subject.to_hash).to include(global: { global_thing: :global_foo_bar })
Expand All @@ -212,6 +233,8 @@ module Util
namespace_inheritable_thing: :namespace_inheritable_foo_bar
})
expect(subject.to_hash).to include(namespace_stackable: { namespace_stackable_thing: [:namespace_stackable_foo_bar, [:namespace_stackable_foo_bar]] })
expect(subject.to_hash).to include(namespace_reverse_stackable:
{ namespace_reverse_stackable_thing: [[:namespace_reverse_stackable_foo_bar], :namespace_reverse_stackable_foo_bar] })
expect(subject.to_hash).to include(route: { route_thing: :route_foo_bar })
end
end
Expand Down
Loading

0 comments on commit 38c6bc0

Please sign in to comment.