Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
* [#2726](https://github.com/ruby-grape/grape/pull/2726): Reuse one `AttributesIterator` per validator and drop the unused `Enumerable` mixin - [@ericproulx](https://github.com/ericproulx).
* [#2728](https://github.com/ruby-grape/grape/pull/2728): Deprecate passing a positional options Hash to `auth`/`http_basic`/`http_digest`; pass keyword arguments instead - [@ericproulx](https://github.com/ericproulx).
* [#2733](https://github.com/ruby-grape/grape/pull/2733): Drop the dead `active_support/core_ext/hash/reverse_merge` require; call `ActiveSupport::HashWithIndifferentAccess.new(...)` directly at call sites - [@ericproulx](https://github.com/ericproulx).
* [#2739](https://github.com/ruby-grape/grape/pull/2739): Lazy-allocate `@new_values` in `Grape::Util::BaseInheritable` so settings layers that only inherit never carry an empty Hash; readers in `InheritableValues`/`StackableValues` handle nil - [@ericproulx](https://github.com/ericproulx).
* Your contribution here.

#### Fixes
Expand Down
22 changes: 12 additions & 10 deletions lib/grape/util/base_inheritable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,39 @@ module Grape
module Util
# Base for classes which need to operate with own values kept
# in the hash and inherited values kept in a Hash-like object.
#
# +@new_values+ is lazily allocated on first write so settings layers
# that only inherit (never override) don't carry an empty Hash each.
class BaseInheritable
attr_accessor :inherited_values, :new_values

# @param inherited_values [Object] An object implementing an interface
# of the Hash class.
def initialize(inherited_values = nil)
@inherited_values = inherited_values || {}
@new_values = {}
# @new_values stays nil until the first write.
end

def delete(*keys)
keys.map do |key|
# since delete returns the deleted value, seems natural to `map` the result
new_values.delete key
end
return [] unless @new_values

keys.map { |key| @new_values.delete(key) }
end

def initialize_copy(other)
super
self.inherited_values = other.inherited_values
self.new_values = other.new_values.dup
@inherited_values = other.inherited_values
@new_values = other.new_values&.dup
end

def keys
return inherited_values.keys if new_values.empty?
return @inherited_values.keys if @new_values.nil? || @new_values.empty?

(inherited_values.keys + new_values.keys).uniq
(@inherited_values.keys + @new_values.keys).uniq
end

def key?(name)
inherited_values.key?(name) || new_values.key?(name)
@inherited_values.key?(name) || @new_values&.key?(name) || false
end
end
end
Expand Down
8 changes: 6 additions & 2 deletions lib/grape/util/inheritable_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ module Grape
module Util
class InheritableValues < BaseInheritable
def [](name)
return @inherited_values[name] unless @new_values

@new_values.fetch(name) { @inherited_values[name] }
end

def []=(name, value)
new_values[name] = value
(@new_values ||= {})[name] = value
end

def merge(new_hash)
Expand All @@ -22,7 +24,9 @@ def to_hash
protected

def values
@inherited_values.merge(@new_values)
return @inherited_values.merge(@new_values) if @new_values && !@new_values.empty?

@inherited_values.is_a?(Hash) ? @inherited_values.dup : @inherited_values.to_hash
end
end
end
Expand Down
9 changes: 5 additions & 4 deletions lib/grape/util/stackable_values.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ class StackableValues < BaseInheritable

# Even if there is no value, an empty (frozen) array will be returned.
def [](name)
inherited_value = inherited_values[name]
new_value = new_values[name]
inherited_value = @inherited_values[name]
new_value = @new_values && @new_values[name]

return new_value || EMPTY unless inherited_value

concat_values(inherited_value, new_value)
end

def []=(name, value)
new_values[name] ||= []
new_values[name].push value
@new_values ||= {}
@new_values[name] ||= []
@new_values[name].push value
end

def to_hash
Expand Down
Loading