knight 0.0.1 → 0.0.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.
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.travis.yml +9 -0
- data/Gemfile +7 -0
- data/Gemfile.dev +16 -0
- data/README.md +3 -1
- data/Rakefile +6 -1
- data/knight.gemspec +4 -1
- data/lib/knight.rb +18 -2
- data/lib/knight/error.rb +60 -0
- data/lib/knight/instance_methods.rb +69 -0
- data/lib/knight/result.rb +86 -0
- data/lib/knight/rule.rb +74 -0
- data/lib/knight/rule/exact_length.rb +63 -0
- data/lib/knight/rule/format.rb +62 -0
- data/lib/knight/rule/inclusion.rb +62 -0
- data/lib/knight/rule/maximum_length.rb +63 -0
- data/lib/knight/rule/minimum_length.rb +63 -0
- data/lib/knight/rule/presence.rb +37 -0
- data/lib/knight/rule/range_length.rb +64 -0
- data/lib/knight/validator.rb +91 -0
- data/lib/knight/version.rb +3 -1
- data/metrics/flog.yml +2 -0
- data/metrics/rubocop.yml +17 -0
- data/spec/integration/contextual_validator_spec.rb +41 -0
- data/spec/integration/knight_spec.rb +30 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/unit/knight/error/class_methods/new_spec.rb +15 -0
- data/spec/unit/knight/error/message_spec.rb +26 -0
- data/spec/unit/knight/instance_methods/class_methods/included_spec.rb +24 -0
- data/spec/unit/knight/instance_methods/class_methods/new_spec.rb +12 -0
- data/spec/unit/knight/instance_methods/class_methods/validator_spec.rb +13 -0
- data/spec/unit/knight/instance_methods/run_spec.rb +29 -0
- data/spec/unit/knight/result/class_methods/new_spec.rb +13 -0
- data/spec/unit/knight/result/errors_spec.rb +71 -0
- data/spec/unit/knight/result/valid_predicate_spec.rb +28 -0
- data/spec/unit/knight/rule/class_methods/new_spec.rb +38 -0
- data/spec/unit/knight/rule/error_spec.rb +29 -0
- data/spec/unit/knight/rule/exact_length/class_methods/new_spec.rb +27 -0
- data/spec/unit/knight/rule/exact_length/matches_predicate_spec.rb +19 -0
- data/spec/unit/knight/rule/exact_length/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/format/class_methods/new_spec.rb +26 -0
- data/spec/unit/knight/rule/format/matches_predicate_spec.rb +31 -0
- data/spec/unit/knight/rule/format/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/inclusion/class_methods/new_spec.rb +28 -0
- data/spec/unit/knight/rule/inclusion/matches_predicate_spec.rb +48 -0
- data/spec/unit/knight/rule/inclusion/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/maximum_length/class_methods/new_spec.rb +27 -0
- data/spec/unit/knight/rule/maximum_length/matches_predicate_spec.rb +21 -0
- data/spec/unit/knight/rule/maximum_length/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/minimum_length/class_methods/new_spec.rb +27 -0
- data/spec/unit/knight/rule/minimum_length/matches_predicate_spec.rb +21 -0
- data/spec/unit/knight/rule/minimum_length/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/presence/matches_predicate_spec.rb +27 -0
- data/spec/unit/knight/rule/range_length/class_methods/new_spec.rb +27 -0
- data/spec/unit/knight/rule/range_length/matches_predicate_spec.rb +19 -0
- data/spec/unit/knight/rule/range_length/to_hash_spec.rb +12 -0
- data/spec/unit/knight/rule/to_hash_spec.rb +13 -0
- data/spec/unit/knight/validator/add_spec.rb +29 -0
- data/spec/unit/knight/validator/class_methods/new_spec.rb +18 -0
- data/spec/unit/knight/validator/context_spec.rb +13 -0
- data/spec/unit/knight/validator/rules_spec.rb +29 -0
- data/spec/unit/knight/validator/run_spec.rb +26 -0
- metadata +138 -6
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the format of a value
|
7
|
+
class Format < self
|
8
|
+
DEFAULT_MESSAGE = '%{attribute} has an invalid format'.freeze
|
9
|
+
|
10
|
+
# Regexp format
|
11
|
+
#
|
12
|
+
# @return [Regexp]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :regexp
|
16
|
+
|
17
|
+
# Initialize a format rule
|
18
|
+
#
|
19
|
+
# @param [Symbol] attribute_name
|
20
|
+
# @param [Regexp] regexp
|
21
|
+
#
|
22
|
+
# @option options [String] :message error_message
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# rule = Knight::Rule::Format.new(:username, /A-Z0-9/)
|
26
|
+
#
|
27
|
+
# @return [undefined]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def initialize(attribute_name, regexp, options = {})
|
31
|
+
super(attribute_name, options)
|
32
|
+
@regexp = regexp
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check value presence
|
36
|
+
#
|
37
|
+
# @param [String] value
|
38
|
+
#
|
39
|
+
# @return [true] if matched with the format
|
40
|
+
# @return [false] otherwise
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def matches?(value)
|
44
|
+
!!(regexp =~ value)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the rule as a hash
|
48
|
+
#
|
49
|
+
# @return [Hash]
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# hash = rule.to_hash
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def to_hash
|
56
|
+
super.merge({
|
57
|
+
format: regexp
|
58
|
+
}).freeze
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the inclusion of a value
|
7
|
+
class Inclusion < self
|
8
|
+
DEFAULT_MESSAGE = '%{attribute} is not included in the list'.freeze
|
9
|
+
|
10
|
+
# Inclusion value
|
11
|
+
#
|
12
|
+
# @return [Range, Array]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :within
|
16
|
+
|
17
|
+
# Initialize an inclusion rule
|
18
|
+
#
|
19
|
+
# @param [Symbol] attribute_name
|
20
|
+
# @param [Range, Array] within
|
21
|
+
#
|
22
|
+
# @option options [String] :message error_message
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# rule = Knight::Rule::Inclusion.new(:username, ['john', 'jane'])
|
26
|
+
#
|
27
|
+
# @return [undefined]
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
def initialize(attribute_name, within, options = {})
|
31
|
+
super(attribute_name, options)
|
32
|
+
@within = within
|
33
|
+
end
|
34
|
+
|
35
|
+
# Check value presence
|
36
|
+
#
|
37
|
+
# @param [Object] value
|
38
|
+
#
|
39
|
+
# @return [true] if the value within inclusion value
|
40
|
+
# @return [false] otherwise
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def matches?(value)
|
44
|
+
within.respond_to?(:cover?) ? within.cover?(value) : within.include?(value)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the rule as a hash
|
48
|
+
#
|
49
|
+
# @return [Hash]
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# hash = rule.to_hash
|
53
|
+
#
|
54
|
+
# @api public
|
55
|
+
def to_hash
|
56
|
+
super.merge({
|
57
|
+
within: within
|
58
|
+
}).freeze
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the length of a value
|
7
|
+
class MaximumLength < self
|
8
|
+
DEFAULT_MESSAGE = '%{attribute} is too long'.freeze
|
9
|
+
|
10
|
+
# Length value
|
11
|
+
#
|
12
|
+
# @return [Fixnum]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :maximum
|
16
|
+
|
17
|
+
# Initialize an exact length rule
|
18
|
+
#
|
19
|
+
# @param [Symbol] attribute_name
|
20
|
+
# @param [Fixnum] maximum
|
21
|
+
# @param [Hash] options for this rule
|
22
|
+
#
|
23
|
+
# @option options [String] :message error_message
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# rule = Knight::Rule::MaximumLength.new(:username, 20)
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def initialize(attribute_name, maximum, options = {})
|
32
|
+
super(attribute_name, options)
|
33
|
+
@maximum = maximum
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check length of the value
|
37
|
+
#
|
38
|
+
# @param [Object] value
|
39
|
+
#
|
40
|
+
# @return [true] if the length is matched
|
41
|
+
# @return [false] otherwise
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
def matches?(value)
|
45
|
+
value.length <= maximum
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the rule as a hash
|
49
|
+
#
|
50
|
+
# @return [Hash]
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# hash = rule.to_hash
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def to_hash
|
57
|
+
super.merge({
|
58
|
+
maximum: maximum
|
59
|
+
}).freeze
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the length of a value
|
7
|
+
class MinimumLength < self
|
8
|
+
DEFAULT_MESSAGE = '%{attribute} is too short'.freeze
|
9
|
+
|
10
|
+
# Length value
|
11
|
+
#
|
12
|
+
# @return [Fixnum]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :minimum
|
16
|
+
|
17
|
+
# Initialize an exact length rule
|
18
|
+
#
|
19
|
+
# @param [Symbol] attribute_name
|
20
|
+
# @param [Fixnum] minimum
|
21
|
+
# @param [Hash] options for this rule
|
22
|
+
#
|
23
|
+
# @option options [String] :message error_message
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# rule = Knight::Rule::MinimumLength.new(:username, 3)
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def initialize(attribute_name, minimum, options = {})
|
32
|
+
super(attribute_name, options)
|
33
|
+
@minimum = minimum
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check length of the value
|
37
|
+
#
|
38
|
+
# @param [Object] value
|
39
|
+
#
|
40
|
+
# @return [true] if the length is matched
|
41
|
+
# @return [false] otherwise
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
def matches?(value)
|
45
|
+
value.length >= minimum
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the rule as a hash
|
49
|
+
#
|
50
|
+
# @return [Hash]
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# hash = rule.to_hash
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def to_hash
|
57
|
+
super.merge({
|
58
|
+
minimum: minimum
|
59
|
+
}).freeze
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the presence of a value
|
7
|
+
class Presence < self
|
8
|
+
DEFAULT_MESSAGE = "%{attribute} can't be blank".freeze
|
9
|
+
|
10
|
+
# Check the presence of a value
|
11
|
+
#
|
12
|
+
# @param [Object] value
|
13
|
+
#
|
14
|
+
# @return [true] if not blank
|
15
|
+
# @return [false] otherwise
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def matches?(value)
|
19
|
+
!blank?(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Blank function
|
25
|
+
#
|
26
|
+
# @param [Object] value
|
27
|
+
#
|
28
|
+
# @return [true] if nil, empty string/array/hash
|
29
|
+
# @return [false] otherwise
|
30
|
+
#
|
31
|
+
# @api private
|
32
|
+
def blank?(value)
|
33
|
+
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Rule
|
5
|
+
|
6
|
+
# A rule for checking the length of a value
|
7
|
+
class RangeLength < self
|
8
|
+
DEFAULT_MESSAGE = '%{attribute} has an invalid length'.freeze
|
9
|
+
|
10
|
+
# Length value
|
11
|
+
#
|
12
|
+
# @return [Range]
|
13
|
+
#
|
14
|
+
# @api private
|
15
|
+
attr_reader :range
|
16
|
+
|
17
|
+
# Initialize a length rule
|
18
|
+
#
|
19
|
+
# @param [Symbol] attribute_name
|
20
|
+
# @param [Range] range
|
21
|
+
# @param [Hash] options for this rule
|
22
|
+
#
|
23
|
+
# @option options [String] :message error_message
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# rule = Knight::Rule::RangeLength.new(:username, 3..20)
|
27
|
+
#
|
28
|
+
# @return [undefined]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def initialize(attribute_name, range, options = {})
|
32
|
+
super(attribute_name, options)
|
33
|
+
@range = range
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check length of the value
|
37
|
+
#
|
38
|
+
# @param [Object] value
|
39
|
+
#
|
40
|
+
# @return [true] if the length is accepted
|
41
|
+
# @return [false] otherwise
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
def matches?(value)
|
45
|
+
range.cover?(value.length)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the rule as a hash
|
49
|
+
#
|
50
|
+
# @return [Hash]
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# hash = rule.to_hash
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def to_hash
|
57
|
+
super.merge({
|
58
|
+
minimum: range.min,
|
59
|
+
maximum: range.max
|
60
|
+
}).freeze
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Knight
|
4
|
+
class Validator
|
5
|
+
|
6
|
+
DEFAULT_CONTEXT = :default
|
7
|
+
|
8
|
+
# Initialize a validator
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Validator.new
|
12
|
+
# Validator.new(Rule::Presence.new(:username))
|
13
|
+
#
|
14
|
+
# @param [Array(Rule)] rules
|
15
|
+
#
|
16
|
+
# @return [Validator]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def initialize(*rules)
|
20
|
+
@rules = {}
|
21
|
+
rules.each { |rule| add(rule) }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a rule
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# validator = Validator.new
|
28
|
+
# validator.add(Rule::Presence.new(:username))
|
29
|
+
#
|
30
|
+
# @param [Rule] rule
|
31
|
+
# @param [Symbol] context
|
32
|
+
#
|
33
|
+
# @return [Set(Rule)]
|
34
|
+
#
|
35
|
+
# @api public
|
36
|
+
def add(rule, context_name = DEFAULT_CONTEXT)
|
37
|
+
context(context_name) { |rules| rules << rule } if rule
|
38
|
+
end
|
39
|
+
|
40
|
+
# Run the validator
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# validator = Validator.new(Rule::Presence.new(:username))
|
44
|
+
# user = User.new(username: 'john')
|
45
|
+
# validator.run(user)
|
46
|
+
#
|
47
|
+
# @param [Object] resource
|
48
|
+
# @param [Symbol] context
|
49
|
+
#
|
50
|
+
# @return [Result]
|
51
|
+
#
|
52
|
+
# @api public
|
53
|
+
def run(resource, context_name = DEFAULT_CONTEXT)
|
54
|
+
Result.new(resource, rules(context_name))
|
55
|
+
end
|
56
|
+
|
57
|
+
# Run the validator
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# validator = Validator.new(Rule::Presence.new(:username))
|
61
|
+
# validator.rules
|
62
|
+
#
|
63
|
+
# @param [Symbol] context
|
64
|
+
#
|
65
|
+
# @return [Set(Rule)]
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def rules(context_name = DEFAULT_CONTEXT)
|
69
|
+
@rules.fetch(context_name) do |key|
|
70
|
+
@rules[key] = Set.new
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Accept block
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# validator = Validator.new
|
78
|
+
# validator.context :registration do |context|
|
79
|
+
# context << Rule::Presence.new(:username)
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# @param [Symbol] name
|
83
|
+
#
|
84
|
+
# @return [undefined]
|
85
|
+
#
|
86
|
+
# @api public
|
87
|
+
def context(name)
|
88
|
+
yield rules(name)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|