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
data/.gitignore CHANGED
@@ -15,3 +15,4 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ bin
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --order random
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ script: "COVERAGE=true bundle exec rspec"
3
+ rvm:
4
+ - 1.9.3
5
+ - 2.0.0
6
+ notifications:
7
+ email:
8
+ - handi_wiguna@yahoo.com
9
+
data/Gemfile CHANGED
@@ -1,4 +1,11 @@
1
+ # encoding: utf-8
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in knight.gemspec
4
6
  gemspec
7
+
8
+ group :development, :test do
9
+ gem 'rontgen'
10
+ eval_gemfile('Gemfile.dev') if File.exists?('Gemfile.dev')
11
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ group :development do
4
+ gem 'rake', '~>10.1'
5
+ gem 'rspec', '~>2.13'
6
+ end
7
+
8
+ group :metrics do
9
+ gem 'flog', '~>4.1'
10
+ gem 'rubocop', git: 'https://github.com/bbatsov/rubocop'
11
+ gem 'simplecov', '~>0.7'
12
+ gem 'mutant', '0.3.0.rc1'
13
+ gem 'coveralls', '~>0.6.7', require: false
14
+ end
15
+
16
+ eval_gemfile('Gemfile.local') if File.exists?('Gemfile.local')
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Knight
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://travis-ci.org/handiwiguna/knight.png?branch=master)](https://travis-ci.org/handiwiguna/knight)
4
+ [![Code Climate](https://codeclimate.com/github/handiwiguna/knight.png)](https://codeclimate.com/github/handiwiguna/knight)
5
+ [![Coverage Status](https://coveralls.io/repos/handiwiguna/knight/badge.png?branch=master)](https://coveralls.io/r/handiwiguna/knight)
4
6
 
5
7
  ## Installation
6
8
 
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
- require "bundler/gem_tasks"
1
+ # encoding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'rontgen'
6
+ Rontgen.load_tasks
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
7
7
  gem.name = "knight"
8
8
  gem.version = Knight::VERSION
9
9
  gem.authors = ["Handi Wiguna"]
10
- gem.email = ["handi@domikado.com"]
10
+ gem.email = ["handi_wiguna@yahoo.com"]
11
11
  gem.description = ""
12
12
  gem.summary = ""
13
13
  gem.homepage = ""
@@ -16,4 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency('equalizer', '~>0.0.5')
21
+ gem.add_dependency('abstract_type', '~>0.0.5')
19
22
  end
@@ -1,5 +1,21 @@
1
- require "knight/version"
1
+ # encoding: utf-8
2
+
3
+ require 'equalizer'
4
+ require 'abstract_type'
5
+
6
+ require 'knight/version'
7
+ require 'knight/rule'
8
+ require 'knight/rule/presence'
9
+ require 'knight/rule/exact_length'
10
+ require 'knight/rule/maximum_length'
11
+ require 'knight/rule/minimum_length'
12
+ require 'knight/rule/range_length'
13
+ require 'knight/rule/format'
14
+ require 'knight/rule/inclusion'
15
+ require 'knight/error'
16
+ require 'knight/result'
17
+ require 'knight/validator'
18
+ require 'knight/instance_methods'
2
19
 
3
20
  module Knight
4
- # Your code goes here...
5
21
  end
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ module Knight
4
+
5
+ # A representation of an error
6
+ class Error
7
+ include Equalizer.new(:rule, :resource)
8
+
9
+ # Broken rule
10
+ #
11
+ # @return [Rule]
12
+ #
13
+ # @api private
14
+ attr_reader :rule
15
+
16
+ # Object under validation
17
+ #
18
+ # @return [Object]
19
+ #
20
+ # @api private
21
+ attr_reader :resource
22
+
23
+ # Initialize an error
24
+ #
25
+ # @param [Rule] rule
26
+ # @param [Object] resource
27
+ #
28
+ # @return [Error]
29
+ #
30
+ # @api private
31
+ def initialize(rule, resource)
32
+ @rule = rule
33
+ @resource = resource
34
+ end
35
+
36
+ # Return formatted error message
37
+ #
38
+ # @example
39
+ # error_message = error.message
40
+ #
41
+ # @return [String]
42
+ #
43
+ # @api public
44
+ def message
45
+ rule.message % rule.to_hash.merge(value: value)
46
+ end
47
+
48
+ private
49
+
50
+ # Return attribute value
51
+ #
52
+ # @return [Object]
53
+ #
54
+ # @api private
55
+ def value
56
+ attribute = rule.attribute_name
57
+ resource.public_send(attribute) if resource.respond_to?(attribute)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,69 @@
1
+ # encoding: utf-8
2
+
3
+ module Knight
4
+
5
+ # Base functionality that added when included to a class
6
+ module InstanceMethods
7
+
8
+ # Return the resource
9
+ #
10
+ # @return [Object]
11
+ #
12
+ # @api private
13
+ attr_reader :resource
14
+
15
+ # Extend base class to support validation
16
+ #
17
+ # @param [Object] object
18
+ #
19
+ # @return [undefined]
20
+ #
21
+ # @api private
22
+ def self.included(base)
23
+ super
24
+ base.extend(ClassMethods)
25
+ end
26
+
27
+ # Set the resource
28
+ #
29
+ # @param [Object] resource
30
+ #
31
+ # @return [undefined]
32
+ #
33
+ # @api private
34
+ def initialize(resource)
35
+ @resource = resource
36
+ end
37
+
38
+ # Return the validation result
39
+ #
40
+ # @example
41
+ # user = User.new
42
+ #
43
+ # class RegistrationValidator
44
+ # include Knight::InstanceMethods
45
+ # validator.add(Knight::Rule::Presence.new(:username))
46
+ # end
47
+ #
48
+ # result = RegistrationValidator.new(user).result
49
+ #
50
+ # @return [Result]
51
+ #
52
+ # @api public
53
+ def run(context = Validator::DEFAULT_CONTEXT)
54
+ self.class.validator.run(resource, context)
55
+ end
56
+
57
+ module ClassMethods
58
+
59
+ # Return the validator object
60
+ #
61
+ # @return [Validator]
62
+ #
63
+ # @api private
64
+ def validator
65
+ @validator = @validator || Validator.new
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ module Knight
4
+ class Result
5
+ include Equalizer.new(:resource, :rules)
6
+
7
+ # Return the resource
8
+ #
9
+ # @return [Object]
10
+ #
11
+ # @api private
12
+ attr_reader :resource
13
+
14
+ # Return the rule set
15
+ #
16
+ # @return [Set(Rule)]
17
+ #
18
+ # @api private
19
+ attr_reader :rules
20
+
21
+ # Initialize a validation result
22
+ #
23
+ # @param [Object] resource
24
+ # @param [Set(Rule)] rules
25
+ #
26
+ # @return [Result]
27
+ #
28
+ # @api private
29
+ def initialize(resource, rules)
30
+ @resource = resource
31
+ @rules = rules
32
+ end
33
+
34
+ # Check the result valid or not
35
+ #
36
+ # @example
37
+ # validator = Validator.new(Rule::Presence.new(:username))
38
+ # user = User.new(username: 'john')
39
+ #
40
+ # result = validator.run(resource)
41
+ # result.valid?
42
+ #
43
+ # @return [true] if valid
44
+ # @return [false] otherwise
45
+ #
46
+ # @api public
47
+ def valid?
48
+ errors.empty?
49
+ end
50
+
51
+ # Return the result errors
52
+ #
53
+ # @example
54
+ # validator = Validator.new(Rule::Presence.new(:username))
55
+ # user = User.new(username: 'john')
56
+ #
57
+ # result = validator.run(resource)
58
+ # result.errors
59
+ #
60
+ # @return [Set(Error)]
61
+ #
62
+ # @api public
63
+ def errors
64
+ rules.each_with_object(Set.new) do |rule, errors|
65
+ error = attribute_check(rule)
66
+ errors << error if error
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ # Check an attribute to the rule
73
+ #
74
+ # @return [Error] if invalid
75
+ # @return [nil] otherwise
76
+ #
77
+ # @api private
78
+ def attribute_check(rule)
79
+ if resource.respond_to?(rule.attribute_name)
80
+ rule.error(resource)
81
+ else
82
+ Error.new(rule, resource) if rule.kind_of?(Rule::Presence)
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+
3
+ module Knight
4
+
5
+ # An abstract class of a rule
6
+ class Rule
7
+ include AbstractType
8
+ include Equalizer.new(:attribute_name, :options)
9
+
10
+ DEFAULT_MESSAGE = ''.freeze
11
+
12
+ # Attribute name to validate
13
+ #
14
+ # @return [Symbol]
15
+ #
16
+ # @api private
17
+ attr_reader :attribute_name
18
+
19
+ # Attribute name to validate
20
+ #
21
+ # @return [Symbol]
22
+ #
23
+ # @api private
24
+ attr_reader :options
25
+
26
+ # Message to displayed when rule is violated
27
+ #
28
+ # @return [String]
29
+ #
30
+ # @api private
31
+ attr_reader :message
32
+
33
+ # Initialize a rule
34
+ #
35
+ # @param [Symbol] attribute_name
36
+ # @param [Hash] options for this rule
37
+ #
38
+ # @option options [String] :message error message
39
+ #
40
+ # @return [undefined]
41
+ #
42
+ # @api private
43
+ def initialize(attribute_name, options = {})
44
+ @attribute_name = attribute_name
45
+ @options = options
46
+ @message = @options.fetch(:message, self.class::DEFAULT_MESSAGE)
47
+ end
48
+
49
+ # Return error if the rule not satisfied
50
+ #
51
+ # @param [Object] resource
52
+ #
53
+ # @return [Error] if the rule not matched
54
+ # @return [nil] if the rule is satisfied
55
+ #
56
+ # @api private
57
+ def error(resource)
58
+ value = resource.public_send(attribute_name)
59
+ Error.new(self, resource) unless matches?(value)
60
+ end
61
+
62
+ # Return the rule as a hash
63
+ #
64
+ # @return [Hash]
65
+ #
66
+ # @example
67
+ # hash = rule.to_hash
68
+ #
69
+ # @api public
70
+ def to_hash
71
+ { attribute: attribute_name }.freeze
72
+ end
73
+ end
74
+ 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 ExactLength < self
8
+ DEFAULT_MESSAGE = '%{attribute} has an invalid length'.freeze
9
+
10
+ # Length value
11
+ #
12
+ # @return [Fixnum]
13
+ #
14
+ # @api private
15
+ attr_reader :length
16
+
17
+ # Initialize an exact length rule
18
+ #
19
+ # @param [Symbol] attribute_name
20
+ # @param [Fixnum] length
21
+ # @param [Hash] options for this rule
22
+ #
23
+ # @option options [String] :message error message
24
+ #
25
+ # @example
26
+ # rule = Knight::Rule::ExactLength.new(:username, 20)
27
+ #
28
+ # @return [undefined]
29
+ #
30
+ # @api public
31
+ def initialize(attribute_name, length, options = {})
32
+ super(attribute_name, options)
33
+ @length = length
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
+ length == 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
+ length: length
59
+ }).freeze
60
+ end
61
+ end
62
+ end
63
+ end