shoulda-matchers 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- shoulda-matchers (1.2.0)
4
+ shoulda-matchers (1.3.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
data/NEWS.md CHANGED
@@ -1,3 +1,21 @@
1
+ # v1.3.0
2
+
3
+ * `validate_format_of` will accept `allow_blank(bool)` and `allow_nil(bool)`
4
+
5
+ * Prefer Test::Unit to Minitest when loading integrations so that RubyMine is
6
+ happy (#88).
7
+
8
+ * `validates_uniqueness_of` will now create a record if one does not exist.
9
+ Previously, users were required to create a record in the database before
10
+ using this matcher.
11
+
12
+ * Fix an edge case when where the matchers weren't loaded into Test::Unit when
13
+ mixing RSpec and Test::Unit tests and also loading both the 'rspec-rails' gem
14
+ and 'shoulda-matchers' gem from the same Gemfile group, namely [:test,
15
+ :development].
16
+
17
+ * `controller.should_not render_partial` now correctly matches `render partial: "partial"`.
18
+
1
19
  # v1.2.0
2
20
 
3
21
  * `ensure_inclusion_of` now has an `in_array` parameter:
data/README.md CHANGED
@@ -30,12 +30,7 @@ Matchers to test validations and mass assignments:
30
30
  it { should validate_presence_of(:body).with_message(/wtf/) }
31
31
  it { should validate_presence_of(:title) }
32
32
  it { should validate_numericality_of(:user_id) }
33
-
34
- # validates_uniqueness_of requires an entry to be in the database already
35
- it "validates uniqueness of title" do
36
- Post.create!(title: "My Awesome Post", body: "whatever")
37
- should validate_uniqueness_of(:title)
38
- end
33
+ it { should ensure_inclusion_of(:status).in_array(['draft', 'public']) }
39
34
  end
40
35
 
41
36
  describe User do
@@ -86,3 +86,31 @@ Feature: integrate with Rails
86
86
  Then the output should contain "2 examples, 0 failures"
87
87
  And the output should contain "should require name to be set"
88
88
  And the output should contain "should assign @example"
89
+
90
+ Scenario: generate a Rails application that mixes Rspec and Test::Unit
91
+ When I configure the application to use rspec-rails in test and development
92
+ And I configure the application to use "shoulda-matchers" from this project in test and development
93
+ And I run the rspec generator
94
+ And I write to "spec/models/user_spec.rb" with:
95
+ """
96
+ require 'spec_helper'
97
+
98
+ describe User do
99
+ it { should validate_presence_of(:name) }
100
+ end
101
+ """
102
+ When I write to "test/functional/examples_controller_test.rb" with:
103
+ """
104
+ require 'test_helper'
105
+
106
+ class ExamplesControllerTest < ActionController::TestCase
107
+ test 'assigns to @example' do
108
+ get :show
109
+ assert assign_to(:example).matches?(@controller)
110
+ end
111
+ end
112
+ """
113
+ When I successfully run `bundle exec rake spec test:functionals SPEC_OPTS=-fs --trace`
114
+ Then the output should contain "1 example, 0 failures"
115
+ And the output should contain "1 tests, 1 assertions, 0 failures, 0 errors"
116
+ And the output should contain "should require name to be set"
@@ -36,6 +36,15 @@ When /^I configure the application to use "([^\"]+)" from this project$/ do |nam
36
36
  steps %{And I run `bundle install --local`}
37
37
  end
38
38
 
39
+ When /^I configure the application to use "([^\"]+)" from this project in test and development$/ do |name|
40
+ append_to_gemfile <<-GEMFILE
41
+ group :test, :development do
42
+ gem '#{name}', :path => '#{PROJECT_ROOT}'
43
+ end
44
+ GEMFILE
45
+ steps %{And I run `bundle install --local`}
46
+ end
47
+
39
48
  When 'I run the rspec generator' do
40
49
  steps %{
41
50
  When I successfully run `rails generate rspec:install`
@@ -47,6 +56,15 @@ When 'I configure the application to use rspec-rails' do
47
56
  steps %{And I run `bundle install --local`}
48
57
  end
49
58
 
59
+ When 'I configure the application to use rspec-rails in test and development' do
60
+ append_to_gemfile <<-GEMFILE
61
+ group :test, :development do
62
+ gem 'rspec-rails', '~> 2.8.1'
63
+ end
64
+ GEMFILE
65
+ steps %{And I run `bundle install --local`}
66
+ end
67
+
50
68
  When 'I configure the application to use shoulda-context' do
51
69
  append_to_gemfile "gem 'shoulda-context', '~> 1.0.0'"
52
70
  steps %{And I run `bundle install --local`}
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: /Users/gabe/thoughtbot/open-source/shoulda-matchers
2
+ remote: /Users/joshuaclayton/dev/gems/shoulda-matchers
3
3
  specs:
4
- shoulda-matchers (1.2.0)
4
+ shoulda-matchers (1.3.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
@@ -46,8 +46,8 @@ GEM
46
46
  bourne (1.1.2)
47
47
  mocha (= 0.10.5)
48
48
  builder (2.1.2)
49
- childprocess (0.3.2)
50
- ffi (~> 1.0.6)
49
+ childprocess (0.3.5)
50
+ ffi (~> 1.0, >= 1.0.6)
51
51
  cucumber (1.1.9)
52
52
  builder (>= 2.1.2)
53
53
  diff-lcs (>= 1.1.2)
@@ -57,18 +57,18 @@ GEM
57
57
  diff-lcs (1.1.3)
58
58
  erubis (2.6.6)
59
59
  abstract (>= 1.0.0)
60
- ffi (1.0.11)
60
+ ffi (1.1.5)
61
61
  gherkin (2.9.3)
62
62
  json (>= 1.4.6)
63
63
  i18n (0.5.0)
64
- json (1.7.3)
64
+ json (1.7.5)
65
65
  mail (2.2.19)
66
66
  activesupport (>= 2.3.6)
67
67
  i18n (>= 0.4.0)
68
68
  mime-types (~> 1.16)
69
69
  treetop (~> 1.4.8)
70
70
  metaclass (0.0.1)
71
- mime-types (1.18)
71
+ mime-types (1.19)
72
72
  mocha (0.10.5)
73
73
  metaclass (~> 0.0.1)
74
74
  polyglot (0.3.3)
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: /Users/gabe/thoughtbot/open-source/shoulda-matchers
2
+ remote: /Users/joshuaclayton/dev/gems/shoulda-matchers
3
3
  specs:
4
- shoulda-matchers (1.2.0)
4
+ shoulda-matchers (1.3.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
@@ -47,8 +47,8 @@ GEM
47
47
  bourne (1.1.2)
48
48
  mocha (= 0.10.5)
49
49
  builder (3.0.0)
50
- childprocess (0.3.2)
51
- ffi (~> 1.0.6)
50
+ childprocess (0.3.5)
51
+ ffi (~> 1.0, >= 1.0.6)
52
52
  cucumber (1.1.9)
53
53
  builder (>= 2.1.2)
54
54
  diff-lcs (>= 1.1.2)
@@ -57,21 +57,21 @@ GEM
57
57
  term-ansicolor (>= 1.0.6)
58
58
  diff-lcs (1.1.3)
59
59
  erubis (2.7.0)
60
- ffi (1.0.11)
60
+ ffi (1.1.5)
61
61
  gherkin (2.9.3)
62
62
  json (>= 1.4.6)
63
63
  hike (1.2.1)
64
64
  i18n (0.6.0)
65
- jquery-rails (1.0.19)
66
- railties (~> 3.0)
65
+ jquery-rails (2.1.1)
66
+ railties (>= 3.1.0, < 5.0)
67
67
  thor (~> 0.14)
68
- json (1.7.3)
68
+ json (1.7.5)
69
69
  mail (2.3.3)
70
70
  i18n (>= 0.4.0)
71
71
  mime-types (~> 1.16)
72
72
  treetop (~> 1.4.8)
73
73
  metaclass (0.0.1)
74
- mime-types (1.18)
74
+ mime-types (1.19)
75
75
  mocha (0.10.5)
76
76
  metaclass (~> 0.0.1)
77
77
  multi_json (1.3.6)
@@ -116,7 +116,7 @@ GEM
116
116
  activesupport (>= 3.0)
117
117
  railties (>= 3.0)
118
118
  rspec (~> 2.8.0)
119
- sass (3.1.19)
119
+ sass (3.2.1)
120
120
  sass-rails (3.1.6)
121
121
  actionpack (~> 3.1.0)
122
122
  railties (~> 3.1.0)
@@ -1,7 +1,7 @@
1
1
  PATH
2
- remote: /Users/gabe/thoughtbot/open-source/shoulda-matchers
2
+ remote: /Users/joshuaclayton/dev/gems/shoulda-matchers
3
3
  specs:
4
- shoulda-matchers (1.2.0)
4
+ shoulda-matchers (1.3.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
@@ -46,8 +46,8 @@ GEM
46
46
  bourne (1.1.2)
47
47
  mocha (= 0.10.5)
48
48
  builder (3.0.0)
49
- childprocess (0.3.2)
50
- ffi (~> 1.0.6)
49
+ childprocess (0.3.5)
50
+ ffi (~> 1.0, >= 1.0.6)
51
51
  cucumber (1.1.9)
52
52
  builder (>= 2.1.2)
53
53
  diff-lcs (>= 1.1.2)
@@ -56,22 +56,22 @@ GEM
56
56
  term-ansicolor (>= 1.0.6)
57
57
  diff-lcs (1.1.3)
58
58
  erubis (2.7.0)
59
- ffi (1.0.11)
59
+ ffi (1.1.5)
60
60
  gherkin (2.9.3)
61
61
  json (>= 1.4.6)
62
62
  hike (1.2.1)
63
63
  i18n (0.6.0)
64
- journey (1.0.3)
65
- jquery-rails (2.0.2)
66
- railties (>= 3.2.0, < 5.0)
64
+ journey (1.0.4)
65
+ jquery-rails (2.1.1)
66
+ railties (>= 3.1.0, < 5.0)
67
67
  thor (~> 0.14)
68
- json (1.7.3)
68
+ json (1.7.5)
69
69
  mail (2.4.4)
70
70
  i18n (>= 0.4.0)
71
71
  mime-types (~> 1.16)
72
72
  treetop (~> 1.4.8)
73
73
  metaclass (0.0.1)
74
- mime-types (1.18)
74
+ mime-types (1.19)
75
75
  mocha (0.10.5)
76
76
  metaclass (~> 0.0.1)
77
77
  multi_json (1.3.6)
@@ -114,7 +114,7 @@ GEM
114
114
  activesupport (>= 3.0)
115
115
  railties (>= 3.0)
116
116
  rspec (~> 2.8.0)
117
- sass (3.1.19)
117
+ sass (3.2.1)
118
118
  sass-rails (3.2.5)
119
119
  railties (~> 3.2.0)
120
120
  sass (>= 3.1.10)
@@ -3,6 +3,6 @@ require 'shoulda/matchers/assertion_error'
3
3
 
4
4
  if defined?(RSpec)
5
5
  require 'shoulda/matchers/integrations/rspec'
6
- else
7
- require 'shoulda/matchers/integrations/test_unit'
8
6
  end
7
+
8
+ require 'shoulda/matchers/integrations/test_unit'
@@ -66,16 +66,7 @@ module Shoulda # :nodoc:
66
66
  end
67
67
 
68
68
  def rendered_layouts
69
- if recorded_layouts.size > 0
70
- recorded_layouts.keys.compact.map { |layout| layout.sub(%r{^layouts/}, '') }
71
- else
72
- layout = @controller.response.layout
73
- if layout.nil?
74
- []
75
- else
76
- [layout.split('/').last]
77
- end
78
- end
69
+ recorded_layouts.keys.compact.map { |layout| layout.sub(%r{^layouts/}, '') }
79
70
  end
80
71
 
81
72
  def recorded_layouts
@@ -7,6 +7,8 @@ module Shoulda # :nodoc:
7
7
  #
8
8
  # Options:
9
9
  # * <tt>with_message</tt> - value the test expects to find in
10
+ # <tt>allow_blank</tt> - allows a blank value
11
+ # <tt>allow_nil</tt> - allows a nil value
10
12
  # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
11
13
  # Defaults to the translation for <tt>:invalid</tt>.
12
14
  # * <tt>with(string to test against)</tt>
@@ -28,6 +30,17 @@ module Shoulda # :nodoc:
28
30
 
29
31
  def initialize(attribute)
30
32
  super
33
+ @options = {}
34
+ end
35
+
36
+ def allow_blank(allow_blank = true)
37
+ @options[:allow_blank] = allow_blank
38
+ self
39
+ end
40
+
41
+ def allow_nil(allow_nil = true)
42
+ @options[:allow_nil] = allow_nil
43
+ self
31
44
  end
32
45
 
33
46
  def with_message(message)
@@ -50,14 +63,35 @@ module Shoulda # :nodoc:
50
63
  def matches?(subject)
51
64
  super(subject)
52
65
  @expected_message ||= :invalid
53
- return disallows_value_of(@value_to_fail, @expected_message) if @value_to_fail
54
- allows_value_of(@value_to_pass, @expected_message) if @value_to_pass
66
+
67
+ if @value_to_fail
68
+ disallows_value_of(@value_to_fail, @expected_message) && allows_blank_value? && allows_nil_value?
69
+ else
70
+ allows_value_of(@value_to_pass, @expected_message) && allows_blank_value? && allows_nil_value?
71
+ end
55
72
  end
56
73
 
57
74
  def description
58
75
  "#{@attribute} have a valid format"
59
76
  end
60
77
 
78
+ private
79
+
80
+ def allows_blank_value?
81
+ if @options.key?(:allow_blank)
82
+ @options[:allow_blank] == allows_value_of('')
83
+ else
84
+ true
85
+ end
86
+ end
87
+
88
+ def allows_nil_value?
89
+ if @options.key?(:allow_nil)
90
+ @options[:allow_nil] == allows_value_of(nil)
91
+ else
92
+ true
93
+ end
94
+ end
61
95
  end
62
96
 
63
97
  end
@@ -68,20 +68,25 @@ module Shoulda # :nodoc:
68
68
  def matches?(subject)
69
69
  @subject = subject.class.new
70
70
  @expected_message ||= :taken
71
- has_existing? &&
72
- set_scoped_attributes &&
71
+ set_scoped_attributes &&
73
72
  validate_attribute? &&
74
73
  validate_after_scope_change?
75
74
  end
76
75
 
77
76
  private
78
77
 
79
- def has_existing?
80
- if @existing = @subject.class.find(:first)
81
- true
82
- else
83
- @failure_message = "Can't find first #{class_name}"
84
- false
78
+ def existing
79
+ @existing ||= first_instance
80
+ end
81
+
82
+ def first_instance
83
+ @subject.class.first || create_instance_in_database
84
+ end
85
+
86
+ def create_instance_in_database
87
+ @subject.class.new.tap do |instance|
88
+ instance.send("#{@attribute}=", "arbitrary_string")
89
+ instance.save(:validate => false)
85
90
  end
86
91
  end
87
92
 
@@ -90,7 +95,7 @@ module Shoulda # :nodoc:
90
95
  @options[:scopes].all? do |scope|
91
96
  setter = :"#{scope}="
92
97
  if @subject.respond_to?(setter)
93
- @subject.send("#{scope}=", @existing.send(scope))
98
+ @subject.send(setter, existing.send(scope))
94
99
  true
95
100
  else
96
101
  @failure_message = "#{class_name} doesn't seem to have a #{scope} attribute."
@@ -114,7 +119,7 @@ module Shoulda # :nodoc:
114
119
  true
115
120
  else
116
121
  @options[:scopes].all? do |scope|
117
- previous_value = @existing.send(scope)
122
+ previous_value = existing.send(scope)
118
123
 
119
124
  # Assume the scope is a foreign key if the field is nil
120
125
  previous_value ||= 0
@@ -128,6 +133,8 @@ module Shoulda # :nodoc:
128
133
  @subject.send("#{scope}=", next_value)
129
134
 
130
135
  if allows_value_of(existing_value, @expected_message)
136
+ @subject.send("#{scope}=", previous_value)
137
+
131
138
  @negative_failure_message <<
132
139
  " (with different value of #{scope})"
133
140
  true
@@ -144,7 +151,7 @@ module Shoulda # :nodoc:
144
151
  end
145
152
 
146
153
  def existing_value
147
- value = @existing.send(@attribute)
154
+ value = existing.send(@attribute)
148
155
  if @options[:case_insensitive] && value.respond_to?(:swapcase!)
149
156
  value.swapcase!
150
157
  end
@@ -3,6 +3,12 @@ module Shoulda # :nodoc:
3
3
  module ActiveRecord # :nodoc:
4
4
  # Ensure that the belongs_to relationship exists.
5
5
  #
6
+ # Options:
7
+ # * <tt>:class_name</tt> - tests that the association makes use of the class_name option.
8
+ # * <tt>:validate</tt> - tests that the association makes use of the validate
9
+ # option.
10
+ #
11
+ # Example:
6
12
  # it { should belong_to(:parent) }
7
13
  #
8
14
  def belong_to(name)
@@ -18,6 +24,8 @@ module Shoulda # :nodoc:
18
24
  # * <tt>dependent</tt> - tests that the association makes use of the
19
25
  # dependent option.
20
26
  # * <tt>:class_name</tt> - tests that the association makes use of the class_name option.
27
+ # * <tt>:validate</tt> - tests that the association makes use of the validate
28
+ # option.
21
29
  #
22
30
  # Example:
23
31
  # it { should have_many(:friends) }
@@ -36,6 +44,8 @@ module Shoulda # :nodoc:
36
44
  # * <tt>:dependent</tt> - tests that the association makes use of the
37
45
  # dependent option.
38
46
  # * <tt>:class_name</tt> - tests that the association makes use of the class_name option.
47
+ # * <tt>:validate</tt> - tests that the association makes use of the validate
48
+ # option.
39
49
  #
40
50
  # Example:
41
51
  # it { should have_one(:god) } # unless hindu
@@ -47,6 +57,11 @@ module Shoulda # :nodoc:
47
57
  # Ensures that the has_and_belongs_to_many relationship exists, and that
48
58
  # the join table is in place.
49
59
  #
60
+ # Options:
61
+ # * <tt>:validate</tt> - tests that the association makes use of the validate
62
+ # option.
63
+ #
64
+ # Example:
50
65
  # it { should have_and_belong_to_many(:posts) }
51
66
  #
52
67
  def have_and_belong_to_many(name)
@@ -85,6 +100,11 @@ module Shoulda # :nodoc:
85
100
  self
86
101
  end
87
102
 
103
+ def validate(validate = true)
104
+ @validate = validate
105
+ self
106
+ end
107
+
88
108
  def matches?(subject)
89
109
  @subject = subject
90
110
  association_exists? &&
@@ -95,7 +115,8 @@ module Shoulda # :nodoc:
95
115
  class_name_correct? &&
96
116
  order_correct? &&
97
117
  conditions_correct? &&
98
- join_table_exists?
118
+ join_table_exists? &&
119
+ validate_correct?
99
120
  end
100
121
 
101
122
  def failure_message
@@ -230,6 +251,15 @@ module Shoulda # :nodoc:
230
251
  end
231
252
  end
232
253
 
254
+ def validate_correct?
255
+ if !@validate && !reflection.options[:validate] || @validate == reflection.options[:validate]
256
+ true
257
+ else
258
+ @missing = "#{@name} should have #{@validate} as validate"
259
+ false
260
+ end
261
+ end
262
+
233
263
  def class_has_foreign_key?(klass)
234
264
  if klass.column_names.include?(foreign_key)
235
265
  true
@@ -1,11 +1,13 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- if RUBY_VERSION > "1.9"
3
+ if Gem.ruby_version >= Gem::Version.new('1.8') && Gem.ruby_version < Gem::Version.new('1.9')
4
+ require 'test/unit'
5
+ AssertionError = Test::Unit::AssertionFailedError
6
+ elsif Gem.ruby_version >= Gem::Version.new("1.9")
4
7
  require 'minitest/unit'
5
8
  AssertionError = MiniTest::Assertion
6
9
  else
7
- require 'test/unit/assertionfailederror'
8
- AssertionError = Test::Unit::AssertionFailedError
10
+ raise "No unit test library available"
9
11
  end
10
12
  end
11
13
  end
@@ -1,5 +1,5 @@
1
1
  module Shoulda
2
2
  module Matchers
3
- VERSION = '1.2.0'.freeze
3
+ VERSION = '1.3.0'.freeze
4
4
  end
5
5
  end
@@ -31,6 +31,14 @@ describe Shoulda::Matchers::ActionController::RenderWithLayoutMatcher do
31
31
  end
32
32
  end
33
33
 
34
+ context "a controller that renders a partial" do
35
+ let(:controller) { build_response { render :partial => 'partial' } }
36
+
37
+ it "should reject rendering with a layout" do
38
+ controller.should_not render_with_layout
39
+ end
40
+ end
41
+
34
42
  context "given a context with layouts" do
35
43
  let(:layout) { 'happy' }
36
44
  let(:controller) { build_response { render :layout => false } }
@@ -1,7 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActiveModel::ValidateFormatOfMatcher do
4
-
5
4
  context "a postal code" do
6
5
  before do
7
6
  define_model :example, :attr => :string do
@@ -10,29 +9,71 @@ describe Shoulda::Matchers::ActiveModel::ValidateFormatOfMatcher do
10
9
  @model = Example.new
11
10
  end
12
11
 
13
- it "should be valid" do
12
+ it "is valid" do
14
13
  @model.should validate_format_of(:attr).with('12345')
15
14
  end
16
15
 
17
- it "should not be valid with alpha in zip" do
16
+ it "is not valid with blank" do
17
+ @model.should_not validate_format_of(:attr).with(' ')
18
+ @model.should validate_format_of(:attr).not_with(' ')
19
+ end
20
+
21
+ it "is not valid with nil" do
22
+ @model.should_not validate_format_of(:attr).with(nil)
23
+ end
24
+
25
+ it "is not valid with alpha in zip" do
18
26
  @model.should_not validate_format_of(:attr).with('1234a')
19
27
  @model.should validate_format_of(:attr).not_with('1234a')
20
28
  end
21
29
 
22
- it "should not be valid with too few digits" do
30
+ it "is not valid with too few digits" do
23
31
  @model.should_not validate_format_of(:attr).with('1234')
24
32
  @model.should validate_format_of(:attr).not_with('1234')
25
33
  end
26
34
 
27
- it "should not be valid with too many digits" do
35
+ it "is not valid with too many digits" do
28
36
  @model.should_not validate_format_of(:attr).with('123456')
29
37
  @model.should validate_format_of(:attr).not_with('123456')
30
38
  end
31
39
 
32
- it "should raise error if you try to call both with and not_with" do
40
+ it "raises error if you try to call both with and not_with" do
33
41
  expect { validate_format_of(:attr).not_with('123456').with('12345') }.
34
42
  to raise_error(RuntimeError)
35
43
  end
36
44
  end
37
45
 
46
+ context "when allow_blank and allow_nil are set" do
47
+ before do
48
+ define_model :example, :attr => :string do
49
+ validates_format_of :attr, :with => /^\d{5}$/, :allow_blank => true, :allow_nil => true
50
+ end
51
+ @model = Example.new
52
+ end
53
+
54
+ it "is valid when attr is nil" do
55
+ @model.should validate_format_of(:attr).with(nil)
56
+ end
57
+
58
+ it "is valid when attr is blank" do
59
+ @model.should validate_format_of(:attr).with(' ')
60
+ end
61
+
62
+ describe "#allow_blank" do
63
+ it "allows allow_blank" do
64
+ @model.should validate_format_of(:attr).allow_blank
65
+ @model.should validate_format_of(:attr).allow_blank(true)
66
+ @model.should_not validate_format_of(:attr).allow_blank(false)
67
+ end
68
+ end
69
+
70
+ describe "#allow_nil" do
71
+ it "allows allow_nil" do
72
+ @model.should validate_format_of(:attr).allow_nil
73
+ @model.should validate_format_of(:attr).allow_nil(true)
74
+ @model.should_not validate_format_of(:attr).allow_nil(false)
75
+ end
76
+ end
77
+ end
78
+
38
79
  end
@@ -34,13 +34,8 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
34
34
  @matcher = validate_uniqueness_of(:attr)
35
35
  end
36
36
 
37
- it "should fail to require a unique value" do
38
- @model.should_not @matcher
39
- end
40
-
41
- it "should alert the tester that an existing value is not present" do
42
- @matcher.matches?(@model)
43
- @matcher.negative_failure_message.should =~ /^Can't find first .*/
37
+ it "does not not require a created instance" do
38
+ @model.should @matcher
44
39
  end
45
40
  end
46
41
  end
@@ -71,11 +66,12 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
71
66
  before do
72
67
  @model = define_model(:example, :attr => :string,
73
68
  :scope1 => :integer,
74
- :scope2 => :integer) do
75
- attr_accessible :attr, :scope1, :scope2
69
+ :scope2 => :integer,
70
+ :other => :integer) do
71
+ attr_accessible :attr, :scope1, :scope2, :other
76
72
  validates_uniqueness_of :attr, :scope => [:scope1, :scope2]
77
73
  end.new
78
- @existing = Example.create!(:attr => 'value', :scope1 => 1, :scope2 => 2)
74
+ @existing = Example.create!(:attr => 'value', :scope1 => 1, :scope2 => 2, :other => 3)
79
75
  end
80
76
 
81
77
  it "should pass when the correct scope is specified" do
@@ -86,10 +82,18 @@ describe Shoulda::Matchers::ActiveModel::ValidateUniquenessOfMatcher do
86
82
  @existing.should validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2)
87
83
  end
88
84
 
89
- it "should fail when a different scope is specified" do
85
+ it "should fail when too narrow of a scope is specified" do
86
+ @model.should_not validate_uniqueness_of(:attr).scoped_to(:scope1, :scope2, :other)
87
+ end
88
+
89
+ it "should fail when too broad of a scope is specified" do
90
90
  @model.should_not validate_uniqueness_of(:attr).scoped_to(:scope1)
91
91
  end
92
92
 
93
+ it "should fail when a different scope is specified" do
94
+ @model.should_not validate_uniqueness_of(:attr).scoped_to(:other)
95
+ end
96
+
93
97
  it "should fail when no scope is specified" do
94
98
  @model.should_not validate_uniqueness_of(:attr)
95
99
  end
@@ -98,6 +98,46 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
98
98
  end
99
99
  Child.new.should_not @matcher.class_name('TreeChild')
100
100
  end
101
+
102
+ context 'should accept an association with a false :validate option' do
103
+ before do
104
+ define_model :parent, :adopter => :boolean
105
+ define_model :child, :parent_id => :integer do
106
+ belongs_to :parent, :validate => false
107
+ end
108
+ end
109
+ subject { Child.new }
110
+ specify { subject.should @matcher.validate(false) }
111
+ specify { subject.should_not @matcher.validate(true) }
112
+ specify { subject.should_not @matcher.validate }
113
+ end
114
+
115
+ context 'should accept an association with a true :validate option' do
116
+ before do
117
+ define_model :parent, :adopter => :boolean
118
+ define_model :child, :parent_id => :integer do
119
+ belongs_to :parent, :validate => true
120
+ end
121
+ end
122
+ subject { Child.new }
123
+ specify { subject.should_not @matcher.validate(false) }
124
+ specify { subject.should @matcher.validate(true) }
125
+ specify { subject.should @matcher.validate }
126
+ end
127
+
128
+ context 'should accept an association without a :validate option' do
129
+ before do
130
+ define_model :parent, :adopter => :boolean
131
+ define_model :child, :parent_id => :integer do
132
+ belongs_to :parent
133
+ end
134
+ end
135
+ subject { Child.new }
136
+ specify { subject.should @matcher.validate(false) }
137
+ specify { subject.should_not @matcher.validate(true) }
138
+ specify { subject.should_not @matcher.validate }
139
+ end
140
+
101
141
  end
102
142
 
103
143
  context "have_many" do
@@ -240,6 +280,45 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
240
280
  Parent.new.should_not @matcher.class_name('Node')
241
281
  end
242
282
 
283
+ context 'should accept an association with a false :validate option' do
284
+ before do
285
+ define_model :child, :parent_id => :integer, :adopted => :boolean
286
+ define_model :parent do
287
+ has_many :children, :validate => false
288
+ end
289
+ end
290
+ subject { Parent.new }
291
+ specify { subject.should @matcher.validate(false) }
292
+ specify { subject.should_not @matcher.validate(true) }
293
+ specify { subject.should_not @matcher.validate }
294
+ end
295
+
296
+ context 'should accept an association with a true :validate option' do
297
+ before do
298
+ define_model :child, :parent_id => :integer, :adopted => :boolean
299
+ define_model :parent do
300
+ has_many :children, :validate => true
301
+ end
302
+ end
303
+ subject { Parent.new }
304
+ specify { subject.should_not @matcher.validate(false) }
305
+ specify { subject.should @matcher.validate(true) }
306
+ specify { subject.should @matcher.validate }
307
+ end
308
+
309
+ context 'should accept an association without a :validate option' do
310
+ before do
311
+ define_model :child, :parent_id => :integer, :adopted => :boolean
312
+ define_model :parent do
313
+ has_many :children
314
+ end
315
+ end
316
+ subject { Parent.new }
317
+ specify { subject.should @matcher.validate(false) }
318
+ specify { subject.should_not @matcher.validate(true) }
319
+ specify { subject.should_not @matcher.validate }
320
+ end
321
+
243
322
  it "should accept an association with a nonstandard reverse foreign key, using :inverse_of" do
244
323
  define_model :child, :ancestor_id => :integer do
245
324
  belongs_to :ancestor, :inverse_of => :children, :class_name => :Parent
@@ -364,6 +443,70 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
364
443
  Person.new.should_not @matcher.class_name('PersonDetail')
365
444
  end
366
445
 
446
+ it "should accept an association with a through" do
447
+ define_model :detail
448
+
449
+ define_model :account do
450
+ has_one :detail
451
+ end
452
+
453
+ define_model :person do
454
+ has_one :account
455
+ has_one :detail, :through => :account
456
+ end
457
+
458
+ Person.new.should @matcher.through(:account)
459
+ end
460
+
461
+ it "should reject an association with a through" do
462
+ define_model :detail
463
+
464
+ define_model :person do
465
+ has_one :detail
466
+ end
467
+
468
+ Person.new.should_not @matcher.through(:account)
469
+ end
470
+
471
+ context 'should accept an association with a false :validate option' do
472
+ before do
473
+ define_model :detail, :person_id => :integer, :disabled => :boolean
474
+ define_model :person do
475
+ has_one :detail, :validate => false
476
+ end
477
+ end
478
+ subject { Person.new }
479
+ specify { subject.should @matcher.validate(false) }
480
+ specify { subject.should_not @matcher.validate(true) }
481
+ specify { subject.should_not @matcher.validate }
482
+ end
483
+
484
+ context 'should accept an association with a true :validate option' do
485
+ before do
486
+ define_model :detail, :person_id => :integer, :disabled => :boolean
487
+ define_model :person do
488
+ has_one :detail, :validate => true
489
+ end
490
+ end
491
+ subject { Person.new }
492
+ specify { subject.should_not @matcher.validate(false) }
493
+ specify { subject.should @matcher.validate(true) }
494
+ specify { subject.should @matcher.validate }
495
+ end
496
+
497
+ context 'should accept an association without a :validate option' do
498
+ before do
499
+ define_model :detail, :person_id => :integer, :disabled => :boolean
500
+ define_model :person do
501
+ has_one :detail
502
+ end
503
+ end
504
+ subject { Person.new }
505
+ specify { subject.should @matcher.validate(false) }
506
+ specify { subject.should_not @matcher.validate(true) }
507
+ specify { subject.should_not @matcher.validate }
508
+ end
509
+
367
510
  end
368
511
 
369
512
  context "have_and_belong_to_many" do
@@ -445,5 +588,47 @@ describe Shoulda::Matchers::ActiveRecord::AssociationMatcher do
445
588
  Person.new.should_not @matcher.class_name('PersonRelatives')
446
589
  end
447
590
 
591
+ context 'should accept an association with a false :validate option' do
592
+ before do
593
+ define_model :relatives, :adopted => :boolean
594
+ define_model :person do
595
+ has_and_belongs_to_many :relatives, :validate => false
596
+ end
597
+ define_model :people_relative, :person_id => :integer, :relative_id => :integer
598
+ end
599
+ subject { Person.new }
600
+ specify { subject.should @matcher.validate(false) }
601
+ specify { subject.should_not @matcher.validate(true) }
602
+ specify { subject.should_not @matcher.validate }
603
+ end
604
+
605
+ context 'should accept an association with a true :validate option' do
606
+ before do
607
+ define_model :relatives, :adopted => :boolean
608
+ define_model :person do
609
+ has_and_belongs_to_many :relatives, :validate => true
610
+ end
611
+ define_model :people_relative, :person_id => :integer, :relative_id => :integer
612
+ end
613
+ subject { Person.new }
614
+ specify { subject.should_not @matcher.validate(false) }
615
+ specify { subject.should @matcher.validate(true) }
616
+ specify { subject.should @matcher.validate }
617
+ end
618
+
619
+ context 'should accept an association without a :validate option' do
620
+ before do
621
+ define_model :relatives, :adopted => :boolean
622
+ define_model :person do
623
+ has_and_belongs_to_many :relatives
624
+ end
625
+ define_model :people_relative, :person_id => :integer, :relative_id => :integer
626
+ end
627
+ subject { Person.new }
628
+ specify { subject.should @matcher.validate(false) }
629
+ specify { subject.should_not @matcher.validate(true) }
630
+ specify { subject.should_not @matcher.validate }
631
+ end
632
+
448
633
  end
449
634
  end
@@ -59,7 +59,7 @@ describe Shoulda::Matchers::ActiveRecord::SerializeMatcher do
59
59
 
60
60
  context "a serializer that is an instance of a class" do
61
61
  before do
62
- define_class(:ExampleSerializer, Object) do
62
+ define_class(:ExampleSerializer) do
63
63
  def load(*); end
64
64
  def dump(*); end
65
65
  end
@@ -0,0 +1,42 @@
1
+ module ClassBuilder
2
+ def self.included(example_group)
3
+ example_group.class_eval do
4
+ after do
5
+ teardown_defined_constants
6
+ end
7
+ end
8
+ end
9
+
10
+ def define_class(class_name, base = Object, &block)
11
+ class_name = class_name.to_s.camelize
12
+
13
+ # FIXME: ActionMailer 3.2 calls `name.underscore` immediately upon
14
+ # subclassing. Class.new.name == nil. So, Class.new(ActionMailer::Base)
15
+ # errors out since it's trying to do `nil.underscore`. This is very ugly but
16
+ # allows us to test against ActionMailer 3.2.x.
17
+ eval <<-A_REAL_CLASS_FOR_ACTION_MAILER_3_2
18
+ class ::#{class_name} < #{base}
19
+ end
20
+ A_REAL_CLASS_FOR_ACTION_MAILER_3_2
21
+
22
+ Object.const_get(class_name).tap do |constant_class|
23
+ constant_class.unloadable
24
+
25
+ if block_given?
26
+ constant_class.class_eval(&block)
27
+ end
28
+
29
+ if constant_class.respond_to?(:reset_column_information)
30
+ constant_class.reset_column_information
31
+ end
32
+ end
33
+ end
34
+
35
+ def teardown_defined_constants
36
+ ActiveSupport::Dependencies.clear
37
+ end
38
+ end
39
+
40
+ RSpec.configure do |config|
41
+ config.include ClassBuilder
42
+ end
@@ -0,0 +1,78 @@
1
+ module ControllerBuilder
2
+ TMP_VIEW_PATH =
3
+ File.expand_path(File.join(TESTAPP_ROOT, 'tmp', 'views')).freeze
4
+
5
+ def self.included(example_group)
6
+ example_group.class_eval do
7
+ after do
8
+ delete_temporary_views
9
+ restore_original_routes
10
+ end
11
+ end
12
+ end
13
+
14
+ def define_controller(class_name, &block)
15
+ class_name = class_name.to_s
16
+ class_name << 'Controller' unless class_name =~ /Controller$/
17
+ define_class(class_name, ActionController::Base, &block)
18
+ end
19
+
20
+ def define_routes(&block)
21
+ Rails.application.routes.draw(&block)
22
+ @routes = Rails.application.routes
23
+ class << self
24
+ include ActionDispatch::Assertions
25
+ end
26
+ end
27
+
28
+ def build_response(opts = {}, &block)
29
+ action = opts[:action] || 'example'
30
+ partial = opts[:partial] || '_partial'
31
+ block ||= lambda { render :nothing => true }
32
+ controller_class = define_controller('Examples') do
33
+ layout false
34
+ define_method(action, &block)
35
+ end
36
+ controller_class.view_paths = [TMP_VIEW_PATH]
37
+
38
+ define_routes do
39
+ match 'examples', :to => "examples##{action}"
40
+ end
41
+
42
+ create_view("examples/#{action}.html.erb", "abc")
43
+ create_view("examples/#{partial}.html.erb", "partial")
44
+
45
+ @controller = controller_class.new
46
+ @request = ActionController::TestRequest.new
47
+ @response = ActionController::TestResponse.new
48
+
49
+ class << self
50
+ include ActionController::TestCase::Behavior
51
+ end
52
+ @routes = Rails.application.routes
53
+
54
+ get action
55
+
56
+ @controller
57
+ end
58
+
59
+ def create_view(path, contents)
60
+ full_path = File.join(TMP_VIEW_PATH, path)
61
+ FileUtils.mkdir_p(File.dirname(full_path))
62
+ File.open(full_path, 'w') { |file| file.write(contents) }
63
+ end
64
+
65
+ private
66
+
67
+ def delete_temporary_views
68
+ FileUtils.rm_rf(TMP_VIEW_PATH)
69
+ end
70
+
71
+ def restore_original_routes
72
+ Rails.application.reload_routes!
73
+ end
74
+ end
75
+
76
+ RSpec.configure do |config|
77
+ config.include ControllerBuilder
78
+ end
@@ -0,0 +1,10 @@
1
+ module MailerBuilder
2
+ def define_mailer(name, paths, &block)
3
+ class_name = name.to_s.pluralize.classify
4
+ define_class(class_name, ActionMailer::Base, &block)
5
+ end
6
+ end
7
+
8
+ RSpec.configure do |config|
9
+ config.include MailerBuilder
10
+ end
@@ -1,7 +1,4 @@
1
1
  module ModelBuilder
2
- TMP_VIEW_PATH =
3
- File.expand_path(File.join(TESTAPP_ROOT, 'tmp', 'views')).freeze
4
-
5
2
  def self.included(example_group)
6
3
  example_group.class_eval do
7
4
  before do
@@ -9,7 +6,7 @@ module ModelBuilder
9
6
  end
10
7
 
11
8
  after do
12
- teardown_defined_constants
9
+ drop_created_tables
13
10
  end
14
11
  end
15
12
  end
@@ -28,37 +25,12 @@ module ModelBuilder
28
25
  end
29
26
  end
30
27
 
31
- def define_class(class_name, base = Object, &block)
32
- class_name = class_name.to_s.camelize
33
-
34
- # FIXME: ActionMailer 3.2 calls `name.underscore` immediately upon
35
- # subclassing. Class.new.name == nil. So, Class.new(ActionMailer::Base)
36
- # errors out since it's trying to do `nil.underscore`. This is very ugly but
37
- # allows us to test against ActionMailer 3.2.x.
38
- eval <<-A_REAL_CLASS_FOR_ACTION_MAILER_3_2
39
- class ::#{class_name} < #{base}
40
- end
41
- A_REAL_CLASS_FOR_ACTION_MAILER_3_2
42
-
43
- Object.const_get(class_name).tap do |constant_class|
44
- constant_class.unloadable
45
-
46
- if block_given?
47
- constant_class.class_eval(&block)
48
- end
49
-
50
- if constant_class.respond_to?(:reset_column_information)
51
- constant_class.reset_column_information
52
- end
53
- end
54
- end
55
-
56
28
  def define_model_class(class_name, &block)
57
29
  define_class(class_name, ActiveRecord::Base, &block)
58
30
  end
59
31
 
60
32
  def define_active_model_class(class_name, options = {}, &block)
61
- define_class(class_name, Object) do
33
+ define_class(class_name) do
62
34
  include ActiveModel::Validations
63
35
 
64
36
  options[:accessors].each do |column|
@@ -84,71 +56,12 @@ module ModelBuilder
84
56
  define_model_class(class_name, &block)
85
57
  end
86
58
 
87
- def define_mailer(name, paths, &block)
88
- class_name = name.to_s.pluralize.classify
89
- define_class(class_name, ActionMailer::Base, &block)
90
- end
91
-
92
- def define_controller(class_name, &block)
93
- class_name = class_name.to_s
94
- class_name << 'Controller' unless class_name =~ /Controller$/
95
- define_class(class_name, ActionController::Base, &block)
96
- end
97
-
98
- def define_routes(&block)
99
- Rails.application.routes.draw(&block)
100
- @routes = Rails.application.routes
101
- class << self
102
- include ActionDispatch::Assertions
103
- end
104
- end
105
-
106
- def build_response(opts = {}, &block)
107
- action = opts[:action] || 'example'
108
- partial = opts[:partial] || '_partial'
109
- klass = define_controller('Examples')
110
- block ||= lambda { render :nothing => true }
111
- klass.class_eval { layout false; define_method(action, &block) }
112
- define_routes do
113
- match 'examples', :to => "examples##{action}"
114
- end
115
-
116
- create_view("examples/#{action}.html.erb", "abc")
117
- create_view("examples/#{partial}.html.erb", "partial")
118
- klass.view_paths = [TMP_VIEW_PATH]
119
-
120
- @controller = klass.new
121
- @request = ActionController::TestRequest.new
122
- @response = ActionController::TestResponse.new
123
-
124
- class << self
125
- include ActionController::TestCase::Behavior
126
- end
127
- @routes = Rails.application.routes
128
-
129
- get action
130
-
131
- @controller
132
- end
133
-
134
- def create_view(path, contents)
135
- full_path = File.join(TMP_VIEW_PATH, path)
136
- FileUtils.mkdir_p(File.dirname(full_path))
137
- File.open(full_path, 'w') { |file| file.write(contents) }
138
- end
139
-
140
- def teardown_defined_constants
141
- ActiveSupport::Dependencies.clear
142
-
59
+ def drop_created_tables
143
60
  connection = ActiveRecord::Base.connection
144
61
 
145
62
  @created_tables.each do |table_name|
146
63
  connection.execute("DROP TABLE IF EXISTS #{table_name}")
147
64
  end
148
-
149
- FileUtils.rm_rf(TMP_VIEW_PATH)
150
-
151
- Rails.application.reload_routes!
152
65
  end
153
66
  end
154
67
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoulda-matchers
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -14,7 +14,7 @@ authors:
14
14
  autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
- date: 2012-06-14 00:00:00.000000000 Z
17
+ date: 2012-08-30 00:00:00.000000000 Z
18
18
  dependencies:
19
19
  - !ruby/object:Gem::Dependency
20
20
  name: activesupport
@@ -266,6 +266,9 @@ files:
266
266
  - spec/shoulda/active_record/serialize_matcher_spec.rb
267
267
  - spec/spec_helper.rb
268
268
  - spec/support/active_model_versions.rb
269
+ - spec/support/class_builder.rb
270
+ - spec/support/controller_builder.rb
271
+ - spec/support/mailer_builder.rb
269
272
  - spec/support/model_builder.rb
270
273
  homepage: http://thoughtbot.com/community/
271
274
  licenses: []
@@ -279,18 +282,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
279
282
  - - ! '>='
280
283
  - !ruby/object:Gem::Version
281
284
  version: '0'
282
- segments:
283
- - 0
284
- hash: -2813664721542156285
285
285
  required_rubygems_version: !ruby/object:Gem::Requirement
286
286
  none: false
287
287
  requirements:
288
288
  - - ! '>='
289
289
  - !ruby/object:Gem::Version
290
290
  version: '0'
291
- segments:
292
- - 0
293
- hash: -2813664721542156285
294
291
  requirements: []
295
292
  rubyforge_project:
296
293
  rubygems_version: 1.8.23
@@ -340,4 +337,8 @@ test_files:
340
337
  - spec/shoulda/active_record/serialize_matcher_spec.rb
341
338
  - spec/spec_helper.rb
342
339
  - spec/support/active_model_versions.rb
340
+ - spec/support/class_builder.rb
341
+ - spec/support/controller_builder.rb
342
+ - spec/support/mailer_builder.rb
343
343
  - spec/support/model_builder.rb
344
+ has_rdoc: