Skip to content

Commit deeb826

Browse files
authored
Merge pull request #1915 from dnesteryuk/chore/micro-optimization-in-hashes-arrays
micro optimizations in allocating hashes and arrays
2 parents 4aa910e + 4905995 commit deeb826

12 files changed

+119
-93
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#### Features
44

55
* Your contribution here.
6+
* [#1915](https://github.com/ruby-grape/grape/pull/1915): Micro optimizations in allocating hashes and arrays - [@dnesteryuk](https://github.com/dnesteryuk).
67
* [#1904](https://github.com/ruby-grape/grape/pull/1904): Allows Grape to load files on startup rather than on the first call - [@myxoh](https://github.com/myxoh).
78
* [#1907](https://github.com/ruby-grape/grape/pull/1907): Adds outside configuration to Grape with `configure` - [@unleashy](https://github.com/unleashy).
89
* [#1914](https://github.com/ruby-grape/grape/pull/1914): Run specs in random order - [@splattael](https://github.com/splattael).

benchmark/nested_params.rb

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
require 'grape'
2+
require 'benchmark/ips'
3+
4+
class API < Grape::API
5+
prefix :api
6+
version 'v1', using: :path
7+
8+
params do
9+
requires :address, type: Hash do
10+
requires :street, type: String
11+
requires :postal_code, type: Integer
12+
optional :city, type: String
13+
end
14+
end
15+
post '/' do
16+
'hello'
17+
end
18+
end
19+
20+
options = {
21+
method: 'POST',
22+
params: {
23+
address: {
24+
street: 'Alexis Pl.',
25+
postal_code: '90210',
26+
city: 'Beverly Hills'
27+
}
28+
}
29+
}
30+
31+
env = Rack::MockRequest.env_for('/api/v1', options)
32+
33+
10.times do |i|
34+
env["HTTP_HEADER#{i}"] = '123'
35+
end
36+
37+
Benchmark.ips do |ips|
38+
ips.report('POST with nested params') do
39+
API.call env
40+
end
41+
end

lib/grape/endpoint.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def prepare_version
200200
end
201201

202202
def merge_route_options(**default)
203-
options[:route_options].clone.merge(**default)
203+
options[:route_options].clone.merge!(**default)
204204
end
205205

206206
def map_routes

lib/grape/error_formatter.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def builtin_formatters
1414
end
1515

1616
def formatters(options)
17-
builtin_formatters.merge(default_elements).merge(options[:error_formatters] || {})
17+
builtin_formatters.merge(default_elements).merge!(options[:error_formatters] || {})
1818
end
1919

2020
def formatter_for(api_format, **options)

lib/grape/exceptions/validation_errors.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ def to_json(**_opts)
3939
end
4040

4141
def full_messages
42-
map { |attributes, error| full_message(attributes, error) }.uniq
42+
messages = map { |attributes, error| full_message(attributes, error) }
43+
messages.uniq!
44+
messages
4345
end
4446

4547
private
4648

4749
def full_message(attributes, error)
4850
I18n.t(
49-
'grape.errors.format'.to_sym,
51+
'grape.errors.format',
5052
default: '%{attributes} %{message}',
5153
attributes: attributes.count == 1 ? translate_attribute(attributes.first) : translate_attributes(attributes),
5254
message: error.message

lib/grape/formatter.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def builtin_formmaters
1414
end
1515

1616
def formatters(options)
17-
builtin_formmaters.merge(default_elements).merge(options[:formatters] || {})
17+
builtin_formmaters.merge(default_elements).merge!(options[:formatters] || {})
1818
end
1919

2020
def formatter_for(api_format, **options)

lib/grape/parser.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def builtin_parsers
1212
end
1313

1414
def parsers(options)
15-
builtin_parsers.merge(default_elements).merge(options[:parsers] || {})
15+
builtin_parsers.merge(default_elements).merge!(options[:parsers] || {})
1616
end
1717

1818
def parser_for(api_format, **options)

lib/grape/util/base_inheritable.rb

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module Grape
2+
module Util
3+
# Base for classes which need to operate with own values kept
4+
# in the hash and inherited values kept in a Hash-like object.
5+
class BaseInheritable
6+
attr_accessor :inherited_values
7+
attr_accessor :new_values
8+
9+
# @param inherited_values [Object] An object implementing an interface
10+
# of the Hash class.
11+
def initialize(inherited_values = {})
12+
@inherited_values = inherited_values
13+
@new_values = {}
14+
end
15+
16+
def delete(key)
17+
new_values.delete key
18+
end
19+
20+
def initialize_copy(other)
21+
super
22+
self.inherited_values = other.inherited_values
23+
self.new_values = other.new_values.dup
24+
end
25+
26+
def keys
27+
combined = inherited_values.keys
28+
combined.concat(new_values.keys)
29+
combined.uniq!
30+
combined
31+
end
32+
end
33+
end
34+
end

lib/grape/util/inheritable_values.rb

+5-25
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1+
require_relative 'base_inheritable'
2+
13
module Grape
24
module Util
3-
class InheritableValues
4-
attr_accessor :inherited_values
5-
attr_accessor :new_values
6-
7-
def initialize(inherited_values = {})
8-
self.inherited_values = inherited_values
9-
self.new_values = {}
10-
end
11-
5+
class InheritableValues < BaseInheritable
126
def [](name)
137
values[name]
148
end
@@ -17,26 +11,12 @@ def []=(name, value)
1711
new_values[name] = value
1812
end
1913

20-
def delete(key)
21-
new_values.delete key
22-
end
23-
2414
def merge(new_hash)
25-
values.merge(new_hash)
26-
end
27-
28-
def keys
29-
(new_values.keys + inherited_values.keys).sort.uniq
15+
values.merge!(new_hash)
3016
end
3117

3218
def to_hash
33-
values.clone
34-
end
35-
36-
def initialize_copy(other)
37-
super
38-
self.inherited_values = other.inherited_values
39-
self.new_values = other.new_values.dup
19+
values
4020
end
4121

4222
protected
+7-36
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,16 @@
1+
require_relative 'stackable_values'
2+
13
module Grape
24
module Util
3-
class ReverseStackableValues
4-
attr_accessor :inherited_values
5-
attr_accessor :new_values
6-
7-
def initialize(inherited_values = {})
8-
@inherited_values = inherited_values
9-
@new_values = {}
10-
end
5+
class ReverseStackableValues < StackableValues
6+
protected
117

12-
def [](name)
8+
def concat_values(inherited_value, new_value)
139
[].tap do |value|
14-
value.concat(@new_values[name] || [])
15-
value.concat(@inherited_values[name] || [])
16-
end
17-
end
18-
19-
def []=(name, value)
20-
@new_values[name] ||= []
21-
@new_values[name].push value
22-
end
23-
24-
def delete(key)
25-
new_values.delete key
26-
end
27-
28-
def keys
29-
(@new_values.keys + @inherited_values.keys).sort.uniq
30-
end
31-
32-
def to_hash
33-
keys.each_with_object({}) do |key, result|
34-
result[key] = self[key]
10+
value.concat(new_value)
11+
value.concat(inherited_value)
3512
end
3613
end
37-
38-
def initialize_copy(other)
39-
super
40-
self.inherited_values = other.inherited_values
41-
self.new_values = other.new_values.dup
42-
end
4314
end
4415
end
4516
end

lib/grape/util/stackable_values.rb

+19-22
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,25 @@
1+
require_relative 'base_inheritable'
2+
13
module Grape
24
module Util
3-
class StackableValues
4-
attr_accessor :inherited_values
5-
attr_accessor :new_values
5+
class StackableValues < BaseInheritable
66
attr_reader :frozen_values
77

8-
def initialize(inherited_values = {})
9-
@inherited_values = inherited_values
10-
@new_values = {}
8+
def initialize(*_args)
9+
super
10+
1111
@frozen_values = {}
1212
end
1313

1414
def [](name)
1515
return @frozen_values[name] if @frozen_values.key? name
1616

17-
value = []
18-
value.concat(@inherited_values[name] || [])
19-
value.concat(@new_values[name] || [])
20-
value
17+
inherited_value = @inherited_values[name]
18+
new_value = @new_values[name] || []
19+
20+
return new_value unless inherited_value
21+
22+
concat_values(inherited_value, new_value)
2123
end
2224

2325
def []=(name, value)
@@ -26,14 +28,6 @@ def []=(name, value)
2628
@new_values[name].push value
2729
end
2830

29-
def delete(key)
30-
new_values.delete key
31-
end
32-
33-
def keys
34-
(@new_values.keys + @inherited_values.keys).sort.uniq
35-
end
36-
3731
def to_hash
3832
keys.each_with_object({}) do |key, result|
3933
result[key] = self[key]
@@ -44,10 +38,13 @@ def freeze_value(key)
4438
@frozen_values[key] = self[key].freeze
4539
end
4640

47-
def initialize_copy(other)
48-
super
49-
self.inherited_values = other.inherited_values
50-
self.new_values = other.new_values.dup
41+
protected
42+
43+
def concat_values(inherited_value, new_value)
44+
[].tap do |value|
45+
value.concat(inherited_value)
46+
value.concat(new_value)
47+
end
5148
end
5249
end
5350
end

lib/grape/validations/validators/base.rb

+4-4
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ def validate!(params)
5656

5757
def self.convert_to_short_name(klass)
5858
ret = klass.name.gsub(/::/, '/')
59-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
60-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
61-
.tr('-', '_')
62-
.downcase
59+
ret.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
60+
ret.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
61+
ret.tr!('-', '_')
62+
ret.downcase!
6363
File.basename(ret, '_validator')
6464
end
6565

0 commit comments

Comments
 (0)