faster_shoulda 2.11.3

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 (259) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
  2. data/MIT-LICENSE +22 -0
  3. data/README.rdoc +154 -0
  4. data/Rakefile +74 -0
  5. data/bin/convert_to_should_syntax +42 -0
  6. data/lib/shoulda/action_controller/macros.rb +221 -0
  7. data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +114 -0
  8. data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +74 -0
  9. data/lib/shoulda/action_controller/matchers/redirect_to_matcher.rb +62 -0
  10. data/lib/shoulda/action_controller/matchers/render_template_matcher.rb +54 -0
  11. data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +99 -0
  12. data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +74 -0
  13. data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +85 -0
  14. data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
  15. data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +98 -0
  16. data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +100 -0
  17. data/lib/shoulda/action_controller/matchers.rb +39 -0
  18. data/lib/shoulda/action_controller.rb +34 -0
  19. data/lib/shoulda/action_mailer/assertions.rb +42 -0
  20. data/lib/shoulda/action_mailer/matchers/have_sent_email.rb +110 -0
  21. data/lib/shoulda/action_mailer/matchers.rb +22 -0
  22. data/lib/shoulda/action_mailer.rb +13 -0
  23. data/lib/shoulda/active_record/assertions.rb +69 -0
  24. data/lib/shoulda/active_record/helpers.rb +32 -0
  25. data/lib/shoulda/active_record/macros.rb +457 -0
  26. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  27. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +110 -0
  28. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  29. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  30. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  31. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  32. data/lib/shoulda/active_record/matchers/have_db_index_matcher.rb +112 -0
  33. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  34. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  35. data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +65 -0
  36. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  37. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  38. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  39. data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
  40. data/lib/shoulda/active_record/matchers.rb +42 -0
  41. data/lib/shoulda/active_record.rb +16 -0
  42. data/lib/shoulda/assertions.rb +79 -0
  43. data/lib/shoulda/autoload_macros.rb +46 -0
  44. data/lib/shoulda/context.rb +537 -0
  45. data/lib/shoulda/helpers.rb +8 -0
  46. data/lib/shoulda/integrations/rspec.rb +13 -0
  47. data/lib/shoulda/integrations/rspec2.rb +22 -0
  48. data/lib/shoulda/integrations/test_unit.rb +22 -0
  49. data/lib/shoulda/macros.rb +161 -0
  50. data/lib/shoulda/private_helpers.rb +13 -0
  51. data/lib/shoulda/proc_extensions.rb +14 -0
  52. data/lib/shoulda/rails.rb +8 -0
  53. data/lib/shoulda/tasks/list_tests.rake +29 -0
  54. data/lib/shoulda/tasks/yaml_to_shoulda.rake +28 -0
  55. data/lib/shoulda/tasks.rb +3 -0
  56. data/lib/shoulda/version.rb +4 -0
  57. data/lib/shoulda.rb +9 -0
  58. data/rails/init.rb +8 -0
  59. data/test/README +36 -0
  60. data/test/fail_macros.rb +55 -0
  61. data/test/fixtures/addresses.yml +3 -0
  62. data/test/fixtures/friendships.yml +0 -0
  63. data/test/fixtures/posts.yml +5 -0
  64. data/test/fixtures/products.yml +0 -0
  65. data/test/fixtures/taggings.yml +0 -0
  66. data/test/fixtures/tags.yml +9 -0
  67. data/test/fixtures/users.yml +6 -0
  68. data/test/functional/posts_controller_test.rb +121 -0
  69. data/test/functional/users_controller_test.rb +19 -0
  70. data/test/hello_should.rb +37 -0
  71. data/test/matchers/action_mailer/have_sent_email_test.rb +76 -0
  72. data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +74 -0
  73. data/test/matchers/active_record/allow_value_matcher_test.rb +64 -0
  74. data/test/matchers/active_record/association_matcher_test.rb +263 -0
  75. data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +81 -0
  76. data/test/matchers/active_record/ensure_length_of_matcher_test.rb +158 -0
  77. data/test/matchers/active_record/have_db_column_matcher_test.rb +169 -0
  78. data/test/matchers/active_record/have_db_index_matcher_test.rb +91 -0
  79. data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +29 -0
  80. data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +44 -0
  81. data/test/matchers/active_record/validate_format_of_matcher_test.rb +39 -0
  82. data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +52 -0
  83. data/test/matchers/active_record/validate_presence_of_matcher_test.rb +86 -0
  84. data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +147 -0
  85. data/test/matchers/controller/assign_to_matcher_test.rb +55 -0
  86. data/test/matchers/controller/filter_param_matcher_test.rb +40 -0
  87. data/test/matchers/controller/redirect_to_matcher_test.rb +37 -0
  88. data/test/matchers/controller/render_template_matcher_test.rb +37 -0
  89. data/test/matchers/controller/render_with_layout_matcher_test.rb +47 -0
  90. data/test/matchers/controller/respond_with_content_type_matcher_test.rb +32 -0
  91. data/test/matchers/controller/respond_with_matcher_test.rb +96 -0
  92. data/test/matchers/controller/route_matcher_test.rb +75 -0
  93. data/test/matchers/controller/set_session_matcher_test.rb +48 -0
  94. data/test/matchers/controller/set_the_flash_matcher.rb +95 -0
  95. data/test/other/autoload_macro_test.rb +18 -0
  96. data/test/other/context_test.rb +372 -0
  97. data/test/other/convert_to_should_syntax_test.rb +63 -0
  98. data/test/other/helpers_test.rb +317 -0
  99. data/test/other/private_helpers_test.rb +32 -0
  100. data/test/other/should_test.rb +271 -0
  101. data/test/rails2_model_builder.rb +130 -0
  102. data/test/rails2_root/app/controllers/application_controller.rb +22 -0
  103. data/test/rails2_root/app/controllers/posts_controller.rb +87 -0
  104. data/test/rails2_root/app/controllers/users_controller.rb +84 -0
  105. data/test/rails2_root/app/helpers/application_helper.rb +3 -0
  106. data/test/rails2_root/app/helpers/posts_helper.rb +2 -0
  107. data/test/rails2_root/app/helpers/users_helper.rb +2 -0
  108. data/test/rails2_root/app/models/address.rb +7 -0
  109. data/test/rails2_root/app/models/flea.rb +11 -0
  110. data/test/rails2_root/app/models/friendship.rb +4 -0
  111. data/test/rails2_root/app/models/notifier.rb +8 -0
  112. data/test/rails2_root/app/models/pets/cat.rb +7 -0
  113. data/test/rails2_root/app/models/pets/dog.rb +10 -0
  114. data/test/rails2_root/app/models/post.rb +12 -0
  115. data/test/rails2_root/app/models/product.rb +12 -0
  116. data/test/rails2_root/app/models/profile.rb +2 -0
  117. data/test/rails2_root/app/models/registration.rb +2 -0
  118. data/test/rails2_root/app/models/tag.rb +8 -0
  119. data/test/rails2_root/app/models/tagging.rb +4 -0
  120. data/test/rails2_root/app/models/treat.rb +3 -0
  121. data/test/rails2_root/app/models/user.rb +32 -0
  122. data/test/rails2_root/app/views/layouts/posts.rhtml +19 -0
  123. data/test/rails2_root/app/views/layouts/users.rhtml +17 -0
  124. data/test/rails2_root/app/views/layouts/wide.html.erb +1 -0
  125. data/test/rails2_root/app/views/notifier/the_email.html.erb +1 -0
  126. data/test/rails2_root/app/views/posts/edit.rhtml +27 -0
  127. data/test/rails2_root/app/views/posts/index.rhtml +25 -0
  128. data/test/rails2_root/app/views/posts/new.rhtml +26 -0
  129. data/test/rails2_root/app/views/posts/show.rhtml +18 -0
  130. data/test/rails2_root/app/views/users/edit.rhtml +22 -0
  131. data/test/rails2_root/app/views/users/index.rhtml +22 -0
  132. data/test/rails2_root/app/views/users/new.rhtml +21 -0
  133. data/test/rails2_root/app/views/users/show.rhtml +13 -0
  134. data/test/rails2_root/config/boot.rb +110 -0
  135. data/test/rails2_root/config/database.yml +4 -0
  136. data/test/rails2_root/config/environment.rb +17 -0
  137. data/test/rails2_root/config/environments/test.rb +23 -0
  138. data/test/rails2_root/config/initializers/new_rails_defaults.rb +15 -0
  139. data/test/rails2_root/config/initializers/shoulda.rb +8 -0
  140. data/test/rails2_root/config/routes.rb +6 -0
  141. data/test/rails2_root/db/migrate/001_create_users.rb +19 -0
  142. data/test/rails2_root/db/migrate/002_create_posts.rb +13 -0
  143. data/test/rails2_root/db/migrate/003_create_taggings.rb +12 -0
  144. data/test/rails2_root/db/migrate/004_create_tags.rb +11 -0
  145. data/test/rails2_root/db/migrate/005_create_dogs.rb +12 -0
  146. data/test/rails2_root/db/migrate/006_create_addresses.rb +14 -0
  147. data/test/rails2_root/db/migrate/007_create_fleas.rb +11 -0
  148. data/test/rails2_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  149. data/test/rails2_root/db/migrate/009_create_products.rb +17 -0
  150. data/test/rails2_root/db/migrate/010_create_friendships.rb +14 -0
  151. data/test/rails2_root/db/migrate/011_create_treats.rb +12 -0
  152. data/test/rails2_root/db/migrate/20090506203502_create_profiles.rb +12 -0
  153. data/test/rails2_root/db/migrate/20090506203536_create_registrations.rb +14 -0
  154. data/test/rails2_root/db/migrate/20090513104502_create_cats.rb +12 -0
  155. data/test/rails2_root/db/schema.rb +0 -0
  156. data/test/rails2_root/public/404.html +30 -0
  157. data/test/rails2_root/public/422.html +30 -0
  158. data/test/rails2_root/public/500.html +30 -0
  159. data/test/rails2_root/script/console +3 -0
  160. data/test/rails2_root/script/generate +3 -0
  161. data/test/rails2_root/test/shoulda_macros/custom_macro.rb +6 -0
  162. data/test/rails2_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  163. data/test/rails2_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  164. data/test/rails2_test_helper.rb +6 -0
  165. data/test/rails3_model_builder.rb +118 -0
  166. data/test/rails3_root/Gemfile +28 -0
  167. data/test/rails3_root/README +244 -0
  168. data/test/rails3_root/Rakefile +10 -0
  169. data/test/rails3_root/app/controllers/application_controller.rb +22 -0
  170. data/test/rails3_root/app/controllers/posts_controller.rb +87 -0
  171. data/test/rails3_root/app/controllers/users_controller.rb +82 -0
  172. data/test/rails3_root/app/helpers/application_helper.rb +2 -0
  173. data/test/rails3_root/app/models/address.rb +7 -0
  174. data/test/rails3_root/app/models/flea.rb +11 -0
  175. data/test/rails3_root/app/models/friendship.rb +4 -0
  176. data/test/rails3_root/app/models/notifier.rb +8 -0
  177. data/test/rails3_root/app/models/pets/cat.rb +7 -0
  178. data/test/rails3_root/app/models/pets/dog.rb +10 -0
  179. data/test/rails3_root/app/models/post.rb +12 -0
  180. data/test/rails3_root/app/models/product.rb +12 -0
  181. data/test/rails3_root/app/models/profile.rb +2 -0
  182. data/test/rails3_root/app/models/registration.rb +2 -0
  183. data/test/rails3_root/app/models/tag.rb +8 -0
  184. data/test/rails3_root/app/models/tagging.rb +4 -0
  185. data/test/rails3_root/app/models/treat.rb +3 -0
  186. data/test/rails3_root/app/models/user.rb +32 -0
  187. data/test/rails3_root/app/views/layouts/application.html.erb +14 -0
  188. data/test/rails3_root/app/views/layouts/posts.rhtml +19 -0
  189. data/test/rails3_root/app/views/layouts/users.rhtml +17 -0
  190. data/test/rails3_root/app/views/layouts/wide.html.erb +1 -0
  191. data/test/rails3_root/app/views/notifier/the_email.html.erb +1 -0
  192. data/test/rails3_root/app/views/posts/edit.rhtml +27 -0
  193. data/test/rails3_root/app/views/posts/index.rhtml +25 -0
  194. data/test/rails3_root/app/views/posts/new.rhtml +24 -0
  195. data/test/rails3_root/app/views/posts/show.rhtml +18 -0
  196. data/test/rails3_root/app/views/users/edit.rhtml +22 -0
  197. data/test/rails3_root/app/views/users/index.rhtml +22 -0
  198. data/test/rails3_root/app/views/users/new.rhtml +21 -0
  199. data/test/rails3_root/app/views/users/show.rhtml +13 -0
  200. data/test/rails3_root/config/application.rb +46 -0
  201. data/test/rails3_root/config/boot.rb +6 -0
  202. data/test/rails3_root/config/database.yml +22 -0
  203. data/test/rails3_root/config/environment.rb +5 -0
  204. data/test/rails3_root/config/environments/development.rb +19 -0
  205. data/test/rails3_root/config/environments/production.rb +42 -0
  206. data/test/rails3_root/config/environments/test.rb +32 -0
  207. data/test/rails3_root/config/initializers/backtrace_silencers.rb +7 -0
  208. data/test/rails3_root/config/initializers/inflections.rb +10 -0
  209. data/test/rails3_root/config/initializers/mime_types.rb +5 -0
  210. data/test/rails3_root/config/initializers/secret_token.rb +7 -0
  211. data/test/rails3_root/config/initializers/session_store.rb +8 -0
  212. data/test/rails3_root/config/locales/en.yml +5 -0
  213. data/test/rails3_root/config/routes.rb +4 -0
  214. data/test/rails3_root/config.ru +4 -0
  215. data/test/rails3_root/db/migrate/001_create_users.rb +19 -0
  216. data/test/rails3_root/db/migrate/002_create_posts.rb +13 -0
  217. data/test/rails3_root/db/migrate/003_create_taggings.rb +12 -0
  218. data/test/rails3_root/db/migrate/004_create_tags.rb +11 -0
  219. data/test/rails3_root/db/migrate/005_create_dogs.rb +12 -0
  220. data/test/rails3_root/db/migrate/006_create_addresses.rb +14 -0
  221. data/test/rails3_root/db/migrate/007_create_fleas.rb +11 -0
  222. data/test/rails3_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  223. data/test/rails3_root/db/migrate/009_create_products.rb +17 -0
  224. data/test/rails3_root/db/migrate/010_create_friendships.rb +14 -0
  225. data/test/rails3_root/db/migrate/011_create_treats.rb +12 -0
  226. data/test/rails3_root/db/migrate/20090506203502_create_profiles.rb +12 -0
  227. data/test/rails3_root/db/migrate/20090506203536_create_registrations.rb +14 -0
  228. data/test/rails3_root/db/migrate/20090513104502_create_cats.rb +12 -0
  229. data/test/rails3_root/db/seeds.rb +7 -0
  230. data/test/rails3_root/public/404.html +26 -0
  231. data/test/rails3_root/public/422.html +26 -0
  232. data/test/rails3_root/public/500.html +26 -0
  233. data/test/rails3_root/public/favicon.ico +0 -0
  234. data/test/rails3_root/public/images/rails.png +0 -0
  235. data/test/rails3_root/public/index.html +279 -0
  236. data/test/rails3_root/public/javascripts/application.js +2 -0
  237. data/test/rails3_root/public/javascripts/controls.js +965 -0
  238. data/test/rails3_root/public/javascripts/dragdrop.js +974 -0
  239. data/test/rails3_root/public/javascripts/effects.js +1123 -0
  240. data/test/rails3_root/public/javascripts/prototype.js +4874 -0
  241. data/test/rails3_root/public/javascripts/rails.js +118 -0
  242. data/test/rails3_root/public/robots.txt +5 -0
  243. data/test/rails3_root/script/rails +9 -0
  244. data/test/rails3_root/test/performance/browsing_test.rb +9 -0
  245. data/test/rails3_root/test/test_helper.rb +13 -0
  246. data/test/rails3_test_helper.rb +6 -0
  247. data/test/rspec_test.rb +207 -0
  248. data/test/test_helper.rb +36 -0
  249. data/test/unit/address_test.rb +10 -0
  250. data/test/unit/cat_test.rb +7 -0
  251. data/test/unit/dog_test.rb +9 -0
  252. data/test/unit/flea_test.rb +14 -0
  253. data/test/unit/friendship_test.rb +6 -0
  254. data/test/unit/post_test.rb +15 -0
  255. data/test/unit/product_test.rb +23 -0
  256. data/test/unit/tag_test.rb +11 -0
  257. data/test/unit/tagging_test.rb +6 -0
  258. data/test/unit/user_test.rb +46 -0
  259. metadata +325 -0
@@ -0,0 +1,112 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that there are DB indices on the given columns or tuples of
6
+ # columns.
7
+ #
8
+ # Options:
9
+ # * <tt>unique</tt> - whether or not the index has a unique
10
+ # constraint. Use <tt>true</tt> to explicitly test for a unique
11
+ # constraint. Use <tt>false</tt> to explicitly test for a non-unique
12
+ # constraint. Use <tt>nil</tt> if you don't care whether the index is
13
+ # unique or not. Default = <tt>nil</tt>
14
+ #
15
+ # Examples:
16
+ #
17
+ # it { should have_db_index(:age) }
18
+ # it { should have_db_index([:commentable_type, :commentable_id]) }
19
+ # it { should have_db_index(:ssn).unique(true) }
20
+ #
21
+ def have_db_index(columns)
22
+ HaveDbIndexMatcher.new(:have_index, columns)
23
+ end
24
+
25
+ class HaveDbIndexMatcher # :nodoc:
26
+ def initialize(macro, columns)
27
+ @macro = macro
28
+ @columns = normalize_columns_to_array(columns)
29
+ end
30
+
31
+ def unique(unique)
32
+ @unique = unique
33
+ self
34
+ end
35
+
36
+ def matches?(subject)
37
+ @subject = subject
38
+ index_exists? && correct_unique?
39
+ end
40
+
41
+ def failure_message
42
+ "Expected #{expectation} (#{@missing})"
43
+ end
44
+
45
+ def negative_failure_message
46
+ "Did not expect #{expectation}"
47
+ end
48
+
49
+ def description
50
+ "have a #{index_type} index on columns #{@columns.join(' and ')}"
51
+ end
52
+
53
+ protected
54
+
55
+ def index_exists?
56
+ ! matched_index.nil?
57
+ end
58
+
59
+ def correct_unique?
60
+ return true if @unique.nil?
61
+ if matched_index.unique == @unique
62
+ true
63
+ else
64
+ @missing = "#{table_name} has an index named #{matched_index.name} " <<
65
+ "of unique #{matched_index.unique}, not #{@unique}."
66
+ false
67
+ end
68
+ end
69
+
70
+ def matched_index
71
+ indexes.detect { |each| each.columns == @columns }
72
+ end
73
+
74
+ def model_class
75
+ @subject.class
76
+ end
77
+
78
+ def table_name
79
+ model_class.table_name
80
+ end
81
+
82
+ def indexes
83
+ ::ActiveRecord::Base.connection.indexes(table_name)
84
+ end
85
+
86
+ def expectation
87
+ expected = "#{model_class.name} to #{description}"
88
+ end
89
+
90
+ def index_type
91
+ case @unique
92
+ when nil
93
+ ''
94
+ when false
95
+ 'non-unique'
96
+ else
97
+ 'unique'
98
+ end
99
+ end
100
+
101
+ def normalize_columns_to_array(columns)
102
+ if columns.class == Array
103
+ columns.collect { |each| each.to_s }
104
+ else
105
+ [columns.to_s]
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,59 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the attribute cannot be changed once the record has been
6
+ # created.
7
+ #
8
+ # it { should have_readonly_attributes(:password) }
9
+ #
10
+ def have_readonly_attribute(value)
11
+ HaveReadonlyAttributeMatcher.new(value)
12
+ end
13
+
14
+ class HaveReadonlyAttributeMatcher # :nodoc:
15
+
16
+ def initialize(attribute)
17
+ @attribute = attribute.to_s
18
+ end
19
+
20
+ def matches?(subject)
21
+ @subject = subject
22
+ if readonly_attributes.include?(@attribute)
23
+ @negative_failure_message =
24
+ "Did not expect #{@attribute} to be read-only"
25
+ true
26
+ else
27
+ if readonly_attributes.empty?
28
+ @failure_message = "#{class_name} attribute #{@attribute} " <<
29
+ "is not read-only"
30
+ else
31
+ @failure_message = "#{class_name} is making " <<
32
+ "#{readonly_attributes.to_sentence} " <<
33
+ "read-only, but not #{@attribute}."
34
+ end
35
+ false
36
+ end
37
+ end
38
+
39
+ attr_reader :failure_message, :negative_failure_message
40
+
41
+ def description
42
+ "make #{@attribute} read-only"
43
+ end
44
+
45
+ private
46
+
47
+ def readonly_attributes
48
+ @readonly_attributes ||= (@subject.class.readonly_attributes || [])
49
+ end
50
+
51
+ def class_name
52
+ @subject.class.name
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,41 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the model cannot be saved the given attribute is not
6
+ # accepted.
7
+ #
8
+ # Options:
9
+ # * <tt>with_message</tt> - value the test expects to find in
10
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
11
+ # translation for <tt>:accepted</tt>.
12
+ #
13
+ # Example:
14
+ # it { should validate_acceptance_of(:eula) }
15
+ #
16
+ def validate_acceptance_of(attr)
17
+ ValidateAcceptanceOfMatcher.new(attr)
18
+ end
19
+
20
+ class ValidateAcceptanceOfMatcher < ValidationMatcher # :nodoc:
21
+
22
+ def with_message(message)
23
+ @expected_message = message if message
24
+ self
25
+ end
26
+
27
+ def matches?(subject)
28
+ super(subject)
29
+ @expected_message ||= :accepted
30
+ disallows_value_of(false, @expected_message)
31
+ end
32
+
33
+ def description
34
+ "require #{@attribute} to be accepted"
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,65 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the model is not valid if the given attribute is not
6
+ # formatted correctly.
7
+ #
8
+ # Options:
9
+ # * <tt>with_message</tt> - value the test expects to find in
10
+ # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
11
+ # Defaults to the translation for <tt>:blank</tt>.
12
+ # * <tt>with(string to test against)</tt>
13
+ # * <tt>not_with(string to test against)</tt>
14
+ #
15
+ # Examples:
16
+ # it { should validate_format_of(:name).
17
+ # with('12345').
18
+ # with_message(/is not optional/) }
19
+ # it { should validate_format_of(:name).
20
+ # not_with('12D45').
21
+ # with_message(/is not optional/) }
22
+ #
23
+ def validate_format_of(attr)
24
+ ValidateFormatOfMatcher.new(attr)
25
+ end
26
+
27
+ class ValidateFormatOfMatcher < ValidationMatcher # :nodoc:
28
+
29
+ def initialize(attribute)
30
+ super
31
+ end
32
+
33
+ def with_message(message)
34
+ @expected_message = message if message
35
+ self
36
+ end
37
+
38
+ def with(value)
39
+ raise "You may not call both with and not_with" if @value_to_fail
40
+ @value_to_pass = value
41
+ self
42
+ end
43
+
44
+ def not_with(value)
45
+ raise "You may not call both with and not_with" if @value_to_pass
46
+ @value_to_fail = value
47
+ self
48
+ end
49
+
50
+ def matches?(subject)
51
+ super(subject)
52
+ @expected_message ||= :blank
53
+ return disallows_value_of(@value_to_fail, @expected_message) if @value_to_fail
54
+ allows_value_of(@value_to_pass, @expected_message) if @value_to_pass
55
+ end
56
+
57
+ def description
58
+ "#{@attribute} have a valid format"
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,39 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensure that the attribute is numeric
6
+ #
7
+ # Options:
8
+ # * <tt>with_message</tt> - value the test expects to find in
9
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
10
+ # translation for <tt>:not_a_number</tt>.
11
+ #
12
+ # Example:
13
+ # it { should validate_numericality_of(:age) }
14
+ #
15
+ def validate_numericality_of(attr)
16
+ ValidateNumericalityOfMatcher.new(attr)
17
+ end
18
+
19
+ class ValidateNumericalityOfMatcher < ValidationMatcher # :nodoc:
20
+
21
+ def with_message(message)
22
+ @expected_message = message if message
23
+ self
24
+ end
25
+
26
+ def matches?(subject)
27
+ super(subject)
28
+ @expected_message ||= :not_a_number
29
+ disallows_value_of('abcd', @expected_message)
30
+ end
31
+
32
+ def description
33
+ "only allow numeric values for #{@attribute}"
34
+ end
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,60 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the model is not valid if the given attribute is not
6
+ # present.
7
+ #
8
+ # Options:
9
+ # * <tt>with_message</tt> - value the test expects to find in
10
+ # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
11
+ # Defaults to the translation for <tt>:blank</tt>.
12
+ #
13
+ # Examples:
14
+ # it { should validate_presence_of(:name) }
15
+ # it { should validate_presence_of(:name).
16
+ # with_message(/is not optional/) }
17
+ #
18
+ def validate_presence_of(attr)
19
+ ValidatePresenceOfMatcher.new(attr)
20
+ end
21
+
22
+ class ValidatePresenceOfMatcher < ValidationMatcher # :nodoc:
23
+
24
+ def with_message(message)
25
+ @expected_message = message if message
26
+ self
27
+ end
28
+
29
+ def matches?(subject)
30
+ super(subject)
31
+ @expected_message ||= :blank
32
+ disallows_value_of(blank_value, @expected_message)
33
+ end
34
+
35
+ def description
36
+ "require #{@attribute} to be set"
37
+ end
38
+
39
+ private
40
+
41
+ def blank_value
42
+ if collection?
43
+ []
44
+ else
45
+ nil
46
+ end
47
+ end
48
+
49
+ def collection?
50
+ if reflection = @subject.class.reflect_on_association(@attribute)
51
+ [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
52
+ else
53
+ false
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,148 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the model is invalid if the given attribute is not unique.
6
+ #
7
+ # Internally, this uses values from existing records to test validations,
8
+ # so this will always fail if you have not saved at least one record for
9
+ # the model being tested, like so:
10
+ #
11
+ # describe User do
12
+ # before(:each) { User.create!(:email => 'address@example.com') }
13
+ # it { should validate_uniqueness_of(:email) }
14
+ # end
15
+ #
16
+ # Options:
17
+ #
18
+ # * <tt>with_message</tt> - value the test expects to find in
19
+ # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
20
+ # Defaults to the translation for <tt>:taken</tt>.
21
+ # * <tt>scoped_to</tt> - field(s) to scope the uniqueness to.
22
+ # * <tt>case_insensitive</tt> - ensures that the validation does not
23
+ # check case. Off by default. Ignored by non-text attributes.
24
+ #
25
+ # Examples:
26
+ # it { should validate_uniqueness_of(:keyword) }
27
+ # it { should validate_uniqueness_of(:keyword).with_message(/dup/) }
28
+ # it { should validate_uniqueness_of(:email).scoped_to(:name) }
29
+ # it { should validate_uniqueness_of(:email).
30
+ # scoped_to(:first_name, :last_name) }
31
+ # it { should validate_uniqueness_of(:keyword).case_insensitive }
32
+ #
33
+ def validate_uniqueness_of(attr)
34
+ ValidateUniquenessOfMatcher.new(attr)
35
+ end
36
+
37
+ class ValidateUniquenessOfMatcher < ValidationMatcher # :nodoc:
38
+ include Helpers
39
+
40
+ def initialize(attribute)
41
+ @attribute = attribute
42
+ end
43
+
44
+ def scoped_to(*scopes)
45
+ @scopes = [*scopes].flatten
46
+ self
47
+ end
48
+
49
+ def with_message(message)
50
+ @expected_message = message
51
+ self
52
+ end
53
+
54
+ def case_insensitive
55
+ @case_insensitive = true
56
+ self
57
+ end
58
+
59
+ def description
60
+ result = "require "
61
+ result << "case sensitive " unless @case_insensitive
62
+ result << "unique value for #{@attribute}"
63
+ result << " scoped to #{@scopes.join(', ')}" unless @scopes.blank?
64
+ result
65
+ end
66
+
67
+ def matches?(subject)
68
+ @subject = subject.class.new
69
+ @expected_message ||= :taken
70
+ find_existing &&
71
+ set_scoped_attributes &&
72
+ validate_attribute &&
73
+ validate_after_scope_change
74
+ end
75
+
76
+ private
77
+
78
+ def find_existing
79
+ if @existing = @subject.class.find(:first)
80
+ true
81
+ else
82
+ @failure_message = "Can't find first #{class_name}"
83
+ false
84
+ end
85
+ end
86
+
87
+ def set_scoped_attributes
88
+ unless @scopes.blank?
89
+ @scopes.each do |scope|
90
+ setter = :"#{scope}="
91
+ unless @subject.respond_to?(setter)
92
+ @failure_message =
93
+ "#{class_name} doesn't seem to have a #{scope} attribute."
94
+ return false
95
+ end
96
+ @subject.send("#{scope}=", @existing.send(scope))
97
+ end
98
+ end
99
+ true
100
+ end
101
+
102
+ def validate_attribute
103
+ disallows_value_of(existing_value, @expected_message)
104
+ end
105
+
106
+ # TODO: There is a chance that we could change the scoped field
107
+ # to a value that's already taken. An alternative implementation
108
+ # could actually find all values for scope and create a unique
109
+ def validate_after_scope_change
110
+ if @scopes.blank?
111
+ true
112
+ else
113
+ @scopes.all? do |scope|
114
+ previous_value = @existing.send(scope)
115
+
116
+ # Assume the scope is a foreign key if the field is nil
117
+ previous_value ||= 0
118
+
119
+ next_value = previous_value.next
120
+
121
+ @subject.send("#{scope}=", next_value)
122
+
123
+ if allows_value_of(existing_value, @expected_message)
124
+ @negative_failure_message <<
125
+ " (with different value of #{scope})"
126
+ true
127
+ else
128
+ @failure_message << " (with different value of #{scope})"
129
+ false
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def class_name
136
+ @subject.class.name
137
+ end
138
+
139
+ def existing_value
140
+ value = @existing.send(@attribute)
141
+ value.swapcase! if @case_insensitive && value.respond_to?(:swapcase!)
142
+ value
143
+ end
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,56 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ class ValidationMatcher # :nodoc:
6
+
7
+ attr_reader :failure_message
8
+
9
+ def initialize(attribute)
10
+ @attribute = attribute
11
+ end
12
+
13
+ def negative_failure_message
14
+ @negative_failure_message || @failure_message
15
+ end
16
+
17
+ def matches?(subject)
18
+ @subject = subject
19
+ false
20
+ end
21
+
22
+ private
23
+
24
+ def allows_value_of(value, message = nil)
25
+ allow = AllowValueMatcher.
26
+ new(value).
27
+ for(@attribute).
28
+ with_message(message)
29
+ if allow.matches?(@subject)
30
+ @negative_failure_message = allow.failure_message
31
+ true
32
+ else
33
+ @failure_message = allow.negative_failure_message
34
+ false
35
+ end
36
+ end
37
+
38
+ def disallows_value_of(value, message = nil)
39
+ disallow = AllowValueMatcher.
40
+ new(value).
41
+ for(@attribute).
42
+ with_message(message)
43
+ if disallow.matches?(@subject)
44
+ @failure_message = disallow.negative_failure_message
45
+ false
46
+ else
47
+ @negative_failure_message = disallow.failure_message
48
+ true
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
@@ -0,0 +1,42 @@
1
+ require 'shoulda/active_record/helpers'
2
+ require 'shoulda/active_record/matchers/validation_matcher'
3
+ require 'shoulda/active_record/matchers/allow_value_matcher'
4
+ require 'shoulda/active_record/matchers/ensure_length_of_matcher'
5
+ require 'shoulda/active_record/matchers/ensure_inclusion_of_matcher'
6
+ require 'shoulda/active_record/matchers/validate_presence_of_matcher'
7
+ require 'shoulda/active_record/matchers/validate_format_of_matcher'
8
+ require 'shoulda/active_record/matchers/validate_uniqueness_of_matcher'
9
+ require 'shoulda/active_record/matchers/validate_acceptance_of_matcher'
10
+ require 'shoulda/active_record/matchers/validate_numericality_of_matcher'
11
+ require 'shoulda/active_record/matchers/association_matcher'
12
+ require 'shoulda/active_record/matchers/have_db_column_matcher'
13
+ require 'shoulda/active_record/matchers/have_db_index_matcher'
14
+ require 'shoulda/active_record/matchers/have_readonly_attribute_matcher'
15
+ require 'shoulda/active_record/matchers/allow_mass_assignment_of_matcher'
16
+
17
+
18
+ module Shoulda # :nodoc:
19
+ module ActiveRecord # :nodoc:
20
+ # = Matchers for your active record models
21
+ #
22
+ # These matchers will test most of the validations and associations for your
23
+ # ActiveRecord models.
24
+ #
25
+ # describe User do
26
+ # it { should validate_presence_of(:name) }
27
+ # it { should validate_presence_of(:phone_number) }
28
+ # %w(abcd 1234).each do |value|
29
+ # it { should_not allow_value(value).for(:phone_number) }
30
+ # end
31
+ # it { should allow_value("(123) 456-7890").for(:phone_number) }
32
+ # it { should_not allow_mass_assignment_of(:password) }
33
+ # it { should have_one(:profile) }
34
+ # it { should have_many(:dogs) }
35
+ # it { should have_many(:messes).through(:dogs) }
36
+ # it { should belong_to(:lover) }
37
+ # end
38
+ #
39
+ module Matchers
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,16 @@
1
+ require 'shoulda'
2
+ require 'shoulda/active_record/helpers'
3
+ require 'shoulda/active_record/matchers'
4
+ require 'shoulda/active_record/assertions'
5
+ require 'shoulda/active_record/macros'
6
+
7
+ module Test # :nodoc: all
8
+ module Unit
9
+ class TestCase
10
+ include Shoulda::ActiveRecord::Helpers
11
+ include Shoulda::ActiveRecord::Matchers
12
+ include Shoulda::ActiveRecord::Assertions
13
+ extend Shoulda::ActiveRecord::Macros
14
+ end
15
+ end
16
+ end