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
@@ -56,31 +56,32 @@ module Shoulda # :nodoc:
56
56
  class AssociationMatcher # :nodoc:
57
57
  def initialize(macro, name)
58
58
  @macro = macro
59
- @name = name
59
+ @name = name
60
+ @options = {}
60
61
  end
61
62
 
62
63
  def through(through)
63
- @through = through
64
+ @options[:through] = through
64
65
  self
65
66
  end
66
67
 
67
68
  def dependent(dependent)
68
- @dependent = dependent
69
+ @options[:dependent] = dependent
69
70
  self
70
71
  end
71
72
 
72
73
  def order(order)
73
- @order = order
74
+ @options[:order] = order
74
75
  self
75
76
  end
76
77
 
77
78
  def conditions(conditions)
78
- @conditions = conditions
79
+ @options[:conditions] = conditions
79
80
  self
80
81
  end
81
82
 
82
83
  def class_name(class_name)
83
- @class_name = class_name
84
+ @options[:class_name] = class_name
84
85
  self
85
86
  end
86
87
 
@@ -107,10 +108,10 @@ module Shoulda # :nodoc:
107
108
 
108
109
  def description
109
110
  description = "#{macro_description} #{@name}"
110
- description += " through #{@through}" if @through
111
- description += " dependent => #{@dependent}" if @dependent
112
- description += " class_name => #{@class_name}" if @class_name
113
- description += " order => #{@order}" if @order
111
+ description += " through #{@options[:through]}" if @options.key?(:through)
112
+ description += " dependent => #{@options[:dependent]}" if @options.key?(:dependent)
113
+ description += " class_name => #{@options[:class_name]}" if @options.key?(:class_name)
114
+ description += " order => #{@options[:order]}" if @options.key?(:order)
114
115
  description
115
116
  end
116
117
 
@@ -149,12 +150,12 @@ module Shoulda # :nodoc:
149
150
  end
150
151
 
151
152
  def through_association_valid?
152
- @through.nil? || (through_association_exists? && through_association_correct?)
153
+ @options[:through].nil? || (through_association_exists? && through_association_correct?)
153
154
  end
154
155
 
155
156
  def through_association_exists?
156
157
  if through_reflection.nil?
157
- @missing = "#{model_class.name} does not have any relationship to #{@through}"
158
+ @missing = "#{model_class.name} does not have any relationship to #{@options[:through]}"
158
159
  false
159
160
  else
160
161
  true
@@ -162,48 +163,60 @@ module Shoulda # :nodoc:
162
163
  end
163
164
 
164
165
  def through_association_correct?
165
- if @through == reflection.options[:through]
166
+ if @options[:through] == reflection.options[:through]
166
167
  true
167
168
  else
168
- @missing = "Expected #{model_class.name} to have #{@name} through #{@through}, " +
169
+ @missing = "Expected #{model_class.name} to have #{@name} through #{@options[:through]}, " +
169
170
  "but got it through #{reflection.options[:through]}"
170
171
  false
171
172
  end
172
173
  end
173
174
 
174
175
  def dependent_correct?
175
- if @dependent.nil? || @dependent.to_s == reflection.options[:dependent].to_s
176
+ if @options[:dependent].nil? || @options[:dependent].to_s == reflection.options[:dependent].to_s
176
177
  true
177
178
  else
178
- @missing = "#{@name} should have #{@dependent} dependency"
179
+ @missing = "#{@name} should have #{@options[:dependent]} dependency"
179
180
  false
180
181
  end
181
182
  end
182
183
 
183
184
  def class_name_correct?
184
- if @class_name.nil? || @class_name.to_s == reflection.options[:class_name].to_s
185
- true
185
+ if @options.key?(:class_name)
186
+ if @options[:class_name].to_s == reflection.options[:class_name].to_s
187
+ true
188
+ else
189
+ @missing = "#{@name} should have #{@options[:class_name]} as class_name"
190
+ false
191
+ end
186
192
  else
187
- @missing = "#{@name} should have #{@class_name} as class_name"
188
- false
193
+ true
189
194
  end
190
195
  end
191
196
 
192
197
  def order_correct?
193
- if @order.nil? || @order.to_s == reflection.options[:order].to_s
194
- true
198
+ if @options.key?(:order)
199
+ if @options[:order].to_s == reflection.options[:order].to_s
200
+ true
201
+ else
202
+ @missing = "#{@name} should be ordered by #{@options[:order]}"
203
+ false
204
+ end
195
205
  else
196
- @missing = "#{@name} should be ordered by #{@order}"
197
- false
206
+ true
198
207
  end
199
208
  end
200
209
 
201
210
  def conditions_correct?
202
- if @conditions.nil? || @conditions.to_s == reflection.options[:conditions].to_s
203
- true
211
+ if @options.key?(:conditions)
212
+ if @options[:conditions].to_s == reflection.options[:conditions].to_s
213
+ true
214
+ else
215
+ @missing = "#{@name} should have the following conditions: #{@options[:conditions]}"
216
+ false
217
+ end
204
218
  else
205
- @missing = "#{@name} should have the following conditions: #{@conditions}"
206
- false
219
+ true
207
220
  end
208
221
  end
209
222
 
@@ -265,7 +278,7 @@ module Shoulda # :nodoc:
265
278
  end
266
279
 
267
280
  def through_reflection
268
- @through_reflection ||= model_class.reflect_on_association(@through)
281
+ @through_reflection ||= model_class.reflect_on_association(@options[:through])
269
282
  end
270
283
 
271
284
  def expectation
@@ -274,10 +287,13 @@ module Shoulda # :nodoc:
274
287
 
275
288
  def macro_description
276
289
  case @macro.to_s
277
- when 'belongs_to' then 'belong to'
278
- when 'has_many' then 'have many'
279
- when 'has_one' then 'have one'
280
- when 'has_and_belongs_to_many' then
290
+ when 'belongs_to'
291
+ 'belong to'
292
+ when 'has_many'
293
+ 'have many'
294
+ when 'has_one'
295
+ 'have one'
296
+ when 'has_and_belongs_to_many'
281
297
  'have and belong to many'
282
298
  end
283
299
  end
@@ -22,20 +22,20 @@ module Shoulda # :nodoc:
22
22
  class HaveDbColumnMatcher # :nodoc:
23
23
  def initialize(column)
24
24
  @column = column
25
+ @options = {}
25
26
  end
26
27
 
27
28
  def of_type(column_type)
28
- @column_type = column_type
29
+ @options[:column_type] = column_type
29
30
  self
30
31
  end
31
32
 
32
33
  def with_options(opts = {})
33
- @precision = opts[:precision]
34
- @limit = opts[:limit]
35
- @default = opts[:default]
36
- @null = opts[:null]
37
- @scale = opts[:scale]
38
- @primary = opts[:primary]
34
+ %w(precision limit default null scale primary).each do |attribute|
35
+ if opts.key?(attribute.to_sym)
36
+ @options[attribute.to_sym] = opts[attribute.to_sym]
37
+ end
38
+ end
39
39
  self
40
40
  end
41
41
 
@@ -61,13 +61,13 @@ module Shoulda # :nodoc:
61
61
 
62
62
  def description
63
63
  desc = "have db column named #{@column}"
64
- desc << " of type #{@column_type}" unless @column_type.nil?
65
- desc << " of precision #{@precision}" unless @precision.nil?
66
- desc << " of limit #{@limit}" unless @limit.nil?
67
- desc << " of default #{@default}" unless @default.nil?
68
- desc << " of null #{@null}" unless @null.nil?
69
- desc << " of primary #{@primary}" unless @primary.nil?
70
- desc << " of scale #{@scale}" unless @scale.nil?
64
+ desc << " of type #{@options[:column_type]}" if @options.key?(:column_type)
65
+ desc << " of precision #{@options[:precision]}" if @options.key?(:precision)
66
+ desc << " of limit #{@options[:limit]}" if @options.key?(:limit)
67
+ desc << " of default #{@options[:default]}" if @options.key?(:default)
68
+ desc << " of null #{@options[:null]}" if @options.key?(:null)
69
+ desc << " of primary #{@options[:primary]}" if @options.key?(:primary)
70
+ desc << " of scale #{@options[:scale]}" if @options.key?(:scale)
71
71
  desc
72
72
  end
73
73
 
@@ -83,82 +83,89 @@ module Shoulda # :nodoc:
83
83
  end
84
84
 
85
85
  def correct_column_type?
86
- return true if @column_type.nil?
87
- if matched_column.type.to_s == @column_type.to_s
86
+ return true unless @options.key?(:column_type)
87
+
88
+ if matched_column.type.to_s == @options[:column_type].to_s
88
89
  true
89
90
  else
90
91
  @missing = "#{model_class} has a db column named #{@column} " <<
91
- "of type #{matched_column.type}, not #{@column_type}."
92
+ "of type #{matched_column.type}, not #{@options[:column_type]}."
92
93
  false
93
94
  end
94
95
  end
95
96
 
96
97
  def correct_precision?
97
- return true if @precision.nil?
98
- if matched_column.precision.to_s == @precision.to_s
98
+ return true unless @options.key?(:precision)
99
+
100
+ if matched_column.precision.to_s == @options[:precision].to_s
99
101
  true
100
102
  else
101
103
  @missing = "#{model_class} has a db column named #{@column} " <<
102
104
  "of precision #{matched_column.precision}, " <<
103
- "not #{@precision}."
105
+ "not #{@options[:precision]}."
104
106
  false
105
107
  end
106
108
  end
107
109
 
108
110
  def correct_limit?
109
- return true if @limit.nil?
110
- if matched_column.limit.to_s == @limit.to_s
111
+ return true unless @options.key?(:limit)
112
+
113
+ if matched_column.limit.to_s == @options[:limit].to_s
111
114
  true
112
115
  else
113
116
  @missing = "#{model_class} has a db column named #{@column} " <<
114
117
  "of limit #{matched_column.limit}, " <<
115
- "not #{@limit}."
118
+ "not #{@options[:limit]}."
116
119
  false
117
120
  end
118
121
  end
119
122
 
120
123
  def correct_default?
121
- return true if @default.nil?
122
- if matched_column.default.to_s == @default.to_s
124
+ return true unless @options.key?(:default)
125
+
126
+ if matched_column.default.to_s == @options[:default].to_s
123
127
  true
124
128
  else
125
129
  @missing = "#{model_class} has a db column named #{@column} " <<
126
130
  "of default #{matched_column.default}, " <<
127
- "not #{@default}."
131
+ "not #{@options[:default]}."
128
132
  false
129
133
  end
130
134
  end
131
135
 
132
136
  def correct_null?
133
- return true if @null.nil?
134
- if matched_column.null.to_s == @null.to_s
137
+ return true unless @options.key?(:null)
138
+
139
+ if matched_column.null.to_s == @options[:null].to_s
135
140
  true
136
141
  else
137
142
  @missing = "#{model_class} has a db column named #{@column} " <<
138
143
  "of null #{matched_column.null}, " <<
139
- "not #{@null}."
144
+ "not #{@options[:null]}."
140
145
  false
141
146
  end
142
147
  end
143
148
 
144
149
  def correct_scale?
145
- return true if @scale.nil?
146
- if matched_column.scale.to_s == @scale.to_s
150
+ return true unless @options.key?(:scale)
151
+
152
+ if matched_column.scale.to_s == @options[:scale].to_s
147
153
  true
148
154
  else
149
155
  @missing = "#{model_class} has a db column named #{@column} " <<
150
- "of scale #{matched_column.scale}, not #{@scale}."
156
+ "of scale #{matched_column.scale}, not #{@options[:scale]}."
151
157
  false
152
158
  end
153
159
  end
154
160
 
155
161
  def correct_primary?
156
- return true if @primary.nil?
157
- if matched_column.primary == @primary
162
+ return true unless @options.key?(:primary)
163
+
164
+ if matched_column.primary == @options[:primary]
158
165
  true
159
166
  else
160
167
  @missing = "#{model_class} has a db column named #{@column} "
161
- if @primary
168
+ if @options[:primary]
162
169
  @missing << "that is not primary, but should be"
163
170
  else
164
171
  @missing << "that is primary, but should not be"
@@ -9,8 +9,7 @@ module Shoulda # :nodoc:
9
9
  # * <tt>unique</tt> - whether or not the index has a unique
10
10
  # constraint. Use <tt>true</tt> to explicitly test for a unique
11
11
  # constraint. Use <tt>false</tt> to explicitly test for a non-unique
12
- # constraint. Use <tt>nil</tt> if you don't care whether the index is
13
- # unique or not. Default = <tt>nil</tt>
12
+ # constraint.
14
13
  #
15
14
  # Examples:
16
15
  #
@@ -25,10 +24,11 @@ module Shoulda # :nodoc:
25
24
  class HaveDbIndexMatcher # :nodoc:
26
25
  def initialize(columns)
27
26
  @columns = normalize_columns_to_array(columns)
27
+ @options = {}
28
28
  end
29
29
 
30
30
  def unique(unique)
31
- @unique = unique
31
+ @options[:unique] = unique
32
32
  self
33
33
  end
34
34
 
@@ -46,7 +46,11 @@ module Shoulda # :nodoc:
46
46
  end
47
47
 
48
48
  def description
49
- "have a #{index_type} index on columns #{@columns.join(' and ')}"
49
+ if @options.key?(:unique)
50
+ "have a #{index_type} index on columns #{@columns.join(' and ')}"
51
+ else
52
+ "have an index on columns #{@columns.join(' and ')}"
53
+ end
50
54
  end
51
55
 
52
56
  protected
@@ -56,12 +60,13 @@ module Shoulda # :nodoc:
56
60
  end
57
61
 
58
62
  def correct_unique?
59
- return true if @unique.nil?
60
- if !!matched_index.unique == @unique
63
+ return true unless @options.key?(:unique)
64
+
65
+ if matched_index.unique == @options[:unique]
61
66
  true
62
67
  else
63
68
  @missing = "#{table_name} has an index named #{matched_index.name} " <<
64
- "of unique #{!!matched_index.unique}, not #{@unique}."
69
+ "of unique #{matched_index.unique}, not #{@options[:unique]}."
65
70
  false
66
71
  end
67
72
  end
@@ -87,13 +92,10 @@ module Shoulda # :nodoc:
87
92
  end
88
93
 
89
94
  def index_type
90
- case @unique
91
- when nil
92
- ''
93
- when false
94
- 'non-unique'
95
- else
95
+ if @options[:unique]
96
96
  'unique'
97
+ else
98
+ 'non-unique'
97
99
  end
98
100
  end
99
101
 
@@ -20,65 +20,66 @@ module Shoulda # :nodoc:
20
20
 
21
21
  class QueryTheDatabaseMatcher # :nodoc:
22
22
  def initialize(times)
23
+ @queries = []
24
+ @options = {}
25
+
23
26
  if times.respond_to?(:count)
24
- @expected_query_count = times.count
27
+ @options[:expected_query_count] = times.count
25
28
  else
26
- @expected_query_count = times
29
+ @options[:expected_query_count] = times
27
30
  end
28
31
  end
29
32
 
30
33
  def when_calling(method_name)
31
- @method_name = method_name
34
+ @options[:method_name] = method_name
32
35
  self
33
36
  end
34
37
 
35
38
  def with(*method_arguments)
36
- @method_arguments = method_arguments
39
+ @options[:method_arguments] = method_arguments
37
40
  self
38
41
  end
39
42
 
40
43
  def or_less
41
- @expected_count_is_maximum = true
44
+ @options[:expected_count_is_maximum] = true
42
45
  self
43
46
  end
44
47
 
45
48
  def matches?(subject)
46
- @queries = []
47
-
48
49
  subscriber = ActiveSupport::Notifications.subscribe('sql.active_record') do |name, started, finished, id, payload|
49
50
  @queries << payload unless filter_query(payload)
50
51
  end
51
52
 
52
- if @method_arguments
53
- subject.send(@method_name, *@method_arguments)
53
+ if @options[:method_arguments]
54
+ subject.send(@options[:method_name], *@options[:method_arguments])
54
55
  else
55
- subject.send(@method_name)
56
+ subject.send(@options[:method_name])
56
57
  end
57
58
 
58
59
  ActiveSupport::Notifications.unsubscribe(subscriber)
59
60
 
60
- if @expected_count_is_maximum
61
- @queries.length <= @expected_query_count
62
- elsif @expected_query_count.present?
63
- @queries.length == @expected_query_count
61
+ if @options[:expected_count_is_maximum]
62
+ @queries.length <= @options[:expected_query_count]
63
+ elsif @options[:expected_query_count].present?
64
+ @queries.length == @options[:expected_query_count]
64
65
  else
65
66
  @queries.length > 0
66
67
  end
67
68
  end
68
69
 
69
70
  def failure_message
70
- if @expected_query_count
71
- "Expected ##{@method_name} to cause #{@expected_query_count} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
71
+ if @options.key?(:expected_query_count)
72
+ "Expected ##{@options[:method_name]} to cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
72
73
  else
73
- "Expected ##{@method_name} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries
74
+ "Expected ##{@options[:method_name]} to query the database but it actually caused #{@queries.length} queries:" + friendly_queries
74
75
  end
75
76
  end
76
77
 
77
78
  def negative_failure_message
78
- if @expected_query_count
79
- "Expected ##{@method_name} to not cause #{@expected_query_count} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
79
+ if @options[:expected_query_count]
80
+ "Expected ##{@options[:method_name]} to not cause #{@options[:expected_query_count]} database queries but it actually caused #{@queries.length} queries:" + friendly_queries
80
81
  else
81
- "Expected ##{@method_name} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries
82
+ "Expected ##{@options[:method_name]} to not query the database but it actually caused #{@queries.length} queries:" + friendly_queries
82
83
  end
83
84
  end
84
85
 
@@ -91,14 +92,14 @@ module Shoulda # :nodoc:
91
92
  end
92
93
 
93
94
  def filter_query(query)
94
- query[:name] == 'SCHEMA' || looks_like_schema(query[:sql])
95
+ query[:name] == 'SCHEMA' || looks_like_schema?(query[:sql])
95
96
  end
96
97
 
97
98
  def schema_terms
98
- ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM']
99
+ ['FROM sqlite_master', 'PRAGMA', 'SHOW TABLES', 'SHOW KEYS FROM', 'SHOW FIELDS FROM', 'begin transaction', 'commit transaction']
99
100
  end
100
101
 
101
- def looks_like_schema(sql)
102
+ def looks_like_schema?(sql)
102
103
  schema_terms.any? { |term| sql.include?(term) }
103
104
  end
104
105
  end