errapi 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -2
- data/README.md +6 -2
- data/VERSION +1 -1
- data/lib/errapi/condition.rb +71 -0
- data/lib/errapi/configuration.rb +79 -0
- data/lib/errapi/errors.rb +15 -0
- data/lib/errapi/location_builders.rb +17 -0
- data/lib/errapi/locations/dotted.rb +33 -0
- data/lib/errapi/locations/json.rb +33 -0
- data/lib/errapi/locations/none.rb +28 -0
- data/lib/errapi/locations.rb +4 -0
- data/lib/errapi/model.rb +30 -0
- data/lib/errapi/object_validator.rb +230 -0
- data/lib/errapi/plugins/i18n_messages.rb +20 -0
- data/lib/errapi/plugins/location.rb +29 -0
- data/lib/errapi/plugins/reason.rb +22 -0
- data/lib/errapi/plugins.rb +4 -0
- data/lib/errapi/single_validator.rb +19 -0
- data/lib/errapi/utils.rb +12 -0
- data/lib/errapi/validation_context.rb +49 -0
- data/lib/errapi/validation_error.rb +43 -0
- data/lib/errapi/validations/clusivity.rb +45 -0
- data/lib/errapi/validations/exclusion.rb +28 -0
- data/lib/errapi/validations/format.rb +33 -0
- data/lib/errapi/validations/inclusion.rb +28 -0
- data/lib/errapi/validations/length.rb +66 -0
- data/lib/errapi/validations/presence.rb +39 -0
- data/lib/errapi/validations/trim.rb +10 -0
- data/lib/errapi/validations/type.rb +54 -0
- data/lib/errapi/validations.rb +57 -0
- data/lib/errapi/validator_proxy.rb +21 -0
- data/lib/errapi.rb +45 -1
- metadata +63 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcae888fc36cd422f1fda237dd5381aa10e776d1
|
4
|
+
data.tar.gz: 757ad4e09cea3666e7279d59d544ff2e99c6bab4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fe74be1e6ef55493f04accf713edfc0ce497c982dc84d68e92262bc7183f4d4076fd872d53f15b27a782e7f613054dc76fd8f26c9f2ef95842cfc9bb020c5a3
|
7
|
+
data.tar.gz: 3ff0b1fee2094513a11daf6ef7d8eb12155a23cadcb4d318843d1bb57dbae2e6c4cf570dac8e1015343a50acf928ccaa4c91da8ad3800b4a92075369124d9c2b
|
data/Gemfile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
3
|
group :development do
|
4
|
+
gem 'i18n', '~> 0.7.0'
|
4
5
|
gem 'rake', '~> 10.3'
|
5
6
|
gem 'rspec', '~> 3.1'
|
7
|
+
gem 'rspec-collection_matchers', '~> 1.1'
|
6
8
|
gem 'jeweler', '~> 2.0'
|
7
9
|
gem 'rake-version', '~> 0.4'
|
8
|
-
gem 'simplecov', '~> 0.9'
|
9
|
-
gem 'coveralls', '~> 0.7', require: false
|
10
|
+
gem 'simplecov', '~> 0.9.1'
|
11
|
+
gem 'coveralls', '~> 0.7.3', require: false
|
10
12
|
end
|
data/README.md
CHANGED
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
**An extensible API-oriented validation library.**
|
4
4
|
|
5
|
-
[](http://badge.fury.io/rb/errapi)
|
6
6
|
[](https://gemnasium.com/AlphaHydrae/errapi)
|
7
7
|
[](http://travis-ci.org/AlphaHydrae/errapi)
|
8
|
-
[](https://coveralls.io/r/AlphaHydrae/errapi?branch=master)
|
9
9
|
|
10
10
|
## Installation
|
11
11
|
|
@@ -17,6 +17,10 @@ gem 'errapi', '~> 0.1.0'
|
|
17
17
|
|
18
18
|
Then run `bundle install`.
|
19
19
|
|
20
|
+
### Requirements
|
21
|
+
|
22
|
+
* Ruby 2+
|
23
|
+
|
20
24
|
## Meta
|
21
25
|
|
22
26
|
* **Author:** Simon Oulevay (Alpha Hydrae)
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
@@ -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
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'utils.rb')
|
2
|
+
|
3
|
+
module Errapi
|
4
|
+
|
5
|
+
class Configuration
|
6
|
+
attr_reader :options
|
7
|
+
attr_reader :plugins
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@options = OpenStruct.new
|
11
|
+
@plugins = OpenStruct.new
|
12
|
+
@validation_factories = {}
|
13
|
+
@condition_factories = {}
|
14
|
+
@location_factories = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure
|
18
|
+
yield self
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_error options = {}
|
22
|
+
Errapi::ValidationError.new options
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_error error, context
|
26
|
+
apply_plugins :build_error, error, context
|
27
|
+
end
|
28
|
+
|
29
|
+
def serialize_error error, serialized
|
30
|
+
apply_plugins :serialize_error, error, serialized
|
31
|
+
end
|
32
|
+
|
33
|
+
def new_context
|
34
|
+
Errapi::ValidationContext.new config: self
|
35
|
+
end
|
36
|
+
|
37
|
+
def plugin impl, options = {}
|
38
|
+
name = options[:name] || Utils.underscore(impl.to_s.sub(/.*::/, '')).to_sym
|
39
|
+
impl.config = self if impl.respond_to? :config=
|
40
|
+
@plugins[name] = impl
|
41
|
+
end
|
42
|
+
|
43
|
+
def validation_factory factory, options = {}
|
44
|
+
name = options[:name] || Utils.underscore(factory.to_s.sub(/.*::/, '')).to_sym
|
45
|
+
factory.config = self if factory.respond_to? :config=
|
46
|
+
@validation_factories[name] = factory
|
47
|
+
end
|
48
|
+
|
49
|
+
def validation name, options = {}
|
50
|
+
raise ArgumentError, "No validation factory registered for name #{name.inspect}" unless @validation_factories.key? name
|
51
|
+
factory = @validation_factories[name]
|
52
|
+
factory.respond_to?(:validation) ? factory.validation(options) : factory.new(options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def register_condition factory
|
56
|
+
factory.conditionals.each do |conditional|
|
57
|
+
raise ArgumentError, "Conditional #{conditional} should start with 'if' or 'unless'." unless conditional.to_s.match /^(if|unless)/
|
58
|
+
@condition_factories[conditional] = factory
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_conditions! source, options = {}
|
63
|
+
[].tap do |conditions|
|
64
|
+
@condition_factories.each_pair do |conditional,factory|
|
65
|
+
next unless source.key? conditional
|
66
|
+
conditions << factory.new(conditional, source.delete(conditional), options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def apply_plugins operation, *args
|
74
|
+
@plugins.each_pair do |name,plugin|
|
75
|
+
plugin.send operation, *args if plugin.respond_to? operation
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Errapi
|
2
|
+
# TODO: check all "raise" statements and use custom errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
class ValidationErrorInvalid < Error; end
|
5
|
+
class ValidationDefinitionInvalid < Error; end
|
6
|
+
|
7
|
+
class ValidationFailed < Error
|
8
|
+
attr_reader :context
|
9
|
+
|
10
|
+
def initialize context
|
11
|
+
super "#{context.errors.length} errors were found during validation."
|
12
|
+
@context = context
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Errapi
|
2
|
+
|
3
|
+
module LocationBuilders
|
4
|
+
|
5
|
+
def json_location string = nil
|
6
|
+
Locations::Json.new string
|
7
|
+
end
|
8
|
+
|
9
|
+
def dotted_location string = nil
|
10
|
+
Locations::Dotted.new string
|
11
|
+
end
|
12
|
+
|
13
|
+
def no_location
|
14
|
+
Locations::None.instance
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Errapi
|
2
|
+
|
3
|
+
class Locations::Dotted
|
4
|
+
|
5
|
+
def initialize location = nil
|
6
|
+
@location = location.to_s.sub /^\./, '' unless location.nil?
|
7
|
+
end
|
8
|
+
|
9
|
+
def relative parts
|
10
|
+
if @location.nil?
|
11
|
+
self.class.new parts
|
12
|
+
else
|
13
|
+
self.class.new "#{@location}.#{parts.to_s.sub(/^\./, '')}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def location_type
|
18
|
+
:dotted
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize
|
22
|
+
@location.nil? ? nil : @location
|
23
|
+
end
|
24
|
+
|
25
|
+
def === location
|
26
|
+
@location.to_s == location.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@location.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Errapi
|
2
|
+
|
3
|
+
class Locations::Json
|
4
|
+
|
5
|
+
def initialize location = nil
|
6
|
+
@location = location.nil? ? '' : "/#{location.to_s.sub(/^\//, '').sub(/\/$/, '')}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def relative parts
|
10
|
+
if @location.nil?
|
11
|
+
self.class.new parts
|
12
|
+
else
|
13
|
+
self.class.new "#{@location}/#{parts.to_s.sub(/^\./, '').sub(/\/$/, '')}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def location_type
|
18
|
+
:json
|
19
|
+
end
|
20
|
+
|
21
|
+
def serialize
|
22
|
+
@location
|
23
|
+
end
|
24
|
+
|
25
|
+
def === location
|
26
|
+
@location.to_s == location.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@location
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Errapi
|
4
|
+
|
5
|
+
class Locations::None
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
def relative parts
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def serialize
|
13
|
+
nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def === location
|
17
|
+
location.nil? || self == location
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
LOCATION_STRING
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
LOCATION_STRING = ''.freeze
|
27
|
+
end
|
28
|
+
end
|
data/lib/errapi/model.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Errapi
|
2
|
+
|
3
|
+
module Model
|
4
|
+
|
5
|
+
def errapi name = :default
|
6
|
+
validator = self.class.errapi name
|
7
|
+
ValidatorProxy.new self, validator
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.included mod
|
11
|
+
mod.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def errapi *args, &block
|
17
|
+
|
18
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
19
|
+
config = options[:config] || Errapi.config
|
20
|
+
config = Errapi.config config if config.kind_of? Symbol
|
21
|
+
|
22
|
+
name = args.shift || :default
|
23
|
+
|
24
|
+
@errapi_validators ||= {}
|
25
|
+
@errapi_validators[name] = Errapi::ObjectValidator.new(config, &block) if block
|
26
|
+
@errapi_validators[name]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'location_builders.rb')
|
2
|
+
|
3
|
+
module Errapi
|
4
|
+
|
5
|
+
class ObjectValidator
|
6
|
+
include LocationBuilders
|
7
|
+
|
8
|
+
def initialize config, options = {}, &block
|
9
|
+
# TODO: remove these options or if used, pass them to new validators instantiated in #register_validations
|
10
|
+
@config = config
|
11
|
+
@validations = []
|
12
|
+
instance_eval &block if block
|
13
|
+
end
|
14
|
+
|
15
|
+
def validates *args, &block
|
16
|
+
register_validations *args, &block
|
17
|
+
end
|
18
|
+
|
19
|
+
def validates_each *args, &block
|
20
|
+
|
21
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
22
|
+
options[:each] = args.shift
|
23
|
+
options[:each_options] = options.delete(:each_options) || {}
|
24
|
+
args << options
|
25
|
+
|
26
|
+
validates *args, &block
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate value, context, options = {}
|
30
|
+
# TODO: skip validation by default if previous errors at current location
|
31
|
+
# TODO: add support for previous value and skip validation by default if value is unchanged
|
32
|
+
|
33
|
+
return context.valid? unless @validations
|
34
|
+
|
35
|
+
location = if options[:location]
|
36
|
+
options[:location]
|
37
|
+
elsif options[:location_type]
|
38
|
+
builder = "#{options[:location_type]}_location"
|
39
|
+
raise "Unknown location type #{options[:location_type].inspect}" unless respond_to? builder
|
40
|
+
send builder
|
41
|
+
else
|
42
|
+
no_location
|
43
|
+
end
|
44
|
+
|
45
|
+
source = options[:source]
|
46
|
+
context_proxy = ContextProxy.new context, self, location
|
47
|
+
|
48
|
+
@validations.each do |validation_definition|
|
49
|
+
|
50
|
+
each = validation_definition[:each]
|
51
|
+
|
52
|
+
each_values = nil
|
53
|
+
each_values_set = nil
|
54
|
+
|
55
|
+
if each
|
56
|
+
each_values = extract(value, each[:target], options)[:value]
|
57
|
+
each_values_set = each_values.collect{ |v| true } if each_values.kind_of? Array
|
58
|
+
each_values = [] unless each_values.kind_of? Array
|
59
|
+
each_sources = each_values.collect{ |v| source }
|
60
|
+
else
|
61
|
+
each_values = [ value ]
|
62
|
+
each_values_set = [ options.fetch(:value_set, true) ]
|
63
|
+
each_sources = [ source ]
|
64
|
+
end
|
65
|
+
|
66
|
+
each_location = each ? location.relative(each[:options][:as] || each[:target]) : location
|
67
|
+
|
68
|
+
each_values.each.with_index do |each_value,i|
|
69
|
+
|
70
|
+
each_index_location = each ? each_location.relative(i) : location
|
71
|
+
context_proxy.current_location = each_index_location
|
72
|
+
|
73
|
+
next if validation_definition[:conditions].any?{ |condition| !condition.fulfilled?(each_value, context_proxy) }
|
74
|
+
|
75
|
+
validation_definition[:validations].each do |validation|
|
76
|
+
|
77
|
+
next if validation[:conditions] && validation[:conditions].any?{ |condition| !condition.fulfilled?(each_value, context_proxy) }
|
78
|
+
|
79
|
+
validation_definition[:targets].each do |target|
|
80
|
+
|
81
|
+
target_value_info = extract each_value, target, value_set: each_values_set[i], source: source
|
82
|
+
|
83
|
+
validation_location = target ? each_index_location.relative(validation[:target_alias] || target) : each_index_location
|
84
|
+
context_proxy.current_location = validation_location
|
85
|
+
|
86
|
+
error_options = {
|
87
|
+
value: target_value_info[:value],
|
88
|
+
source: target_value_info[:source],
|
89
|
+
value_set: target_value_info[:value_set],
|
90
|
+
constraints: validation[:validation_options]
|
91
|
+
}
|
92
|
+
|
93
|
+
error_options[:location] = validation_location unless validation_location.kind_of? Errapi::Locations::None
|
94
|
+
|
95
|
+
error_options[:validation] = validation[:validation_name] if validation[:validation_name]
|
96
|
+
|
97
|
+
context_proxy.with_error_options error_options do
|
98
|
+
validation_options = { location: validation_location, value_set: target_value_info[:value_set] }
|
99
|
+
validation[:validation].validate target_value_info[:value], context_proxy, validation_options
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: add config option to raise error by default
|
107
|
+
raise Errapi::ValidationFailed.new(context) if options[:raise_error] && context.errors?
|
108
|
+
|
109
|
+
context_proxy.valid?
|
110
|
+
end
|
111
|
+
|
112
|
+
def relative_location location
|
113
|
+
RelativeLocation.new location
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
class RelativeLocation
|
119
|
+
attr_reader :location
|
120
|
+
|
121
|
+
def initialize location
|
122
|
+
@location = location
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def extract value, target, options = {}
|
127
|
+
|
128
|
+
source = options[:source]
|
129
|
+
value_set = options.fetch :value_set, true
|
130
|
+
|
131
|
+
if target.nil?
|
132
|
+
{ value: value, value_set: value_set, source: source }
|
133
|
+
elsif target.respond_to? :call
|
134
|
+
{ value: target.call(value), value_set: value_set, source: value }
|
135
|
+
elsif value.kind_of? Hash
|
136
|
+
{ value: value[target], value_set: value.key?(target), source: value }
|
137
|
+
elsif value.respond_to?(target)
|
138
|
+
{ value: value.send(target), value_set: value_set, source: value }
|
139
|
+
else
|
140
|
+
{ value_set: false, source: value }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def register_validations *args, &block
|
145
|
+
# TODO: allow to set custom error options (e.g. reason) when registering validation
|
146
|
+
|
147
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
148
|
+
target_alias = options.delete :as
|
149
|
+
|
150
|
+
validations_definition = {
|
151
|
+
validations: []
|
152
|
+
}
|
153
|
+
|
154
|
+
# FIXME: register all validations (from :with, from block and from hash) in the order they are given
|
155
|
+
if options[:with]
|
156
|
+
validations_definition[:validations] += [*options.delete(:with)].collect{ |with| { validation: with, validation_options: {}, target_alias: target_alias } }
|
157
|
+
end
|
158
|
+
|
159
|
+
if block
|
160
|
+
validations_definition[:validations] << { validation: self.class.new(@config, &block), validation_options: {}, target_alias: target_alias }
|
161
|
+
end
|
162
|
+
|
163
|
+
if options[:each]
|
164
|
+
validations_definition[:each] = {
|
165
|
+
target: options.delete(:each),
|
166
|
+
options: options.delete(:each_options)
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
validations_definition[:conditions] = @config.extract_conditions! options
|
171
|
+
|
172
|
+
validations = options
|
173
|
+
raise Errapi::ValidationDefinitionInvalid, "No validation was defined. Use registered validations (e.g. `presence: true`), the :with option, or a block to define validations." if validations_definition[:validations].empty? && validations.empty?
|
174
|
+
|
175
|
+
validations.each do |validation_name,options|
|
176
|
+
next unless options
|
177
|
+
validation_options = options.kind_of?(Hash) ? options : {}
|
178
|
+
validation_target_alias = validation_options.delete(:as) || target_alias
|
179
|
+
conditions = @config.extract_conditions! validation_options
|
180
|
+
validation = @config.validation validation_name, validation_options
|
181
|
+
validations_definition[:validations] << { validation: validation, validation_name: validation_name, validation_options: validation_options, target_alias: validation_target_alias, conditions: conditions }
|
182
|
+
end
|
183
|
+
|
184
|
+
validations_definition[:targets] = args.empty? ? [ nil ] : args
|
185
|
+
|
186
|
+
@validations << validations_definition
|
187
|
+
end
|
188
|
+
|
189
|
+
class ContextProxy
|
190
|
+
instance_methods.each{ |m| undef_method m unless m =~ /(^__|^send$|^object_id$)/ }
|
191
|
+
attr_accessor :current_location
|
192
|
+
|
193
|
+
def initialize context, validator, location
|
194
|
+
@context = context
|
195
|
+
@validator = validator
|
196
|
+
@current_location = location
|
197
|
+
@error_options = {}
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_error options = {}, &block
|
201
|
+
@context.add_error @error_options.merge(options), &block
|
202
|
+
end
|
203
|
+
|
204
|
+
def errors? criteria = {}, &block
|
205
|
+
|
206
|
+
if criteria[:location].kind_of? RelativeLocation
|
207
|
+
criteria = criteria.dup
|
208
|
+
criteria[:location] = @current_location.relative criteria[:location].location
|
209
|
+
end
|
210
|
+
|
211
|
+
@context.errors? criteria, &block
|
212
|
+
end
|
213
|
+
|
214
|
+
# TODO: override errors? to support matching relative error locations
|
215
|
+
|
216
|
+
def with_error_options error_options = {}, &block
|
217
|
+
previous_error_options = @error_options
|
218
|
+
@error_options = error_options
|
219
|
+
block.call
|
220
|
+
@error_options = previous_error_options
|
221
|
+
end
|
222
|
+
|
223
|
+
protected
|
224
|
+
|
225
|
+
def method_missing name, *args, &block
|
226
|
+
@context.send name, *args, &block
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'i18n'
|
2
|
+
|
3
|
+
# TODO: support interpolating source and target name (e.g. "Project name cannot be null.")
|
4
|
+
class Errapi::Plugins::I18nMessages
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def serialize_error error, serialized
|
8
|
+
return if serialized.key? :message
|
9
|
+
|
10
|
+
if I18n.exists? translation_key = "errapi.#{error.reason}"
|
11
|
+
interpolation_values = INTERPOLATION_KEYS.inject({}){ |memo,key| memo[key] = error.send(key); memo }.reject{ |k,v| v.nil? }
|
12
|
+
serialized[:message] = I18n.t translation_key, interpolation_values
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
INTERPOLATION_KEYS = %i(check_value checked_value)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Errapi
|
2
|
+
class Plugins::Location
|
3
|
+
class << self
|
4
|
+
attr_writer :config
|
5
|
+
attr_accessor :camelize
|
6
|
+
|
7
|
+
def serialize_error error, serialized
|
8
|
+
if error.location && error.location.respond_to?(:serialize)
|
9
|
+
|
10
|
+
serialized_location = error.location.serialize
|
11
|
+
unless serialized_location.nil?
|
12
|
+
serialized[:location] = serialized_location
|
13
|
+
serialized[location_type_key] = error.location.location_type if error.location.respond_to? :location_type
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def location_type_key
|
21
|
+
camelize? ? :locationType : :location_type
|
22
|
+
end
|
23
|
+
|
24
|
+
def camelize?
|
25
|
+
@camelize.nil? ? @config.options.camelize : @camelize
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Errapi
|
2
|
+
class Plugins::Reason
|
3
|
+
class << self
|
4
|
+
attr_writer :config
|
5
|
+
attr_accessor :camelize
|
6
|
+
|
7
|
+
def serialize_error error, serialized
|
8
|
+
serialized[:reason] = serialized_reason error
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def serialized_reason error
|
14
|
+
camelize? ? Utils.camelize(error.reason.to_s).to_sym : error.reason
|
15
|
+
end
|
16
|
+
|
17
|
+
def camelize?
|
18
|
+
@camelize.nil? ? @config.options.camelize : @camelize
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|