shoulda-matchers 2.6.2 → 2.7.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +6 -1
  3. data/Gemfile +1 -1
  4. data/Gemfile.lock +26 -22
  5. data/NEWS.md +52 -1
  6. data/README.md +8 -6
  7. data/Rakefile +1 -1
  8. data/doc_config/gh-pages/index.html.erb +1 -1
  9. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +1 -1
  10. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +26 -0
  11. data/features/rails_integration.feature +18 -3
  12. data/features/step_definitions/rails_steps.rb +21 -3
  13. data/gemfiles/3.0.gemfile +1 -1
  14. data/gemfiles/3.0.gemfile.lock +30 -24
  15. data/gemfiles/3.1.gemfile +1 -1
  16. data/gemfiles/3.1.gemfile.lock +32 -26
  17. data/gemfiles/3.1_1.9.2.gemfile +1 -1
  18. data/gemfiles/3.1_1.9.2.gemfile.lock +22 -19
  19. data/gemfiles/3.2.gemfile +1 -1
  20. data/gemfiles/3.2.gemfile.lock +32 -26
  21. data/gemfiles/3.2_1.9.2.gemfile +1 -1
  22. data/gemfiles/3.2_1.9.2.gemfile.lock +8 -8
  23. data/gemfiles/4.0.0.gemfile +2 -2
  24. data/gemfiles/4.0.0.gemfile.lock +35 -31
  25. data/gemfiles/4.0.1.gemfile +2 -2
  26. data/gemfiles/4.0.1.gemfile.lock +24 -22
  27. data/gemfiles/4.1.gemfile +1 -1
  28. data/gemfiles/4.1.gemfile.lock +26 -22
  29. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +10 -3
  30. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +24 -6
  31. data/lib/shoulda/matchers/active_model.rb +2 -2
  32. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +1 -1
  33. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +1 -1
  34. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +5 -3
  35. data/lib/shoulda/matchers/active_model/{ensure_exclusion_of_matcher.rb → validate_exclusion_of_matcher.rb} +20 -10
  36. data/lib/shoulda/matchers/active_model/{ensure_inclusion_of_matcher.rb → validate_inclusion_of_matcher.rb} +52 -28
  37. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +28 -19
  38. data/lib/shoulda/matchers/active_record.rb +18 -16
  39. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -4
  40. data/lib/shoulda/matchers/active_record/association_matcher.rb +17 -12
  41. data/lib/shoulda/matchers/active_record/association_matchers/join_table_matcher.rb +86 -0
  42. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +19 -0
  43. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +2 -1
  44. data/lib/shoulda/matchers/active_record/define_enum_for_matcher.rb +138 -0
  45. data/lib/shoulda/matchers/independent.rb +3 -2
  46. data/lib/shoulda/matchers/independent/{delegate_matcher.rb → delegate_method_matcher.rb} +69 -49
  47. data/lib/shoulda/matchers/independent/delegate_method_matcher/stubbed_target.rb +37 -0
  48. data/lib/shoulda/matchers/independent/delegate_method_matcher/target_not_defined_error.rb +15 -0
  49. data/lib/shoulda/matchers/version.rb +1 -1
  50. data/lib/shoulda/matchers/warn.rb +30 -2
  51. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +6 -0
  52. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +67 -5
  53. data/spec/shoulda/matchers/action_controller/strong_parameters_matcher_spec.rb +9 -9
  54. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +30 -3
  55. data/spec/shoulda/matchers/active_model/{ensure_exclusion_of_matcher_spec.rb → validate_exclusion_of_matcher_spec.rb} +29 -13
  56. data/spec/shoulda/matchers/active_model/{ensure_inclusion_of_matcher_spec.rb → validate_inclusion_of_matcher_spec.rb} +34 -16
  57. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +35 -0
  58. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +56 -1
  59. data/spec/shoulda/matchers/active_record/define_enum_for_matcher_spec.rb +101 -0
  60. data/spec/shoulda/matchers/doublespeak/object_double_spec.rb +6 -6
  61. data/spec/shoulda/matchers/independent/{delegate_matcher → delegate_method_matcher}/stubbed_target_spec.rb +1 -1
  62. data/spec/shoulda/matchers/independent/{delegate_matcher_spec.rb → delegate_method_matcher_spec.rb} +88 -29
  63. data/spec/spec_helper.rb +2 -3
  64. data/spec/support/fail_with_message_including_matcher.rb +14 -3
  65. data/spec/support/fail_with_message_matcher.rb +14 -2
  66. data/spec/support/rails_versions.rb +4 -0
  67. metadata +19 -14
  68. data/lib/shoulda/matchers/independent/delegate_matcher/stubbed_target.rb +0 -35
@@ -0,0 +1,37 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module Independent
4
+ class DelegateMethodMatcher
5
+ # @private
6
+ class StubbedTarget
7
+ def initialize(method)
8
+ @received_method = false
9
+ @received_arguments = []
10
+ stub_method(method)
11
+ end
12
+
13
+ def has_received_method?
14
+ received_method
15
+ end
16
+
17
+ def has_received_arguments?(*args)
18
+ args == received_arguments
19
+ end
20
+
21
+ protected
22
+
23
+ def stub_method(method)
24
+ class_eval do
25
+ define_method method do |*args|
26
+ @received_method = true
27
+ @received_arguments = args
28
+ end
29
+ end
30
+ end
31
+
32
+ attr_reader :received_method, :received_arguments
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,15 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module Independent
4
+ class DelegateMethodMatcher
5
+ # @private
6
+ class TargetNotDefinedError < StandardError
7
+ def message
8
+ 'Delegation needs a target. Use the #to method to define one, e.g.
9
+ `post_office.should delegate(:deliver_mail).to(:mailman)`'.squish
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,6 +1,6 @@
1
1
  module Shoulda
2
2
  module Matchers
3
3
  # @private
4
- VERSION = '2.6.2'.freeze
4
+ VERSION = '2.7.0'.freeze
5
5
  end
6
6
  end
@@ -1,8 +1,36 @@
1
1
  module Shoulda
2
2
  module Matchers
3
+ TERMINAL_MAX_WIDTH = 72
4
+
3
5
  # @private
4
- def self.warn(msg)
5
- Kernel.warn "Warning from shoulda-matchers:\n\n#{msg}"
6
+ def self.warn(message)
7
+ header = "Warning from shoulda-matchers:"
8
+ divider = "*" * TERMINAL_MAX_WIDTH
9
+ wrapped_message = word_wrap(message, TERMINAL_MAX_WIDTH)
10
+ full_message = [
11
+ divider,
12
+ [header, wrapped_message.strip].join("\n\n"),
13
+ divider
14
+ ].join("\n")
15
+
16
+ Kernel.warn(full_message)
17
+ end
18
+
19
+ # @private
20
+ def self.warn_about_deprecated_method(old_method, new_method)
21
+ warn <<EOT
22
+ #{old_method} is deprecated and will be removed in the next major
23
+ release. Please use #{new_method} instead.
24
+ EOT
25
+ end
26
+
27
+ # Source: <https://www.ruby-forum.com/topic/57805>
28
+ # @private
29
+ def self.word_wrap(text, width=80)
30
+ text.
31
+ gsub(/\n+/, " ").
32
+ gsub( /(\S{#{width}})(?=\S)/, '\1 ' ).
33
+ gsub( /(.{1,#{width}})(?:\s+|$)/, "\\1\n" )
6
34
  end
7
35
  end
8
36
  end
@@ -7,6 +7,12 @@ describe Shoulda::Matchers::ActionController::FilterParamMatcher do
7
7
  expect(nil).to filter_param(:secret)
8
8
  end
9
9
 
10
+ it 'accepts filtering a parameter matching a filtered regex' do
11
+ filter(/(?!tip)pin(?!g)/)
12
+
13
+ expect(nil).to filter_param(:pin)
14
+ end
15
+
10
16
  it 'rejects filtering an unfiltered parameter' do
11
17
  filter(:secret)
12
18
  matcher = filter_param(:other)
@@ -6,6 +6,10 @@ describe Shoulda::Matchers::ActionController::SetSessionMatcher do
6
6
  expect(controller_with_session(var: 'hi')).to set_session(:var)
7
7
  end
8
8
 
9
+ it 'accepts assigning to that variable with specifying the key by string' do
10
+ expect(controller_with_session(var: 'hi')).to set_session('var')
11
+ end
12
+
9
13
  it 'accepts assigning the correct value to that variable' do
10
14
  expect(controller_with_session(var: 'hi')).to set_session(:var).to('hi')
11
15
  end
@@ -22,22 +26,80 @@ describe Shoulda::Matchers::ActionController::SetSessionMatcher do
22
26
  expect(controller_with_session(var: 'hi')).to set_session(:other).to(nil)
23
27
  end
24
28
 
29
+ it 'rejects assigning nil to that variable' do
30
+ expect(controller_with_session(var: 'hi')).not_to set_session(:var).to(nil)
31
+ end
32
+
33
+ it 'accepts assigning nil to cleared variable' do
34
+ expect(controller_with_session(var: nil)).to set_session(:var).to(nil)
35
+ end
36
+
25
37
  it 'accepts assigning false to that variable' do
26
38
  expect(controller_with_session(var: false)).to set_session(:var).to(false)
27
39
  end
28
40
 
41
+ it 'rejects assigning false to other variable' do
42
+ expect(controller_with_session(var: false)).not_to set_session(:other).to(false)
43
+ end
44
+
45
+ it 'rejects assigning false to a variable with value' do
46
+ expect(controller_with_session(var: 'hi')).not_to set_session(:other).to(false)
47
+ end
48
+
29
49
  it 'accepts assigning to the same value in the test context' do
30
- expected = 'value'
50
+ context = stub(expected: 'value')
31
51
 
32
- expect(controller_with_session(var: expected)).
33
- to set_session(:var).in_context(self).to { expected }
52
+ expect(controller_with_session(var: 'value')).
53
+ to set_session(:var).in_context(context).to { expected }
34
54
  end
35
55
 
36
56
  it 'rejects assigning to the another value in the test context' do
37
- expected = 'other'
57
+ context = stub(expected: 'other')
38
58
 
39
59
  expect(controller_with_session(var: 'unexpected')).
40
- not_to set_session(:var).in_context(self).to { expected }
60
+ not_to set_session(:var).in_context(context).to { expected }
61
+ end
62
+
63
+ it 'accepts assigning nil to another variable in the test context' do
64
+ context = stub(expected: nil)
65
+
66
+ expect(controller_with_session(var: 'hi')).
67
+ to set_session(:other).in_context(context).to { expected }
68
+ end
69
+
70
+ it 'rejects assigning nil to that variable in the test context' do
71
+ context = stub(expected: nil)
72
+
73
+ expect(controller_with_session(var: 'hi')).
74
+ not_to set_session(:var).in_context(context).to { expected }
75
+ end
76
+
77
+ it 'accepts assigning nil to a cleared variable in the test context' do
78
+ context = stub(expected: nil)
79
+
80
+ expect(controller_with_session(var: nil)).
81
+ to set_session(:var).in_context(context).to { expected }
82
+ end
83
+
84
+ it 'accepts assigning false to that variable in the test context' do
85
+ context = stub(expected: false)
86
+
87
+ expect(controller_with_session(var: false)).
88
+ to set_session(:var).in_context(context).to { expected }
89
+ end
90
+
91
+ it 'accepts assigning false to other variable in the test context' do
92
+ context = stub(expected: false)
93
+
94
+ expect(controller_with_session(var: false)).
95
+ not_to set_session(:other).in_context(context).to { expected }
96
+ end
97
+
98
+ it 'accepts assigning false to other variable in the test context' do
99
+ context = stub(expected: false)
100
+
101
+ expect(controller_with_session(var: 'hi')).
102
+ not_to set_session(:var).in_context(context).to { expected }
41
103
  end
42
104
 
43
105
  def controller_with_session(session_hash)
@@ -72,7 +72,7 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
72
72
  end
73
73
 
74
74
  matcher = described_class.new([:name]).in_context(self).for(:create)
75
- expect(matcher.matches?(@controller)).to be_true
75
+ expect(matcher.matches?(@controller)).to be true
76
76
  end
77
77
 
78
78
  it 'is true for all the allowable attributes' do
@@ -81,7 +81,7 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
81
81
  end
82
82
 
83
83
  matcher = described_class.new([:name, :age]).in_context(self).for(:create)
84
- expect(matcher.matches?(@controller)).to be_true
84
+ expect(matcher.matches?(@controller)).to be true
85
85
  end
86
86
 
87
87
  it 'is false when any attributes are not allowed' do
@@ -90,14 +90,14 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
90
90
  end
91
91
 
92
92
  matcher = described_class.new([:name, :admin]).in_context(self).for(:create)
93
- expect(matcher.matches?(@controller)).to be_false
93
+ expect(matcher.matches?(@controller)).to be false
94
94
  end
95
95
 
96
96
  it 'is false when permit is not called' do
97
97
  controller_for_resource_with_strong_parameters(action: :create)
98
98
 
99
99
  matcher = described_class.new([:name]).in_context(self).for(:create)
100
- expect(matcher.matches?(@controller)).to be_false
100
+ expect(matcher.matches?(@controller)).to be false
101
101
  end
102
102
 
103
103
  it 'requires an action' do
@@ -129,7 +129,7 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
129
129
  matcher = described_class.new([:name]).
130
130
  in_context(self).
131
131
  for(:show, verb: :get, params: { slug: 'foo' })
132
- expect(matcher.matches?(@controller)).to be_true
132
+ expect(matcher.matches?(@controller)).to be true
133
133
  end
134
134
 
135
135
  it 'works with #update specifically' do
@@ -140,7 +140,7 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
140
140
  matcher = described_class.new([:name]).
141
141
  in_context(self).
142
142
  for(:update, params: { id: 1 })
143
- expect(matcher.matches?(@controller)).to be_true
143
+ expect(matcher.matches?(@controller)).to be true
144
144
  end
145
145
 
146
146
  it 'does not raise an error when #fetch was used instead of #require (issue #495)' do
@@ -151,7 +151,7 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
151
151
  matcher = described_class.new([:eta, :diner_id]).
152
152
  in_context(self).
153
153
  for(:create)
154
- expect(matcher.matches?(@controller)).to be_true
154
+ expect(matcher.matches?(@controller)).to be true
155
155
  end
156
156
 
157
157
  it 'tracks multiple calls to #permit' do
@@ -167,12 +167,12 @@ describe Shoulda::Matchers::ActionController::StrongParametersMatcher do
167
167
  matcher = described_class.new(sets_of_attributes[0]).
168
168
  in_context(self).
169
169
  for(:create)
170
- expect(matcher.matches?(@controller)).to be_true
170
+ expect(matcher.matches?(@controller)).to be true
171
171
 
172
172
  matcher = described_class.new(sets_of_attributes[1]).
173
173
  in_context(self).
174
174
  for(:create)
175
- expect(matcher.matches?(@controller)).to be_true
175
+ expect(matcher.matches?(@controller)).to be true
176
176
  end
177
177
 
178
178
  context 'stubbing params on the controller' do
@@ -2,6 +2,21 @@ require 'spec_helper'
2
2
 
3
3
  describe Shoulda::Matchers::ActiveModel::ValidateAbsenceOfMatcher do
4
4
  if active_model_4_0?
5
+ def self.available_column_types
6
+ [
7
+ :string,
8
+ :text,
9
+ :integer,
10
+ :float,
11
+ :decimal,
12
+ :datetime,
13
+ :timestamp,
14
+ :time,
15
+ :date,
16
+ :binary
17
+ ]
18
+ end
19
+
5
20
  context 'a model with an absence validation' do
6
21
  it 'accepts' do
7
22
  expect(validating_absence_of(:attr)).to validate_absence_of(:attr)
@@ -10,6 +25,15 @@ describe Shoulda::Matchers::ActiveModel::ValidateAbsenceOfMatcher do
10
25
  it 'does not override the default message with a present' do
11
26
  expect(validating_absence_of(:attr)).to validate_absence_of(:attr).with_message(nil)
12
27
  end
28
+
29
+ available_column_types.each do |type|
30
+ context "when column is of type #{type}" do
31
+ it "accepts" do
32
+ expect(validating_absence_of(:attr, {}, type: type)).
33
+ to validate_absence_of(:attr)
34
+ end
35
+ end
36
+ end
13
37
  end
14
38
 
15
39
  context 'a model without an absence validation' do
@@ -95,9 +119,12 @@ describe Shoulda::Matchers::ActiveModel::ValidateAbsenceOfMatcher do
95
119
  end
96
120
  end
97
121
 
98
- def validating_absence_of(attr, options = {})
99
- define_model :example, attr => :string do
100
- validates_absence_of attr, options
122
+ def validating_absence_of(attr, validation_options = {}, given_column_options = {})
123
+ default_column_options = { type: :string, options: {} }
124
+ column_options = default_column_options.merge(given_column_options)
125
+
126
+ define_model :example, attr => column_options do
127
+ validates_absence_of attr, validation_options
101
128
  end.new
102
129
  end
103
130
 
@@ -1,39 +1,55 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
3
+ describe Shoulda::Matchers::ActiveModel do
4
+ describe '#ensure_exclusion_of' do
5
+ it 'is aliased to #validate_exclusion_of' do
6
+ matchers.expects(:validate_exclusion_of).with(:attr)
7
+
8
+ silence_warnings do
9
+ matchers.ensure_exclusion_of(:attr)
10
+ end
11
+ end
12
+ end
13
+
14
+ def matchers
15
+ @_matchers ||= Object.new.extend(described_class)
16
+ end
17
+ end
18
+
19
+ describe Shoulda::Matchers::ActiveModel::ValidateExclusionOfMatcher do
4
20
  context 'an attribute which must be excluded from a range' do
5
21
  it 'accepts ensuring the correct range' do
6
22
  expect(validating_exclusion(in: 2..5)).
7
- to ensure_exclusion_of(:attr).in_range(2..5)
23
+ to validate_exclusion_of(:attr).in_range(2..5)
8
24
  end
9
25
 
10
26
  it 'rejects ensuring excluded value' do
11
27
  expect(validating_exclusion(in: 2..5)).
12
- not_to ensure_exclusion_of(:attr).in_range(2..6)
28
+ not_to validate_exclusion_of(:attr).in_range(2..6)
13
29
  end
14
30
 
15
31
  it 'does not override the default message with a blank' do
16
32
  expect(validating_exclusion(in: 2..5)).
17
- to ensure_exclusion_of(:attr).in_range(2..5).with_message(nil)
33
+ to validate_exclusion_of(:attr).in_range(2..5).with_message(nil)
18
34
  end
19
35
  end
20
36
 
21
37
  context 'an attribute which must be excluded from a range with excluded end' do
22
38
  it 'accepts ensuring the correct range' do
23
39
  expect(validating_exclusion(in: 2...5)).
24
- to ensure_exclusion_of(:attr).in_range(2...5)
40
+ to validate_exclusion_of(:attr).in_range(2...5)
25
41
  end
26
42
 
27
43
  it 'rejects ensuring excluded value' do
28
44
  expect(validating_exclusion(in: 2...5)).
29
- not_to ensure_exclusion_of(:attr).in_range(2...4)
45
+ not_to validate_exclusion_of(:attr).in_range(2...4)
30
46
  end
31
47
  end
32
48
 
33
49
  context 'an attribute with a custom validation message' do
34
50
  it 'accepts ensuring the correct range' do
35
51
  expect(validating_exclusion(in: 2..4, message: 'not good')).
36
- to ensure_exclusion_of(:attr).in_range(2..4).with_message(/not good/)
52
+ to validate_exclusion_of(:attr).in_range(2..4).with_message(/not good/)
37
53
  end
38
54
  end
39
55
 
@@ -45,7 +61,7 @@ describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
45
61
  end
46
62
  end
47
63
 
48
- expect(model).to ensure_exclusion_of(:attr).in_range(2..5).
64
+ expect(model).to validate_exclusion_of(:attr).in_range(2..5).
49
65
  with_message(/should be out of this range/)
50
66
 
51
67
  model = custom_validation do
@@ -54,7 +70,7 @@ describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
54
70
  end
55
71
  end
56
72
 
57
- expect(model).to ensure_exclusion_of(:attr).in_range(2...5).
73
+ expect(model).to validate_exclusion_of(:attr).in_range(2...5).
58
74
  with_message(/should be out of this range/)
59
75
  end
60
76
  end
@@ -62,21 +78,21 @@ describe Shoulda::Matchers::ActiveModel::EnsureExclusionOfMatcher do
62
78
  context 'an attribute which must be excluded from an array' do
63
79
  it 'accepts with correct array' do
64
80
  expect(validating_exclusion(in: %w(one two))).
65
- to ensure_exclusion_of(:attr).in_array(%w(one two))
81
+ to validate_exclusion_of(:attr).in_array(%w(one two))
66
82
  end
67
83
 
68
84
  it 'rejects when only part of array matches' do
69
85
  expect(validating_exclusion(in: %w(one two))).
70
- not_to ensure_exclusion_of(:attr).in_array(%w(one wrong_value))
86
+ not_to validate_exclusion_of(:attr).in_array(%w(one wrong_value))
71
87
  end
72
88
 
73
89
  it 'rejects when array does not match at all' do
74
90
  expect(validating_exclusion(in: %w(one two))).
75
- not_to ensure_exclusion_of(:attr).in_array(%w(cat dog))
91
+ not_to validate_exclusion_of(:attr).in_array(%w(cat dog))
76
92
  end
77
93
 
78
94
  it 'has correct description' do
79
- expect(ensure_exclusion_of(:attr).in_array([true, 'dog']).description).
95
+ expect(validate_exclusion_of(:attr).in_array([true, 'dog']).description).
80
96
  to eq 'ensure exclusion of attr in [true, "dog"]'
81
97
  end
82
98