-
Notifications
You must be signed in to change notification settings - Fork 205
Expand file tree
/
Copy pathparams.cr
More file actions
175 lines (145 loc) · 4.75 KB
/
params.cr
File metadata and controls
175 lines (145 loc) · 4.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
module Amber::Validators
# Holds a validation error message
record Error, param : String, value : String?, message : String
# This struct holds the validation rules to be performed
class BaseRule
getter predicate : (String -> Bool)
getter field : String
getter value : String | Array(JSON::Any) | JSON::Any | Nil
getter present : Bool
def initialize(field : String | Symbol, @msg : String?, @allow_blank : Bool = true)
@field = field.to_s
@present = false
@predicate = ->(_s : String) { true }
end
def initialize(field : String | Symbol, @msg : String?, @allow_blank : Bool = true, &block : String -> Bool)
@field = field.to_s
@present = false
@predicate = block
end
def apply(params : Amber::Router::Params)
raise Exceptions::Validator::InvalidParam.new(@field) unless params.has_key? @field
call_predicate(params)
end
def error
Error.new @field, @value.to_s, error_message
end
private def call_predicate(params : Amber::Router::Params)
@present = params.has_key?(@field)
begin
value = JSON.parse(params[@field])
if value.is_a? Array(JSON::Any)
@value = value
elsif value.as_a?
@value = value.as_a
else
@value = value
end
rescue
@value = params[@field]
end
return true if params[@field].blank? && @allow_blank
@predicate.call params[@field] unless @predicate.nil?
end
private def error_message
@msg || "Field #{@field} is required"
end
end
# RequiredRule returns false if key is missing or value is blank or if block returns false.
class RequiredRule < BaseRule
def apply(params : Amber::Router::Params)
return false unless params.has_key?(@field)
return false if params[@field].blank? && !@allow_blank
call_predicate(params)
end
end
# OptionalRule only validates (evaluates block) if the key is present and the value is not blank (see call_predicate).
class OptionalRule < BaseRule
def apply(params : Amber::Router::Params)
return true if !params.has_key?(@field)
call_predicate(params)
end
end
record ValidationBuilder, _validator : Params do
def required(param : String | Symbol, msg : String? = nil, allow_blank = false)
_validator.add_rule RequiredRule.new(param, msg, allow_blank)
end
def required(param : String | Symbol, msg : String? = nil, allow_blank = false, &b : String -> Bool)
_validator.add_rule RequiredRule.new(param, msg, allow_blank, &b)
end
def optional(param : String | Symbol, msg : String? = nil, allow_blank = true)
_validator.add_rule OptionalRule.new(param, msg, allow_blank)
end
def optional(param : String | Symbol, msg : String? = nil, allow_blank = true, &b : String -> Bool)
_validator.add_rule OptionalRule.new(param, msg, allow_blank, &b)
end
end
class Params
getter raw_params : Amber::Router::Params
getter rules = [] of BaseRule
getter params = {} of String => String | Array(JSON::Any) | JSON::Any | Nil
getter errors = [] of Error
def initialize(@raw_params)
end
# This will allow params to respond to HTTP::Params methods.
# For example: [], []?, add, delete, each, fetch, etc.
forward_missing_to @raw_params
# Setups validation rules to be performed
#
# ```
# params.validation do
# required(:email) { |p| p.url? }
# required(:age, UInt32)
# end
# ```
def validation(&)
with ValidationBuilder.new(self) yield
self
end
# Input must be valid otherwise raises error, if valid returns a hash
# of validated params Otherwise raises a Validator::ValidationFailed error
# messages contain errors.
#
# ```
# user = User.new params.validate!
# ```
def validate!
return params if valid?
raise Amber::Exceptions::Validator::ValidationFailed.new errors
end
# Returns True or false whether the validation passed
#
# ```
# unless params.valid?
# response.puts {errors: params.errors}.to_json
# response.status_code 400
# end
# ```
def valid?
@errors.clear
@params.clear
@rules.each do |rule|
unless rule.apply(raw_params)
@errors << rule.error
end
@params[rule.field] = rule.value if rule.present
end
errors.empty?
end
# Validates each field with a given set of predicates returns true if the
# field is valid otherwise returns false
#
# ```
# required(:email) { |p| p.email? & p.size.between? 1..10 }
# ```
def add_rule(rule : BaseRule)
@rules << rule
end
def to_h
@params
end
def to_unsafe_h
@raw_params.to_h
end
end
end