class Grape::Validations::AttributesIterator
def do_each(params_to_process, original_params, parent_indices = [], &block)
def do_each(params_to_process, original_params, parent_indices = [], &block) params_to_process.each_with_index do |resource_params, index| # when we get arrays of arrays it means that target element located inside array # we need this because we want to know parent arrays indices if resource_params.is_a?(Array) do_each(resource_params, original_params, [index] + parent_indices, &block) next end if @scope.type == Array next unless original_params.is_a?(Array) # do not validate content of array if it isn't array store_indices(@scope, index, parent_indices) elsif original_params.is_a?(Array) # Lateral scope (no @element) whose params resolved to an array — # delegate index tracking to the nearest array-typed ancestor so # that full_name produces the correct bracketed index. target = @scope.nearest_array_ancestor store_indices(target, index, parent_indices) if target end yield_attributes(resource_params, &block) end end
def each(params, &)
def each(params, &) original_params = @scope.params(params) # because we need recursion for nested arrays do_each(Array.wrap(original_params), original_params, &) end
def initialize(attrs, scope)
no request-derived state). Reused instances are shared across
per request, so an instance can be built once and reused (it keeps
+attrs+ and +scope+ are static per validator; only +params+ varies
def initialize(attrs, scope) @attrs = attrs @scope = scope end
def skip?(val)
at the parameter parsing stage as they are required to ensure
values are missing lower down. Unfortunately we can't remove this
This is a special case so that we can ignore trees where option
def skip?(val) val == Grape::DSL::Parameters::EmptyOptionalValue end
def store_indices(target_scope, index, parent_indices)
def store_indices(target_scope, index, parent_indices) # No tracker means we're outside a ParamScopeTracker.track block (e.g. # a unit test that invokes a validator directly). Index tracking is # skipped — full_name will produce bracket-less names — but validation # continues rather than crashing. tracker = ParamScopeTracker.current or return parent_scope = target_scope.parent parent_indices.each do |parent_index| break unless parent_scope tracker.store_index(parent_scope, parent_index) parent_scope = parent_scope.parent end tracker.store_index(target_scope, index) end
def yield_attributes(_resource_params)
def yield_attributes(_resource_params) raise NotImplementedError end