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
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Knight
2
- VERSION = "0.0.1"
4
+ VERSION = '0.0.2'
3
5
  end
@@ -0,0 +1,2 @@
1
+ ---
2
+ threshold: 9.7
@@ -0,0 +1,17 @@
1
+ AllCops:
2
+ Includes:
3
+ - 'Rakefile'
4
+ - 'Gemfile'
5
+ - 'Gemfile.dev'
6
+ - 'Gemfile.local'
7
+ - '../**/*.rake'
8
+ Excludes:
9
+ - '**/bin/*'
10
+
11
+ # Document classes and non-namespace modules.
12
+ Documentation:
13
+ Enabled: false
14
+
15
+ # Limit lines to 80 characters.
16
+ LineLength:
17
+ Enabled: false
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Knight do
6
+ subject { result.valid? }
7
+
8
+ let(:validator) do
9
+ Class.new do
10
+ include InstanceMethods
11
+ validator.add(Rule::Presence.new(:username))
12
+ validator.add(Rule::ExactLength.new(:username, 3..4), :registration)
13
+
14
+ validator.context :registration do |context|
15
+ context << Rule::Presence.new(:title)
16
+ context << Rule::ExactLength.new(:description, 40)
17
+ end
18
+ end
19
+ end
20
+
21
+ let(:user_class) do
22
+ Class.new do
23
+ attr_reader :username
24
+ def initialize(username)
25
+ @username = username
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'valid' do
31
+ let(:result) { validator.new(resource).run }
32
+ let(:resource) { user_class.new('john') }
33
+ it { should be_true }
34
+ end
35
+
36
+ context 'invalid' do
37
+ let(:result) { validator.new(resource).run(:registration) }
38
+ let(:resource) { user_class.new('john') }
39
+ it { should be_false }
40
+ end
41
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Knight do
6
+ subject { result.valid? }
7
+
8
+ let(:result) { validator.new(resource).run }
9
+ let(:validator) do
10
+ Class.new do
11
+ include InstanceMethods
12
+ validator.add(Rule::Presence.new(:username))
13
+ validator.add(Rule::ExactLength.new(:username, 4))
14
+ validator.add(Rule::Inclusion.new(:username, %w(john bejo)))
15
+ validator.add(Rule::Format.new(:username, /\Ajo/))
16
+ end
17
+ end
18
+
19
+ let(:user_class) do
20
+ Class.new do
21
+ attr_reader :username
22
+ def initialize(username)
23
+ @username = username
24
+ end
25
+ end
26
+ end
27
+
28
+ let(:resource) { user_class.new('john') }
29
+ it { should be_true }
30
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+
3
+ if ENV['COVERAGE'] == 'true'
4
+ require 'simplecov'
5
+ require 'coveralls'
6
+
7
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
8
+ SimpleCov::Formatter::HTMLFormatter,
9
+ Coveralls::SimpleCov::Formatter
10
+ ]
11
+ SimpleCov.start do
12
+ add_filter "/spec/"
13
+ end
14
+ end
15
+
16
+ require 'knight'
17
+ include Knight
18
+
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Error, '.new' do
6
+ subject { described_class.new(rule, resource) }
7
+
8
+ let(:resource) { double('resource') }
9
+ let(:rule) { Rule::Presence.new(:foo) }
10
+
11
+ it { should be_instance_of(described_class) }
12
+
13
+ its(:rule) { should eql(rule) }
14
+ its(:resource) { should eql(resource) }
15
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Error, '#message' do
6
+ subject { object.message }
7
+
8
+ let(:object) { described_class.new(rule, resource) }
9
+ let(:resource) { klass.new }
10
+ let(:klass) do
11
+ Class.new do
12
+ def foo
13
+ 'ab'
14
+ end
15
+ end
16
+ end
17
+ let(:message) { '%{attribute} must between %{minimum}-%{maximum} characters, value: %{value}' }
18
+ let(:rule) { Rule::RangeLength.new(:foo, 3..5, message: message) }
19
+
20
+ it { should eql('foo must between 3-5 characters, value: ab') }
21
+
22
+ context 'with no attribute defined' do
23
+ let(:klass) { Class.new }
24
+ it { should eql('foo must between 3-5 characters, value: ') }
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe InstanceMethods, '.included' do
6
+ subject { object }
7
+
8
+ let(:object) { described_class }
9
+ let(:klass) { Class.new }
10
+
11
+ it 'delegates to the ancestor' do
12
+ included_ancestor = false
13
+ mod = Module.new do
14
+ define_method(:included) { |_| included_ancestor = true }
15
+ end
16
+ subject.extend mod
17
+ expect { klass.send(:include, subject) }.to change { included_ancestor }.from(false).to(true)
18
+ end
19
+
20
+ it 'respond_to validator' do
21
+ klass.send(:include, subject)
22
+ expect(klass).to respond_to(:validator)
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe InstanceMethods, '.new' do
6
+ subject { described_class.new(resource) }
7
+
8
+ let(:described_class) { Class.new { include InstanceMethods } }
9
+ let(:resource) { double('resource') }
10
+
11
+ its(:resource) { should eql(resource) }
12
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe InstanceMethods, '.validator' do
6
+ subject { described_class.validator }
7
+
8
+ let(:described_class) { Class.new { include InstanceMethods } }
9
+ let(:object) { described_class.new(double('1')) }
10
+
11
+ it { should be_instance_of(Validator) }
12
+ specify { expect(subject).to equal(object.class.validator) }
13
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe InstanceMethods, '.run' do
6
+ subject { object.run }
7
+
8
+ let(:described_class) do
9
+ Class.new do
10
+ include InstanceMethods
11
+ validator.add(Rule::Presence.new(:username))
12
+ validator.add(Rule::Presence.new(:title), :registration)
13
+ end
14
+ end
15
+ let(:object) { described_class.new(resource) }
16
+ let(:resource) { double('resource') }
17
+
18
+ context 'default context' do
19
+ subject { object.run }
20
+ specify { expect(subject.rules).to eql(described_class.validator.rules) }
21
+ specify { expect(subject.rules.size).to eql(1) }
22
+ end
23
+
24
+ context 'register context' do
25
+ subject { object.run(:registration) }
26
+ specify { expect(subject.rules).to eql(described_class.validator.rules(:registration)) }
27
+ specify { expect(subject.rules.size).to eql(1) }
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Result, '.new' do
6
+ subject { described_class.new(resource, rules) }
7
+
8
+ let(:validator) { Validator.new(Rule::Presence.new(:title)) }
9
+ let(:rules) { validator.rules }
10
+ let(:resource) { double('resource') }
11
+
12
+ it { should be_instance_of(described_class) }
13
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Result, '#errors' do
6
+ subject { result.errors }
7
+
8
+ let(:klass) do
9
+ Class.new do
10
+ attr_reader :foo
11
+ def initialize(foo)
12
+ @foo = foo
13
+ end
14
+ end
15
+ end
16
+ let(:validator) { Validator.new(*rules) }
17
+ let(:result) { validator.run(resource) }
18
+
19
+ context 'valid object' do
20
+ let(:resource) { klass.new('bar') }
21
+ let(:rules) { [Rule::Presence.new(:foo)] }
22
+ specify { expect(subject.size).to eql(0) }
23
+ end
24
+
25
+ context 'valid object' do
26
+ let(:resource) { Class.new }
27
+ let(:rules) { [Rule::ExactLength.new(:bar, 3)] }
28
+ specify { expect(subject.size).to eql(0) }
29
+ end
30
+
31
+ context 'multi rules on distinct attribute' do
32
+ let(:rules) do
33
+ [Rule::Presence.new(:foo), Rule::ExactLength.new(:bar, 3)]
34
+ end
35
+
36
+ context 'empty string' do
37
+ let(:resource) { klass.new('') }
38
+ specify { expect(subject.size).to eql(1) }
39
+ specify { expect(subject.first.rule).to be_instance_of(Rule::Presence) }
40
+ end
41
+
42
+ context 'invalid object' do
43
+ let(:resource) { Class.new }
44
+ specify { expect(subject.size).to eql(1) }
45
+ specify { expect(subject.first.rule).to be_instance_of(Rule::Presence) }
46
+ end
47
+ end
48
+
49
+ context 'multi rules on 1 attribute' do
50
+ let(:rules) do
51
+ [Rule::Presence.new(:foo), Rule::ExactLength.new(:foo, 3)]
52
+ end
53
+
54
+ context 'empty string' do
55
+ let(:resource) { klass.new('') }
56
+ specify { expect(subject.size).to eql(2) }
57
+ end
58
+
59
+ context 'invalid value' do
60
+ let(:resource) { klass.new('john') }
61
+ specify { expect(subject.size).to eql(1) }
62
+ specify { expect(subject.first.rule).to be_instance_of(Rule::ExactLength) }
63
+ end
64
+
65
+ context 'invalid object' do
66
+ let(:resource) { Class.new }
67
+ specify { expect(subject.size).to eql(1) }
68
+ specify { expect(subject.first.rule).to be_instance_of(Rule::Presence) }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Result, '#valid?' do
6
+ subject { result.valid? }
7
+
8
+ let(:klass) do
9
+ Class.new do
10
+ attr_reader :foo
11
+ def initialize(foo)
12
+ @foo = foo
13
+ end
14
+ end
15
+ end
16
+ let(:validator) { Validator.new(Rule::Presence.new(:foo)) }
17
+ let(:result) { validator.run(resource) }
18
+
19
+ context 'valid result' do
20
+ let(:resource) { klass.new('bar') }
21
+ it { should be_true }
22
+ end
23
+
24
+ context 'invalid result' do
25
+ let(:resource) { klass.new('') }
26
+ it { should be_false }
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Rule, '.new' do
6
+
7
+ {
8
+ Rule::Presence => "%{attribute} can't be blank"
9
+ }.each do |described_class, default_message|
10
+ context 'when called on the Rule subclass' do
11
+ let(:object) { described_class }
12
+ let(:attribute) { :foo }
13
+ let(:options) { { message: 'message' } }
14
+
15
+ context 'with options' do
16
+ subject { object.new(attribute, options) }
17
+
18
+ it { should be_instance_of(object) }
19
+ its(:attribute_name) { should eql(attribute) }
20
+ its(:options) { should eql(options) }
21
+ its(:message) { should eql('message') }
22
+ end
23
+
24
+ context 'with no options' do
25
+ subject { object.new(attribute) }
26
+
27
+ it { should be_instance_of(object) }
28
+ its(:options) { should be_empty }
29
+ its(:message) { should eql(default_message) }
30
+ end
31
+ end
32
+ end
33
+
34
+ context 'when called on the Rule class' do
35
+ subject { described_class.new(:foo) }
36
+ specify { expect { subject }.to raise_error(NotImplementedError, "#{described_class} is an abstract type") }
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Rule, '#error' do
6
+ subject { rule.error(resource) }
7
+
8
+ let(:klass) do
9
+ Class.new do
10
+ def foo
11
+ 'value'
12
+ end
13
+ end
14
+ end
15
+ let(:rule) { Rule::Presence.new(:foo) }
16
+ let(:resource) { klass.new }
17
+
18
+ context 'not matched' do
19
+ before { rule.should_receive(:matches?).with('value').and_return(false) }
20
+
21
+ it { should eql(Error.new(rule, resource)) }
22
+ end
23
+
24
+ context 'matched' do
25
+ before { rule.should_receive(:matches?).with('value').and_return(true) }
26
+
27
+ it { should be_nil }
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Rule::ExactLength, '.new' do
6
+
7
+ let(:attribute) { :foo }
8
+ let(:length) { 3 }
9
+ let(:options) { { message: 'message' } }
10
+
11
+ context 'with options' do
12
+ subject { described_class.new(attribute, length, options) }
13
+
14
+ it { should be_instance_of(described_class) }
15
+ its(:attribute_name) { should eql(attribute) }
16
+ its(:length) { should eql(length) }
17
+ its(:options) { should eql(options) }
18
+ end
19
+
20
+ context 'with no options' do
21
+ subject { described_class.new(attribute, length) }
22
+
23
+ it { should be_instance_of(described_class) }
24
+ its(:options) { should be_empty }
25
+ its(:message) { should eql('%{attribute} has an invalid length') }
26
+ end
27
+ end