shoulda-matchers 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. data/.travis.yml +5 -0
  2. data/Appraisals +6 -0
  3. data/Gemfile.lock +15 -15
  4. data/NEWS.md +20 -1
  5. data/README.md +6 -1
  6. data/features/step_definitions/rails_steps.rb +3 -3
  7. data/gemfiles/3.0.gemfile.lock +23 -27
  8. data/gemfiles/3.1.gemfile.lock +26 -30
  9. data/gemfiles/3.2.gemfile +16 -0
  10. data/gemfiles/3.2.gemfile.lock +157 -0
  11. data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +46 -29
  12. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +6 -2
  13. data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +12 -7
  14. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +28 -15
  15. data/lib/shoulda/matchers/action_mailer/have_sent_email_matcher.rb +1 -1
  16. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +13 -9
  17. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +9 -8
  18. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +8 -6
  19. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +63 -8
  20. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +67 -34
  21. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +3 -3
  22. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +9 -5
  23. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +6 -3
  24. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +27 -23
  25. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +38 -25
  26. data/lib/shoulda/matchers/active_record/association_matcher.rb +49 -33
  27. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +42 -35
  28. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +15 -13
  29. data/lib/shoulda/matchers/active_record/query_the_database_matcher.rb +24 -23
  30. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +13 -12
  31. data/lib/shoulda/matchers/version.rb +1 -1
  32. data/shoulda-matchers.gemspec +1 -1
  33. data/spec/shoulda/action_controller/assign_to_matcher_spec.rb +5 -3
  34. data/spec/shoulda/action_mailer/have_sent_email_spec.rb +40 -0
  35. data/spec/shoulda/active_model/allow_mass_assignment_of_matcher_spec.rb +12 -10
  36. data/spec/shoulda/active_model/ensure_inclusion_of_matcher_spec.rb +52 -0
  37. data/spec/shoulda/active_model/helpers_spec.rb +35 -6
  38. data/spec/shoulda/active_model/validate_presence_of_matcher_spec.rb +0 -1
  39. data/spec/shoulda/active_model/validate_uniqueness_of_matcher_spec.rb +8 -1
  40. data/spec/shoulda/active_record/serialize_matcher_spec.rb +1 -1
  41. data/spec/support/active_model_versions.rb +9 -0
  42. data/spec/support/model_builder.rb +15 -7
  43. metadata +123 -128
@@ -24,30 +24,35 @@ module Shoulda # :nodoc:
24
24
 
25
25
  def initialize(variable)
26
26
  @variable = variable.to_s
27
- @check_value = false
27
+ @options = {}
28
+ @options[:check_value] = false
28
29
  end
29
30
 
30
31
  def with_kind_of(expected_class)
31
- @expected_class = expected_class
32
+ @options[:expected_class] = expected_class
32
33
  self
33
34
  end
34
35
 
35
36
  def with(expected_value = nil, &block)
36
- @check_value = true
37
- @expected_value = expected_value
38
- @expectation_block = block
37
+ @options[:check_value] = true
38
+ @options[:expected_value] = expected_value
39
+ @options[:expectation_block] = block
39
40
  self
40
41
  end
41
42
 
42
43
  def matches?(controller)
43
44
  @controller = controller
44
- @expected_value = @context.instance_eval(&@expectation_block) if @expectation_block
45
- assigned_value? && kind_of_expected_class? && equal_to_expected_value?
45
+ normalize_expected_value!
46
+ assigned_value? &&
47
+ kind_of_expected_class? &&
48
+ equal_to_expected_value?
46
49
  end
47
50
 
48
51
  def description
49
52
  description = "assign @#{@variable}"
50
- description << " with a kind of #{@expected_class}" if @expected_class
53
+ if @options.key?(:expected_class)
54
+ description << " with a kind of #{@options[:expected_class]}"
55
+ end
51
56
  description
52
57
  end
53
58
 
@@ -72,33 +77,45 @@ module Shoulda # :nodoc:
72
77
  end
73
78
 
74
79
  def kind_of_expected_class?
75
- return true unless @expected_class
76
- if assigned_value.kind_of?(@expected_class)
77
- @negative_failure_message =
78
- "Didn't expect action to assign a kind of #{@expected_class} " <<
79
- "for #{@variable}, but got one anyway"
80
- true
80
+ if @options.key?(:expected_class)
81
+ if assigned_value.kind_of?(@options[:expected_class])
82
+ @negative_failure_message =
83
+ "Didn't expect action to assign a kind of #{@options[:expected_class]} " <<
84
+ "for #{@variable}, but got one anyway"
85
+ true
86
+ else
87
+ @failure_message =
88
+ "Expected action to assign a kind of #{@options[:expected_class]} " <<
89
+ "for #{@variable}, but got #{assigned_value.inspect} " <<
90
+ "(#{assigned_value.class.name})"
91
+ false
92
+ end
81
93
  else
82
- @failure_message =
83
- "Expected action to assign a kind of #{@expected_class} " <<
84
- "for #{@variable}, but got #{assigned_value.inspect} " <<
85
- "(#{assigned_value.class.name})"
86
- false
94
+ true
87
95
  end
88
96
  end
89
97
 
90
98
  def equal_to_expected_value?
91
- return true unless @check_value
92
- if @expected_value == assigned_value
93
- @negative_failure_message =
94
- "Didn't expect action to assign #{@expected_value.inspect} " <<
95
- "for #{@variable}, but got it anyway"
96
- true
99
+ if @options[:check_value]
100
+ if @options[:expected_value] == assigned_value
101
+ @negative_failure_message =
102
+ "Didn't expect action to assign #{@options[:expected_value].inspect} " <<
103
+ "for #{@variable}, but got it anyway"
104
+ true
105
+ else
106
+ @failure_message =
107
+ "Expected action to assign #{@options[:expected_value].inspect} " <<
108
+ "for #{@variable}, but got #{assigned_value.inspect}"
109
+ false
110
+ end
97
111
  else
98
- @failure_message =
99
- "Expected action to assign #{@expected_value.inspect} " <<
100
- "for #{@variable}, but got #{assigned_value.inspect}"
101
- false
112
+ true
113
+ end
114
+ end
115
+
116
+ def normalize_expected_value!
117
+ if @options[:expectation_block]
118
+ @options[:expected_value] = @context.instance_eval(&@options[:expectation_block])
102
119
  end
103
120
  end
104
121
 
@@ -16,7 +16,9 @@ module Shoulda # :nodoc:
16
16
  class RenderWithLayoutMatcher # :nodoc:
17
17
 
18
18
  def initialize(expected_layout)
19
- @expected_layout = expected_layout.to_s unless expected_layout.nil?
19
+ unless expected_layout.nil?
20
+ @expected_layout = expected_layout.to_s
21
+ end
20
22
  end
21
23
 
22
24
  # Used to provide access to layouts recorded by
@@ -64,7 +66,7 @@ module Shoulda # :nodoc:
64
66
  end
65
67
 
66
68
  def rendered_layouts
67
- if recorded_layouts
69
+ if recorded_layouts.size > 0
68
70
  recorded_layouts.keys.compact.map { |layout| layout.sub(%r{^layouts/}, '') }
69
71
  else
70
72
  layout = @controller.response.layout
@@ -79,6 +81,8 @@ module Shoulda # :nodoc:
79
81
  def recorded_layouts
80
82
  if @context
81
83
  @context.instance_variable_get('@layouts')
84
+ else
85
+ {}
82
86
  end
83
87
  end
84
88
 
@@ -32,11 +32,7 @@ module Shoulda # :nodoc:
32
32
 
33
33
  def matches?(controller)
34
34
  @controller = controller
35
- if @content_type.is_a?(Regexp)
36
- response_content_type =~ @content_type
37
- else
38
- response_content_type == @content_type
39
- end
35
+ content_type_matches_regexp? || content_type_matches_string?
40
36
  end
41
37
 
42
38
  def failure_message
@@ -49,6 +45,16 @@ module Shoulda # :nodoc:
49
45
 
50
46
  protected
51
47
 
48
+ def content_type_matches_regexp?
49
+ if @content_type.is_a?(Regexp)
50
+ response_content_type =~ @content_type
51
+ end
52
+ end
53
+
54
+ def content_type_matches_string?
55
+ response_content_type == @content_type
56
+ end
57
+
52
58
  def response_content_type
53
59
  @controller.response.content_type.to_s
54
60
  end
@@ -66,8 +72,7 @@ module Shoulda # :nodoc:
66
72
  end
67
73
 
68
74
  def expectation
69
- "content type to be #{@content_type}, " <<
70
- "but was #{response_content_type}"
75
+ "content type to be #{@content_type}, but was #{response_content_type}"
71
76
  end
72
77
  end
73
78
  end
@@ -18,6 +18,10 @@ module Shoulda # :nodoc:
18
18
  end
19
19
 
20
20
  class SetTheFlashMatcher # :nodoc:
21
+ def initialize
22
+ @options = {}
23
+ end
24
+
21
25
  attr_reader :failure_message, :negative_failure_message
22
26
 
23
27
  def to(value)
@@ -26,12 +30,12 @@ module Shoulda # :nodoc:
26
30
  end
27
31
 
28
32
  def now
29
- @now = true
33
+ @options[:now] = true
30
34
  self
31
35
  end
32
36
 
33
37
  def [](key)
34
- @key = key
38
+ @options[:key] = key
35
39
  self
36
40
  end
37
41
 
@@ -77,8 +81,8 @@ module Shoulda # :nodoc:
77
81
  end
78
82
 
79
83
  def flash_values
80
- if @key
81
- [flash.to_hash[@key]]
84
+ if @options.key?(:key)
85
+ [flash.to_hash[@options[:key]]]
82
86
  else
83
87
  flash.to_hash.values
84
88
  end
@@ -90,13 +94,17 @@ module Shoulda # :nodoc:
90
94
  else
91
95
  @flash = @controller.flash.dup
92
96
  @flash.instance_variable_set(:@used, @controller.flash.instance_variable_get(:@used).dup)
93
- if ! @now
94
- @flash.sweep
95
- end
97
+ sweep_flash_if_necessary
96
98
  @flash
97
99
  end
98
100
  end
99
101
 
102
+ def sweep_flash_if_necessary
103
+ unless @options[:now]
104
+ @flash.sweep
105
+ end
106
+ end
107
+
100
108
  def expectation
101
109
  expectation = "the #{expected_flash_invocation} to be set"
102
110
  expectation << " to #{@value.inspect}" unless @value.nil?
@@ -113,18 +121,23 @@ module Shoulda # :nodoc:
113
121
  end
114
122
 
115
123
  def expected_flash_invocation
116
- now = ""
117
- key = ""
124
+ "flash#{pretty_now}#{pretty_key}"
125
+ end
118
126
 
119
- if @now
120
- now = ".now"
127
+ def pretty_now
128
+ if @options[:now]
129
+ ".now"
130
+ else
131
+ ""
121
132
  end
133
+ end
122
134
 
123
- if @key
124
- key = "[:#{@key}]"
135
+ def pretty_key
136
+ if @options[:key]
137
+ "[:#{@key}]"
138
+ else
139
+ ""
125
140
  end
126
-
127
- "flash#{now}#{key}"
128
141
  end
129
142
  end
130
143
  end
@@ -101,7 +101,7 @@ module Shoulda # :nodoc:
101
101
 
102
102
  def matches?(subject)
103
103
  normalize_blocks
104
- ::ActionMailer::Base.deliveries.all? do |mail|
104
+ ::ActionMailer::Base.deliveries.any? do |mail|
105
105
  mail_matches?(mail)
106
106
  end
107
107
  end
@@ -20,19 +20,19 @@ module Shoulda # :nodoc:
20
20
 
21
21
  def initialize(attribute)
22
22
  @attribute = attribute.to_s
23
+ @options = {}
23
24
  end
24
25
 
25
26
  def as(role)
26
- unless at_least_rails_3_1?
27
- raise "You can specify role only in Rails 3.1 or greater"
27
+ if active_model_less_than_3_1?
28
+ raise "You can specify role only in Rails 3.1 or greater"
28
29
  end
29
- @role = role
30
+ @options[:role] = role
30
31
  self
31
32
  end
32
33
 
33
34
  def matches?(subject)
34
35
  @subject = subject
35
- @role ||= :default
36
36
  if attr_mass_assignable?
37
37
  if whitelisting?
38
38
  @negative_failure_message = "#{@attribute} was made accessible"
@@ -62,6 +62,10 @@ module Shoulda # :nodoc:
62
62
 
63
63
  private
64
64
 
65
+ def role
66
+ @options[:role] || :default
67
+ end
68
+
65
69
  def protected_attributes
66
70
  @protected_attributes ||= (@subject.class.protected_attributes || [])
67
71
  end
@@ -79,10 +83,10 @@ module Shoulda # :nodoc:
79
83
  end
80
84
 
81
85
  def authorizer
82
- if at_least_rails_3_1?
83
- @subject.class.active_authorizer[@role]
84
- else
86
+ if active_model_less_than_3_1?
85
87
  @subject.class.active_authorizer
88
+ else
89
+ @subject.class.active_authorizer[role]
86
90
  end
87
91
  end
88
92
 
@@ -90,8 +94,8 @@ module Shoulda # :nodoc:
90
94
  @subject.class.name
91
95
  end
92
96
 
93
- def at_least_rails_3_1?
94
- ::ActiveModel::VERSION::MAJOR == 3 && ::ActiveModel::VERSION::MINOR >= 1
97
+ def active_model_less_than_3_1?
98
+ ::ActiveModel::VERSION::STRING.to_f < 3.1
95
99
  end
96
100
  end
97
101
  end
@@ -29,6 +29,7 @@ module Shoulda # :nodoc:
29
29
 
30
30
  def initialize(*values)
31
31
  @values_to_match = values
32
+ @options = {}
32
33
  end
33
34
 
34
35
  def for(attribute)
@@ -37,7 +38,7 @@ module Shoulda # :nodoc:
37
38
  end
38
39
 
39
40
  def with_message(message)
40
- @expected_message = message
41
+ @options[:expected_message] = message
41
42
  self
42
43
  end
43
44
 
@@ -69,7 +70,7 @@ module Shoulda # :nodoc:
69
70
  false
70
71
  else
71
72
  if expected_message
72
- errors_match_regexp? || errors_match_string?
73
+ @matched_error = errors_match_regexp? || errors_match_string?
73
74
  else
74
75
  errors_for_attribute.compact.any?
75
76
  end
@@ -87,13 +88,13 @@ module Shoulda # :nodoc:
87
88
 
88
89
  def errors_match_regexp?
89
90
  if Regexp === expected_message
90
- @matched_error = errors_for_attribute.detect { |e| e =~ expected_message }
91
+ errors_for_attribute.detect { |e| e =~ expected_message }
91
92
  end
92
93
  end
93
94
 
94
95
  def errors_match_string?
95
96
  if errors_for_attribute.include?(expected_message)
96
- @matched_error = expected_message
97
+ expected_message
97
98
  end
98
99
  end
99
100
 
@@ -119,11 +120,11 @@ module Shoulda # :nodoc:
119
120
  end
120
121
 
121
122
  def expected_message
122
- if @expected_message
123
- if Symbol === @expected_message
124
- default_error_message(@expected_message, :model_name => model_name, :attribute => @attribute)
123
+ if @options.key?(:expected_message)
124
+ if Symbol === @options[:expected_message]
125
+ default_error_message(@options[:expected_message], :model_name => model_name, :attribute => @attribute)
125
126
  else
126
- @expected_message
127
+ @options[:expected_message]
127
128
  end
128
129
  end
129
130
  end
@@ -38,8 +38,6 @@ module Shoulda # :nodoc:
38
38
  def matches?(subject)
39
39
  super(subject)
40
40
 
41
- @expected_message ||= :exclusion
42
-
43
41
  allows_lower_value &&
44
42
  disallows_minimum_value &&
45
43
  allows_higher_value &&
@@ -49,19 +47,23 @@ module Shoulda # :nodoc:
49
47
  private
50
48
 
51
49
  def allows_lower_value
52
- @minimum == 0 || allows_value_of(@minimum - 1, @expected_message)
50
+ @minimum == 0 || allows_value_of(@minimum - 1, expected_message)
53
51
  end
54
52
 
55
53
  def allows_higher_value
56
- allows_value_of(@maximum + 1, @expected_message)
54
+ allows_value_of(@maximum + 1, expected_message)
57
55
  end
58
56
 
59
57
  def disallows_minimum_value
60
- disallows_value_of(@minimum, @expected_message)
58
+ disallows_value_of(@minimum, expected_message)
61
59
  end
62
60
 
63
61
  def disallows_maximum_value
64
- disallows_value_of(@maximum, @expected_message)
62
+ disallows_value_of(@maximum, expected_message)
63
+ end
64
+
65
+ def expected_message
66
+ @expected_message || :exclusion
65
67
  end
66
68
  end
67
69
 
@@ -5,6 +5,7 @@ module Shoulda # :nodoc:
5
5
  # Ensure that the attribute's value is in the range specified
6
6
  #
7
7
  # Options:
8
+ # * <tt>in_array</tt> - the range of allowed values for this attribute
8
9
  # * <tt>in_range</tt> - the range of allowed values for this attribute
9
10
  # * <tt>with_low_message</tt> - value the test expects to find in
10
11
  # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
@@ -21,6 +22,15 @@ module Shoulda # :nodoc:
21
22
  end
22
23
 
23
24
  class EnsureInclusionOfMatcher < ValidationMatcher # :nodoc:
25
+ def initialize(attribute)
26
+ super(attribute)
27
+ @options = {}
28
+ end
29
+
30
+ def in_array(array)
31
+ @array = array
32
+ self
33
+ end
24
34
 
25
35
  def in_range(range)
26
36
  @range = range
@@ -29,6 +39,16 @@ module Shoulda # :nodoc:
29
39
  self
30
40
  end
31
41
 
42
+ def allow_blank(allow_blank = true)
43
+ @options[:allow_blank] = allow_blank
44
+ self
45
+ end
46
+
47
+ def allow_nil(allow_nil = true)
48
+ @options[:allow_nil] = allow_nil
49
+ self
50
+ end
51
+
32
52
  def with_message(message)
33
53
  if message
34
54
  @low_message = message
@@ -48,23 +68,58 @@ module Shoulda # :nodoc:
48
68
  end
49
69
 
50
70
  def description
51
- "ensure inclusion of #{@attribute} in #{@range.inspect}"
71
+ "ensure inclusion of #{@attribute} in #{inspect_message}"
52
72
  end
53
73
 
54
74
  def matches?(subject)
55
75
  super(subject)
56
76
 
57
- @low_message ||= :inclusion
58
- @high_message ||= :inclusion
59
-
60
- disallows_lower_value &&
61
- allows_minimum_value &&
62
- disallows_higher_value &&
63
- allows_maximum_value
77
+ if @range
78
+ @low_message ||= :inclusion
79
+ @high_message ||= :inclusion
80
+
81
+ disallows_lower_value &&
82
+ allows_minimum_value &&
83
+ disallows_higher_value &&
84
+ allows_maximum_value
85
+ elsif @array
86
+ if allows_all_values_in_array? && allows_blank_value? && allows_nil_value?
87
+ true
88
+ else
89
+ @failure_message = "#{@array} doesn't match array in validation"
90
+ false
91
+ end
92
+ end
64
93
  end
65
94
 
66
95
  private
67
96
 
97
+ def allows_blank_value?
98
+ if @options.key?(:allow_blank)
99
+ @options[:allow_blank] == allows_value_of('')
100
+ else
101
+ true
102
+ end
103
+ end
104
+
105
+ def allows_nil_value?
106
+ if @options.key?(:allow_nil)
107
+ @options[:allow_nil] == allows_value_of(nil)
108
+ else
109
+ true
110
+ end
111
+ end
112
+
113
+ def inspect_message
114
+ @range.nil? ? @array.inspect : @range.inspect
115
+ end
116
+
117
+ def allows_all_values_in_array?
118
+ @array.all? do |value|
119
+ allows_value_of(value, @low_message)
120
+ end
121
+ end
122
+
68
123
  def disallows_lower_value
69
124
  @minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
70
125
  end