vanguard 0.0.3
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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +17 -0
- data/Gemfile +7 -0
- data/Gemfile.devtools +56 -0
- data/Guardfile +18 -0
- data/LICENSE +21 -0
- data/README.md +80 -0
- data/Rakefile +2 -0
- data/TODO +3 -0
- data/config/flay.yml +3 -0
- data/config/flog.yml +2 -0
- data/config/mutant.yml +3 -0
- data/config/reek.yml +99 -0
- data/config/roodi.yml +26 -0
- data/config/yardstick.yml +2 -0
- data/lib/vanguard.rb +85 -0
- data/lib/vanguard/builder.rb +14 -0
- data/lib/vanguard/builder/nullary.rb +182 -0
- data/lib/vanguard/dsl.rb +43 -0
- data/lib/vanguard/dsl/evaluator.rb +197 -0
- data/lib/vanguard/evaluator.rb +72 -0
- data/lib/vanguard/instance_methods.rb +76 -0
- data/lib/vanguard/matcher.rb +19 -0
- data/lib/vanguard/matcher/binary.rb +59 -0
- data/lib/vanguard/matcher/binary/and.rb +20 -0
- data/lib/vanguard/matcher/binary/or.rb +20 -0
- data/lib/vanguard/matcher/binary/xor.rb +22 -0
- data/lib/vanguard/matcher/nullary.rb +27 -0
- data/lib/vanguard/matcher/nullary/equality.rb +46 -0
- data/lib/vanguard/matcher/nullary/format.rb +126 -0
- data/lib/vanguard/matcher/nullary/greater_than.rb +45 -0
- data/lib/vanguard/matcher/nullary/identity.rb +50 -0
- data/lib/vanguard/matcher/nullary/inclusion.rb +67 -0
- data/lib/vanguard/matcher/nullary/less_than.rb +48 -0
- data/lib/vanguard/matcher/nullary/primitive.rb +47 -0
- data/lib/vanguard/matcher/nullary/proc.rb +44 -0
- data/lib/vanguard/matcher/nullary/value.rb +32 -0
- data/lib/vanguard/matcher/unary.rb +45 -0
- data/lib/vanguard/matcher/unary/attribute.rb +49 -0
- data/lib/vanguard/matcher/unary/not.rb +25 -0
- data/lib/vanguard/result.rb +93 -0
- data/lib/vanguard/rule.rb +43 -0
- data/lib/vanguard/rule/guard.rb +103 -0
- data/lib/vanguard/rule/nullary.rb +81 -0
- data/lib/vanguard/rule/nullary/attribute.rb +69 -0
- data/lib/vanguard/rule/nullary/attribute/absence.rb +19 -0
- data/lib/vanguard/rule/nullary/attribute/format.rb +46 -0
- data/lib/vanguard/rule/nullary/attribute/inclusion.rb +62 -0
- data/lib/vanguard/rule/nullary/attribute/length.rb +184 -0
- data/lib/vanguard/rule/nullary/attribute/predicate.rb +10 -0
- data/lib/vanguard/rule/nullary/attribute/presence.rb +32 -0
- data/lib/vanguard/rule/nullary/attribute/presence/not_blank.rb +0 -0
- data/lib/vanguard/rule/nullary/attribute/presence/not_nil.rb +0 -0
- data/lib/vanguard/rule/nullary/attribute/primitive.rb +35 -0
- data/lib/vanguard/rule/nullary/confirmation.rb +210 -0
- data/lib/vanguard/support/blank.rb +29 -0
- data/lib/vanguard/validator.rb +116 -0
- data/lib/vanguard/validator/builder.rb +52 -0
- data/lib/vanguard/violation.rb +84 -0
- data/spec/integration/vanguard/dsl/guard_spec.rb +58 -0
- data/spec/integration/vanguard/dsl/validates_absence_of_spec.rb +19 -0
- data/spec/integration/vanguard/dsl/validates_acceptance_of_spec.rb +19 -0
- data/spec/integration/vanguard/dsl/validates_confirmation_of_spec.rb +27 -0
- data/spec/integration/vanguard/dsl/validates_format_of_spec.rb +79 -0
- data/spec/integration/vanguard/dsl/validates_inclusion_of_spec.rb +23 -0
- data/spec/integration/vanguard/dsl/validates_length_of_spec.rb +77 -0
- data/spec/integration/vanguard/dsl/validates_presence_of_spec.rb +19 -0
- data/spec/integration/vanguard/dsl/validates_value_of_spec.rb +30 -0
- data/spec/integration/vanguard/validator_spec.rb +57 -0
- data/spec/rcov.opts +6 -0
- data/spec/shared/dsl_spec.rb +73 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/suite.rb +10 -0
- data/spec/unit/vanguard/support/blank_spec.rb +72 -0
- data/vanguard.gemspec +23 -0
- metadata +190 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
module Vanguard
|
2
|
+
# Instance level mixins
|
3
|
+
module InstanceMethods
|
4
|
+
include Adamantium
|
5
|
+
|
6
|
+
# Return validated resource
|
7
|
+
#
|
8
|
+
# @return [Object]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
#
|
12
|
+
attr_reader :resource
|
13
|
+
|
14
|
+
# Return violation set
|
15
|
+
#
|
16
|
+
# @return [ViolationSet]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
def result
|
21
|
+
self.class.validator.evaluate(resource)
|
22
|
+
end
|
23
|
+
memoize :result
|
24
|
+
|
25
|
+
# Test if validator is valid
|
26
|
+
#
|
27
|
+
# @return [true]
|
28
|
+
# if valid
|
29
|
+
#
|
30
|
+
# @return [false]
|
31
|
+
# otherwise
|
32
|
+
#
|
33
|
+
# @api private
|
34
|
+
#
|
35
|
+
def valid?
|
36
|
+
result.valid?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return violations on attribute name
|
40
|
+
#
|
41
|
+
# @param [Symbol] attribute_name
|
42
|
+
#
|
43
|
+
# @return [Enumerable<Violation>]
|
44
|
+
#
|
45
|
+
# @api private
|
46
|
+
#
|
47
|
+
def on(attribute_name)
|
48
|
+
result.on(attribute_name)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Return violations
|
52
|
+
#
|
53
|
+
# @return [Enumerable<Violation>]
|
54
|
+
#
|
55
|
+
# @api private
|
56
|
+
#
|
57
|
+
def violations
|
58
|
+
result.violations
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Initialize resource
|
64
|
+
#
|
65
|
+
# @param [Object] resource
|
66
|
+
#
|
67
|
+
# @return [undefined]
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
#
|
71
|
+
def initialize(resource)
|
72
|
+
@resource = resource
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Vanguard
|
2
|
+
# Abstract base class for matchers
|
3
|
+
class Matcher
|
4
|
+
include Adamantium::Flat, AbstractType
|
5
|
+
|
6
|
+
# Test if value matches
|
7
|
+
#
|
8
|
+
# @return [true]
|
9
|
+
# if value matches
|
10
|
+
#
|
11
|
+
# @return [false]
|
12
|
+
# otherwise
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
#
|
16
|
+
abstract_method :matches?
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
# Abstract base class for binary matcher
|
4
|
+
class Binary < self
|
5
|
+
include AbstractType, Equalizer.new(:left, :right)
|
6
|
+
|
7
|
+
# Return left
|
8
|
+
#
|
9
|
+
# @return [Matcher]
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
#
|
13
|
+
attr_reader :left
|
14
|
+
|
15
|
+
# Return right
|
16
|
+
#
|
17
|
+
# @return [Matcher]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
#
|
21
|
+
attr_reader :right
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
# Initialize object
|
26
|
+
#
|
27
|
+
# @param [Matcher] left
|
28
|
+
# @param [Matcher] right
|
29
|
+
#
|
30
|
+
# @return [undefined]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
#
|
34
|
+
def initialize(left, right)
|
35
|
+
@left, @right = left, right
|
36
|
+
end
|
37
|
+
|
38
|
+
# Test if left matches
|
39
|
+
#
|
40
|
+
# @param [Object] value
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
#
|
44
|
+
def left_matches?(value)
|
45
|
+
left.matches?(value)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Test if right matches
|
49
|
+
#
|
50
|
+
# @param [Object] value
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
#
|
54
|
+
def right_matches?(value)
|
55
|
+
right.matches?(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Binary
|
4
|
+
# Boolean and connector
|
5
|
+
class AND < self
|
6
|
+
# Test if value matches left and right
|
7
|
+
#
|
8
|
+
# @return [true]
|
9
|
+
#
|
10
|
+
# @return [false]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def matches?(value)
|
15
|
+
left_matches?(value) && right_matches?(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Binary
|
4
|
+
# Boolean or connector
|
5
|
+
class OR < self
|
6
|
+
# Test if value matches left or right
|
7
|
+
#
|
8
|
+
# @return [true]
|
9
|
+
#
|
10
|
+
# @return [false]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def matches?(value)
|
15
|
+
left_matches?(value) || right_matches?(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Binary
|
4
|
+
# Boolean xor connector
|
5
|
+
class XOR < self
|
6
|
+
|
7
|
+
# Test if value matches left xor right
|
8
|
+
#
|
9
|
+
# @return [true]
|
10
|
+
#
|
11
|
+
# @return [false]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def matches?(value)
|
16
|
+
left_matches?(value) ^ right_matches?(value)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
|
4
|
+
# Abstract base class for nullary matchers
|
5
|
+
class Nullary < self
|
6
|
+
|
7
|
+
# Matcher that matches blank values
|
8
|
+
Vanguard.singleton_constant(self, :BLANK) do
|
9
|
+
|
10
|
+
# Test if value is blank
|
11
|
+
#
|
12
|
+
# @return [true]
|
13
|
+
# if value is blank
|
14
|
+
#
|
15
|
+
# @return [false]
|
16
|
+
# otherwise
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
#
|
20
|
+
def matches?(value)
|
21
|
+
Vanguard.blank?(value)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Nullary
|
4
|
+
# Equality matcher
|
5
|
+
class Equality < self
|
6
|
+
include Equalizer.new(:value)
|
7
|
+
|
8
|
+
# Return value
|
9
|
+
#
|
10
|
+
# @return [Object]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
# Test if value for equality
|
17
|
+
#
|
18
|
+
# @param [Object] value
|
19
|
+
#
|
20
|
+
# @return [true]
|
21
|
+
#
|
22
|
+
# @return [false]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
#
|
26
|
+
def matches?(value)
|
27
|
+
value == self.value
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Intialize object
|
33
|
+
#
|
34
|
+
# @param [Object] value
|
35
|
+
#
|
36
|
+
# @return [undefined]
|
37
|
+
#
|
38
|
+
# @api private
|
39
|
+
#
|
40
|
+
def initialize(value)
|
41
|
+
@value = value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Nullary
|
4
|
+
# Abstract base class for format matchers
|
5
|
+
class Format < self
|
6
|
+
include AbstractType
|
7
|
+
|
8
|
+
# Matcher for regexps
|
9
|
+
class Regexp < self
|
10
|
+
include Equalizer.new(:regexp)
|
11
|
+
|
12
|
+
# Return regexp
|
13
|
+
#
|
14
|
+
# @return [Regexp]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
attr_reader :regexp
|
19
|
+
|
20
|
+
# Test if input matches regexp
|
21
|
+
#
|
22
|
+
# @param [String] input
|
23
|
+
#
|
24
|
+
# @return [true]
|
25
|
+
# if input matches regexp
|
26
|
+
#
|
27
|
+
# @return [false]
|
28
|
+
# otherwise
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
#
|
32
|
+
def matches?(input)
|
33
|
+
!!(regexp =~ input)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# Initialize object
|
39
|
+
#
|
40
|
+
# @param [Regexp] regexp
|
41
|
+
#
|
42
|
+
# @return [undefined]
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
#
|
46
|
+
def initialize(regexp)
|
47
|
+
@regexp = regexp
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Almost RFC2822 (No attribution reference available).
|
52
|
+
#
|
53
|
+
# This differs in that it does not allow local domains (test@localhost).
|
54
|
+
# 99% of the time you do not want to allow these email addresses
|
55
|
+
# in a public web application.
|
56
|
+
EMAIL_ADDRESS_REGEXP = begin
|
57
|
+
#if (RUBY_VERSION == '1.9.2' && RUBY_ENGINE == 'jruby' && JRUBY_VERSION <= '1.6.3') || RUBY_VERSION == '1.9.3'
|
58
|
+
# There is an obscure bug in jruby 1.6 that prevents matching
|
59
|
+
# on unicode properties here. Remove this logic branch once
|
60
|
+
# a stable jruby release fixes this.
|
61
|
+
#
|
62
|
+
# http://jira.codehaus.org/browse/JRUBY-5622
|
63
|
+
#
|
64
|
+
# There is a similar bug in preview releases of 1.9.3
|
65
|
+
#
|
66
|
+
# http://redmine.ruby-lang.org/issues/5126
|
67
|
+
letter = 'a-zA-Z'
|
68
|
+
#else
|
69
|
+
# letter = 'a-zA-Z\p{L}' # Changed from RFC2822 to include unicode chars
|
70
|
+
#end
|
71
|
+
digit = '0-9'
|
72
|
+
atext = "[#{letter}#{digit}\!\#\$\%\&\'\*+\/\=\?\^\_\`\{\|\}\~\-]"
|
73
|
+
dot_atom_text = "#{atext}+([.]#{atext}*)+"
|
74
|
+
dot_atom = dot_atom_text
|
75
|
+
no_ws_ctl = '\x01-\x08\x11\x12\x14-\x1f\x7f'
|
76
|
+
qtext = "[^#{no_ws_ctl}\\x0d\\x22\\x5c]" # Non-whitespace, non-control character except for \ and "
|
77
|
+
text = '[\x01-\x09\x11\x12\x14-\x7f]'
|
78
|
+
quoted_pair = "(\\x5c#{text})"
|
79
|
+
qcontent = "(?:#{qtext}|#{quoted_pair})"
|
80
|
+
quoted_string = "[\"]#{qcontent}+[\"]"
|
81
|
+
atom = "#{atext}+"
|
82
|
+
word = "(?:#{atom}|#{quoted_string})"
|
83
|
+
obs_local_part = "#{word}([.]#{word})*"
|
84
|
+
local_part = "(?:#{dot_atom}|#{quoted_string}|#{obs_local_part})"
|
85
|
+
dtext = "[#{no_ws_ctl}\\x21-\\x5a\\x5e-\\x7e]"
|
86
|
+
dcontent = "(?:#{dtext}|#{quoted_pair})"
|
87
|
+
domain_literal = "\\[#{dcontent}+\\]"
|
88
|
+
obs_domain = "#{atom}([.]#{atom})+"
|
89
|
+
domain = "(?:#{dot_atom}|#{domain_literal}|#{obs_domain})"
|
90
|
+
addr_spec = "#{local_part}\@#{domain}"
|
91
|
+
pattern = /\A#{addr_spec}\z/u
|
92
|
+
end.freeze
|
93
|
+
|
94
|
+
# Regex from http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/
|
95
|
+
URL_REGEXP =
|
96
|
+
/(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}((\:[0-9]{1,5})?\/?.*)?$)/ix.freeze
|
97
|
+
|
98
|
+
EMAIL_ADDRESS = Regexp.new(EMAIL_ADDRESS_REGEXP)
|
99
|
+
URL = Regexp.new(URL_REGEXP)
|
100
|
+
|
101
|
+
# Build format matcher
|
102
|
+
#
|
103
|
+
# @param [Object] value
|
104
|
+
#
|
105
|
+
# @return [Matcher::Format]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
#
|
109
|
+
def self.build(value)
|
110
|
+
case value
|
111
|
+
when :email_address
|
112
|
+
EMAIL_ADDRESS
|
113
|
+
when :url
|
114
|
+
URL
|
115
|
+
when ::Regexp
|
116
|
+
Regexp.new(value)
|
117
|
+
when ::Proc
|
118
|
+
Proc.new(value)
|
119
|
+
else
|
120
|
+
raise "Cannot build format matcher from #{value.inspect}"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Vanguard
|
2
|
+
class Matcher
|
3
|
+
class Nullary
|
4
|
+
# Greather than matcher
|
5
|
+
class GreaterThan < self
|
6
|
+
include Equalizer.new(:value)
|
7
|
+
|
8
|
+
# Test if value is greater
|
9
|
+
#
|
10
|
+
# @param [Object] value
|
11
|
+
#
|
12
|
+
# @return [true]
|
13
|
+
#
|
14
|
+
# @return [false]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
#
|
18
|
+
def matches?(value)
|
19
|
+
self.value < value
|
20
|
+
end
|
21
|
+
|
22
|
+
# Return value
|
23
|
+
#
|
24
|
+
# @return [Object]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
#
|
28
|
+
attr_reader :value
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
# Initialize object
|
33
|
+
#
|
34
|
+
# @param [Object] value
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
#
|
38
|
+
def initialize(value)
|
39
|
+
@value = value
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|