Module: Ippon::Validate::Builder
- Defined in:
- lib/ippon/validate.rb
Overview
Class Method Summary collapse
-
.boolean(**props) ⇒ Step
The boolean schema converts falsy values (
false
andnil
) tofalse
and all other values totrue
. -
.fetch(key, **props, &blk) ⇒ Step
The fetch schema extracts a field (given by
key
) from a value by using#fetch
. -
.float(**props) ⇒ Step
A number schema with convert: :float.
-
.for_each(schema) ⇒ ForEach
The for-each schema applies the given
schema
to each element of the input data. -
.form(fields = {}, &blk) ⇒ Form
A form schema.
-
.match(predicate, **props) ⇒ Step
The match schema produces an error if predicate === value is false.
-
.number(**props) ⇒ Step
The number schema converts a String into a number.
-
.optional(**props, &blk) ⇒ Step
The optional schema halts on
nil
input values. -
.required(**props) ⇒ Step
The required schema produces an error if the input value is non-nil.
-
.transform(**props) { ... } ⇒ Step
The transform schema yields the value and updates the result with the returned value.
-
.trim(**props) ⇒ Step
The trim schema trims leading and trailing whitespace and then converts the value to nil if it’s empty.
-
.validate(**props) { ... } ⇒ Step
The validate schema produces an error if the yielded block returns false.
Class Method Details
.boolean(**props) ⇒ Step
The boolean schema converts falsy values (false
and nil
) to false
and all other values to true
.
boolean.validate!(nil) # => false
boolean.validate!("123") # => true
1155 1156 1157 1158 1159 |
# File 'lib/ippon/validate.rb', line 1155 def boolean(**props) transform(type: :boolean, **props) do |value| !!value end end |
.fetch(key, **props, &blk) ⇒ Step
The fetch schema extracts a field (given by key
) from a value by using #fetch
.
This is strictly equivalent to:
transform { |value| value.fetch(key) { error_is_produced } }
963 964 965 966 967 968 |
# File 'lib/ippon/validate.rb', line 963 def fetch(key, **props, &blk) blk ||= proc { StepError } transform(type: :fetch, key: key, message: "must be present", **props) do |value| value.fetch(key, &blk) end end |
.float(**props) ⇒ Step
Returns a number schema with convert: :float.
1143 1144 1145 |
# File 'lib/ippon/validate.rb', line 1143 def float(**props) number(convert: :float, **props) end |
.for_each(schema) ⇒ ForEach
The for-each schema applies the given schema
to each element of the input data.
1197 1198 1199 |
# File 'lib/ippon/validate.rb', line 1197 def for_each(schema) ForEach.new(schema) end |
.form(fields = {}, &blk) ⇒ Form
Returns a form schema.
1162 1163 1164 |
# File 'lib/ippon/validate.rb', line 1162 def form(fields = {}, &blk) Form.new(&blk).with_keys(fields) end |
.match(predicate, **props) ⇒ Step
The match schema produces an error if predicate === value is false.
This is a versatile validator which you can use for many different purposes:
# Number is within range
match(1..20)
# Value is of type
match(String)
# String matches regexp
match(/@/)
1186 1187 1188 1189 1190 |
# File 'lib/ippon/validate.rb', line 1186 def match(predicate, **props) validate(type: :match, predicate: predicate, message: "must match #{predicate}", **props) do |value| predicate === value end end |
.number(**props) ⇒ Step
The number schema converts a String into a number.
The input value must be a String. You should use required, optional or match(String) to enforce this.
By default the number schema will ignore spaces and convert the number to an Integer. If the value contains a fractional part, a validation error is produced.
number.validate!("1 000") # => 1000
number.validate!("1 000.00") # => 1000
number.validate!("1 000.05") # => Error
You can change the set of ignored characters with the :ignore
option. This can either be a string (in which case all characters in the string will be removed) or a regexp (in which case all matches of the regexp will be removed).
# Also ignore commas
number(ignore: ", ").validate!("1,000") # => 1000
# Remove dashes, but only in the middle by using a negative lookbehind
with_dash = number(ignore: / |(?<!\A)-/)
with_dash.validate!("-10") # => -10
with_dash.validate!("10-10-10") # => 101010
The :convert
option instructs how to handle fractional parts. The following values are supported:
-
:integer
: Return an Integer, but produce an error if it has a fractional part. -
:round
: Return an Integer by rounding it to the nearest integer. -
:ceil
: Return an Integer by rounding it down. -
:floor
: Return an Integer by rounding it up. -
:float
: Return a Float. -
:decimal
: Return a BigDecimal. -
:rational
: Return a Rational.
You can change the decimal separator as well:
# Convention for writing numbers in Norwegian
nor_number = number(decimal_separator: ",", ignore: " .", convert: :float)
nor_number.validate!("1.000,50") # => 1000.50
If you’re dealing with numbers where there’s a smaller, fractional unit, you can provide the :scale
option in order to represent the number exactly as an Integer:
dollars = number(ignore: " $,", scale: 100)
dollars.validate!("$100") # => 10000
dollars.validate!("$100.33") # => 10033
dollars.validate!("$100.333") # => Error
:scale
works together with :convert
as expected. For instance, if you want to round numbers that are smaller than the fractional unit, you can combine it with convert: :round.
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 |
# File 'lib/ippon/validate.rb', line 1088 def number(**props) transform(type: :number, message: "must be a number", **props) do |value| ignore = props.fetch(:ignore, / /) ignore_regex = case ignore when Regexp ignore when String /[#{Regexp.escape(ignore)}]/ else raise ArgumentError, "unknown pattern: #{ignore}" end value = value.gsub(ignore_regex, "") if sep = props[:decimal_separator] value = value.sub(sep, ".") end begin num = Rational(value) rescue ArgumentError next StepError end if scale = props[:scale] num *= scale end case convert = props.fetch(:convert, :integer) when :integer if num.denominator == 1 num.numerator else StepError end when :round num.round when :floor num.floor when :ceil num.ceil when :float num.to_f when :rational num when :decimal BigDecimal(num, value.size) else raise ArgumentError, "unknown convert: #{convert.inspect}" end end end |
.optional(**props) ⇒ Step .optional(**props) {|value| ... } ⇒ Step
The optional schema halts on nil
input values.
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 |
# File 'lib/ippon/validate.rb', line 1012 def optional(**props, &blk) Step.new(type: :optional, **props) do |result| value = result.value should_halt = blk ? blk.call(value) : value.nil? if should_halt result.value = nil result.halt end result end end |
.required(**props) ⇒ Step
The required schema produces an error if the input value is non-nil.
992 993 994 995 996 |
# File 'lib/ippon/validate.rb', line 992 def required(**props) validate(type: :required, message: "is required", **props) do |value| !value.nil? end end |
.transform(**props) { ... } ⇒ Step
The transform schema yields the value and updates the result with the returned value.
transform { |val| val * 2 }.validate!(2) # => 4
1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 |
# File 'lib/ippon/validate.rb', line 1227 def transform(**props, &blk) step = Step.new(**props) do |result| new_value = yield result.value if StepError.equal?(new_value) result.halt result.mutable_errors.add_step(step) else result.value = new_value end result end end |
.trim(**props) ⇒ Step
The trim schema trims leading and trailing whitespace and then converts the value to nil if it’s empty. The input data must either be a String or nil (in which case nothing happens).
976 977 978 979 980 981 982 983 984 |
# File 'lib/ippon/validate.rb', line 976 def trim(**props) transform(type: :trim, **props) do |value| if value value = value.strip value = nil if value.empty? end value end end |
.validate(**props) { ... } ⇒ Step
The validate schema produces an error if the yielded block returns false.
validate { |num| num.even? }
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 |
# File 'lib/ippon/validate.rb', line 1209 def validate(**props, &blk) step = Step.new(**props) do |result| is_valid = yield result.value if !is_valid result.halt result.mutable_errors.add_step(step) end result end end |