thoughtbot-shoulda 2.10.2 → 2.11.1

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 (241) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +5 -5
  2. data/README.rdoc +72 -89
  3. data/Rakefile +31 -33
  4. data/lib/shoulda.rb +6 -6
  5. data/lib/shoulda/action_controller.rb +8 -0
  6. data/lib/shoulda/action_controller/macros.rb +54 -73
  7. data/lib/shoulda/action_controller/matchers.rb +7 -5
  8. data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +10 -7
  9. data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +18 -1
  10. data/lib/shoulda/action_controller/matchers/redirect_to_matcher.rb +62 -0
  11. data/lib/shoulda/action_controller/matchers/render_template_matcher.rb +54 -0
  12. data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +33 -15
  13. data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +10 -10
  14. data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +17 -13
  15. data/lib/shoulda/action_controller/matchers/route_matcher.rb +3 -3
  16. data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +13 -2
  17. data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +1 -1
  18. data/lib/shoulda/action_mailer.rb +3 -0
  19. data/lib/shoulda/action_mailer/assertions.rb +4 -0
  20. data/lib/shoulda/action_mailer/matchers.rb +22 -0
  21. data/lib/shoulda/action_mailer/matchers/have_sent_email.rb +110 -0
  22. data/lib/shoulda/active_record/helpers.rb +8 -3
  23. data/lib/shoulda/active_record/macros.rb +88 -143
  24. data/lib/shoulda/active_record/matchers.rb +0 -1
  25. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +1 -1
  26. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +10 -2
  27. data/lib/shoulda/active_record/matchers/association_matcher.rb +8 -8
  28. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +1 -1
  29. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +2 -2
  30. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +11 -11
  31. data/lib/shoulda/active_record/matchers/have_db_index_matcher.rb +8 -8
  32. data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +2 -4
  33. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +3 -3
  34. data/lib/shoulda/active_record/matchers/validation_matcher.rb +0 -1
  35. data/lib/shoulda/assertions.rb +10 -2
  36. data/lib/shoulda/autoload_macros.rb +20 -20
  37. data/lib/shoulda/context.rb +70 -39
  38. data/lib/shoulda/{rspec.rb → integrations/rspec.rb} +2 -0
  39. data/lib/shoulda/integrations/rspec2.rb +22 -0
  40. data/lib/shoulda/{test_unit.rb → integrations/test_unit.rb} +0 -0
  41. data/lib/shoulda/macros.rb +46 -18
  42. data/lib/shoulda/rails.rb +0 -5
  43. data/lib/shoulda/tasks/yaml_to_shoulda.rake +11 -11
  44. data/lib/shoulda/version.rb +4 -0
  45. data/rails/init.rb +3 -2
  46. data/test/fail_macros.rb +20 -4
  47. data/test/functional/posts_controller_test.rb +7 -7
  48. data/test/functional/users_controller_test.rb +1 -1
  49. data/test/matchers/action_mailer/have_sent_email_test.rb +70 -0
  50. data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +7 -1
  51. data/test/matchers/active_record/allow_value_matcher_test.rb +1 -1
  52. data/test/matchers/active_record/association_matcher_test.rb +16 -16
  53. data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +3 -2
  54. data/test/matchers/active_record/ensure_length_of_matcher_test.rb +1 -1
  55. data/test/matchers/active_record/have_db_column_matcher_test.rb +1 -1
  56. data/test/matchers/active_record/have_db_index_matcher_test.rb +1 -1
  57. data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +1 -1
  58. data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +1 -1
  59. data/test/matchers/active_record/validate_format_of_matcher_test.rb +1 -1
  60. data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +1 -1
  61. data/test/matchers/active_record/validate_presence_of_matcher_test.rb +1 -1
  62. data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +1 -1
  63. data/test/matchers/controller/assign_to_matcher_test.rb +11 -1
  64. data/test/matchers/controller/filter_param_matcher_test.rb +10 -2
  65. data/test/matchers/controller/redirect_to_matcher_test.rb +37 -0
  66. data/test/matchers/controller/render_template_matcher_test.rb +37 -0
  67. data/test/matchers/controller/render_with_layout_matcher_test.rb +15 -1
  68. data/test/matchers/controller/respond_with_content_type_matcher_test.rb +1 -1
  69. data/test/matchers/controller/respond_with_matcher_test.rb +1 -11
  70. data/test/matchers/controller/route_matcher_test.rb +18 -1
  71. data/test/matchers/controller/set_session_matcher_test.rb +11 -1
  72. data/test/matchers/controller/set_the_flash_matcher.rb +1 -1
  73. data/test/other/autoload_macro_test.rb +1 -1
  74. data/test/other/context_test.rb +210 -27
  75. data/test/other/convert_to_should_syntax_test.rb +1 -1
  76. data/test/other/helpers_test.rb +13 -36
  77. data/test/other/private_helpers_test.rb +2 -2
  78. data/test/other/should_test.rb +13 -13
  79. data/test/{model_builder.rb → rails2_model_builder.rb} +32 -8
  80. data/test/{rails_root → rails2_root}/app/controllers/application_controller.rb +0 -3
  81. data/test/{rails_root → rails2_root}/app/controllers/posts_controller.rb +0 -0
  82. data/test/{rails_root → rails2_root}/app/controllers/users_controller.rb +0 -0
  83. data/test/{rails_root → rails2_root}/app/helpers/application_helper.rb +0 -0
  84. data/test/{rails_root → rails2_root}/app/helpers/posts_helper.rb +0 -0
  85. data/test/{rails_root → rails2_root}/app/helpers/users_helper.rb +0 -0
  86. data/test/{rails_root → rails2_root}/app/models/address.rb +0 -0
  87. data/test/rails2_root/app/models/flea.rb +11 -0
  88. data/test/{rails_root → rails2_root}/app/models/friendship.rb +0 -0
  89. data/test/rails2_root/app/models/notifier.rb +8 -0
  90. data/test/{rails_root → rails2_root}/app/models/pets/cat.rb +0 -0
  91. data/test/{rails_root → rails2_root}/app/models/pets/dog.rb +0 -0
  92. data/test/{rails_root → rails2_root}/app/models/post.rb +1 -1
  93. data/test/{rails_root → rails2_root}/app/models/product.rb +0 -0
  94. data/test/{rails_root → rails2_root}/app/models/profile.rb +0 -0
  95. data/test/{rails_root → rails2_root}/app/models/registration.rb +0 -0
  96. data/test/{rails_root → rails2_root}/app/models/tag.rb +0 -0
  97. data/test/{rails_root → rails2_root}/app/models/tagging.rb +0 -0
  98. data/test/{rails_root → rails2_root}/app/models/treat.rb +0 -0
  99. data/test/{rails_root → rails2_root}/app/models/user.rb +0 -0
  100. data/test/{rails_root → rails2_root}/app/views/layouts/posts.rhtml +0 -0
  101. data/test/{rails_root → rails2_root}/app/views/layouts/users.rhtml +0 -0
  102. data/test/{rails_root → rails2_root}/app/views/layouts/wide.html.erb +0 -0
  103. data/test/rails2_root/app/views/notifier/the_email.html.erb +1 -0
  104. data/test/{rails_root → rails2_root}/app/views/posts/edit.rhtml +0 -0
  105. data/test/{rails_root → rails2_root}/app/views/posts/index.rhtml +0 -0
  106. data/test/{rails_root → rails2_root}/app/views/posts/new.rhtml +0 -0
  107. data/test/{rails_root → rails2_root}/app/views/posts/show.rhtml +0 -0
  108. data/test/{rails_root → rails2_root}/app/views/users/edit.rhtml +0 -0
  109. data/test/{rails_root → rails2_root}/app/views/users/index.rhtml +0 -0
  110. data/test/{rails_root → rails2_root}/app/views/users/new.rhtml +0 -0
  111. data/test/{rails_root → rails2_root}/app/views/users/show.rhtml +0 -0
  112. data/test/{rails_root → rails2_root}/config/boot.rb +0 -0
  113. data/test/{rails_root → rails2_root}/config/database.yml +1 -1
  114. data/test/{rails_root → rails2_root}/config/environment.rb +0 -1
  115. data/test/rails2_root/config/environments/test.rb +23 -0
  116. data/test/{rails_root → rails2_root}/config/initializers/new_rails_defaults.rb +0 -0
  117. data/test/{rails_root → rails2_root}/config/initializers/shoulda.rb +0 -0
  118. data/test/{rails_root → rails2_root}/config/routes.rb +0 -0
  119. data/test/{rails_root → rails2_root}/db/migrate/001_create_users.rb +0 -0
  120. data/test/{rails_root → rails2_root}/db/migrate/002_create_posts.rb +0 -0
  121. data/test/{rails_root → rails2_root}/db/migrate/003_create_taggings.rb +0 -0
  122. data/test/{rails_root → rails2_root}/db/migrate/004_create_tags.rb +0 -0
  123. data/test/{rails_root → rails2_root}/db/migrate/005_create_dogs.rb +0 -0
  124. data/test/{rails_root → rails2_root}/db/migrate/006_create_addresses.rb +0 -0
  125. data/test/{rails_root → rails2_root}/db/migrate/007_create_fleas.rb +0 -0
  126. data/test/{rails_root → rails2_root}/db/migrate/008_create_dogs_fleas.rb +0 -0
  127. data/test/{rails_root → rails2_root}/db/migrate/009_create_products.rb +0 -0
  128. data/test/{rails_root → rails2_root}/db/migrate/010_create_friendships.rb +0 -0
  129. data/test/{rails_root → rails2_root}/db/migrate/011_create_treats.rb +0 -0
  130. data/test/{rails_root → rails2_root}/db/migrate/20090506203502_create_profiles.rb +0 -0
  131. data/test/{rails_root → rails2_root}/db/migrate/20090506203536_create_registrations.rb +0 -0
  132. data/test/{rails_root → rails2_root}/db/migrate/20090513104502_create_cats.rb +0 -0
  133. data/test/{rails_root → rails2_root}/db/schema.rb +0 -0
  134. data/test/{rails_root → rails2_root}/public/404.html +0 -0
  135. data/test/{rails_root → rails2_root}/public/422.html +0 -0
  136. data/test/{rails_root → rails2_root}/public/500.html +0 -0
  137. data/test/{rails_root → rails2_root}/script/console +0 -0
  138. data/test/{rails_root → rails2_root}/script/generate +0 -0
  139. data/test/{rails_root → rails2_root}/test/shoulda_macros/custom_macro.rb +0 -0
  140. data/test/{rails_root → rails2_root}/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +0 -0
  141. data/test/{rails_root → rails2_root}/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +0 -0
  142. data/test/rails2_test_helper.rb +6 -0
  143. data/test/rails3_model_builder.rb +118 -0
  144. data/test/rails3_root/Gemfile +28 -0
  145. data/test/rails3_root/README +244 -0
  146. data/test/rails3_root/Rakefile +10 -0
  147. data/test/rails3_root/app/controllers/application_controller.rb +22 -0
  148. data/test/rails3_root/app/controllers/posts_controller.rb +87 -0
  149. data/test/rails3_root/app/controllers/users_controller.rb +82 -0
  150. data/test/rails3_root/app/helpers/application_helper.rb +2 -0
  151. data/test/rails3_root/app/models/address.rb +7 -0
  152. data/test/rails3_root/app/models/flea.rb +11 -0
  153. data/test/rails3_root/app/models/friendship.rb +4 -0
  154. data/test/rails3_root/app/models/notifier.rb +8 -0
  155. data/test/rails3_root/app/models/pets/cat.rb +7 -0
  156. data/test/rails3_root/app/models/pets/dog.rb +10 -0
  157. data/test/rails3_root/app/models/post.rb +12 -0
  158. data/test/rails3_root/app/models/product.rb +12 -0
  159. data/test/rails3_root/app/models/profile.rb +2 -0
  160. data/test/rails3_root/app/models/registration.rb +2 -0
  161. data/test/rails3_root/app/models/tag.rb +8 -0
  162. data/test/rails3_root/app/models/tagging.rb +4 -0
  163. data/test/rails3_root/app/models/treat.rb +3 -0
  164. data/test/rails3_root/app/models/user.rb +32 -0
  165. data/test/rails3_root/app/views/layouts/application.html.erb +14 -0
  166. data/test/rails3_root/app/views/layouts/posts.rhtml +19 -0
  167. data/test/rails3_root/app/views/layouts/users.rhtml +17 -0
  168. data/test/rails3_root/app/views/layouts/wide.html.erb +1 -0
  169. data/test/rails3_root/app/views/notifier/the_email.html.erb +1 -0
  170. data/test/rails3_root/app/views/posts/edit.rhtml +27 -0
  171. data/test/rails3_root/app/views/posts/index.rhtml +25 -0
  172. data/test/rails3_root/app/views/posts/new.rhtml +24 -0
  173. data/test/rails3_root/app/views/posts/show.rhtml +18 -0
  174. data/test/rails3_root/app/views/users/edit.rhtml +22 -0
  175. data/test/rails3_root/app/views/users/index.rhtml +22 -0
  176. data/test/rails3_root/app/views/users/new.rhtml +21 -0
  177. data/test/rails3_root/app/views/users/show.rhtml +13 -0
  178. data/test/rails3_root/config.ru +4 -0
  179. data/test/rails3_root/config/application.rb +46 -0
  180. data/test/rails3_root/config/boot.rb +6 -0
  181. data/test/rails3_root/config/database.yml +22 -0
  182. data/test/rails3_root/config/environment.rb +5 -0
  183. data/test/rails3_root/config/environments/development.rb +19 -0
  184. data/test/rails3_root/config/environments/production.rb +42 -0
  185. data/test/rails3_root/config/environments/test.rb +32 -0
  186. data/test/rails3_root/config/initializers/backtrace_silencers.rb +7 -0
  187. data/test/rails3_root/config/initializers/inflections.rb +10 -0
  188. data/test/rails3_root/config/initializers/mime_types.rb +5 -0
  189. data/test/rails3_root/config/initializers/secret_token.rb +7 -0
  190. data/test/rails3_root/config/initializers/session_store.rb +8 -0
  191. data/test/rails3_root/config/locales/en.yml +5 -0
  192. data/test/rails3_root/config/routes.rb +4 -0
  193. data/test/rails3_root/db/migrate/001_create_users.rb +19 -0
  194. data/test/rails3_root/db/migrate/002_create_posts.rb +13 -0
  195. data/test/rails3_root/db/migrate/003_create_taggings.rb +12 -0
  196. data/test/rails3_root/db/migrate/004_create_tags.rb +11 -0
  197. data/test/rails3_root/db/migrate/005_create_dogs.rb +12 -0
  198. data/test/rails3_root/db/migrate/006_create_addresses.rb +14 -0
  199. data/test/rails3_root/db/migrate/007_create_fleas.rb +11 -0
  200. data/test/rails3_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  201. data/test/rails3_root/db/migrate/009_create_products.rb +17 -0
  202. data/test/rails3_root/db/migrate/010_create_friendships.rb +14 -0
  203. data/test/rails3_root/db/migrate/011_create_treats.rb +12 -0
  204. data/test/rails3_root/db/migrate/20090506203502_create_profiles.rb +12 -0
  205. data/test/rails3_root/db/migrate/20090506203536_create_registrations.rb +14 -0
  206. data/test/rails3_root/db/migrate/20090513104502_create_cats.rb +12 -0
  207. data/test/rails3_root/db/seeds.rb +7 -0
  208. data/test/rails3_root/public/404.html +26 -0
  209. data/test/rails3_root/public/422.html +26 -0
  210. data/test/rails3_root/public/500.html +26 -0
  211. data/test/{rails_root/config/environments/test.rb → rails3_root/public/favicon.ico} +0 -0
  212. data/test/rails3_root/public/images/rails.png +0 -0
  213. data/test/rails3_root/public/index.html +279 -0
  214. data/test/rails3_root/public/javascripts/application.js +2 -0
  215. data/test/rails3_root/public/javascripts/controls.js +965 -0
  216. data/test/rails3_root/public/javascripts/dragdrop.js +974 -0
  217. data/test/rails3_root/public/javascripts/effects.js +1123 -0
  218. data/test/rails3_root/public/javascripts/prototype.js +4874 -0
  219. data/test/rails3_root/public/javascripts/rails.js +118 -0
  220. data/test/rails3_root/public/robots.txt +5 -0
  221. data/test/rails3_root/script/rails +9 -0
  222. data/test/rails3_root/test/performance/browsing_test.rb +9 -0
  223. data/test/rails3_root/test/test_helper.rb +13 -0
  224. data/test/rails3_test_helper.rb +6 -0
  225. data/test/test_helper.rb +16 -8
  226. data/test/unit/address_test.rb +1 -1
  227. data/test/unit/cat_test.rb +1 -1
  228. data/test/unit/dog_test.rb +1 -1
  229. data/test/unit/flea_test.rb +9 -1
  230. data/test/unit/friendship_test.rb +1 -1
  231. data/test/unit/post_test.rb +1 -5
  232. data/test/unit/product_test.rb +1 -1
  233. data/test/unit/tag_test.rb +1 -5
  234. data/test/unit/tagging_test.rb +1 -1
  235. data/test/unit/user_test.rb +3 -37
  236. metadata +184 -118
  237. data/lib/shoulda/action_view.rb +0 -10
  238. data/lib/shoulda/action_view/macros.rb +0 -61
  239. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +0 -128
  240. data/test/matchers/active_record/have_named_scope_matcher_test.rb +0 -65
  241. data/test/rails_root/app/models/flea.rb +0 -3
@@ -13,7 +13,6 @@ require 'shoulda/active_record/matchers/have_db_column_matcher'
13
13
  require 'shoulda/active_record/matchers/have_db_index_matcher'
14
14
  require 'shoulda/active_record/matchers/have_readonly_attribute_matcher'
15
15
  require 'shoulda/active_record/matchers/allow_mass_assignment_of_matcher'
16
- require 'shoulda/active_record/matchers/have_named_scope_matcher'
17
16
 
18
17
 
19
18
  module Shoulda # :nodoc:
@@ -24,7 +24,7 @@ module Shoulda # :nodoc:
24
24
  @failure_message = "#{@attribute} was made accessible"
25
25
  else
26
26
  if protected_attributes.empty?
27
- @failure_message = "no attributes were protected"
27
+ @negative_failure_message = "no attributes were protected"
28
28
  else
29
29
  @failure_message = "#{class_name} is protecting " <<
30
30
  "#{protected_attributes.to_a.to_sentence}, " <<
@@ -59,9 +59,17 @@ module Shoulda # :nodoc:
59
59
 
60
60
  def errors_match?
61
61
  @instance.valid?
62
- @errors = @instance.errors.on(@attribute)
62
+ @errors = errors_for_attribute(@instance, @attribute)
63
63
  @errors = [@errors] unless @errors.is_a?(Array)
64
- @expected_message ? (errors_match_regexp? || errors_match_string?) : (@errors != [nil])
64
+ @expected_message ? (errors_match_regexp? || errors_match_string?) : (@errors.compact.any?)
65
+ end
66
+
67
+ def errors_for_attribute(instance, attribute)
68
+ if instance.errors.respond_to?(:[])
69
+ instance.errors[attribute]
70
+ else
71
+ instance.errors.on(attribute)
72
+ end
65
73
  end
66
74
 
67
75
  def errors_match_regexp?
@@ -20,9 +20,9 @@ module Shoulda # :nodoc:
20
20
  # dependent option.
21
21
  #
22
22
  # Example:
23
- # it { should_have_many(:friends) }
24
- # it { should_have_many(:enemies).through(:friends) }
25
- # it { should_have_many(:enemies).dependent(:destroy) }
23
+ # it { should have_many(:friends) }
24
+ # it { should have_many(:enemies).through(:friends) }
25
+ # it { should have_many(:enemies).dependent(:destroy) }
26
26
  #
27
27
  def have_many(name)
28
28
  AssociationMatcher.new(:has_many, name)
@@ -70,10 +70,10 @@ module Shoulda # :nodoc:
70
70
 
71
71
  def matches?(subject)
72
72
  @subject = subject
73
- association_exists? &&
74
- macro_correct? &&
75
- foreign_key_exists? &&
76
- through_association_valid? &&
73
+ association_exists? &&
74
+ macro_correct? &&
75
+ foreign_key_exists? &&
76
+ through_association_valid? &&
77
77
  dependent_correct? &&
78
78
  join_table_exists?
79
79
  end
@@ -160,7 +160,7 @@ module Shoulda # :nodoc:
160
160
  end
161
161
 
162
162
  def join_table_exists?
163
- if @macro != :has_and_belongs_to_many ||
163
+ if @macro != :has_and_belongs_to_many ||
164
164
  ::ActiveRecord::Base.connection.tables.include?(join_table.to_s)
165
165
  true
166
166
  else
@@ -57,7 +57,7 @@ module Shoulda # :nodoc:
57
57
  @low_message ||= :inclusion
58
58
  @high_message ||= :inclusion
59
59
 
60
- disallows_lower_value &&
60
+ disallows_lower_value &&
61
61
  allows_minimum_value &&
62
62
  disallows_higher_value &&
63
63
  allows_maximum_value
@@ -84,7 +84,7 @@ module Shoulda # :nodoc:
84
84
  def matches?(subject)
85
85
  super(subject)
86
86
  translate_messages!
87
- disallows_lower_length &&
87
+ disallows_lower_length &&
88
88
  allows_minimum_length &&
89
89
  ((@minimum == @maximum) ||
90
90
  (disallows_higher_length &&
@@ -106,7 +106,7 @@ module Shoulda # :nodoc:
106
106
  end
107
107
 
108
108
  def disallows_lower_length
109
- @minimum == 0 ||
109
+ @minimum == 0 ||
110
110
  @minimum.nil? ||
111
111
  disallows_length_of(@minimum - 1, @short_message)
112
112
  end
@@ -24,12 +24,12 @@ module Shoulda # :nodoc:
24
24
  @macro = macro
25
25
  @column = column
26
26
  end
27
-
27
+
28
28
  def of_type(column_type)
29
29
  @column_type = column_type
30
30
  self
31
31
  end
32
-
32
+
33
33
  def with_options(opts = {})
34
34
  @precision = opts[:precision]
35
35
  @limit = opts[:limit]
@@ -41,8 +41,8 @@ module Shoulda # :nodoc:
41
41
 
42
42
  def matches?(subject)
43
43
  @subject = subject
44
- column_exists? &&
45
- correct_column_type? &&
44
+ column_exists? &&
45
+ correct_column_type? &&
46
46
  correct_precision? &&
47
47
  correct_limit? &&
48
48
  correct_default? &&
@@ -80,7 +80,7 @@ module Shoulda # :nodoc:
80
80
  false
81
81
  end
82
82
  end
83
-
83
+
84
84
  def correct_column_type?
85
85
  return true if @column_type.nil?
86
86
  if matched_column.type.to_s == @column_type.to_s
@@ -91,7 +91,7 @@ module Shoulda # :nodoc:
91
91
  false
92
92
  end
93
93
  end
94
-
94
+
95
95
  def correct_precision?
96
96
  return true if @precision.nil?
97
97
  if matched_column.precision.to_s == @precision.to_s
@@ -103,7 +103,7 @@ module Shoulda # :nodoc:
103
103
  false
104
104
  end
105
105
  end
106
-
106
+
107
107
  def correct_limit?
108
108
  return true if @limit.nil?
109
109
  if matched_column.limit.to_s == @limit.to_s
@@ -115,7 +115,7 @@ module Shoulda # :nodoc:
115
115
  false
116
116
  end
117
117
  end
118
-
118
+
119
119
  def correct_default?
120
120
  return true if @default.nil?
121
121
  if matched_column.default.to_s == @default.to_s
@@ -127,7 +127,7 @@ module Shoulda # :nodoc:
127
127
  false
128
128
  end
129
129
  end
130
-
130
+
131
131
  def correct_null?
132
132
  return true if @null.nil?
133
133
  if matched_column.null.to_s == @null.to_s
@@ -139,7 +139,7 @@ module Shoulda # :nodoc:
139
139
  false
140
140
  end
141
141
  end
142
-
142
+
143
143
  def correct_scale?
144
144
  return true if @scale.nil?
145
145
  if matched_column.scale.to_s == @scale.to_s
@@ -150,7 +150,7 @@ module Shoulda # :nodoc:
150
150
  false
151
151
  end
152
152
  end
153
-
153
+
154
154
  def matched_column
155
155
  model_class.columns.detect { |each| each.name == @column.to_s }
156
156
  end
@@ -27,7 +27,7 @@ module Shoulda # :nodoc:
27
27
  @macro = macro
28
28
  @columns = normalize_columns_to_array(columns)
29
29
  end
30
-
30
+
31
31
  def unique(unique)
32
32
  @unique = unique
33
33
  self
@@ -51,11 +51,11 @@ module Shoulda # :nodoc:
51
51
  end
52
52
 
53
53
  protected
54
-
54
+
55
55
  def index_exists?
56
56
  ! matched_index.nil?
57
57
  end
58
-
58
+
59
59
  def correct_unique?
60
60
  return true if @unique.nil?
61
61
  if matched_index.unique == @unique
@@ -66,7 +66,7 @@ module Shoulda # :nodoc:
66
66
  false
67
67
  end
68
68
  end
69
-
69
+
70
70
  def matched_index
71
71
  indexes.detect { |each| each.columns == @columns }
72
72
  end
@@ -74,11 +74,11 @@ module Shoulda # :nodoc:
74
74
  def model_class
75
75
  @subject.class
76
76
  end
77
-
77
+
78
78
  def table_name
79
79
  model_class.table_name
80
80
  end
81
-
81
+
82
82
  def indexes
83
83
  ::ActiveRecord::Base.connection.indexes(table_name)
84
84
  end
@@ -86,7 +86,7 @@ module Shoulda # :nodoc:
86
86
  def expectation
87
87
  expected = "#{model_class.name} to #{description}"
88
88
  end
89
-
89
+
90
90
  def index_type
91
91
  case @unique
92
92
  when nil
@@ -97,7 +97,7 @@ module Shoulda # :nodoc:
97
97
  'unique'
98
98
  end
99
99
  end
100
-
100
+
101
101
  def normalize_columns_to_array(columns)
102
102
  if columns.class == Array
103
103
  columns.collect { |each| each.to_s }
@@ -25,7 +25,7 @@ module Shoulda # :nodoc:
25
25
  end
26
26
 
27
27
  class ValidateFormatOfMatcher < ValidationMatcher # :nodoc:
28
-
28
+
29
29
  def initialize(attribute)
30
30
  super
31
31
  end
@@ -34,13 +34,12 @@ module Shoulda # :nodoc:
34
34
  @expected_message = message if message
35
35
  self
36
36
  end
37
-
37
+
38
38
  def with(value)
39
39
  raise "You may not call both with and not_with" if @value_to_fail
40
40
  @value_to_pass = value
41
41
  self
42
42
  end
43
-
44
43
 
45
44
  def not_with(value)
46
45
  raise "You may not call both with and not_with" if @value_to_pass
@@ -48,7 +47,6 @@ module Shoulda # :nodoc:
48
47
  self
49
48
  end
50
49
 
51
-
52
50
  def matches?(subject)
53
51
  super(subject)
54
52
  @expected_message ||= :blank
@@ -67,8 +67,8 @@ module Shoulda # :nodoc:
67
67
  def matches?(subject)
68
68
  @subject = subject.class.new
69
69
  @expected_message ||= :taken
70
- find_existing &&
71
- set_scoped_attributes &&
70
+ find_existing &&
71
+ set_scoped_attributes &&
72
72
  validate_attribute &&
73
73
  validate_after_scope_change
74
74
  end
@@ -121,7 +121,7 @@ module Shoulda # :nodoc:
121
121
  @subject.send("#{scope}=", next_value)
122
122
 
123
123
  if allows_value_of(existing_value, @expected_message)
124
- @negative_failure_message <<
124
+ @negative_failure_message <<
125
125
  " (with different value of #{scope})"
126
126
  true
127
127
  else
@@ -18,7 +18,6 @@ module Shoulda # :nodoc:
18
18
  @subject = subject
19
19
  false
20
20
  end
21
-
22
21
 
23
22
  private
24
23
 
@@ -26,7 +26,7 @@ module Shoulda # :nodoc:
26
26
  case x
27
27
  when Regexp
28
28
  assert(collection.detect { |e| e =~ x }, msg)
29
- else
29
+ else
30
30
  assert(collection.include?(x), msg)
31
31
  end
32
32
  end
@@ -39,13 +39,17 @@ module Shoulda # :nodoc:
39
39
  case x
40
40
  when Regexp
41
41
  assert(!collection.detect { |e| e =~ x }, msg)
42
- else
42
+ else
43
43
  assert(!collection.include?(x), msg)
44
44
  end
45
45
  end
46
46
 
47
47
  # Asserts that the given matcher returns true when +target+ is passed to #matches?
48
48
  def assert_accepts(matcher, target, options = {})
49
+ if matcher.respond_to?(:in_context)
50
+ matcher.in_context(self)
51
+ end
52
+
49
53
  if matcher.matches?(target)
50
54
  assert_block { true }
51
55
  if options[:message]
@@ -58,6 +62,10 @@ module Shoulda # :nodoc:
58
62
 
59
63
  # Asserts that the given matcher returns false when +target+ is passed to #matches?
60
64
  def assert_rejects(matcher, target, options = {})
65
+ if matcher.respond_to?(:in_context)
66
+ matcher.in_context(self)
67
+ end
68
+
61
69
  unless matcher.matches?(target)
62
70
  assert_block { true }
63
71
  if options[:message]
@@ -1,40 +1,40 @@
1
1
  module Shoulda # :nodoc:
2
- # Call autoload_macros when you want to load test macros automatically in a non-Rails
2
+ # Call autoload_macros when you want to load test macros automatically in a non-Rails
3
3
  # project (it's done automatically for Rails projects).
4
4
  # You don't need to specify ROOT/test/shoulda_macros explicitly. Your custom macros
5
5
  # are loaded automatically when you call autoload_macros.
6
6
  #
7
7
  # The first argument is the path to you application's root directory.
8
- # All following arguments are directories relative to your root, which contain
9
- # shoulda_macros subdirectories. These directories support the same kinds of globs as the
8
+ # All following arguments are directories relative to your root, which contain
9
+ # shoulda_macros subdirectories. These directories support the same kinds of globs as the
10
10
  # Dir class.
11
- #
11
+ #
12
12
  # Basic usage (from a test_helper):
13
13
  # Shoulda.autoload_macros(File.dirname(__FILE__) + '/..')
14
- # will load everything in
15
- # - your_app/test/shoulda_macros
14
+ # will load everything in
15
+ # - your_app/test/shoulda_macros
16
16
  #
17
- # To load vendored macros as well:
17
+ # To load vendored macros as well:
18
18
  # Shoulda.autoload_macros(APP_ROOT, 'vendor/*')
19
- # will load everything in
20
- # - APP_ROOT/vendor/*/shoulda_macros
21
- # - APP_ROOT/test/shoulda_macros
19
+ # will load everything in
20
+ # - APP_ROOT/vendor/*/shoulda_macros
21
+ # - APP_ROOT/test/shoulda_macros
22
22
  #
23
- # To load macros in an app with a vendor directory laid out like Rails':
23
+ # To load macros in an app with a vendor directory laid out like Rails':
24
24
  # Shoulda.autoload_macros(APP_ROOT, 'vendor/{plugins,gems}/*')
25
25
  # or
26
26
  # Shoulda.autoload_macros(APP_ROOT, 'vendor/plugins/*', 'vendor/gems/*')
27
- # will load everything in
28
- # - APP_ROOT/vendor/plugins/*/shoulda_macros
29
- # - APP_ROOT/vendor/gems/*/shoulda_macros
30
- # - APP_ROOT/test/shoulda_macros
27
+ # will load everything in
28
+ # - APP_ROOT/vendor/plugins/*/shoulda_macros
29
+ # - APP_ROOT/vendor/gems/*/shoulda_macros
30
+ # - APP_ROOT/test/shoulda_macros
31
31
  #
32
- # If you prefer to stick testing dependencies away from your production dependencies:
32
+ # If you prefer to stick testing dependencies away from your production dependencies:
33
33
  # Shoulda.autoload_macros(APP_ROOT, 'vendor/*', 'test/vendor/*')
34
- # will load everything in
35
- # - APP_ROOT/vendor/*/shoulda_macros
36
- # - APP_ROOT/test/vendor/*/shoulda_macros
37
- # - APP_ROOT/test/shoulda_macros
34
+ # will load everything in
35
+ # - APP_ROOT/vendor/*/shoulda_macros
36
+ # - APP_ROOT/test/vendor/*/shoulda_macros
37
+ # - APP_ROOT/test/shoulda_macros
38
38
  def self.autoload_macros(root, *dirs)
39
39
  dirs << File.join('test')
40
40
  complete_dirs = dirs.map{|d| File.join(root, d, 'shoulda_macros')}
@@ -21,9 +21,10 @@ module Shoulda
21
21
  module ClassMethods
22
22
  # == Should statements
23
23
  #
24
- # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
25
- # contains all the normal code and assertions you're used to seeing, with the added benefit that
26
- # they can be wrapped inside context blocks (see below).
24
+ # Should statements are just syntactic sugar over normal Test::Unit test
25
+ # methods. A should block contains all the normal code and assertions
26
+ # you're used to seeing, with the added benefit that they can be wrapped
27
+ # inside context blocks (see below).
27
28
  #
28
29
  # === Example:
29
30
  #
@@ -56,14 +57,43 @@ module Shoulda
56
57
  # assert true
57
58
  # end
58
59
  # end
60
+ #
61
+ # Should statements can also wrap matchers, making virtually any matcher
62
+ # usable in a macro style. The matcher's description is used to generate a
63
+ # test name and failure message, and the test will pass if the matcher
64
+ # matches the subject.
65
+ #
66
+ # === Example:
67
+ #
68
+ # should validate_presence_of(:first_name).with_message(/gotta be there/)
69
+ #
70
+
71
+ def should(name_or_matcher, options = {}, &blk)
72
+ if Shoulda.current_context
73
+ Shoulda.current_context.should(name_or_matcher, options, &blk)
74
+ else
75
+ context_name = self.name.gsub(/Test/, "")
76
+ context = Shoulda::Context.new(context_name, self) do
77
+ should(name_or_matcher, options, &blk)
78
+ end
79
+ context.build
80
+ end
81
+ end
59
82
 
60
- def should(name, options = {}, &blk)
83
+ # Allows negative tests using matchers. The matcher's description is used
84
+ # to generate a test name and negative failure message, and the test will
85
+ # pass unless the matcher matches the subject.
86
+ #
87
+ # === Example:
88
+ #
89
+ # should_not set_the_flash
90
+ def should_not(matcher)
61
91
  if Shoulda.current_context
62
- block_given? ? Shoulda.current_context.should(name, options, &blk) : Shoulda.current_context.should_eventually(name)
92
+ Shoulda.current_context.should_not(matcher)
63
93
  else
64
94
  context_name = self.name.gsub(/Test/, "")
65
95
  context = Shoulda::Context.new(context_name, self) do
66
- block_given? ? should(name, options, &blk) : should_eventually(name)
96
+ should_not(matcher)
67
97
  end
68
98
  context.build
69
99
  end
@@ -86,7 +116,7 @@ module Shoulda
86
116
  # context "on GET" do
87
117
  # setup { get :index }
88
118
  #
89
- # should_respond_with :success
119
+ # should respond_with(:success)
90
120
  #
91
121
  # # runs before "get :index"
92
122
  # before_should "find all users" do
@@ -185,7 +215,7 @@ module Shoulda
185
215
  # subject { User.first }
186
216
  #
187
217
  # # uses the existing user
188
- # should_validate_uniqueness_of :email
218
+ # should validate_uniqueness_of(:email)
189
219
  # end
190
220
  def subject(&block)
191
221
  @subject_block = block
@@ -214,27 +244,10 @@ module Shoulda
214
244
  # end
215
245
  # end
216
246
  #
217
- # If an instance variable exists named after the described class, that
218
- # instance variable will be used as the subject. This behavior is
219
- # deprecated, and will be removed in a future version of Shoulda. The
220
- # recommended approach for using a different subject is to use the subject
221
- # class method.
222
- #
223
- # class UserTest
224
- # should "be the existing user" do
225
- # @user = User.new
226
- # assert_equal @user, subject # passes
227
- # end
228
- # end
229
- #
230
247
  # The subject is used by all macros that require an instance of the class
231
248
  # being tested.
232
249
  def subject
233
- if subject_block
234
- instance_eval(&subject_block)
235
- else
236
- get_instance_of(self.class.described_type)
237
- end
250
+ @shoulda_subject ||= construct_subject
238
251
  end
239
252
 
240
253
  def subject_block # :nodoc:
@@ -243,17 +256,7 @@ module Shoulda
243
256
 
244
257
  def get_instance_of(object_or_klass) # :nodoc:
245
258
  if object_or_klass.is_a?(Class)
246
- klass = object_or_klass
247
- ivar = "@#{instance_variable_name_for(klass)}"
248
- if instance = instance_variable_get(ivar)
249
- warn "[WARNING] Using #{ivar} as the subject. Future versions " <<
250
- "of Shoulda will require an explicit subject using the " <<
251
- "subject class method. Add this after your setup to avoid " <<
252
- "this warning: subject { #{ivar} }"
253
- instance
254
- else
255
- klass.new
256
- end
259
+ object_or_klass.new
257
260
  else
258
261
  object_or_klass
259
262
  end
@@ -262,6 +265,16 @@ module Shoulda
262
265
  def instance_variable_name_for(klass) # :nodoc:
263
266
  klass.to_s.split('::').last.underscore
264
267
  end
268
+
269
+ private
270
+
271
+ def construct_subject
272
+ if subject_block
273
+ instance_eval(&subject_block)
274
+ else
275
+ get_instance_of(self.class.described_type)
276
+ end
277
+ end
265
278
  end
266
279
 
267
280
  class Context # :nodoc:
@@ -305,14 +318,27 @@ module Shoulda
305
318
  self.teardown_blocks << blk
306
319
  end
307
320
 
308
- def should(name, options = {}, &blk)
309
- if block_given?
321
+ def should(name_or_matcher, options = {}, &blk)
322
+ if name_or_matcher.respond_to?(:description) && name_or_matcher.respond_to?(:matches?)
323
+ name = name_or_matcher.description
324
+ blk = lambda { assert_accepts name_or_matcher, subject }
325
+ else
326
+ name = name_or_matcher
327
+ end
328
+
329
+ if blk
310
330
  self.shoulds << { :name => name, :before => options[:before], :block => blk }
311
331
  else
312
332
  self.should_eventuallys << { :name => name }
313
333
  end
314
334
  end
315
335
 
336
+ def should_not(matcher)
337
+ name = matcher.description
338
+ blk = lambda { assert_rejects matcher, subject }
339
+ self.shoulds << { :name => "not #{name}", :block => blk }
340
+ end
341
+
316
342
  def should_eventually(name, &blk)
317
343
  self.should_eventuallys << { :name => name, :block => blk }
318
344
  end
@@ -321,6 +347,11 @@ module Shoulda
321
347
  self.subject_block = block
322
348
  end
323
349
 
350
+ def subject_block
351
+ return @subject_block if @subject_block
352
+ parent.subject_block
353
+ end
354
+
324
355
  def full_name
325
356
  parent_name = parent.full_name if am_subcontext?
326
357
  return [parent_name, name].join(" ").strip