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

ruby-grape / grape / 23097513014

14 Mar 2026 10:16PM UTC coverage: 96.715% (-0.1%) from 96.843%
23097513014

Pull #2657

github

ericproulx
Instantiate validators at definition time

Validators are now instantiated once at route definition time rather
than per-request, eliminating repeated allocation overhead. Instances
are frozen to make them safe for sharing across requests.

Freezing strategy: inputs (attrs, options, opts) are frozen at the DSL
boundary before entering the validator, so subclass ivars derived from
them are frozen by construction. Base.new reduces to super.freeze.
Remove freeze_state! and ValidatorFactory.

ParamsScope: precompute full_path via build_full_path before
instance_eval so child scopes can read the parent path immediately.
Simplify meets_hash_dependency? with all? and dependency.first.

Validators::Base: add validation_error! helper to replace repeated
Grape::Exceptions::Validation.new calls across single-attr validators.

Fix DefaultValidator to always dup duplicable default values regardless
of frozen state, preserving per-request isolation.

Add DeepFreeze utility (freezes Hash/Array/String recursively, skips
Procs, coercers, and classes). Add specs for DeepFreeze,
SameAsValidator, and ExceptValuesValidator.
Pull Request #2657: Instantiate validators at definition time

1073 of 1166 branches covered (92.02%)

Branch coverage included in aggregate %.

165 of 168 new or added lines in 19 files covered. (98.21%)

1 existing line in 1 file now uncovered.

3372 of 3430 relevant lines covered (98.31%)

32669.09 hits per line

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

92.31
/lib/grape/validations/validators/default_validator.rb
1
# frozen_string_literal: true
2

3
module Grape
37✔
4
  module Validations
37✔
5
    module Validators
37✔
6
      class DefaultValidator < Base
37✔
7
        def initialize(attrs, options, required, scope, opts)
37✔
8
          super
5,025✔
9
          # !important, lazy call at runtime
10
          @default_call =
11
            if @option.is_a?(Proc)
5,025✔
12
              @option.arity.zero? ? ->(_p) { @option.call } : ->(p) { @option.call(p) }
448✔
13
            elsif @option.duplicable?
4,801!
14
              ->(_p) { @option.dup }
7,746✔
15
            else
NEW
16
              ->(_p) { @option }
×
17
            end
18
        end
19

20
        def validate!(params)
37✔
21
          attrs = SingleAttributeIterator.new(@attrs, @scope, params)
4,449✔
22
          attrs.each do |resource_params, attr_name|
4,449✔
23
            next unless @scope.meets_dependency?(resource_params, params)
4,609✔
24

25
            resource_params[attr_name] = @default_call.call(resource_params) if resource_params.is_a?(Hash) && resource_params[attr_name].nil?
4,513✔
26
          end
27
        end
28
      end
29
    end
30
  end
31
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