valid_attribute 0.1.0 → 0.2.0
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/HISTORY +4 -0
- data/README.markdown +35 -8
- data/lib/valid_attribute/matcher.rb +71 -0
- data/lib/valid_attribute/method.rb +38 -0
- data/lib/valid_attribute/rspec.rb +3 -0
- data/lib/valid_attribute/test_unit.rb +3 -0
- data/lib/valid_attribute/version.rb +1 -1
- data/lib/valid_attribute.rb +8 -84
- data/spec/spec_helper.rb +1 -1
- data/spec/test_unit_spec.rb +12 -0
- data/spec/user.rb +12 -0
- data/spec/valid_attribute_spec.rb +76 -71
- metadata +10 -2
data/HISTORY
CHANGED
data/README.markdown
CHANGED
@@ -4,17 +4,26 @@ ValidAttribute is a minimalist framework for validation BDD.
|
|
4
4
|
|
5
5
|
## Installation ##
|
6
6
|
|
7
|
-
If you're using
|
7
|
+
If you're using `RSpec` just add the `valid_attribute` to your `Gemfile`
|
8
8
|
|
9
9
|
gem 'valid_attribute'
|
10
10
|
|
11
|
-
Then add it to your spec_helper
|
11
|
+
Then add it to your `spec_helper.rb`
|
12
12
|
|
13
13
|
require 'valid_attribute'
|
14
14
|
|
15
|
+
or if you're using `Test::Unit`, you must use [Thoughtbot](http://thoughtbot.com)'s [shoulda-context](https://github.com/thoughtbot/shoulda-context)
|
16
|
+
|
17
|
+
# Gemfile
|
18
|
+
gem 'shoulda-context'
|
19
|
+
|
20
|
+
# test_helper.rb
|
21
|
+
require 'shoulda-context'
|
22
|
+
require 'valid_attribute'
|
23
|
+
|
15
24
|
## Usage ##
|
16
25
|
|
17
|
-
Instead of having validation specific matchers ValidAttribute only cares if the attribute is valid under certain circumstances
|
26
|
+
Instead of having validation specific matchers `ValidAttribute` only cares if the attribute is valid under certain circumstances
|
18
27
|
|
19
28
|
class User
|
20
29
|
include ActiveModel::Validations
|
@@ -26,12 +35,13 @@ Instead of having validation specific matchers ValidAttribute only cares if the
|
|
26
35
|
validates :password, :confirmation => true, :presence => true
|
27
36
|
end
|
28
37
|
|
38
|
+
# RSpec
|
29
39
|
describe User do
|
30
40
|
# The .when method can take any number of values that you want to pass
|
31
41
|
it { should have_valid(:email).when('test@test.com', 'test+spam@gmail.com') }
|
32
42
|
it { should_not have_valid(:email).when('fail', 123) }
|
33
|
-
it { should have_valid(:name).when('TestName')
|
34
|
-
it { should_not have_valid(:name).when('Test')
|
43
|
+
it { should have_valid(:name).when('TestName') }
|
44
|
+
it { should_not have_valid(:name).when('Test') }
|
35
45
|
|
36
46
|
# Because 'should' works off the the 'subject' in RSpec we can set other values if necessary for a given validation test
|
37
47
|
describe 'password' do
|
@@ -41,12 +51,29 @@ Instead of having validation specific matchers ValidAttribute only cares if the
|
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
54
|
+
# TestUnit
|
55
|
+
require 'should/context'
|
56
|
+
class UserTest < Test::Unit::TestCase
|
57
|
+
# The .when method can take any number of values that you want to pass
|
58
|
+
should have_valid(:email).when('test@test.com', 'test+spam@gmail.com')
|
59
|
+
should_not have_valid(:email).when('fail', 123)
|
60
|
+
should have_valid(:name).when('TestName')
|
61
|
+
should_not have_valid(:name).when('Test')
|
62
|
+
|
63
|
+
# Because 'shoulda-context' works off the the 'subject' we can set other values if necessary for a given validation test
|
64
|
+
context 'password' do
|
65
|
+
subject { User.new(:password_confirmation => 'password') }
|
66
|
+
should have_valid(:password).when('password')
|
67
|
+
should_not have_valid(:password).when(nil)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
44
71
|
## Non-ActiveModel models ##
|
45
72
|
|
46
|
-
|
73
|
+
Your model should respond to the following methods:
|
47
74
|
|
48
|
-
* valid
|
49
|
-
* errors - should be a collection of attributes that have validation errors.
|
75
|
+
* `valid?` - only used to generate errors on the model
|
76
|
+
* `errors` - should be a collection of attributes that have validation errors.
|
50
77
|
|
51
78
|
Other than that everything should work!
|
52
79
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module ValidAttribute
|
2
|
+
class Matcher
|
3
|
+
attr_accessor :attr, :values, :subject, :failed_values, :passed_values
|
4
|
+
|
5
|
+
def initialize(attr)
|
6
|
+
self.attr = attr
|
7
|
+
end
|
8
|
+
|
9
|
+
def when(*values)
|
10
|
+
self.values = values
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def failure_message
|
15
|
+
if failed_values.size == 1
|
16
|
+
" expected #{subject.class}##{attr} to accept the value: #{quote_values(failed_values)}"
|
17
|
+
else
|
18
|
+
" expected #{subject.class}##{attr} to accept the values: #{quote_values(failed_values)}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def negative_failure_message
|
23
|
+
if passed_values.size == 1
|
24
|
+
" expected #{subject.class}##{attr} to not accept the value: #{quote_values(passed_values)}"
|
25
|
+
else
|
26
|
+
" expected #{subject.class}##{attr} to not accept the values: #{quote_values(passed_values)}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def description
|
31
|
+
"be valid when: #{quote_values(values)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def matches?(subject)
|
35
|
+
check_values(subject)
|
36
|
+
failed_values.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def does_not_match?(subject)
|
40
|
+
check_values(subject)
|
41
|
+
!failed_values.empty? && passed_values.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def check_values(subject)
|
47
|
+
unless values
|
48
|
+
raise ::ValidAttribute::NoValues, "you need to set the values with .when on the matcher. Example: have_valid(:name).when('Brian')"
|
49
|
+
end
|
50
|
+
|
51
|
+
self.subject = subject
|
52
|
+
self.failed_values = []
|
53
|
+
self.passed_values = []
|
54
|
+
|
55
|
+
values.each do |value|
|
56
|
+
subject.send("#{attr}=", value)
|
57
|
+
subject.valid?
|
58
|
+
if subject.errors.key?(attr)
|
59
|
+
self.failed_values << value
|
60
|
+
else
|
61
|
+
self.passed_values << value
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def quote_values(values)
|
67
|
+
values.map { |value| value.is_a?(String) ? "'#{value}'" : value }.join(', ')
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ValidAttribute
|
2
|
+
module Method
|
3
|
+
# Assert an attribute is valid with any number of test values
|
4
|
+
#
|
5
|
+
# examples:
|
6
|
+
# describe User do
|
7
|
+
# it { should have_valid(:name).when('Brian') }
|
8
|
+
# it { should_not have_valid(:name).when(nil) }
|
9
|
+
#
|
10
|
+
# # if we want to test uniqueness we need to setup an existing value
|
11
|
+
# context 'email' do
|
12
|
+
# before { User.create(:email => 'brian@test.com') }
|
13
|
+
# it { should have_valid(:email).when('test@test.com', 'test+spam@gmail.com') }
|
14
|
+
# it { should_not have_valid(:email).when('abc', 123, 'brian@test.com') }
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# If you are using Test::Unit you should use {http://thoughtbot.com Thoughtbot}'s {https://github.com/thoughtbot/shoulda-context shoulda-context}:
|
19
|
+
# class UserTest < Test::Unit::TestCase
|
20
|
+
# should have_valid(:name).when('Brian')
|
21
|
+
# should_not have_valid(:name).when('nil')
|
22
|
+
#
|
23
|
+
# # if we want to test uniqueness we need to setup an existing value
|
24
|
+
# context 'email' do
|
25
|
+
# setup { User.create(:email => 'brian@test.com') }
|
26
|
+
# should have_valid(:email).when('test@test.com', 'test+spam@gmail.com')
|
27
|
+
# should_not have_valid(:email).when('abc', 123, 'brian@test.com')
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @param [Symbol]
|
32
|
+
#
|
33
|
+
# @return [ValidAttribute::Matcher]
|
34
|
+
def have_valid(attr)
|
35
|
+
::ValidAttribute::Matcher.new(attr)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/valid_attribute.rb
CHANGED
@@ -1,88 +1,12 @@
|
|
1
|
+
require 'valid_attribute/matcher'
|
2
|
+
require 'valid_attribute/method'
|
3
|
+
|
1
4
|
module ValidAttribute
|
2
5
|
class NoValues < StandardError; end
|
3
|
-
# Test if an attribute is valid
|
4
|
-
#
|
5
|
-
# examples:
|
6
|
-
# it { should have_valid(:name).when('Brian') }
|
7
|
-
# it { should_not have_valid(:name).when(nil) }
|
8
|
-
# it { should have_valid(:email).when('test@test.com', 'test+spam@gmail.com') }
|
9
|
-
# it { should_not have_valid(:email).when('abc', 123) }
|
10
|
-
#
|
11
|
-
# @param [Symbol]
|
12
|
-
#
|
13
|
-
# @return [ValidAttribute::ValidAttributeMatcher]
|
14
|
-
def have_valid(attr)
|
15
|
-
ValidAttributeMatcher.new(attr)
|
16
|
-
end
|
17
|
-
|
18
|
-
class ValidAttributeMatcher
|
19
|
-
attr_accessor :attr, :values, :subject, :failed_values, :passed_values
|
20
|
-
|
21
|
-
def initialize(attr)
|
22
|
-
self.attr = attr
|
23
|
-
end
|
24
|
-
|
25
|
-
def when(*values)
|
26
|
-
self.values = values
|
27
|
-
self
|
28
|
-
end
|
29
|
-
|
30
|
-
def failure_message
|
31
|
-
if failed_values.size == 1
|
32
|
-
" expected #{subject.class.model_name}##{attr} to accept the value: #{quote_values(failed_values)}"
|
33
|
-
else
|
34
|
-
" expected #{subject.class.model_name}##{attr} to accept the values: #{quote_values(failed_values)}"
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def negative_failure_message
|
39
|
-
if passed_values.size == 1
|
40
|
-
" expected #{subject.class.model_name}##{attr} to not accept the value: #{quote_values(passed_values)}"
|
41
|
-
else
|
42
|
-
" expected #{subject.class.model_name}##{attr} to not accept the values: #{quote_values(passed_values)}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def matches?(subject)
|
47
|
-
check_values(subject)
|
48
|
-
failed_values.empty?
|
49
|
-
end
|
50
|
-
|
51
|
-
def does_not_match?(subject)
|
52
|
-
check_values(subject)
|
53
|
-
passed_values.empty?
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
def check_values(subject)
|
59
|
-
unless values
|
60
|
-
raise ::ValidAttribute::NoValues, "you need to set the values with .when on the matcher (ex. it { should have_valid(:name).when('Brian') })"
|
61
|
-
end
|
62
|
-
|
63
|
-
self.subject = subject
|
64
|
-
self.failed_values = []
|
65
|
-
self.passed_values = []
|
66
|
-
|
67
|
-
values.each do |value|
|
68
|
-
subject.send("#{attr}=", value)
|
69
|
-
subject.valid?
|
70
|
-
if subject.errors.key?(attr)
|
71
|
-
self.failed_values << value
|
72
|
-
else
|
73
|
-
self.passed_values << value
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def quote_values(values)
|
79
|
-
values.map { |value| value.is_a?(String) ? "'#{value}'" : value }.join(', ')
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
6
|
end
|
84
7
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
8
|
+
if defined?(RSpec)
|
9
|
+
require 'valid_attribute/rspec'
|
10
|
+
else
|
11
|
+
require 'valid_attribute/test_unit'
|
12
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,10 +5,10 @@ Bundler.setup
|
|
5
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
6
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
7
|
require 'ruby-debug'
|
8
|
-
require 'valid_attribute'
|
9
8
|
require 'rspec'
|
10
9
|
require 'rspec/autorun'
|
11
10
|
require 'bourne'
|
11
|
+
require 'valid_attribute'
|
12
12
|
|
13
13
|
RSpec.configure do |config|
|
14
14
|
config.mock_with :mocha
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# This is a RSpec spec to test TestUnit... yeah that's right
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'test/unit'
|
5
|
+
require 'valid_attribute/test_unit'
|
6
|
+
|
7
|
+
describe 'Test::Unit::TestCase' do
|
8
|
+
it '.have_valid' do
|
9
|
+
Test::Unit::TestCase.have_valid(:name).should be_instance_of(ValidAttribute::Matcher)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
data/spec/user.rb
ADDED
@@ -1,20 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'user'
|
2
3
|
|
3
4
|
class Should
|
4
|
-
include ValidAttribute
|
5
|
-
end
|
6
|
-
|
7
|
-
class User
|
8
|
-
attr_accessor :name
|
9
|
-
attr_accessor :email
|
10
|
-
|
11
|
-
def errors
|
12
|
-
@error ||= {}
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.model_name
|
16
|
-
'User'
|
17
|
-
end
|
5
|
+
include ValidAttribute::Method
|
18
6
|
end
|
19
7
|
|
20
8
|
describe 'ValidAttribute' do
|
@@ -24,66 +12,83 @@ describe 'ValidAttribute' do
|
|
24
12
|
@user = User.new
|
25
13
|
end
|
26
14
|
|
27
|
-
describe '
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'does_not_match? returns false' do
|
52
|
-
@does_not_match.should be_false
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'has a failure message of the failed values' do
|
56
|
-
@matcher.failure_message.should == " expected User#name to accept the value: 'abc'"
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'has a negative failure message of the passed values' do
|
60
|
-
@matcher.negative_failure_message.should == " expected User#name to not accept the value: 123"
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe 'data is first invalid then valid then invalid then valid' do
|
65
|
-
before do
|
66
|
-
@user.stubs(:valid?).returns(false).then.returns(true).then.returns(false).then.returns(false).then.returns(true).then.returns(false)
|
67
|
-
@user.stubs(:errors).returns({:name => []}).then.returns({}).then.returns({:name => []}).then.returns({}).then.returns({:name => []}).then.returns({}).then.returns({:name => []}).then.returns({})
|
68
|
-
@matcher = @should.have_valid(:name).when('abc', 123, 456, 'def')
|
69
|
-
@matches = @matcher.matches?(@user)
|
70
|
-
@does_not_match = @matcher.does_not_match?(@user)
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'matches? returns false' do
|
74
|
-
@matches.should be_false
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'does_not_match? returns true' do
|
78
|
-
@does_not_match.should be_false
|
15
|
+
describe 'matcher result' do
|
16
|
+
context 'data is valid' do
|
17
|
+
before do
|
18
|
+
@user.stubs(:valid?).returns(true)
|
19
|
+
@user.stubs(:errors).returns({})
|
20
|
+
@matcher = @should.have_valid(:name).when('abc', 123)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'matches? returns true' do
|
24
|
+
@matcher.matches?(@user).should be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'does_not_match? returns false' do
|
28
|
+
@matcher.does_not_match?(@user).should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
describe 'messages' do
|
32
|
+
it '#negative_failue_message' do
|
33
|
+
@matcher.matches?(@user)
|
34
|
+
@matcher.negative_failure_message.should == " expected User#name to not accept the values: 'abc', 123"
|
35
|
+
end
|
36
|
+
end
|
79
37
|
end
|
80
38
|
|
81
|
-
|
82
|
-
|
39
|
+
context 'data is invalid' do
|
40
|
+
before do
|
41
|
+
@user.stubs(:valid?).returns(false)
|
42
|
+
@user.stubs(:errors).returns({:name => []})
|
43
|
+
@matcher = @should.have_valid(:name).when('abc', 123)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'matches? returns false' do
|
47
|
+
@matcher.matches?(@user).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'does_not_match? returns true' do
|
51
|
+
@matcher.does_not_match?(@user).should be_true
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'messages' do
|
55
|
+
it '#failue_message' do
|
56
|
+
@matcher.matches?(@user)
|
57
|
+
@matcher.failure_message.should == " expected User#name to accept the values: 'abc', 123"
|
58
|
+
end
|
59
|
+
end
|
83
60
|
end
|
84
61
|
|
85
|
-
|
86
|
-
|
62
|
+
context 'data is valid then invalid' do
|
63
|
+
before do
|
64
|
+
@user.stubs(:valid?).returns(true).then.returns(false)
|
65
|
+
@user.stubs(:errors).returns({}).then.returns({:name => []})
|
66
|
+
@matcher = @should.have_valid(:name).when('abc', 123)
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'matches? returns false' do
|
70
|
+
@matcher.matches?(@user).should be_false
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'does_not_match? returns false' do
|
74
|
+
@matcher.does_not_match?(@user).should be_false
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'messages' do
|
78
|
+
it '#failure_message' do
|
79
|
+
@matcher.matches?(@user)
|
80
|
+
@matcher.failure_message.should == " expected User#name to accept the value: 123"
|
81
|
+
end
|
82
|
+
|
83
|
+
it '#negative_failure_message' do
|
84
|
+
@matcher.matches?(@user)
|
85
|
+
@matcher.negative_failure_message.should == " expected User#name to not accept the value: 'abc'"
|
86
|
+
end
|
87
|
+
|
88
|
+
it '#description' do
|
89
|
+
@matcher.description.should == "be valid when: 'abc', 123"
|
90
|
+
end
|
91
|
+
end
|
87
92
|
end
|
88
93
|
end
|
89
94
|
|
@@ -91,7 +96,7 @@ describe 'ValidAttribute' do
|
|
91
96
|
matcher = @should.have_valid(:name)
|
92
97
|
expect do
|
93
98
|
matcher.matches?(@user)
|
94
|
-
end.to raise_error ValidAttribute::NoValues, "you need to set the values with .when on the matcher
|
99
|
+
end.to raise_error ValidAttribute::NoValues, "you need to set the values with .when on the matcher. Example: have_valid(:name).when('Brian')"
|
95
100
|
end
|
96
101
|
|
97
102
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: valid_attribute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Brian Cardarella
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-28 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -64,8 +64,14 @@ files:
|
|
64
64
|
- README.markdown
|
65
65
|
- Rakefile
|
66
66
|
- lib/valid_attribute.rb
|
67
|
+
- lib/valid_attribute/matcher.rb
|
68
|
+
- lib/valid_attribute/method.rb
|
69
|
+
- lib/valid_attribute/rspec.rb
|
70
|
+
- lib/valid_attribute/test_unit.rb
|
67
71
|
- lib/valid_attribute/version.rb
|
68
72
|
- spec/spec_helper.rb
|
73
|
+
- spec/test_unit_spec.rb
|
74
|
+
- spec/user.rb
|
69
75
|
- spec/valid_attribute_spec.rb
|
70
76
|
- valid_attribute.gemspec
|
71
77
|
has_rdoc: true
|
@@ -98,4 +104,6 @@ specification_version: 3
|
|
98
104
|
summary: Minimalist validation matcher framework
|
99
105
|
test_files:
|
100
106
|
- spec/spec_helper.rb
|
107
|
+
- spec/test_unit_spec.rb
|
108
|
+
- spec/user.rb
|
101
109
|
- spec/valid_attribute_spec.rb
|