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
data/lib/knight/version.rb
CHANGED
data/metrics/flog.yml
ADDED
data/metrics/rubocop.yml
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|