rmm5t / shoulda forked from thoughtbot/shoulda

Makes tests easy on the fingers and the eyes

This URL has Read+Write access

shoulda / lib / shoulda / active_record_helpers.rb
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 1 module ThoughtBot # :nodoc:
2 module Shoulda # :nodoc:
3 # = Macro test helpers for your active record models
4 #
5 # These helpers will test most of the validations and associations for your ActiveRecord models.
6 #
7 # class UserTest < Test::Unit::TestCase
8 # should_require_attributes :name, :phone_number
9 # should_not_allow_values_for :phone_number, "abcd", "1234"
10 # should_allow_values_for :phone_number, "(123) 456-7890"
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 11 #
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 12 # should_protect_attributes :password
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 13 #
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 14 # should_have_one :profile
15 # should_have_many :dogs
16 # should_have_many :messes, :through => :dogs
17 # should_belong_to :lover
18 # end
19 #
20 # For all of these helpers, the last parameter may be a hash of options.
21 #
22 module ActiveRecord
23 # Ensures that the model cannot be saved if one of the attributes listed is not present.
24 #
25 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 26 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 27 # Regexp or string. Default = <tt>/blank/</tt>
28 #
29 # Example:
30 # should_require_attributes :name, :phone_number
31 #
32 def should_require_attributes(*attributes)
33 message = get_options!(attributes, :message)
34 message ||= /blank/
35 attributes.each do |attribute|
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 36 should_not_allow_values_for attribute, nil, :message => message, :should => "require #{attribute} to be set"
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 37 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 38 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 39
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 40 # Ensures that the model cannot be saved if one of the attributes listed is not unique.
41 # Requires an existing record
42 #
43 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 44 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 45 # Regexp or string. Default = <tt>/taken/</tt>
46 # * <tt>:scoped_to</tt> - field(s) to scope the uniqueness to.
47 #
48 # Examples:
49 # should_require_unique_attributes :keyword, :username
50 # should_require_unique_attributes :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
51 # should_require_unique_attributes :email, :scoped_to => :name
52 # should_require_unique_attributes :address, :scoped_to => [:first_name, :last_name]
53 #
54 def should_require_unique_attributes(*attributes)
55 message, scope = get_options!(attributes, :message, :scoped_to)
56 scope = [*scope].compact
57 message ||= /taken/
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 58
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 59 klass = model_class
60 attributes.each do |attribute|
61 attribute = attribute.to_sym
66fc5c31 » tsaleh 2008-06-26 fixed naming of should_requ... 62 should "require unique value for #{attribute}#{" scoped to #{scope.join(', ')}" unless scope.blank?}" do
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 63 assert existing = klass.find(:first), "Can't find first #{klass}"
64 object = klass.new
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 65 existing_value = existing.send(attribute)
66
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 67 if !scope.blank?
68 scope.each do |s|
69 assert_respond_to object, :"#{s}=", "#{klass.name} doesn't seem to have a #{s} attribute."
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 70 object.send("#{s}=", existing.send(s))
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 71 end
0a6da8b2 » tsaleh 2007-03-14 Documentation 72 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 73 assert_bad_value(object, attribute, existing_value, message)
74
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 75 # Now test that the object is valid when changing the scoped attribute
76 # TODO: There is a chance that we could change the scoped field
77 # to a value that's already taken. An alternative implementation
78 # could actually find all values for scope and create a unique
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 79 # one.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 80 if !scope.blank?
81 scope.each do |s|
82 # Assume the scope is a foreign key if the field is nil
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 83 object.send("#{s}=", existing.send(s).nil? ? 1 : existing.send(s).next)
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 84 assert_good_value(object, attribute, existing_value, message)
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 85 end
fc938bb1 » tsaleh 2008-06-22 Removed ThoughtBot module. 86 end
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 87 end
88 end
89 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 90
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 91 # Ensures that the attribute cannot be set on mass update.
92 # Requires an existing record.
93 #
94 # should_protect_attributes :password, :admin_flag
95 #
96 def should_protect_attributes(*attributes)
97 get_options!(attributes)
98 klass = model_class
99
100 attributes.each do |attribute|
101 attribute = attribute.to_sym
102 should "protect #{attribute} from mass updates" do
103 protected = klass.protected_attributes || []
104 accessible = klass.accessible_attributes || []
105
106 assert protected.include?(attribute.to_s) || !accessible.include?(attribute.to_s),
107 (accessible.empty? ?
108 "#{klass} is protecting #{protected.to_a.to_sentence}, but not #{attribute}." :
109 "#{klass} has made #{attribute} accessible")
110 end
111 end
112 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 113
5eb93044 » boone 2008-06-24 Added should_have_readonly_... 114 # Ensures that the attribute cannot be changed once the record has been created.
115 # Requires an existing record.
116 #
117 # should_have_readonly_attributes :password, :admin_flag
118 #
119 def should_have_readonly_attributes(*attributes)
120 get_options!(attributes)
121 klass = model_class
122
123 attributes.each do |attribute|
124 attribute = attribute.to_sym
125 should "make #{attribute} read-only" do
126 readonly = klass.readonly_attributes || []
127
128 assert readonly.include?(attribute.to_s),
129 (readonly.empty? ?
130 "#{klass} attribute #{attribute} is not read-only" :
131 "#{klass} is making #{readonly.to_a.to_sentence} read-only, but not #{attribute}.")
132 end
133 end
134 end
135
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 136 # Ensures that the attribute cannot be set to the given values
137 # Requires an existing record
138 #
139 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 140 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 141 # Regexp or string. Default = <tt>/invalid/</tt>
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 142 # * <tt>:should</tt> - an override of the default should test name.
143 # Usually only used by other macros.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 144 #
145 # Example:
146 # should_not_allow_values_for :isbn, "bad 1", "bad 2"
147 #
148 def should_not_allow_values_for(attribute, *bad_values)
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 149 message, name = get_options!(bad_values, :message, :should)
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 150 message ||= /invalid/
151 klass = model_class
152 bad_values.each do |v|
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 153 name ||= "not allow #{attribute} to be set to #{v.inspect}"
154 should name do
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 155 object = klass.new
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 156 assert_bad_value(object, attribute, v, message)
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 157 end
158 end
159 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 160
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 161 # Ensures that the attribute can be set to the given values.
162 # Requires an existing record
163 #
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 164 # Options:
165 # * <tt>:message</tt> - value the test shouldn't find in <tt>errors.on(:attribute)</tt>.
166 # Regexp or string. Default = <tt>//</tt>
167 # * <tt>:should</tt> - an override of the default should test name.
168 # Usually only used by other macros.
169 #
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 170 # Example:
171 # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
172 #
173 def should_allow_values_for(attribute, *good_values)
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 174 message, name = get_options!(good_values, :message, :should)
175 message ||= //
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 176 klass = model_class
177 good_values.each do |v|
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 178 name ||= "allow #{attribute} to be set to #{v.inspect}"
179 should name do
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 180 object = klass.new
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 181 assert_good_value(object, attribute, v, message)
0a6da8b2 » tsaleh 2007-03-14 Documentation 182 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 183 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 184 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 185
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 186 # Ensures that the length of the attribute is in the given range
187 # Requires an existing record
188 #
189 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 190 # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 191 # Regexp or string. Default = <tt>/short/</tt>
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 192 # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 193 # Regexp or string. Default = <tt>/long/</tt>
194 #
195 # Example:
196 # should_ensure_length_in_range :password, (6..20)
197 #
198 def should_ensure_length_in_range(attribute, range, opts = {})
199 short_message, long_message = get_options!([opts], :short_message, :long_message)
200 short_message ||= /short/
201 long_message ||= /long/
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 202
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 203 min_length = range.first
204 max_length = range.last
205 same_length = (min_length == max_length)
206
207 if min_length > 0
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 208 min_value = "x" * (min_length - 1)
209 should_not_allow_values_for attribute, min_value, :message => short_message, :should => "not allow #{attribute} to be less than #{min_length} chars long"
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 210 end
a50e871b » tsaleh 2008-02-28 fixes to should_ensure_leng... 211
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 212 if min_length > 0
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 213 min_value = "x" * min_length
214 should_allow_values_for attribute, min_value, :message => short_message, :should => "allow #{attribute} to be exactly #{min_length} chars long"
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 215 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 216
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 217 max_value = "x" * (max_length + 1)
218 should_not_allow_values_for attribute, max_value, :message => long_message, :should => "not allow #{attribute} to be more than #{max_length} chars long"
a50e871b » tsaleh 2008-02-28 fixes to should_ensure_leng... 219
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 220 unless same_length
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 221 max_value = "x" * max_length
222 should_allow_values_for attribute, max_value, :message => long_message, :should => "allow #{attribute} to be exactly #{max_length} chars long"
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 223 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 224 end
225
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 226 # Ensures that the length of the attribute is at least a certain length
227 # Requires an existing record
228 #
229 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 230 # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 231 # Regexp or string. Default = <tt>/short/</tt>
232 #
233 # Example:
234 # should_ensure_length_at_least :name, 3
235 #
236 def should_ensure_length_at_least(attribute, min_length, opts = {})
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 237 short_message = get_options!([opts], :short_message)
238 short_message ||= /short/
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 239
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 240 if min_length > 0
241 min_value = "x" * (min_length - 1)
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 242 should_not_allow_values_for attribute, min_value, :message => short_message, :should => "not allow #{attribute} to be less than #{min_length} chars long"
ec9ead07 » tsaleh 2008-04-12 applied should_ensure_lengt... 243 end
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 244
245 valid_value = "x" * (min_length)
246 should_allow_values_for attribute, valid_value, :message => short_message, :should => "allow #{attribute} to be at least #{min_length} chars long"
ec9ead07 » tsaleh 2008-04-12 applied should_ensure_lengt... 247 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 248
6511bcf3 » thechrisoshow 2008-06-19 Added should_ensure_length_... 249 # Ensures that the length of the attribute is exactly a certain length
250 # Requires an existing record
251 #
252 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 253 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
6511bcf3 » thechrisoshow 2008-06-19 Added should_ensure_length_... 254 # Regexp or string. Default = <tt>/short/</tt>
255 #
256 # Example:
257 # should_ensure_length_is :ssn, 9
258 #
259 def should_ensure_length_is(attribute, length, opts = {})
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 260 message = get_options!([opts], :message)
261 message ||= /wrong length/
6511bcf3 » thechrisoshow 2008-06-19 Added should_ensure_length_... 262
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 263 min_value = "x" * (length - 1)
264 should_not_allow_values_for attribute, min_value, :message => message, :should => "not allow #{attribute} to be less than #{length} chars long"
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 265
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 266 max_value = "x" * (length + 1)
267 should_not_allow_values_for attribute, max_value, :message => message, :should => "not allow #{attribute} to be greater than #{length} chars long"
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 268
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 269 valid_value = "x" * (length)
270 should_allow_values_for attribute, valid_value, :message => message, :should => "allow #{attribute} to be #{length} chars long"
13f5c2ae » rmm5t 2008-06-27 Removed unnecessary databas... 271 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 272
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 273 # Ensure that the attribute is in the range specified
274 # Requires an existing record
275 #
276 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 277 # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 278 # Regexp or string. Default = <tt>/included/</tt>
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 279 # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 280 # Regexp or string. Default = <tt>/included/</tt>
281 #
282 # Example:
283 # should_ensure_value_in_range :age, (0..100)
284 #
285 def should_ensure_value_in_range(attribute, range, opts = {})
286 low_message, high_message = get_options!([opts], :low_message, :high_message)
287 low_message ||= /included/
288 high_message ||= /included/
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 289
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 290 min = range.first
291 max = range.last
292
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 293 should_not_allow_values_for attribute, (min - 1), :message => low_message, :should => "not allow #{attribute} to be less than #{min}"
294 should_allow_values_for attribute, min, :message => low_message, :should => "allow #{attribute} to be #{min}"
295 should_not_allow_values_for attribute, (max + 1), :message => high_message, :should => "not allow #{attribute} to be more than #{max}"
296 should_allow_values_for attribute, max, :message => high_message, :should => "allow #{attribute} to be #{max}"
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 297 end
298
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 299 # Ensure that the attribute is numeric
300 # Requires an existing record
301 #
302 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 303 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 304 # Regexp or string. Default = <tt>/number/</tt>
305 #
306 # Example:
307 # should_only_allow_numeric_values_for :age
308 #
309 def should_only_allow_numeric_values_for(*attributes)
310 message = get_options!(attributes, :message)
311 message ||= /number/
312 attributes.each do |attribute|
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 313 should_not_allow_values_for attribute, "abcd", :message => message, :should => "only allow numeric values for #{attribute}"
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 314 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 315 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 316
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 317 # Ensures that the has_many relationship exists. Will also test that the
318 # associated table has the required columns. Works with polymorphic
319 # associations.
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 320 #
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 321 # Options:
322 # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 323 # * <tt>:dependent</tt> - tests that the association makes use of the dependent option.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 324 #
325 # Example:
326 # should_have_many :friends
327 # should_have_many :enemies, :through => :friends
328 # should_have_many :enemies, :dependent => :destroy
329 #
330 def should_have_many(*associations)
331 through, dependent = get_options!(associations, :through, :dependent)
332 klass = model_class
333 associations.each do |association|
334 name = "have many #{association}"
335 name += " through #{through}" if through
336 name += " dependent => #{dependent}" if dependent
337 should name do
338 reflection = klass.reflect_on_association(association)
339 assert reflection, "#{klass.name} does not have any relationship to #{association}"
340 assert_equal :has_many, reflection.macro
af11bb11 » tsaleh 2008-06-07 Merge git://github.com/thec... 341
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 342 associated_klass = (reflection.options[:class_name] || association.to_s.classify).constantize
af11bb11 » tsaleh 2008-06-07 Merge git://github.com/thec... 343
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 344 if through
345 through_reflection = klass.reflect_on_association(through)
346 assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
347 assert_equal(through, reflection.options[:through])
348 end
fc938bb1 » tsaleh 2008-06-22 Removed ThoughtBot module. 349
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 350 if dependent
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 351 assert_equal dependent.to_s,
352 reflection.options[:dependent].to_s,
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 353 "#{associated_klass.name} should have #{dependent} dependency"
354 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 355
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 356 # Check for the existence of the foreign key on the other table
357 unless reflection.options[:through]
358 if reflection.options[:foreign_key]
359 fk = reflection.options[:foreign_key]
360 elsif reflection.options[:as]
361 fk = reflection.options[:as].to_s.foreign_key
362 else
363 fk = reflection.primary_key_name
364 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 365
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 366 assert associated_klass.column_names.include?(fk.to_s),
367 "#{associated_klass.name} does not have a #{fk} foreign key."
368 end
369 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 370 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 371 end
fc938bb1 » tsaleh 2008-06-22 Removed ThoughtBot module. 372
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 373 # Ensure that the has_one relationship exists. Will also test that the
374 # associated table has the required columns. Works with polymorphic
375 # associations.
376 #
377 # Example:
378 # should_have_one :god # unless hindu
379 #
380 def should_have_one(*associations)
381 get_options!(associations)
382 klass = model_class
383 associations.each do |association|
384 should "have one #{association}" do
385 reflection = klass.reflect_on_association(association)
386 assert reflection, "#{klass.name} does not have any relationship to #{association}"
387 assert_equal :has_one, reflection.macro
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 388
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 389 associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
c397d016 » tsaleh 2008-02-28 fixed http://tammer.lightho... 390
20c6f910 » tsaleh 2007-11-28 - fixed some overeager helpers 391 if reflection.options[:foreign_key]
392 fk = reflection.options[:foreign_key]
393 elsif reflection.options[:as]
394 fk = reflection.options[:as].to_s.foreign_key
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 395 fk_type = fk.gsub(/_id$/, '_type')
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 396 assert associated_klass.column_names.include?(fk_type),
397 "#{associated_klass.name} does not have a #{fk_type} column."
20c6f910 » tsaleh 2007-11-28 - fixed some overeager helpers 398 else
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 399 fk = klass.name.foreign_key
20c6f910 » tsaleh 2007-11-28 - fixed some overeager helpers 400 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 401 assert associated_klass.column_names.include?(fk.to_s),
402 "#{associated_klass.name} does not have a #{fk} foreign key."
0a6da8b2 » tsaleh 2007-03-14 Documentation 403 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 404 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 405 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 406
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 407 # Ensures that the has_and_belongs_to_many relationship exists, and that the join
408 # table is in place.
409 #
410 # should_have_and_belong_to_many :posts, :cars
411 #
412 def should_have_and_belong_to_many(*associations)
413 get_options!(associations)
414 klass = model_class
415
416 associations.each do |association|
417 should "should have and belong to many #{association}" do
418 reflection = klass.reflect_on_association(association)
419 assert reflection, "#{klass.name} does not have any relationship to #{association}"
420 assert_equal :has_and_belongs_to_many, reflection.macro
421 table = reflection.options[:join_table]
422 assert ::ActiveRecord::Base.connection.tables.include?(table), "table #{table} doesn't exist"
be4c51e7 » tsaleh 2008-02-28 refactored should_protect_a... 423 end
424 end
425 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 426
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 427 # Ensure that the belongs_to relationship exists.
428 #
429 # should_belong_to :parent
430 #
431 def should_belong_to(*associations)
432 get_options!(associations)
433 klass = model_class
434 associations.each do |association|
435 should "belong_to #{association}" do
436 reflection = klass.reflect_on_association(association)
437 assert reflection, "#{klass.name} does not have any relationship to #{association}"
438 assert_equal :belongs_to, reflection.macro
439
440 unless reflection.options[:polymorphic]
441 associated_klass = (reflection.options[:class_name] || association.to_s.classify).constantize
442 fk = reflection.options[:foreign_key] || reflection.primary_key_name
443 assert klass.column_names.include?(fk.to_s), "#{klass.name} does not have a #{fk} foreign key."
444 end
445 end
c07bff35 » tsaleh 2007-10-31 Applied patches from Tim Ca... 446 end
447 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 448
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 449 # Ensure that the given class methods are defined on the model.
450 #
451 # should_have_class_methods :find, :destroy
452 #
453 def should_have_class_methods(*methods)
454 get_options!(methods)
455 klass = model_class
456 methods.each do |method|
457 should "respond to class method ##{method}" do
458 assert_respond_to klass, method, "#{klass.name} does not have class method #{method}"
c07bff35 » tsaleh 2007-10-31 Applied patches from Tim Ca... 459 end
460 end
461 end
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 462
463 # Ensure that the given instance methods are defined on the model.
464 #
465 # should_have_instance_methods :email, :name, :name=
466 #
467 def should_have_instance_methods(*methods)
468 get_options!(methods)
469 klass = model_class
470 methods.each do |method|
471 should "respond to instance method ##{method}" do
472 assert_respond_to klass.new, method, "#{klass.name} does not have instance method #{method}"
473 end
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 474 end
021d7277 » tsaleh 2007-04-06 - refactored into modules 475 end
88ce3dab » tsaleh 2007-11-23 added methods should_have_d... 476
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 477 # Ensure that the given columns are defined on the models backing SQL table.
478 #
479 # should_have_db_columns :id, :email, :name, :created_at
480 #
481 def should_have_db_columns(*columns)
482 column_type = get_options!(columns, :type)
483 klass = model_class
484 columns.each do |name|
485 test_name = "have column #{name}"
486 test_name += " of type #{column_type}" if column_type
487 should test_name do
488 column = klass.columns.detect {|c| c.name == name.to_s }
489 assert column, "#{klass.name} does not have column #{name}"
490 end
88ce3dab » tsaleh 2007-11-23 added methods should_have_d... 491 end
492 end
493
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 494 # Ensure that the given column is defined on the models backing SQL table. The options are the same as
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 495 # the instance variables defined on the column definition: :precision, :limit, :default, :null,
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 496 # :primary, :type, :scale, and :sql_type.
497 #
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 498 # should_have_db_column :email, :type => "string", :default => nil, :precision => nil, :limit => 255,
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 499 # :null => true, :primary => false, :scale => nil, :sql_type => 'varchar(255)'
500 #
501 def should_have_db_column(name, opts = {})
502 klass = model_class
503 test_name = "have column named :#{name}"
504 test_name += " with options " + opts.inspect unless opts.empty?
88ce3dab » tsaleh 2007-11-23 added methods should_have_d... 505 should test_name do
506 column = klass.columns.detect {|c| c.name == name.to_s }
507 assert column, "#{klass.name} does not have column #{name}"
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 508 opts.each do |k, v|
509 assert_equal column.instance_variable_get("@#{k}").to_s, v.to_s, ":#{name} column on table for #{klass} does not match option :#{k}"
510 end
88ce3dab » tsaleh 2007-11-23 added methods should_have_d... 511 end
512 end
102fe37a » tsaleh 2008-04-26 added should_have_indices t... 513
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 514 # Ensures that there are DB indices on the given columns or tuples of columns.
515 # Also aliased to should_have_index for readability
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 516 #
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 517 # should_have_indices :email, :name, [:commentable_type, :commentable_id]
518 # should_have_index :age
519 #
520 def should_have_indices(*columns)
521 table = model_class.name.tableize
522 indices = ::ActiveRecord::Base.connection.indexes(table).map(&:columns)
523
524 columns.each do |column|
525 should "have index on #{table} for #{column.inspect}" do
526 columns = [column].flatten.map(&:to_s)
527 assert_contains(indices, columns)
528 end
102fe37a » tsaleh 2008-04-26 added should_have_indices t... 529 end
530 end
531
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 532 alias_method :should_have_index, :should_have_indices
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 533
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 534 # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
535 #
536 # Options:
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 537 # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 538 # Regexp or string. Default = <tt>/must be accepted/</tt>
539 #
540 # Example:
541 # should_require_acceptance_of :eula
542 #
543 def should_require_acceptance_of(*attributes)
544 message = get_options!(attributes, :message)
545 message ||= /must be accepted/
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 546
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 547 attributes.each do |attribute|
063426f7 » rmm5t 2008-07-28 Further DRY'd Active Record... 548 should_not_allow_values_for attribute, false, :message => message, :should => "require #{attribute} to be accepted"
4f4e52f6 » tsaleh 2008-01-21 - added should_require_acce... 549 end
550 end
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 551
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 552 private
4c5768d1 » rmm5t 2008-07-28 DRY'd up Active Record macr... 553
b415bff8 » tsaleh 2008-06-23 Revert bunch of bad commits: 554 include ThoughtBot::Shoulda::Private
7a4202f9 » tsaleh 2007-03-14 Moved everthing to trunk 555 end
556 end
91a3bb5e » tsaleh 2008-01-20 fixed should_require_unique... 557 end