shoulda-matchers 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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