Class: Grape::Validations::ParamsScope

Inherits:
Object
  • Object
show all
Includes:
DSL::Parameters, ParamsDocumentation
Defined in:
lib/grape/validations/params_scope.rb

Defined Under Namespace

Classes: Attr

Constant Summary collapse

RESERVED_DOCUMENTATION_KEYWORDS =

There are a number of documentation options on entities that don't have corresponding validators. Since there is nowhere that enumerates them all, we maintain a list of them here and skip looking up validators for them.

%i[as required param_type is_array format example].freeze
SPECIAL_JSON =
[JSON, Array[JSON]].freeze

Constants included from DSL::Parameters

DSL::Parameters::EmptyOptionalValue

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ParamsDocumentation

#document_params

Methods included from DSL::Parameters

#build_with, #declared_param?, #given, #optional, #params, #requires, #use, #with

Constructor Details

#initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil) { ... } ⇒ ParamsScope

Open up a new ParamsScope, allowing parameter definitions per Grape::DSL::Params.

Parameters:

  • api (API)

    the API endpoint to modify

  • element (Symbol) (defaults to: nil)

    the element that contains this scope; for this to be relevant, parent must be set

  • element_renamed (Symbol, nil) (defaults to: nil)

    whenever this scope should be renamed and to what, given nil no renaming is done

  • parent (ParamsScope) (defaults to: nil)

    the scope containing this scope

  • optional (Boolean) (defaults to: false)

    whether or not this scope needs to have any parameters set or not

  • type (Class) (defaults to: nil)

    a type meant to govern this scope (deprecated)

  • type (Hash) (defaults to: nil)

    group options for this scope

  • dependent_on (Symbol) (defaults to: nil)

    if present, this scope should only validate if this param is present in the parent scope

Yields:

  • the instance context, open for parameter definitions



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/grape/validations/params_scope.rb', line 67

def initialize(api:, element: nil, element_renamed: nil, parent: nil, optional: false, type: nil, group: nil, dependent_on: nil, &block)
  @element          = element
  @element_renamed  = element_renamed
  @parent           = parent
  @api              = api
  @optional         = optional
  @type             = type
  @group            = group
  @dependent_on     = dependent_on
  # Must be an ivar: push_declared_params is dispatched on self during
  # instance_eval, so local variables from initialize are unreachable.
  # configure_declared_params consumes it and clears @declared_params to nil.
  @declared_params = []
  @full_path = build_full_path
  # Read by the validators instantiated from the block below, so it has to
  # be settled before the instance_eval.
  @array_depth = find_array_depth

  instance_eval(&block) if block

  configure_declared_params
  @nearest_array_ancestor = find_nearest_array_ancestor
  freeze
end

Instance Attribute Details

#array_depthObject (readonly)

Returns the value of attribute array_depth.



6
7
8
# File 'lib/grape/validations/params_scope.rb', line 6

def array_depth
  @array_depth
end

#full_pathObject (readonly)

Returns the value of attribute full_path.



6
7
8
# File 'lib/grape/validations/params_scope.rb', line 6

def full_path
  @full_path
end

#nearest_array_ancestorObject (readonly)

Returns the value of attribute nearest_array_ancestor.



6
7
8
# File 'lib/grape/validations/params_scope.rb', line 6

def nearest_array_ancestor
  @nearest_array_ancestor
end

#parentObject (readonly)

Returns the value of attribute parent.



6
7
8
# File 'lib/grape/validations/params_scope.rb', line 6

def parent
  @parent
end

#typeObject (readonly)

Returns the value of attribute type.



6
7
8
# File 'lib/grape/validations/params_scope.rb', line 6

def type
  @type
end

Instance Method Details

#attr_meets_dependency?(params) ⇒ Boolean

Returns:

  • (Boolean)


122
123
124
125
126
127
# File 'lib/grape/validations/params_scope.rb', line 122

def attr_meets_dependency?(params)
  return true unless @dependent_on
  return false if @parent.present? && !@parent.attr_meets_dependency?(params)

  meets_hash_dependency?(params)
end

#brackets(val) ⇒ Object



160
161
162
# File 'lib/grape/validations/params_scope.rb', line 160

def brackets(val)
  "[#{val}]" if val
end

#configurationObject



92
93
94
95
# File 'lib/grape/validations/params_scope.rb', line 92

def configuration
  config = @api.configuration
  config.is_a?(Grape::Util::Lazy::Base) ? config.evaluate : config
end

#full_name(name, index: nil) ⇒ String

Returns the proper attribute name, with nesting considered.

Returns:

  • (String)

    the proper attribute name, with nesting considered.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/grape/validations/params_scope.rb', line 144

def full_name(name, index: nil)
  tracker = ParamScopeTracker.current
  if nested?
    # Find our containing element's name, and append ours.
    resolved_index = index || tracker&.index_for(self)
    "#{@parent.full_name(@element)}#{brackets(resolved_index)}#{brackets(name)}"
  elsif lateral?
    # Find the name of the element as if it was at the same nesting level
    # as our parent. We need to forward our index upward to achieve this.
    @parent.full_name(name, index: tracker&.index_for(self))
  else
    # We must be the root scope, so no prefix needed.
    name.to_s
  end
end

#iterates_elements?Boolean

Whether this scope's params resolve to one entry per element, which is what makes both an element index and a nesting level meaningful.

type: Array[JSON] counts as much as type: Array does. It is easy to miss because it evaluates to the Array instance [JSON] rather than the Array class, so an == Array test quietly excluded it.

Returns:

  • (Boolean)


182
183
184
# File 'lib/grape/validations/params_scope.rb', line 182

def iterates_elements?
  @type == Array || @type == SPECIAL_JSON.last
end

#lateral?Boolean

A lateral scope is subordinate to its parent, but its keys are at the same level as its parent and thus is not contained within an element.

Returns:

  • (Boolean)

    whether or not this scope is lateral



189
190
191
# File 'lib/grape/validations/params_scope.rb', line 189

def lateral?
  @parent && !@element
end

#meets_dependency?(params, request_params) ⇒ Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/grape/validations/params_scope.rb', line 109

def meets_dependency?(params, request_params)
  return true unless @dependent_on
  return false if @parent.present? && !@parent.meets_dependency?(@parent.params(request_params), request_params)

  if params.is_a?(Array)
    filtered = params.flatten.filter { |param| meets_dependency?(param, request_params) }
    ParamScopeTracker.current&.store_qualifying_params(self, filtered)
    return filtered.present?
  end

  meets_hash_dependency?(params)
end

#meets_hash_dependency?(params) ⇒ Boolean

Returns:

  • (Boolean)


129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/grape/validations/params_scope.rb', line 129

def meets_hash_dependency?(params)
  # params might be anything what looks like a hash, so it must implement a `key?` method
  return false unless params.respond_to?(:key?)

  @dependent_on.all? do |dependency|
    if dependency.is_a?(Hash)
      key, callable = dependency.first
      callable.call(params[key])
    else
      params[dependency].present?
    end
  end
end

#nested?Boolean

A nested scope is contained in one of its parent's elements.

Returns:

  • (Boolean)

    whether or not this scope is nested



171
172
173
# File 'lib/grape/validations/params_scope.rb', line 171

def nested?
  @parent && @element
end

#qualifying_paramsObject



8
9
10
# File 'lib/grape/validations/params_scope.rb', line 8

def qualifying_params
  ParamScopeTracker.current&.qualifying_params(self)
end

#required?Boolean

Returns whether or not this scope needs to be present, or can be blank.

Returns:

  • (Boolean)

    whether or not this scope needs to be present, or can be blank



195
196
197
# File 'lib/grape/validations/params_scope.rb', line 195

def required?
  !@optional
end

#root?Boolean

Returns whether or not this scope is the root-level scope.

Returns:

  • (Boolean)

    whether or not this scope is the root-level scope



165
166
167
# File 'lib/grape/validations/params_scope.rb', line 165

def root?
  !@parent
end

#should_validate?(parameters) ⇒ Boolean

Returns whether or not this entire scope needs to be validated.

Returns:

  • (Boolean)

    whether or not this entire scope needs to be validated



99
100
101
102
103
104
105
106
107
# File 'lib/grape/validations/params_scope.rb', line 99

def should_validate?(parameters)
  scoped_params = params(parameters)

  return false if @optional && (scoped_params.blank? || all_element_blank?(scoped_params))
  return false unless meets_dependency?(scoped_params, parameters)
  return true if @parent.nil?

  @parent.should_validate?(parameters)
end