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
@@ -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