• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ruby-grape / grape / 26722632890

31 May 2026 07:45PM UTC coverage: 96.865% (+0.004%) from 96.861%
26722632890

Pull #2753

github

ericproulx
Lazy-allocate ParamScopeTracker's identity hashes

ParamScopeTracker eagerly built two {}.compare_by_identity hashes in its
constructor, so every validating request allocated both even though many
validators (presence, coercion of flat params) never touch either tracker.

Replace the eager initialize with memoized private accessors that allocate
each hash on first use. Behaviour is unchanged: reads still return nil /
EMPTY_PARAMS when nothing was stored.

In a mixed-endpoint memory_profiler run this drops the two allocation sites
from ~1,900 to ~500 objects each (~0.72 MB / ~2,800 fewer objects overall).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pull Request #2753: Lazy-allocate ParamScopeTracker's identity hashes

1103 of 1193 branches covered (92.46%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

3531 of 3591 relevant lines covered (98.33%)

23292.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.15
/lib/grape/exceptions/validation_errors.rb
1
# frozen_string_literal: true
2

3
module Grape
30✔
4
  module Exceptions
30✔
5
    class ValidationErrors < Base
30✔
6
      include Enumerable
30✔
7

8
      attr_reader :errors
30✔
9

10
      def initialize(exceptions: [], headers: {})
30✔
11
        @errors = exceptions.flat_map(&:errors).group_by(&:params)
8,553✔
12
        super(message: full_messages.join(', '), status: 400, headers:)
8,553✔
13
      end
14

15
      def each
30✔
16
        errors.each_pair do |attribute, errors|
8,697✔
17
          errors.each do |error|
10,619✔
18
            yield attribute, error
11,267✔
19
          end
20
        end
21
      end
22

23
      def as_json(**_opts)
30✔
24
        errors.map do |k, v|
48✔
25
          {
26
            params: k,
48✔
27
            messages: v.map(&:to_s)
28
          }
29
        end
30
      end
31

32
      def to_json(*_opts)
30✔
UNCOV
33
        as_json.to_json
×
34
      end
35

36
      def full_messages
30✔
37
        messages = map do |attributes, error|
8,601✔
38
          translate(
11,171✔
39
            :format,
40
            scope: 'grape.errors',
41
            default: '%<attributes>s %<message>s',
42
            attributes: translate_attributes(attributes),
43
            message: error.message
44
          )
45
        end
46
        messages.uniq!
8,601✔
47
        messages
8,601✔
48
      end
49

50
      private
30✔
51

52
      def translate_attributes(keys)
30✔
53
        keys.map do |key|
11,171✔
54
          translate(key, scope: 'grape.errors.attributes', default: key.to_s)
12,995✔
55
        end.join(', ')
56
      end
57
    end
58
  end
59
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc