shoulda-matchers 1.2.0 → 1.3.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/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: