shoulda-matchers 1.0.0.beta1
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/CONTRIBUTION_GUIDELINES.rdoc +10 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +116 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +70 -0
- data/Rakefile +50 -0
- data/lib/shoulda-matchers.rb +8 -0
- data/lib/shoulda/matchers/action_controller.rb +38 -0
- data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +114 -0
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +50 -0
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +62 -0
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +54 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +99 -0
- data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +74 -0
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +85 -0
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +93 -0
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +98 -0
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +94 -0
- data/lib/shoulda/matchers/action_mailer.rb +22 -0
- data/lib/shoulda/matchers/action_mailer/have_sent_email.rb +115 -0
- data/lib/shoulda/matchers/active_record.rb +42 -0
- data/lib/shoulda/matchers/active_record/allow_mass_assignment_of_matcher.rb +83 -0
- data/lib/shoulda/matchers/active_record/allow_value_matcher.rb +110 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +226 -0
- data/lib/shoulda/matchers/active_record/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/matchers/active_record/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +169 -0
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +112 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +59 -0
- data/lib/shoulda/matchers/active_record/helpers.rb +34 -0
- data/lib/shoulda/matchers/active_record/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/validate_format_of_matcher.rb +65 -0
- data/lib/shoulda/matchers/active_record/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/matchers/active_record/validate_presence_of_matcher.rb +60 -0
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/matchers/active_record/validation_matcher.rb +56 -0
- data/lib/shoulda/matchers/integrations/rspec.rb +23 -0
- data/lib/shoulda/matchers/integrations/test_unit.rb +41 -0
- data/lib/shoulda/matchers/version.rb +5 -0
- metadata +113 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
|
5
|
+
# Ensure that the attribute is numeric
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
9
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
|
10
|
+
# translation for <tt>:not_a_number</tt>.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# it { should validate_numericality_of(:age) }
|
14
|
+
#
|
15
|
+
def validate_numericality_of(attr)
|
16
|
+
ValidateNumericalityOfMatcher.new(attr)
|
17
|
+
end
|
18
|
+
|
19
|
+
class ValidateNumericalityOfMatcher < ValidationMatcher # :nodoc:
|
20
|
+
|
21
|
+
def with_message(message)
|
22
|
+
@expected_message = message if message
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def matches?(subject)
|
27
|
+
super(subject)
|
28
|
+
@expected_message ||= :not_a_number
|
29
|
+
disallows_value_of('abcd', @expected_message)
|
30
|
+
end
|
31
|
+
|
32
|
+
def description
|
33
|
+
"only allow numeric values for #{@attribute}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the model is not valid if the given attribute is not
|
6
|
+
# present.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
10
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
11
|
+
# Defaults to the translation for <tt>:blank</tt>.
|
12
|
+
#
|
13
|
+
# Examples:
|
14
|
+
# it { should validate_presence_of(:name) }
|
15
|
+
# it { should validate_presence_of(:name).
|
16
|
+
# with_message(/is not optional/) }
|
17
|
+
#
|
18
|
+
def validate_presence_of(attr)
|
19
|
+
ValidatePresenceOfMatcher.new(attr)
|
20
|
+
end
|
21
|
+
|
22
|
+
class ValidatePresenceOfMatcher < ValidationMatcher # :nodoc:
|
23
|
+
|
24
|
+
def with_message(message)
|
25
|
+
@expected_message = message if message
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def matches?(subject)
|
30
|
+
super(subject)
|
31
|
+
@expected_message ||= :blank
|
32
|
+
disallows_value_of(blank_value, @expected_message)
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
"require #{@attribute} to be set"
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def blank_value
|
42
|
+
if collection?
|
43
|
+
[]
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def collection?
|
50
|
+
if reflection = @subject.class.reflect_on_association(@attribute)
|
51
|
+
[:has_many, :has_and_belongs_to_many].include?(reflection.macro)
|
52
|
+
else
|
53
|
+
false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the model is invalid if the given attribute is not unique.
|
6
|
+
#
|
7
|
+
# Internally, this uses values from existing records to test validations,
|
8
|
+
# so this will always fail if you have not saved at least one record for
|
9
|
+
# the model being tested, like so:
|
10
|
+
#
|
11
|
+
# describe User do
|
12
|
+
# before(:each) { User.create!(:email => 'address@example.com') }
|
13
|
+
# it { should validate_uniqueness_of(:email) }
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# Options:
|
17
|
+
#
|
18
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
19
|
+
# <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
|
20
|
+
# Defaults to the translation for <tt>:taken</tt>.
|
21
|
+
# * <tt>scoped_to</tt> - field(s) to scope the uniqueness to.
|
22
|
+
# * <tt>case_insensitive</tt> - ensures that the validation does not
|
23
|
+
# check case. Off by default. Ignored by non-text attributes.
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
# it { should validate_uniqueness_of(:keyword) }
|
27
|
+
# it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
|
28
|
+
# it { should validate_uniqueness_of(:email).scoped_to(:name) }
|
29
|
+
# it { should validate_uniqueness_of(:email).
|
30
|
+
# scoped_to(:first_name, :last_name) }
|
31
|
+
# it { should validate_uniqueness_of(:keyword).case_insensitive }
|
32
|
+
#
|
33
|
+
def validate_uniqueness_of(attr)
|
34
|
+
ValidateUniquenessOfMatcher.new(attr)
|
35
|
+
end
|
36
|
+
|
37
|
+
class ValidateUniquenessOfMatcher < ValidationMatcher # :nodoc:
|
38
|
+
include Helpers
|
39
|
+
|
40
|
+
def initialize(attribute)
|
41
|
+
@attribute = attribute
|
42
|
+
end
|
43
|
+
|
44
|
+
def scoped_to(*scopes)
|
45
|
+
@scopes = [*scopes].flatten
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def with_message(message)
|
50
|
+
@expected_message = message
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def case_insensitive
|
55
|
+
@case_insensitive = true
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
def description
|
60
|
+
result = "require "
|
61
|
+
result << "case sensitive " unless @case_insensitive
|
62
|
+
result << "unique value for #{@attribute}"
|
63
|
+
result << " scoped to #{@scopes.join(', ')}" unless @scopes.blank?
|
64
|
+
result
|
65
|
+
end
|
66
|
+
|
67
|
+
def matches?(subject)
|
68
|
+
@subject = subject.class.new
|
69
|
+
@expected_message ||= :taken
|
70
|
+
find_existing &&
|
71
|
+
set_scoped_attributes &&
|
72
|
+
validate_attribute &&
|
73
|
+
validate_after_scope_change
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def find_existing
|
79
|
+
if @existing = @subject.class.find(:first)
|
80
|
+
true
|
81
|
+
else
|
82
|
+
@failure_message = "Can't find first #{class_name}"
|
83
|
+
false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_scoped_attributes
|
88
|
+
unless @scopes.blank?
|
89
|
+
@scopes.each do |scope|
|
90
|
+
setter = :"#{scope}="
|
91
|
+
unless @subject.respond_to?(setter)
|
92
|
+
@failure_message =
|
93
|
+
"#{class_name} doesn't seem to have a #{scope} attribute."
|
94
|
+
return false
|
95
|
+
end
|
96
|
+
@subject.send("#{scope}=", @existing.send(scope))
|
97
|
+
end
|
98
|
+
end
|
99
|
+
true
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate_attribute
|
103
|
+
disallows_value_of(existing_value, @expected_message)
|
104
|
+
end
|
105
|
+
|
106
|
+
# TODO: There is a chance that we could change the scoped field
|
107
|
+
# to a value that's already taken. An alternative implementation
|
108
|
+
# could actually find all values for scope and create a unique
|
109
|
+
def validate_after_scope_change
|
110
|
+
if @scopes.blank?
|
111
|
+
true
|
112
|
+
else
|
113
|
+
@scopes.all? do |scope|
|
114
|
+
previous_value = @existing.send(scope)
|
115
|
+
|
116
|
+
# Assume the scope is a foreign key if the field is nil
|
117
|
+
previous_value ||= 0
|
118
|
+
|
119
|
+
next_value = previous_value.next
|
120
|
+
|
121
|
+
@subject.send("#{scope}=", next_value)
|
122
|
+
|
123
|
+
if allows_value_of(existing_value, @expected_message)
|
124
|
+
@negative_failure_message <<
|
125
|
+
" (with different value of #{scope})"
|
126
|
+
true
|
127
|
+
else
|
128
|
+
@failure_message << " (with different value of #{scope})"
|
129
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def class_name
|
136
|
+
@subject.class.name
|
137
|
+
end
|
138
|
+
|
139
|
+
def existing_value
|
140
|
+
value = @existing.send(@attribute)
|
141
|
+
value.swapcase! if @case_insensitive && value.respond_to?(:swapcase!)
|
142
|
+
value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveRecord # :nodoc:
|
4
|
+
|
5
|
+
class ValidationMatcher # :nodoc:
|
6
|
+
|
7
|
+
attr_reader :failure_message
|
8
|
+
|
9
|
+
def initialize(attribute)
|
10
|
+
@attribute = attribute
|
11
|
+
end
|
12
|
+
|
13
|
+
def negative_failure_message
|
14
|
+
@negative_failure_message || @failure_message
|
15
|
+
end
|
16
|
+
|
17
|
+
def matches?(subject)
|
18
|
+
@subject = subject
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def allows_value_of(value, message = nil)
|
25
|
+
allow = AllowValueMatcher.
|
26
|
+
new(value).
|
27
|
+
for(@attribute).
|
28
|
+
with_message(message)
|
29
|
+
if allow.matches?(@subject)
|
30
|
+
@negative_failure_message = allow.failure_message
|
31
|
+
true
|
32
|
+
else
|
33
|
+
@failure_message = allow.negative_failure_message
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def disallows_value_of(value, message = nil)
|
39
|
+
disallow = AllowValueMatcher.
|
40
|
+
new(value).
|
41
|
+
for(@attribute).
|
42
|
+
with_message(message)
|
43
|
+
if disallow.matches?(@subject)
|
44
|
+
@failure_message = disallow.negative_failure_message
|
45
|
+
false
|
46
|
+
else
|
47
|
+
@negative_failure_message = disallow.failure_message
|
48
|
+
true
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# :enddoc:
|
2
|
+
|
3
|
+
if defined?(::ActiveRecord)
|
4
|
+
require 'shoulda/matchers/active_record'
|
5
|
+
module RSpec::Matchers
|
6
|
+
include Shoulda::Matchers::ActiveRecord
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
if defined?(::ActionController)
|
11
|
+
require 'shoulda/matchers/action_controller'
|
12
|
+
module RSpec::Rails::ControllerExampleGroup
|
13
|
+
include Shoulda::Matchers::ActionController
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
if defined?(::ActionMailer)
|
18
|
+
require 'shoulda/matchers/action_mailer'
|
19
|
+
module RSpec::Rails::MailerExampleGroup
|
20
|
+
include Shoulda::Matchers::ActionMailer
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# :enddoc:
|
2
|
+
|
3
|
+
if defined?(ActionController)
|
4
|
+
require 'shoulda/matchers/action_controller'
|
5
|
+
|
6
|
+
class ActionController::TestCase
|
7
|
+
include Shoulda::Matchers::ActionController
|
8
|
+
extend Shoulda::Matchers::ActionController
|
9
|
+
|
10
|
+
def subject
|
11
|
+
@controller
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if defined?(ActionMailer)
|
17
|
+
require 'shoulda/matchers/action_mailer'
|
18
|
+
|
19
|
+
module Test
|
20
|
+
module Unit
|
21
|
+
class TestCase
|
22
|
+
include Shoulda::Matchers::ActionMailer
|
23
|
+
extend Shoulda::Matchers::ActionMailer
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
if defined?(ActiveRecord)
|
30
|
+
require 'shoulda/matchers/active_record'
|
31
|
+
|
32
|
+
module Test
|
33
|
+
module Unit
|
34
|
+
class TestCase
|
35
|
+
include Shoulda::Matchers::ActiveRecord
|
36
|
+
extend Shoulda::Matchers::ActiveRecord
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shoulda-matchers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 299253597
|
5
|
+
prerelease: true
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
- beta1
|
11
|
+
version: 1.0.0.beta1
|
12
|
+
platform: ruby
|
13
|
+
authors:
|
14
|
+
- Tammer Saleh
|
15
|
+
- Joe Ferris
|
16
|
+
- Ryan McGeary
|
17
|
+
- Dan Croak
|
18
|
+
- Matt Jankowski
|
19
|
+
autorequire:
|
20
|
+
bindir: bin
|
21
|
+
cert_chain: []
|
22
|
+
|
23
|
+
date: 2011-01-19 00:00:00 -05:00
|
24
|
+
default_executable:
|
25
|
+
dependencies: []
|
26
|
+
|
27
|
+
description: Making tests easy on the fingers and eyes
|
28
|
+
email: support@thoughtbot.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files:
|
34
|
+
- README.rdoc
|
35
|
+
- CONTRIBUTION_GUIDELINES.rdoc
|
36
|
+
files:
|
37
|
+
- CONTRIBUTION_GUIDELINES.rdoc
|
38
|
+
- Gemfile
|
39
|
+
- Gemfile.lock
|
40
|
+
- MIT-LICENSE
|
41
|
+
- Rakefile
|
42
|
+
- README.rdoc
|
43
|
+
- lib/shoulda/matchers/action_controller/assign_to_matcher.rb
|
44
|
+
- lib/shoulda/matchers/action_controller/filter_param_matcher.rb
|
45
|
+
- lib/shoulda/matchers/action_controller/redirect_to_matcher.rb
|
46
|
+
- lib/shoulda/matchers/action_controller/render_template_matcher.rb
|
47
|
+
- lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb
|
48
|
+
- lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb
|
49
|
+
- lib/shoulda/matchers/action_controller/respond_with_matcher.rb
|
50
|
+
- lib/shoulda/matchers/action_controller/route_matcher.rb
|
51
|
+
- lib/shoulda/matchers/action_controller/set_session_matcher.rb
|
52
|
+
- lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb
|
53
|
+
- lib/shoulda/matchers/action_controller.rb
|
54
|
+
- lib/shoulda/matchers/action_mailer/have_sent_email.rb
|
55
|
+
- lib/shoulda/matchers/action_mailer.rb
|
56
|
+
- lib/shoulda/matchers/active_record/allow_mass_assignment_of_matcher.rb
|
57
|
+
- lib/shoulda/matchers/active_record/allow_value_matcher.rb
|
58
|
+
- lib/shoulda/matchers/active_record/association_matcher.rb
|
59
|
+
- lib/shoulda/matchers/active_record/ensure_inclusion_of_matcher.rb
|
60
|
+
- lib/shoulda/matchers/active_record/ensure_length_of_matcher.rb
|
61
|
+
- lib/shoulda/matchers/active_record/have_db_column_matcher.rb
|
62
|
+
- lib/shoulda/matchers/active_record/have_db_index_matcher.rb
|
63
|
+
- lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb
|
64
|
+
- lib/shoulda/matchers/active_record/helpers.rb
|
65
|
+
- lib/shoulda/matchers/active_record/validate_acceptance_of_matcher.rb
|
66
|
+
- lib/shoulda/matchers/active_record/validate_format_of_matcher.rb
|
67
|
+
- lib/shoulda/matchers/active_record/validate_numericality_of_matcher.rb
|
68
|
+
- lib/shoulda/matchers/active_record/validate_presence_of_matcher.rb
|
69
|
+
- lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb
|
70
|
+
- lib/shoulda/matchers/active_record/validation_matcher.rb
|
71
|
+
- lib/shoulda/matchers/active_record.rb
|
72
|
+
- lib/shoulda/matchers/integrations/rspec.rb
|
73
|
+
- lib/shoulda/matchers/integrations/test_unit.rb
|
74
|
+
- lib/shoulda/matchers/version.rb
|
75
|
+
- lib/shoulda-matchers.rb
|
76
|
+
has_rdoc: true
|
77
|
+
homepage: http://thoughtbot.com/community/
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options:
|
82
|
+
- --line-numbers
|
83
|
+
- --main
|
84
|
+
- README.rdoc
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
version: "0"
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
104
|
+
version: "0"
|
105
|
+
requirements: []
|
106
|
+
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 1.3.7
|
109
|
+
signing_key:
|
110
|
+
specification_version: 3
|
111
|
+
summary: Making tests easy on the fingers and eyes
|
112
|
+
test_files: []
|
113
|
+
|