resource_controller 0.5.3 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/README.rdoc +18 -14
  2. data/VERSION.yml +5 -0
  3. data/generators/scaffold_resource/templates/rspec/routing_spec.rb +8 -8
  4. data/generators/scaffold_resource/templates/rspec/views/index_spec.rb +2 -2
  5. data/lib/resource_controller.rb +5 -14
  6. data/lib/resource_controller/base.rb +2 -2
  7. data/lib/resource_controller/class_methods.rb +2 -2
  8. data/lib/resource_controller/controller.rb +3 -2
  9. data/lib/resource_controller/helpers.rb +0 -6
  10. data/lib/resource_controller/helpers/nested.rb +1 -1
  11. data/lib/resource_controller/singleton.rb +2 -2
  12. data/test/app/controllers/{application.rb → application_controller.rb} +0 -2
  13. data/test/app/controllers/cms/personnel_controller.rb +2 -0
  14. data/test/app/controllers/cms/photos_controller.rb +6 -0
  15. data/test/app/controllers/photos_controller.rb +1 -0
  16. data/test/app/models/personnel.rb +3 -0
  17. data/test/app/models/photo.rb +1 -0
  18. data/test/app/views/cms/photos/edit.rhtml +17 -0
  19. data/test/app/views/cms/photos/index.rhtml +20 -0
  20. data/test/app/views/cms/photos/new.rhtml +16 -0
  21. data/test/app/views/cms/photos/show.rhtml +8 -0
  22. data/test/config/boot.rb +6 -5
  23. data/test/config/database.yml +6 -10
  24. data/test/config/environment.rb +4 -4
  25. data/test/config/environments/development.rb +0 -4
  26. data/test/config/initializers/inflections.rb +14 -0
  27. data/test/config/routes.rb +3 -0
  28. data/test/db/migrate/013_create_personnel.rb +11 -0
  29. data/test/db/migrate/014_add_personnel_id_to_photos.rb +9 -0
  30. data/test/db/schema.rb +9 -3
  31. data/test/test/fixtures/personnel.yml +5 -0
  32. data/test/test/functional/cms/options_controller_test.rb +1 -1
  33. data/test/test/functional/cms/photos_controller_test.rb +43 -0
  34. data/test/test/functional/cms/products_controller_test.rb +1 -1
  35. data/test/test/functional/comments_controller_test.rb +2 -9
  36. data/test/test/functional/images_controller_test.rb +1 -8
  37. data/test/test/functional/people_controller_test.rb +2 -9
  38. data/test/test/functional/photos_controller_test.rb +2 -9
  39. data/test/test/functional/posts_controller_test.rb +2 -9
  40. data/test/test/functional/projects_controller_test.rb +12 -9
  41. data/test/test/functional/somethings_controller_test.rb +2 -9
  42. data/test/test/functional/tags_controller_test.rb +2 -9
  43. data/test/test/functional/users_controller_test.rb +2 -9
  44. data/test/test/test_helper.rb +4 -3
  45. data/test/test/unit/accessors_test.rb +2 -2
  46. data/test/test/unit/account_test.rb +1 -1
  47. data/test/test/unit/action_options_test.rb +1 -1
  48. data/test/test/unit/base_test.rb +1 -1
  49. data/test/test/unit/comment_test.rb +1 -1
  50. data/test/test/unit/failable_action_options_test.rb +1 -1
  51. data/test/test/unit/helpers_test.rb +1 -1
  52. data/test/test/unit/image_test.rb +1 -1
  53. data/test/test/unit/option_test.rb +1 -1
  54. data/test/test/unit/photo_test.rb +1 -1
  55. data/test/test/unit/post_test.rb +1 -1
  56. data/test/test/unit/project_test.rb +1 -1
  57. data/test/test/unit/response_collector_test.rb +1 -1
  58. data/test/test/unit/something_test.rb +1 -1
  59. data/test/test/unit/tag_test.rb +1 -1
  60. data/test/test/unit/urligence_test.rb +1 -1
  61. metadata +162 -122
  62. data/Rakefile +0 -35
  63. data/init.rb +0 -1
  64. data/lib/resource_controller/version.rb +0 -9
  65. data/test/log/development.log +0 -3350
  66. data/test/log/test.log +0 -174947
  67. data/test/log/thin.log +0 -12
  68. data/test/vendor/plugins/shoulda/Rakefile +0 -32
  69. data/test/vendor/plugins/shoulda/bin/convert_to_should_syntax +0 -40
  70. data/test/vendor/plugins/shoulda/init.rb +0 -3
  71. data/test/vendor/plugins/shoulda/lib/shoulda.rb +0 -43
  72. data/test/vendor/plugins/shoulda/lib/shoulda/active_record_helpers.rb +0 -580
  73. data/test/vendor/plugins/shoulda/lib/shoulda/color.rb +0 -77
  74. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/controller_tests.rb +0 -467
  75. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/html.rb +0 -201
  76. data/test/vendor/plugins/shoulda/lib/shoulda/controller_tests/formats/xml.rb +0 -170
  77. data/test/vendor/plugins/shoulda/lib/shoulda/gem/proc_extensions.rb +0 -14
  78. data/test/vendor/plugins/shoulda/lib/shoulda/gem/shoulda.rb +0 -239
  79. data/test/vendor/plugins/shoulda/lib/shoulda/general.rb +0 -118
  80. data/test/vendor/plugins/shoulda/lib/shoulda/private_helpers.rb +0 -22
@@ -1,12 +0,0 @@
1
- >> Writing PID to tmp/pids/thin.pid
2
- >> Using rails adapter
3
-
4
- *******************************************************************
5
- * config.breakpoint_server has been deprecated and has no effect. *
6
- *******************************************************************
7
-
8
- >> Thin web server (v0.8.1 codename Rebel Porpoise)
9
- >> Threaded mode OFF
10
- >> Maximum connections set to 1024
11
- >> Listening on 0.0.0.0:3000, CTRL+C to stop
12
- >> Exiting!
@@ -1,32 +0,0 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
-
5
- # Test::Unit::UI::VERBOSE
6
-
7
- Rake::TestTask.new do |t|
8
- t.libs << 'lib'
9
- t.pattern = 'test/{unit,functional,other}/**/*_test.rb'
10
- t.verbose = false
11
- end
12
-
13
- Rake::RDocTask.new { |rdoc|
14
- rdoc.rdoc_dir = 'doc'
15
- rdoc.title = "Shoulda -- Making tests easy on the fingers and eyes"
16
- rdoc.options << '--line-numbers' << '--inline-source'
17
- rdoc.template = "#{ENV['template']}.rb" if ENV['template']
18
- rdoc.rdoc_files.include('README', 'lib/**/*.rb')
19
- }
20
-
21
- desc 'Update documentation on website'
22
- task :sync_docs => 'rdoc' do
23
- `rsync -ave ssh doc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/shoulda`
24
- end
25
-
26
- desc 'Default: run tests.'
27
- task :default => ['test']
28
-
29
- Dir['tasks/*.rake'].each do |f|
30
- load f
31
- end
32
-
@@ -1,40 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'fileutils'
3
-
4
- def usage(msg = nil)
5
- puts "Error: #{msg}" if msg
6
- puts if msg
7
- puts "Usage: #{File.basename(__FILE__)} normal_test_file.rb"
8
- puts
9
- puts "Will convert an existing test file with names like "
10
- puts
11
- puts " def test_should_do_stuff"
12
- puts " ..."
13
- puts " end"
14
- puts
15
- puts "to one using the new syntax: "
16
- puts
17
- puts " should \"be super cool\" do"
18
- puts " ..."
19
- puts " end"
20
- puts
21
- puts "A copy of the old file will be left under /tmp/ in case this script just seriously screws up"
22
- puts
23
- exit (msg ? 2 : 0)
24
- end
25
-
26
- usage("Wrong number of arguments.") unless ARGV.size == 1
27
- usage("This system doesn't have a /tmp directory. wtf?") unless File.directory?('/tmp')
28
-
29
- file = ARGV.shift
30
- tmpfile = "/tmp/#{File.basename(file)}"
31
- usage("File '#{file}' doesn't exist") unless File.exists?(file)
32
-
33
- FileUtils.cp(file, tmpfile)
34
- contents = File.read(tmpfile)
35
- contents.gsub!(/def test_should_(.*)\s*$/, 'should "\1" do')
36
- contents.gsub!(/def test_(.*)\s*$/, 'should "RENAME ME: test \1" do')
37
- contents.gsub!(/should ".*" do$/) {|line| line.tr!('_', ' ')}
38
- File.open(file, 'w') { |f| f.write(contents) }
39
-
40
- puts "File '#{file}' has been converted to 'should' syntax. Old version has been stored in '#{tmpfile}'"
@@ -1,3 +0,0 @@
1
- require 'rubygems'
2
- require 'active_support'
3
- require 'shoulda'
@@ -1,43 +0,0 @@
1
- require 'shoulda/gem/shoulda'
2
- require 'shoulda/private_helpers'
3
- require 'shoulda/general'
4
- require 'shoulda/active_record_helpers'
5
- require 'shoulda/controller_tests/controller_tests.rb'
6
- require 'yaml'
7
-
8
- shoulda_options = {}
9
-
10
- possible_config_paths = []
11
- possible_config_paths << File.join(ENV["HOME"], ".shoulda.conf") if ENV["HOME"]
12
- possible_config_paths << "shoulda.conf"
13
- possible_config_paths << File.join("test", "shoulda.conf")
14
- possible_config_paths << File.join(RAILS_ROOT, "test", "shoulda.conf") if defined?(RAILS_ROOT)
15
-
16
- possible_config_paths.each do |config_file|
17
- if File.exists? config_file
18
- shoulda_options = YAML.load_file(config_file).symbolize_keys
19
- break
20
- end
21
- end
22
-
23
- require 'shoulda/color' if shoulda_options[:color]
24
-
25
- module Test # :nodoc: all
26
- module Unit
27
- class TestCase
28
-
29
- include ThoughtBot::Shoulda::General
30
- include ThoughtBot::Shoulda::Controller
31
-
32
- extend ThoughtBot::Shoulda::ActiveRecord
33
- end
34
- end
35
- end
36
-
37
- module ActionController #:nodoc: all
38
- module Integration
39
- class Session
40
- include ThoughtBot::Shoulda::General
41
- end
42
- end
43
- end
@@ -1,580 +0,0 @@
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"
11
- #
12
- # should_protect_attributes :password
13
- #
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:
26
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
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
- klass = model_class
36
-
37
- attributes.each do |attribute|
38
- should "require #{attribute} to be set" do
39
- object = klass.new
40
- object.send("#{attribute}=", nil)
41
- assert !object.valid?, "#{klass.name} does not require #{attribute}."
42
- assert object.errors.on(attribute), "#{klass.name} does not require #{attribute}."
43
- assert_contains(object.errors.on(attribute), message)
44
- end
45
- end
46
- end
47
-
48
- # Ensures that the model cannot be saved if one of the attributes listed is not unique.
49
- # Requires an existing record
50
- #
51
- # Options:
52
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
53
- # Regexp or string. Default = <tt>/taken/</tt>
54
- #
55
- # Example:
56
- # should_require_unique_attributes :keyword, :username
57
- #
58
- def should_require_unique_attributes(*attributes)
59
- message, scope = get_options!(attributes, :message, :scoped_to)
60
- message ||= /taken/
61
-
62
- klass = model_class
63
- attributes.each do |attribute|
64
- attribute = attribute.to_sym
65
- should "require unique value for #{attribute}#{" scoped to #{scope}" if scope}" do
66
- assert existing = klass.find(:first), "Can't find first #{klass}"
67
- object = klass.new
68
-
69
- object.send(:"#{attribute}=", existing.send(attribute))
70
- if scope
71
- assert_respond_to object, :"#{scope}=", "#{klass.name} doesn't seem to have a #{scope} attribute."
72
- object.send(:"#{scope}=", existing.send(scope))
73
- end
74
-
75
- assert !object.valid?, "#{klass.name} does not require a unique value for #{attribute}."
76
- assert object.errors.on(attribute), "#{klass.name} does not require a unique value for #{attribute}."
77
-
78
- assert_contains(object.errors.on(attribute), message)
79
-
80
- # Now test that the object is valid when changing the scoped attribute
81
- # TODO: There is a chance that we could change the scoped field
82
- # to a value that's already taken. An alternative implementation
83
- # could actually find all values for scope and create a unique
84
- # one.
85
- if scope
86
- # Assume the scope is a foreign key if the field is nil
87
- object.send(:"#{scope}=", existing.send(scope).nil? ? 1 : existing.send(scope).next)
88
- object.errors.clear
89
- object.valid?
90
- assert_does_not_contain(object.errors.on(attribute), message,
91
- "after :#{scope} set to #{object.send(scope.to_sym)}")
92
- end
93
- end
94
- end
95
- end
96
-
97
- # Ensures that the attribute cannot be set on mass update.
98
- # Requires an existing record.
99
- #
100
- # should_protect_attributes :password, :admin_flag
101
- #
102
- def should_protect_attributes(*attributes)
103
- get_options!(attributes)
104
- klass = model_class
105
-
106
- attributes.each do |attribute|
107
- attribute = attribute.to_sym
108
- should "protect #{attribute} from mass updates" do
109
- protected = klass.protected_attributes || []
110
- accessible = klass.accessible_attributes || []
111
-
112
- assert protected.include?(attribute.to_s) || !accessible.include?(attribute.to_s),
113
- (accessible.empty? ?
114
- "#{klass} is protecting #{protected.to_a.to_sentence}, but not #{attribute}." :
115
- "#{klass} has made #{attribute} accessible")
116
- end
117
- end
118
- end
119
-
120
- # Ensures that the attribute cannot be set to the given values
121
- # Requires an existing record
122
- #
123
- # Options:
124
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
125
- # Regexp or string. Default = <tt>/invalid/</tt>
126
- #
127
- # Example:
128
- # should_not_allow_values_for :isbn, "bad 1", "bad 2"
129
- #
130
- def should_not_allow_values_for(attribute, *bad_values)
131
- message = get_options!(bad_values, :message)
132
- message ||= /invalid/
133
- klass = model_class
134
- bad_values.each do |v|
135
- should "not allow #{attribute} to be set to #{v.inspect}" do
136
- assert object = klass.find(:first), "Can't find first #{klass}"
137
- object.send("#{attribute}=", v)
138
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
139
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
140
- assert_contains(object.errors.on(attribute), message, "when set to \"#{v}\"")
141
- end
142
- end
143
- end
144
-
145
- # Ensures that the attribute can be set to the given values.
146
- # Requires an existing record
147
- #
148
- # Example:
149
- # should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
150
- #
151
- def should_allow_values_for(attribute, *good_values)
152
- get_options!(good_values)
153
- klass = model_class
154
- good_values.each do |v|
155
- should "allow #{attribute} to be set to #{v.inspect}" do
156
- assert object = klass.find(:first), "Can't find first #{klass}"
157
- object.send("#{attribute}=", v)
158
- object.save
159
- assert_nil object.errors.on(attribute)
160
- end
161
- end
162
- end
163
-
164
- # Ensures that the length of the attribute is in the given range
165
- # Requires an existing record
166
- #
167
- # Options:
168
- # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
169
- # Regexp or string. Default = <tt>/short/</tt>
170
- # * <tt>:long_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
171
- # Regexp or string. Default = <tt>/long/</tt>
172
- #
173
- # Example:
174
- # should_ensure_length_in_range :password, (6..20)
175
- #
176
- def should_ensure_length_in_range(attribute, range, opts = {})
177
- short_message, long_message = get_options!([opts], :short_message, :long_message)
178
- short_message ||= /short/
179
- long_message ||= /long/
180
-
181
- klass = model_class
182
- min_length = range.first
183
- max_length = range.last
184
- same_length = (min_length == max_length)
185
-
186
- if min_length > 0
187
- should "not allow #{attribute} to be less than #{min_length} chars long" do
188
- min_value = "x" * (min_length - 1)
189
- assert object = klass.find(:first), "Can't find first #{klass}"
190
- object.send("#{attribute}=", min_value)
191
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{min_value}\""
192
- assert object.errors.on(attribute),
193
- "There are no errors set on #{attribute} after being set to \"#{min_value}\""
194
- assert_contains(object.errors.on(attribute), short_message, "when set to \"#{min_value}\"")
195
- end
196
- end
197
-
198
- if min_length > 0
199
- should "allow #{attribute} to be exactly #{min_length} chars long" do
200
- min_value = "x" * min_length
201
- assert object = klass.find(:first), "Can't find first #{klass}"
202
- object.send("#{attribute}=", min_value)
203
- object.save
204
- assert_does_not_contain(object.errors.on(attribute), short_message, "when set to \"#{min_value}\"")
205
- end
206
- end
207
-
208
- should "not allow #{attribute} to be more than #{max_length} chars long" do
209
- max_value = "x" * (max_length + 1)
210
- assert object = klass.find(:first), "Can't find first #{klass}"
211
- object.send("#{attribute}=", max_value)
212
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{max_value}\""
213
- assert object.errors.on(attribute),
214
- "There are no errors set on #{attribute} after being set to \"#{max_value}\""
215
- assert_contains(object.errors.on(attribute), long_message, "when set to \"#{max_value}\"")
216
- end
217
-
218
- unless same_length
219
- should "allow #{attribute} to be exactly #{max_length} chars long" do
220
- max_value = "x" * max_length
221
- assert object = klass.find(:first), "Can't find first #{klass}"
222
- object.send("#{attribute}=", max_value)
223
- object.save
224
- assert_does_not_contain(object.errors.on(attribute), long_message, "when set to \"#{max_value}\"")
225
- end
226
- end
227
- end
228
-
229
- # Ensures that the length of the attribute is at least a certain length
230
- # Requires an existing record
231
- #
232
- # Options:
233
- # * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
234
- # Regexp or string. Default = <tt>/short/</tt>
235
- #
236
- # Example:
237
- # should_ensure_length_at_least :name, 3
238
- #
239
- def should_ensure_length_at_least(attribute, min_length, opts = {})
240
- short_message = get_options!([opts], :short_message)
241
- short_message ||= /short/
242
-
243
- klass = model_class
244
-
245
- if min_length > 0
246
- min_value = "x" * (min_length - 1)
247
- should "not allow #{attribute} to be less than #{min_length} chars long" do
248
- assert object = klass.find(:first), "Can't find first #{klass}"
249
- object.send("#{attribute}=", min_value)
250
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{min_value}\""
251
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{min_value}\""
252
- assert_contains(object.errors.on(attribute), short_message, "when set to \"#{min_value}\"")
253
- end
254
- end
255
- should "allow #{attribute} to be at least #{min_length} chars long" do
256
- valid_value = "x" * (min_length)
257
- assert object = klass.find(:first), "Can't find first #{klass}"
258
- object.send("#{attribute}=", valid_value)
259
- assert object.save, "Could not save #{klass} with #{attribute} set to \"#{valid_value}\""
260
- end
261
- end
262
-
263
- # Ensure that the attribute is in the range specified
264
- # Requires an existing record
265
- #
266
- # Options:
267
- # * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
268
- # Regexp or string. Default = <tt>/included/</tt>
269
- # * <tt>:high_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
270
- # Regexp or string. Default = <tt>/included/</tt>
271
- #
272
- # Example:
273
- # should_ensure_value_in_range :age, (0..100)
274
- #
275
- def should_ensure_value_in_range(attribute, range, opts = {})
276
- low_message, high_message = get_options!([opts], :low_message, :high_message)
277
- low_message ||= /included/
278
- high_message ||= /included/
279
-
280
- klass = model_class
281
- min = range.first
282
- max = range.last
283
-
284
- should "not allow #{attribute} to be less than #{min}" do
285
- v = min - 1
286
- assert object = klass.find(:first), "Can't find first #{klass}"
287
- object.send("#{attribute}=", v)
288
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
289
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
290
- assert_contains(object.errors.on(attribute), low_message, "when set to \"#{v}\"")
291
- end
292
-
293
- should "allow #{attribute} to be #{min}" do
294
- v = min
295
- assert object = klass.find(:first), "Can't find first #{klass}"
296
- object.send("#{attribute}=", v)
297
- object.save
298
- assert_does_not_contain(object.errors.on(attribute), low_message, "when set to \"#{v}\"")
299
- end
300
-
301
- should "not allow #{attribute} to be more than #{max}" do
302
- v = max + 1
303
- assert object = klass.find(:first), "Can't find first #{klass}"
304
- object.send("#{attribute}=", v)
305
- assert !object.save, "Saved #{klass} with #{attribute} set to \"#{v}\""
306
- assert object.errors.on(attribute), "There are no errors set on #{attribute} after being set to \"#{v}\""
307
- assert_contains(object.errors.on(attribute), high_message, "when set to \"#{v}\"")
308
- end
309
-
310
- should "allow #{attribute} to be #{max}" do
311
- v = max
312
- assert object = klass.find(:first), "Can't find first #{klass}"
313
- object.send("#{attribute}=", v)
314
- object.save
315
- assert_does_not_contain(object.errors.on(attribute), high_message, "when set to \"#{v}\"")
316
- end
317
- end
318
-
319
- # Ensure that the attribute is numeric
320
- # Requires an existing record
321
- #
322
- # Options:
323
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
324
- # Regexp or string. Default = <tt>/number/</tt>
325
- #
326
- # Example:
327
- # should_only_allow_numeric_values_for :age
328
- #
329
- def should_only_allow_numeric_values_for(*attributes)
330
- message = get_options!(attributes, :message)
331
- message ||= /number/
332
- klass = model_class
333
- attributes.each do |attribute|
334
- attribute = attribute.to_sym
335
- should "only allow numeric values for #{attribute}" do
336
- assert object = klass.find(:first), "Can't find first #{klass}"
337
- object.send(:"#{attribute}=", "abcd")
338
- assert !object.valid?, "Instance is still valid"
339
- assert_contains(object.errors.on(attribute), message)
340
- end
341
- end
342
- end
343
-
344
- # Ensures that the has_many relationship exists. Will also test that the
345
- # associated table has the required columns. Works with polymorphic
346
- # associations.
347
- #
348
- # Options:
349
- # * <tt>:through</tt> - association name for <tt>has_many :through</tt>
350
- #
351
- # Example:
352
- # should_have_many :friends
353
- # should_have_many :enemies, :through => :friends
354
- #
355
- def should_have_many(*associations)
356
- through = get_options!(associations, :through)
357
- klass = model_class
358
- associations.each do |association|
359
- name = "have many #{association}"
360
- name += " through #{through}" if through
361
- should name do
362
- reflection = klass.reflect_on_association(association)
363
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
364
- assert_equal :has_many, reflection.macro
365
-
366
- if through
367
- through_reflection = klass.reflect_on_association(through)
368
- assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
369
- assert_equal(through, reflection.options[:through])
370
- end
371
-
372
- unless reflection.options[:through]
373
- # This is not a through association, so check for the existence of the foreign key on the other table
374
- if reflection.options[:foreign_key]
375
- fk = reflection.options[:foreign_key]
376
- elsif reflection.options[:as]
377
- fk = reflection.options[:as].to_s.foreign_key
378
- else
379
- fk = reflection.primary_key_name
380
- end
381
- associated_klass = (reflection.options[:class_name] || association.to_s.classify).constantize
382
- assert associated_klass.column_names.include?(fk.to_s), "#{associated_klass.name} does not have a #{fk} foreign key."
383
- end
384
- end
385
- end
386
- end
387
-
388
- # Ensure that the has_one relationship exists. Will also test that the
389
- # associated table has the required columns. Works with polymorphic
390
- # associations.
391
- #
392
- # Example:
393
- # should_have_one :god # unless hindu
394
- #
395
- def should_have_one(*associations)
396
- get_options!(associations)
397
- klass = model_class
398
- associations.each do |association|
399
- should "have one #{association}" do
400
- reflection = klass.reflect_on_association(association)
401
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
402
- assert_equal :has_one, reflection.macro
403
-
404
- associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
405
-
406
- if reflection.options[:foreign_key]
407
- fk = reflection.options[:foreign_key]
408
- elsif reflection.options[:as]
409
- fk = reflection.options[:as].to_s.foreign_key
410
- fk_type = fk.gsub(/_id$/, '_type')
411
- assert associated_klass.column_names.include?(fk_type),
412
- "#{associated_klass.name} does not have a #{fk_type} column."
413
- else
414
- fk = klass.name.foreign_key
415
- end
416
- assert associated_klass.column_names.include?(fk.to_s),
417
- "#{associated_klass.name} does not have a #{fk} foreign key."
418
- end
419
- end
420
- end
421
-
422
- # Ensures that the has_and_belongs_to_many relationship exists, and that the join
423
- # table is in place.
424
- #
425
- # should_have_and_belong_to_many :posts, :cars
426
- #
427
- def should_have_and_belong_to_many(*associations)
428
- get_options!(associations)
429
- klass = model_class
430
-
431
- associations.each do |association|
432
- should "should have and belong to many #{association}" do
433
- reflection = klass.reflect_on_association(association)
434
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
435
- assert_equal :has_and_belongs_to_many, reflection.macro
436
- table = reflection.options[:join_table]
437
- assert ::ActiveRecord::Base.connection.tables.include?(table), "table #{table} doesn't exist"
438
- end
439
- end
440
- end
441
-
442
- # Ensure that the belongs_to relationship exists.
443
- #
444
- # should_belong_to :parent
445
- #
446
- def should_belong_to(*associations)
447
- get_options!(associations)
448
- klass = model_class
449
- associations.each do |association|
450
- should "belong_to #{association}" do
451
- reflection = klass.reflect_on_association(association)
452
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
453
- assert_equal :belongs_to, reflection.macro
454
-
455
- unless reflection.options[:polymorphic]
456
- associated_klass = (reflection.options[:class_name] || association.to_s.classify).constantize
457
- fk = reflection.options[:foreign_key] || reflection.primary_key_name
458
- assert klass.column_names.include?(fk.to_s), "#{klass.name} does not have a #{fk} foreign key."
459
- end
460
- end
461
- end
462
- end
463
-
464
- # Ensure that the given class methods are defined on the model.
465
- #
466
- # should_have_class_methods :find, :destroy
467
- #
468
- def should_have_class_methods(*methods)
469
- get_options!(methods)
470
- klass = model_class
471
- methods.each do |method|
472
- should "respond to class method ##{method}" do
473
- assert_respond_to klass, method, "#{klass.name} does not have class method #{method}"
474
- end
475
- end
476
- end
477
-
478
- # Ensure that the given instance methods are defined on the model.
479
- #
480
- # should_have_instance_methods :email, :name, :name=
481
- #
482
- def should_have_instance_methods(*methods)
483
- get_options!(methods)
484
- klass = model_class
485
- methods.each do |method|
486
- should "respond to instance method ##{method}" do
487
- assert_respond_to klass.new, method, "#{klass.name} does not have instance method #{method}"
488
- end
489
- end
490
- end
491
-
492
- # Ensure that the given columns are defined on the models backing SQL table.
493
- #
494
- # should_have_db_columns :id, :email, :name, :created_at
495
- #
496
- def should_have_db_columns(*columns)
497
- column_type = get_options!(columns, :type)
498
- klass = model_class
499
- columns.each do |name|
500
- test_name = "have column #{name}"
501
- test_name += " of type #{column_type}" if column_type
502
- should test_name do
503
- column = klass.columns.detect {|c| c.name == name.to_s }
504
- assert column, "#{klass.name} does not have column #{name}"
505
- end
506
- end
507
- end
508
-
509
- # Ensure that the given column is defined on the models backing SQL table. The options are the same as
510
- # the instance variables defined on the column definition: :precision, :limit, :default, :null,
511
- # :primary, :type, :scale, and :sql_type.
512
- #
513
- # should_have_db_column :email, :type => "string", :default => nil, :precision => nil, :limit => 255,
514
- # :null => true, :primary => false, :scale => nil, :sql_type => 'varchar(255)'
515
- #
516
- def should_have_db_column(name, opts = {})
517
- klass = model_class
518
- test_name = "have column named :#{name}"
519
- test_name += " with options " + opts.inspect unless opts.empty?
520
- should test_name do
521
- column = klass.columns.detect {|c| c.name == name.to_s }
522
- assert column, "#{klass.name} does not have column #{name}"
523
- opts.each do |k, v|
524
- assert_equal column.instance_variable_get("@#{k}").to_s, v.to_s, ":#{name} column on table for #{klass} does not match option :#{k}"
525
- end
526
- end
527
- end
528
-
529
- # Ensures that there are DB indices on the given columns or tuples of columns.
530
- # Also aliased to should_have_index for readability
531
- #
532
- # should_have_indices :email, :name, [:commentable_type, :commentable_id]
533
- # should_have_index :age
534
- #
535
- def should_have_indices(*columns)
536
- table = model_class.name.tableize
537
- indices = ::ActiveRecord::Base.connection.indexes(table).map(&:columns)
538
-
539
- columns.each do |column|
540
- should "have index on #{table} for #{column.inspect}" do
541
- columns = [column].flatten.map(&:to_s)
542
- assert_contains(indices, columns)
543
- end
544
- end
545
- end
546
-
547
- alias_method :should_have_index, :should_have_indices
548
-
549
- # Ensures that the model cannot be saved if one of the attributes listed is not accepted.
550
- #
551
- # Options:
552
- # * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
553
- # Regexp or string. Default = <tt>/must be accepted/</tt>
554
- #
555
- # Example:
556
- # should_require_acceptance_of :eula
557
- #
558
- def should_require_acceptance_of(*attributes)
559
- message = get_options!(attributes, :message)
560
- message ||= /must be accepted/
561
- klass = model_class
562
-
563
- attributes.each do |attribute|
564
- should "require #{attribute} to be accepted" do
565
- object = klass.new
566
- object.send("#{attribute}=", false)
567
-
568
- assert !object.valid?, "#{klass.name} does not require acceptance of #{attribute}."
569
- assert object.errors.on(attribute), "#{klass.name} does not require acceptance of #{attribute}."
570
- assert_contains(object.errors.on(attribute), message)
571
- end
572
- end
573
- end
574
-
575
- private
576
-
577
- include ThoughtBot::Shoulda::Private
578
- end
579
- end
580
- end