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.
Files changed (64) hide show
  1. data/.gitignore +1 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +9 -0
  4. data/Gemfile +7 -0
  5. data/Gemfile.dev +16 -0
  6. data/README.md +3 -1
  7. data/Rakefile +6 -1
  8. data/knight.gemspec +4 -1
  9. data/lib/knight.rb +18 -2
  10. data/lib/knight/error.rb +60 -0
  11. data/lib/knight/instance_methods.rb +69 -0
  12. data/lib/knight/result.rb +86 -0
  13. data/lib/knight/rule.rb +74 -0
  14. data/lib/knight/rule/exact_length.rb +63 -0
  15. data/lib/knight/rule/format.rb +62 -0
  16. data/lib/knight/rule/inclusion.rb +62 -0
  17. data/lib/knight/rule/maximum_length.rb +63 -0
  18. data/lib/knight/rule/minimum_length.rb +63 -0
  19. data/lib/knight/rule/presence.rb +37 -0
  20. data/lib/knight/rule/range_length.rb +64 -0
  21. data/lib/knight/validator.rb +91 -0
  22. data/lib/knight/version.rb +3 -1
  23. data/metrics/flog.yml +2 -0
  24. data/metrics/rubocop.yml +17 -0
  25. data/spec/integration/contextual_validator_spec.rb +41 -0
  26. data/spec/integration/knight_spec.rb +30 -0
  27. data/spec/spec_helper.rb +18 -0
  28. data/spec/unit/knight/error/class_methods/new_spec.rb +15 -0
  29. data/spec/unit/knight/error/message_spec.rb +26 -0
  30. data/spec/unit/knight/instance_methods/class_methods/included_spec.rb +24 -0
  31. data/spec/unit/knight/instance_methods/class_methods/new_spec.rb +12 -0
  32. data/spec/unit/knight/instance_methods/class_methods/validator_spec.rb +13 -0
  33. data/spec/unit/knight/instance_methods/run_spec.rb +29 -0
  34. data/spec/unit/knight/result/class_methods/new_spec.rb +13 -0
  35. data/spec/unit/knight/result/errors_spec.rb +71 -0
  36. data/spec/unit/knight/result/valid_predicate_spec.rb +28 -0
  37. data/spec/unit/knight/rule/class_methods/new_spec.rb +38 -0
  38. data/spec/unit/knight/rule/error_spec.rb +29 -0
  39. data/spec/unit/knight/rule/exact_length/class_methods/new_spec.rb +27 -0
  40. data/spec/unit/knight/rule/exact_length/matches_predicate_spec.rb +19 -0
  41. data/spec/unit/knight/rule/exact_length/to_hash_spec.rb +12 -0
  42. data/spec/unit/knight/rule/format/class_methods/new_spec.rb +26 -0
  43. data/spec/unit/knight/rule/format/matches_predicate_spec.rb +31 -0
  44. data/spec/unit/knight/rule/format/to_hash_spec.rb +12 -0
  45. data/spec/unit/knight/rule/inclusion/class_methods/new_spec.rb +28 -0
  46. data/spec/unit/knight/rule/inclusion/matches_predicate_spec.rb +48 -0
  47. data/spec/unit/knight/rule/inclusion/to_hash_spec.rb +12 -0
  48. data/spec/unit/knight/rule/maximum_length/class_methods/new_spec.rb +27 -0
  49. data/spec/unit/knight/rule/maximum_length/matches_predicate_spec.rb +21 -0
  50. data/spec/unit/knight/rule/maximum_length/to_hash_spec.rb +12 -0
  51. data/spec/unit/knight/rule/minimum_length/class_methods/new_spec.rb +27 -0
  52. data/spec/unit/knight/rule/minimum_length/matches_predicate_spec.rb +21 -0
  53. data/spec/unit/knight/rule/minimum_length/to_hash_spec.rb +12 -0
  54. data/spec/unit/knight/rule/presence/matches_predicate_spec.rb +27 -0
  55. data/spec/unit/knight/rule/range_length/class_methods/new_spec.rb +27 -0
  56. data/spec/unit/knight/rule/range_length/matches_predicate_spec.rb +19 -0
  57. data/spec/unit/knight/rule/range_length/to_hash_spec.rb +12 -0
  58. data/spec/unit/knight/rule/to_hash_spec.rb +13 -0
  59. data/spec/unit/knight/validator/add_spec.rb +29 -0
  60. data/spec/unit/knight/validator/class_methods/new_spec.rb +18 -0
  61. data/spec/unit/knight/validator/context_spec.rb +13 -0
  62. data/spec/unit/knight/validator/rules_spec.rb +29 -0
  63. data/spec/unit/knight/validator/run_spec.rb +26 -0
  64. 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