Skip to content

Commit abd1004

Browse files
author
Oliver Azevedo Barnes
committed
Validations refactor
1 parent 1993aa8 commit abd1004

27 files changed

+245
-242
lines changed

lib/grape.rb

+14-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ module Util
100100
end
101101

102102
module DSL
103-
autoload :API, 'grape/dsl/api'
103+
autoload :API, 'grape/dsl/api'
104104
autoload :Callbacks, 'grape/dsl/callbacks'
105105
autoload :Settings, 'grape/dsl/settings'
106106
autoload :Configuration, 'grape/dsl/configuration'
@@ -118,4 +118,17 @@ class API
118118
end
119119
end
120120

121+
require 'grape/validations/validators/base'
122+
require 'grape/validations/attributes_iterator'
123+
require 'grape/validations/validators/allow_blank'
124+
require 'grape/validations/validators/at_least_one_of'
125+
require 'grape/validations/validators/coerce'
126+
require 'grape/validations/validators/default'
127+
require 'grape/validations/validators/exactly_one_of'
128+
require 'grape/validations/validators/mutual_exclusion'
129+
require 'grape/validations/validators/presence'
130+
require 'grape/validations/validators/regexp'
131+
require 'grape/validations/validators/values'
132+
require 'grape/validations/params_scope'
133+
121134
require 'grape/version'

lib/grape/validations.rb

-223
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,5 @@
11
module Grape
22
module Validations
3-
##
4-
# All validators must inherit from this class.
5-
#
6-
class Validator
7-
attr_reader :attrs
8-
9-
def initialize(attrs, options, required, scope)
10-
@attrs = Array(attrs)
11-
@required = required
12-
@scope = scope
13-
end
14-
15-
def validate!(params)
16-
attributes = AttributesIterator.new(self, @scope, params)
17-
attributes.each do |resource_params, attr_name|
18-
if @required || resource_params.key?(attr_name)
19-
validate_param!(attr_name, resource_params)
20-
end
21-
end
22-
end
23-
24-
class AttributesIterator
25-
include Enumerable
26-
27-
def initialize(validator, scope, params)
28-
@attrs = validator.attrs
29-
@params = scope.params(params)
30-
@params = (@params.is_a?(Array) ? @params : [@params])
31-
end
32-
33-
def each
34-
@params.each do |resource_params|
35-
@attrs.each do |attr_name|
36-
yield resource_params, attr_name
37-
end
38-
end
39-
end
40-
end
41-
42-
def self.convert_to_short_name(klass)
43-
ret = klass.name.gsub(/::/, '/')
44-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
45-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
46-
.tr("-", "_")
47-
.downcase
48-
File.basename(ret, '_validator')
49-
end
50-
end
51-
52-
##
53-
# Base class for all validators taking only one param.
54-
class SingleOptionValidator < Validator
55-
def initialize(attrs, options, required, scope)
56-
@option = options
57-
super
58-
end
59-
end
60-
61-
# We define Validator::inherited here so SingleOptionValidator
62-
# will not be considered a validator.
63-
class Validator
64-
def self.inherited(klass)
65-
short_name = convert_to_short_name(klass)
66-
Validations.register_validator(short_name, klass)
67-
end
68-
end
69-
703
class << self
714
attr_accessor :validators
725
end
@@ -76,161 +9,5 @@ class << self
769
def self.register_validator(short_name, klass)
7710
validators[short_name] = klass
7811
end
79-
80-
class ParamsScope
81-
attr_accessor :element, :parent
82-
83-
include Grape::DSL::Parameters
84-
85-
def initialize(opts, &block)
86-
@element = opts[:element]
87-
@parent = opts[:parent]
88-
@api = opts[:api]
89-
@optional = opts[:optional] || false
90-
@type = opts[:type]
91-
@declared_params = []
92-
93-
instance_eval(&block) if block_given?
94-
95-
configure_declared_params
96-
end
97-
98-
def should_validate?(parameters)
99-
return false if @optional && params(parameters).respond_to?(:all?) && params(parameters).all?(&:blank?)
100-
return true if parent.nil?
101-
parent.should_validate?(parameters)
102-
end
103-
104-
def full_name(name)
105-
return "#{@parent.full_name(@element)}[#{name}]" if @parent
106-
name.to_s
107-
end
108-
109-
def root?
110-
!@parent
111-
end
112-
113-
protected
114-
115-
def push_declared_params(attrs)
116-
@declared_params.concat attrs
117-
end
118-
119-
private
120-
121-
def require_required_and_optional_fields(context, opts)
122-
if context == :all
123-
optional_fields = Array(opts[:except])
124-
required_fields = opts[:using].keys - optional_fields
125-
else # context == :none
126-
required_fields = Array(opts[:except])
127-
optional_fields = opts[:using].keys - required_fields
128-
end
129-
required_fields.each do |field|
130-
field_opts = opts[:using][field]
131-
raise ArgumentError, "required field not exist: #{field}" unless field_opts
132-
requires(field, field_opts)
133-
end
134-
optional_fields.each do |field|
135-
field_opts = opts[:using][field]
136-
optional(field, field_opts) if field_opts
137-
end
138-
end
139-
140-
def validate_attributes(attrs, opts, &block)
141-
validations = { presence: true }
142-
validations.merge!(opts) if opts
143-
validations[:type] ||= Array if block
144-
validates(attrs, validations)
145-
end
146-
147-
def new_scope(attrs, optional = false, &block)
148-
opts = attrs[1] || { type: Array }
149-
raise ArgumentError unless opts.keys.to_set.subset? [:type].to_set
150-
ParamsScope.new(api: @api, element: attrs.first, parent: self, optional: optional, type: opts[:type], &block)
151-
end
152-
153-
# Pushes declared params to parent or settings
154-
def configure_declared_params
155-
if @parent
156-
@parent.push_declared_params [element => @declared_params]
157-
else
158-
@api.namespace_stackable(:declared_params, @declared_params)
159-
160-
@api.route_setting(:declared_params, []) unless @api.route_setting(:declared_params)
161-
@api.route_setting(:declared_params).concat @declared_params
162-
end
163-
end
164-
165-
def validates(attrs, validations)
166-
doc_attrs = { required: validations.keys.include?(:presence) }
167-
168-
# special case (type = coerce)
169-
validations[:coerce] = validations.delete(:type) if validations.key?(:type)
170-
171-
coerce_type = validations[:coerce]
172-
doc_attrs[:type] = coerce_type.to_s if coerce_type
173-
174-
desc = validations.delete(:desc)
175-
doc_attrs[:desc] = desc if desc
176-
177-
default = validations[:default]
178-
doc_attrs[:default] = default if default
179-
180-
values = validations[:values]
181-
doc_attrs[:values] = values if values
182-
183-
values = (values.is_a?(Proc) ? values.call : values)
184-
185-
# default value should be present in values array, if both exist
186-
if default && values && !values.include?(default)
187-
raise Grape::Exceptions::IncompatibleOptionValues.new(:default, default, :values, values)
188-
end
189-
190-
# type should be compatible with values array, if both exist
191-
if coerce_type && values && values.any? { |v| !v.kind_of?(coerce_type) }
192-
raise Grape::Exceptions::IncompatibleOptionValues.new(:type, coerce_type, :values, values)
193-
end
194-
195-
doc_attrs[:documentation] = validations.delete(:documentation) if validations.key?(:documentation)
196-
197-
full_attrs = attrs.collect { |name| { name: name, full_name: full_name(name) } }
198-
@api.document_attribute(full_attrs, doc_attrs)
199-
200-
# Validate for presence before any other validators
201-
if validations.key?(:presence) && validations[:presence]
202-
validate('presence', validations[:presence], attrs, doc_attrs)
203-
validations.delete(:presence)
204-
end
205-
206-
# Before we run the rest of the validators, lets handle
207-
# whatever coercion so that we are working with correctly
208-
# type casted values
209-
if validations.key? :coerce
210-
validate('coerce', validations[:coerce], attrs, doc_attrs)
211-
validations.delete(:coerce)
212-
end
213-
214-
validations.each do |type, options|
215-
validate(type, options, attrs, doc_attrs)
216-
end
217-
end
218-
219-
def validate(type, options, attrs, doc_attrs)
220-
validator_class = Validations.validators[type.to_s]
221-
222-
if validator_class
223-
value = validator_class.new(attrs, options, doc_attrs[:required], self)
224-
@api.namespace_stackable(:validations, value)
225-
else
226-
raise Grape::Exceptions::UnknownValidator.new(type)
227-
end
228-
end
229-
end
23012
end
23113
end
232-
233-
# Load all defined validations.
234-
Dir[File.expand_path('../validations/*.rb', __FILE__)].each do |path|
235-
require(path)
236-
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module Grape
2+
module Validations
3+
class AttributesIterator
4+
include Enumerable
5+
6+
def initialize(validator, scope, params)
7+
@attrs = validator.attrs
8+
@params = scope.params(params)
9+
@params = (@params.is_a?(Array) ? @params : [@params])
10+
end
11+
12+
def each
13+
@params.each do |resource_params|
14+
@attrs.each do |attr_name|
15+
yield resource_params, attr_name
16+
end
17+
end
18+
end
19+
end
20+
end
21+
end

0 commit comments

Comments
 (0)