Skip to content

Commit 0db6ef4

Browse files
author
Simon Oulevay
committedDec 19, 2014
Changed registered validators to validator factories. Extracted conditions into classes.
1 parent 27d314b commit 0db6ef4

File tree

6 files changed

+118
-63
lines changed

6 files changed

+118
-63
lines changed
 

‎lib/errapi.rb

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ def self.config
1313
def self.default_config
1414
Configuration.new.tap do |config|
1515
config.plugins << Errapi::Plugins::ErrorCodes.new
16-
config.register_validator :length, Errapi::Validators::Length.new
17-
config.register_validator :presence, Errapi::Validators::Presence.new
16+
config.register_validator :length, Errapi::Validators::Length
17+
config.register_validator :presence, Errapi::Validators::Presence
18+
config.register_condition Errapi::Condition::SimpleCheck
19+
config.register_condition Errapi::Condition::ErrorCheck
1820
end
1921
end
2022
end

‎lib/errapi/condition.rb

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
class Errapi::Condition
2+
ALLOWED_CONDITIONALS = %i(if unless).freeze
3+
4+
def self.conditionals
5+
h = const_get('CONDITIONALS')
6+
raise LoadError, "The CONDITIONALS constant in class #{self} is of the wrong type (#{h.class}). Either make it a Hash or override #{self}.conditionals to return a list of symbols." unless h.kind_of? Hash
7+
h.keys
8+
end
9+
10+
def initialize conditional, predicate, options = {}
11+
12+
@conditional = resolve_conditional conditional
13+
raise ArgumentError, "Conditional must be either :if or :unless" unless ALLOWED_CONDITIONALS.include? @conditional
14+
15+
@predicate = predicate
16+
end
17+
18+
def fulfilled? *args
19+
result = check @predicate, *args
20+
result = !result if @conditional == :unless
21+
result
22+
end
23+
24+
def resolve_conditional conditional
25+
conditional
26+
end
27+
28+
def check predicate, value, context, options = {}
29+
raise NotImplementedError, "Subclasses should implement the #check method to check whether the value matches the predicate of the condition"
30+
end
31+
32+
class SimpleCheck < Errapi::Condition
33+
34+
CONDITIONALS = {
35+
if: :if,
36+
unless: :unless
37+
}.freeze
38+
39+
def check predicate, value, context, options = {}
40+
if @predicate.kind_of?(Symbol) || @predicate.kind_of?(String)
41+
value.respond_to?(:[]) ? value[@predicate] : value.send(@predicate)
42+
elsif @predicate.respond_to? :call
43+
@predicate.call value, context, options
44+
else
45+
@predicate
46+
end
47+
end
48+
end
49+
50+
class ErrorCheck < Errapi::Condition
51+
52+
CONDITIONALS = {
53+
if_error: :if,
54+
unless_error: :unless
55+
}.freeze
56+
57+
def resolve_conditional conditional
58+
CONDITIONALS[conditional]
59+
end
60+
61+
def check predicate, value, context, options = {}
62+
if @predicate.respond_to? :call
63+
context.errors? &@predicate
64+
elsif @predicate.kind_of? Hash
65+
context.errors? @predicate
66+
else
67+
@predicate ? context.errors? : !context.errors?
68+
end
69+
end
70+
end
71+
end

‎lib/errapi/configuration.rb

+21-6
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,34 @@ class Errapi::Configuration
44
def initialize
55
@plugins = []
66
@validators = {}
7+
@conditions = {}
78
end
89

910
def new_context
1011
Errapi::ValidationContext.new plugins: @plugins
1112
end
1213

13-
def register_validator name, validator
14-
raise ArgumentError, "The supplied object is not a validator (it does not respond to the #validate method)" unless validator.respond_to? :validate
15-
@validators[name] = validator
14+
def register_validator name, factory
15+
@validators[name] = factory
1616
end
1717

18-
def validator name
19-
raise ArgumentError, "No validator found with name #{name.inspect}" unless @validators.key? name
20-
@validators[name]
18+
def validator name, options = {}
19+
raise ArgumentError, "No validator factory registered for name #{name.inspect}" unless @validators.key? name
20+
@validators[name].new options
21+
end
22+
23+
def register_condition factory
24+
factory.conditionals.each do |conditional|
25+
@conditions[conditional] = factory
26+
end
27+
end
28+
29+
def extract_conditions! source, options = {}
30+
[].tap do |conditions|
31+
@conditions.each_pair do |conditional,factory|
32+
next unless source.key? conditional
33+
conditions << factory.new(conditional, source.delete(conditional), options)
34+
end
35+
end
2136
end
2237
end

‎lib/errapi/object_validations.rb

+10-51
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
class Errapi::ObjectValidations
22

3-
def initialize &block
3+
def initialize options = {}, &block
4+
45
@validations = []
56
@current_options = {}
7+
@config = options[:config] || Errapi.config
8+
69
instance_eval &block if block
710
end
811

@@ -23,8 +26,6 @@ def validates_each *args, &block
2326

2427
def validate value, context, options = {}
2528

26-
config = options.delete(:config) || Errapi.config
27-
2829
context.with self do
2930
with_options options do
3031

@@ -53,7 +54,7 @@ def validate value, context, options = {}
5354

5455
values.each.with_index do |value,i|
5556

56-
next if validation[:conditions].any?{ |condition| !evaluate_condition(condition, value, context) }
57+
next if validation[:conditions].any?{ |condition| !condition.fulfilled?(value, context) }
5758

5859
iteration_options = {}
5960
iteration_options = { location: actual_location(relative_location: i), value_set: values_set[i] } if each
@@ -71,7 +72,7 @@ def validate value, context, options = {}
7172
value_context_options[:value] = current_value
7273
value_context_options[:value_set] = value_data[:value_set]
7374

74-
validator = validation[:validator] || config.validator(validation[:validator_name])
75+
validator = validation[:validator]
7576
value_context_options[:validator_name] = validation[:validator_name] if validation[:validator_name]
7677
value_context_options[:validator_options] = validation[:validator_options]
7778

@@ -164,7 +165,7 @@ def register_validations *args, &block
164165
custom_validators << options.delete(:using) if options[:using] # TODO: allow array
165166
custom_validators << Errapi::ObjectValidations.new(&block) if block
166167

167-
conditions = extract_conditions! options
168+
conditions = @config.extract_conditions! options
168169

169170
# TODO: fail if there are no validations declared
170171
args = [ nil ] if args.empty?
@@ -197,55 +198,13 @@ def register_validations *args, &block
197198
validation.merge!({
198199
validator_options: validator_options,
199200
context_options: validator_options.delete(:with) || context_options,
200-
conditions: conditions + extract_conditions!(validator_options)
201+
conditions: conditions + @config.extract_conditions!(validator_options)
201202
})
202203

203-
@validations << validation.merge(target_options)
204-
end
205-
end
206-
end
207-
208-
def extract_conditions! options = {}
209-
# TODO: wrap conditions in objects that can cache the result
210-
[].tap do |conditions|
211-
conditions << { if: options.delete(:if) } if options[:if]
212-
conditions << { unless: options.delete(:unless) } if options[:unless]
213-
conditions << { if_error: options.delete(:if_error) } if options[:if_error]
214-
conditions << { unless_error: options.delete(:unless_error) } if options[:unless_error]
215-
end
216-
end
217-
218-
def evaluate_condition condition, value, context
219-
220-
conditional, condition_type, predicate = if condition.key? :if
221-
[ lambda{ |x| !!x }, :custom, condition[:if] ]
222-
elsif condition.key? :unless
223-
[ lambda{ |x| !x }, :custom, condition[:unless] ]
224-
elsif condition.key? :if_error
225-
[ lambda{ |x| !!x }, :error, condition[:if_error] ]
226-
elsif condition.key? :unless_error
227-
[ lambda{ |x| !x }, :error, condition[:unless_error] ]
228-
end
204+
validation[:validator] = @config.validator validator_name, validator_options
229205

230-
result = case condition_type
231-
when :custom
232-
if predicate.kind_of?(Symbol) || predicate.kind_of?(String)
233-
value.respond_to?(:[]) ? value[predicate] : value.send(predicate)
234-
elsif predicate.respond_to? :call
235-
predicate.call value, context, @current_options
236-
else
237-
predicate
238-
end
239-
when :error
240-
if predicate.respond_to? :call
241-
context.errors? &predicate
242-
elsif predicate.kind_of? Hash
243-
context.errors? predicate
244-
else
245-
predicate ? context.errors? : !context.errors?
206+
@validations << validation.merge(target_options)
246207
end
247208
end
248-
249-
conditional.call result
250209
end
251210
end

‎lib/errapi/validators/length.rb

+5-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@ class Errapi::Validators::Length
22
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
33
CAUSES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
44

5+
def initialize options = {}
6+
@constraints = actual_constraints options
7+
end
8+
59
def validate value, context, options = {}
610
return unless value.respond_to? :length
711

8-
options = actual_constraints options
912
actual_length = value.length
1013

1114
CHECKS.each_pair do |key,check|
12-
next unless check_value = options[key]
15+
next unless check_value = @constraints[key]
1316
next if actual_length.send check, check_value
1417
context.add_error cause: CAUSES[check]
1518
end

‎lib/errapi/validators/presence.rb

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
class Errapi::Validators::Presence
22

3+
def initialize options = {}
4+
end
5+
36
def validate value, context, options = {}
47
if value_blank? value
58
context.add_error cause: :blank
@@ -11,9 +14,11 @@ def validate value, context, options = {}
1114
BLANK_REGEXP = /\A[[:space:]]*\z/
1215

1316
def value_blank? value
14-
if value.kind_of? String
17+
if value.respond_to? :blank?
18+
value.blank?
19+
elsif value.kind_of? String
1520
BLANK_REGEXP === value
16-
elsif value.respond_to?(:empty?)
21+
elsif value.respond_to? :empty?
1722
value.empty?
1823
else
1924
!value

0 commit comments

Comments
 (0)
Please sign in to comment.