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,226 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensure that the belongs_to relationship exists.
6
+ #
7
+ # it { should belong_to(:parent) }
8
+ #
9
+ def belong_to(name)
10
+ AssociationMatcher.new(:belongs_to, name)
11
+ end
12
+
13
+ # Ensures that the has_many relationship exists. Will also test that the
14
+ # associated table has the required columns. Works with polymorphic
15
+ # associations.
16
+ #
17
+ # Options:
18
+ # * <tt>through</tt> - association name for <tt>has_many :through</tt>
19
+ # * <tt>dependent</tt> - tests that the association makes use of the
20
+ # dependent option.
21
+ #
22
+ # Example:
23
+ # it { should have_many(:friends) }
24
+ # it { should have_many(:enemies).through(:friends) }
25
+ # it { should have_many(:enemies).dependent(:destroy) }
26
+ #
27
+ def have_many(name)
28
+ AssociationMatcher.new(:has_many, name)
29
+ end
30
+
31
+ # Ensure that the has_one relationship exists. Will also test that the
32
+ # associated table has the required columns. Works with polymorphic
33
+ # associations.
34
+ #
35
+ # Options:
36
+ # * <tt>:dependent</tt> - tests that the association makes use of the
37
+ # dependent option.
38
+ #
39
+ # Example:
40
+ # it { should have_one(:god) } # unless hindu
41
+ #
42
+ def have_one(name)
43
+ AssociationMatcher.new(:has_one, name)
44
+ end
45
+
46
+ # Ensures that the has_and_belongs_to_many relationship exists, and that
47
+ # the join table is in place.
48
+ #
49
+ # it { should have_and_belong_to_many(:posts) }
50
+ #
51
+ def have_and_belong_to_many(name)
52
+ AssociationMatcher.new(:has_and_belongs_to_many, name)
53
+ end
54
+
55
+ class AssociationMatcher # :nodoc:
56
+ def initialize(macro, name)
57
+ @macro = macro
58
+ @name = name
59
+ end
60
+
61
+ def through(through)
62
+ @through = through
63
+ self
64
+ end
65
+
66
+ def dependent(dependent)
67
+ @dependent = dependent
68
+ self
69
+ end
70
+
71
+ def matches?(subject)
72
+ @subject = subject
73
+ association_exists? &&
74
+ macro_correct? &&
75
+ foreign_key_exists? &&
76
+ through_association_valid? &&
77
+ dependent_correct? &&
78
+ join_table_exists?
79
+ end
80
+
81
+ def failure_message
82
+ "Expected #{expectation} (#{@missing})"
83
+ end
84
+
85
+ def negative_failure_message
86
+ "Did not expect #{expectation}"
87
+ end
88
+
89
+ def description
90
+ description = "#{macro_description} #{@name}"
91
+ description += " through #{@through}" if @through
92
+ description += " dependent => #{@dependent}" if @dependent
93
+ description
94
+ end
95
+
96
+ protected
97
+
98
+ def association_exists?
99
+ if reflection.nil?
100
+ @missing = "no association called #{@name}"
101
+ false
102
+ else
103
+ true
104
+ end
105
+ end
106
+
107
+ def macro_correct?
108
+ if reflection.macro == @macro
109
+ true
110
+ else
111
+ @missing = "actual association type was #{reflection.macro}"
112
+ false
113
+ end
114
+ end
115
+
116
+ def foreign_key_exists?
117
+ !(belongs_foreign_key_missing? || has_foreign_key_missing?)
118
+ end
119
+
120
+ def belongs_foreign_key_missing?
121
+ @macro == :belongs_to && !class_has_foreign_key?(model_class)
122
+ end
123
+
124
+ def has_foreign_key_missing?
125
+ [:has_many, :has_one].include?(@macro) &&
126
+ !through? &&
127
+ !class_has_foreign_key?(associated_class)
128
+ end
129
+
130
+ def through_association_valid?
131
+ @through.nil? || (through_association_exists? && through_association_correct?)
132
+ end
133
+
134
+ def through_association_exists?
135
+ if through_reflection.nil?
136
+ @missing = "#{model_class.name} does not have any relationship to #{@through}"
137
+ false
138
+ else
139
+ true
140
+ end
141
+ end
142
+
143
+ def through_association_correct?
144
+ if @through == reflection.options[:through]
145
+ true
146
+ else
147
+ @missing = "Expected #{model_class.name} to have #{@name} through #{@through}, " <<
148
+ "but got it through #{reflection.options[:through]}"
149
+ false
150
+ end
151
+ end
152
+
153
+ def dependent_correct?
154
+ if @dependent.nil? || @dependent.to_s == reflection.options[:dependent].to_s
155
+ true
156
+ else
157
+ @missing = "#{@name} should have #{@dependent} dependency"
158
+ false
159
+ end
160
+ end
161
+
162
+ def join_table_exists?
163
+ if @macro != :has_and_belongs_to_many ||
164
+ ::ActiveRecord::Base.connection.tables.include?(join_table.to_s)
165
+ true
166
+ else
167
+ @missing = "join table #{join_table} doesn't exist"
168
+ false
169
+ end
170
+ end
171
+
172
+ def class_has_foreign_key?(klass)
173
+ if klass.column_names.include?(foreign_key.to_s)
174
+ true
175
+ else
176
+ @missing = "#{klass} does not have a #{foreign_key} foreign key."
177
+ false
178
+ end
179
+ end
180
+
181
+ def model_class
182
+ @subject.class
183
+ end
184
+
185
+ def join_table
186
+ reflection.options[:join_table]
187
+ end
188
+
189
+ def associated_class
190
+ reflection.klass
191
+ end
192
+
193
+ def foreign_key
194
+ reflection.primary_key_name
195
+ end
196
+
197
+ def through?
198
+ reflection.options[:through]
199
+ end
200
+
201
+ def reflection
202
+ @reflection ||= model_class.reflect_on_association(@name)
203
+ end
204
+
205
+ def through_reflection
206
+ @through_reflection ||= model_class.reflect_on_association(@through)
207
+ end
208
+
209
+ def expectation
210
+ "#{model_class.name} to have a #{@macro} association called #{@name}"
211
+ end
212
+
213
+ def macro_description
214
+ case @macro.to_s
215
+ when 'belongs_to' then 'belong to'
216
+ when 'has_many' then 'have many'
217
+ when 'has_one' then 'have one'
218
+ when 'has_and_belongs_to_many' then
219
+ 'have and belong to many'
220
+ end
221
+ end
222
+ end
223
+
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,87 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensure that the attribute's value is in the range specified
6
+ #
7
+ # Options:
8
+ # * <tt>in_range</tt> - the range of allowed values for this attribute
9
+ # * <tt>with_low_message</tt> - value the test expects to find in
10
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
11
+ # translation for :inclusion.
12
+ # * <tt>with_high_message</tt> - value the test expects to find in
13
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
14
+ # translation for :inclusion.
15
+ #
16
+ # Example:
17
+ # it { should ensure_inclusion_of(:age).in_range(0..100) }
18
+ #
19
+ def ensure_inclusion_of(attr)
20
+ EnsureInclusionOfMatcher.new(attr)
21
+ end
22
+
23
+ class EnsureInclusionOfMatcher < ValidationMatcher # :nodoc:
24
+
25
+ def in_range(range)
26
+ @range = range
27
+ @minimum = range.first
28
+ @maximum = range.last
29
+ self
30
+ end
31
+
32
+ def with_message(message)
33
+ if message
34
+ @low_message = message
35
+ @high_message = message
36
+ end
37
+ self
38
+ end
39
+
40
+ def with_low_message(message)
41
+ @low_message = message if message
42
+ self
43
+ end
44
+
45
+ def with_high_message(message)
46
+ @high_message = message if message
47
+ self
48
+ end
49
+
50
+ def description
51
+ "ensure inclusion of #{@attribute} in #{@range.inspect}"
52
+ end
53
+
54
+ def matches?(subject)
55
+ super(subject)
56
+
57
+ @low_message ||= :inclusion
58
+ @high_message ||= :inclusion
59
+
60
+ disallows_lower_value &&
61
+ allows_minimum_value &&
62
+ disallows_higher_value &&
63
+ allows_maximum_value
64
+ end
65
+
66
+ private
67
+
68
+ def disallows_lower_value
69
+ @minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
70
+ end
71
+
72
+ def disallows_higher_value
73
+ disallows_value_of(@maximum + 1, @high_message)
74
+ end
75
+
76
+ def allows_minimum_value
77
+ allows_value_of(@minimum, @low_message)
78
+ end
79
+
80
+ def allows_maximum_value
81
+ allows_value_of(@maximum, @high_message)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,141 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures that the length of the attribute is validated.
6
+ #
7
+ # Options:
8
+ # * <tt>is_at_least</tt> - minimum length of this attribute
9
+ # * <tt>is_at_most</tt> - maximum length of this attribute
10
+ # * <tt>is_equal_to</tt> - exact requred length of this attribute
11
+ # * <tt>with_short_message</tt> - value the test expects to find in
12
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
13
+ # translation for :too_short.
14
+ # * <tt>with_long_message</tt> - value the test expects to find in
15
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
16
+ # translation for :too_long.
17
+ # * <tt>with_message</tt> - value the test expects to find in
18
+ # <tt>errors.on(:attribute)</tt>. Regexp or string. Defaults to the
19
+ # translation for :wrong_length. Used in conjunction with
20
+ # <tt>is_equal_to</tt>.
21
+ #
22
+ # Examples:
23
+ # it { should ensure_length_of(:password).
24
+ # is_at_least(6).
25
+ # is_at_most(20) }
26
+ # it { should ensure_length_of(:name).
27
+ # is_at_least(3).
28
+ # with_short_message(/not long enough/) }
29
+ # it { should ensure_length_of(:ssn).
30
+ # is_equal_to(9).
31
+ # with_message(/is invalid/) }
32
+ def ensure_length_of(attr)
33
+ EnsureLengthOfMatcher.new(attr)
34
+ end
35
+
36
+ class EnsureLengthOfMatcher < ValidationMatcher # :nodoc:
37
+ include Helpers
38
+
39
+ def is_at_least(length)
40
+ @minimum = length
41
+ @short_message ||= :too_short
42
+ self
43
+ end
44
+
45
+ def is_at_most(length)
46
+ @maximum = length
47
+ @long_message ||= :too_long
48
+ self
49
+ end
50
+
51
+ def is_equal_to(length)
52
+ @minimum = length
53
+ @maximum = length
54
+ @short_message ||= :wrong_length
55
+ self
56
+ end
57
+
58
+ def with_short_message(message)
59
+ @short_message = message if message
60
+ self
61
+ end
62
+ alias_method :with_message, :with_short_message
63
+
64
+ def with_long_message(message)
65
+ @long_message = message if message
66
+ self
67
+ end
68
+
69
+ def description
70
+ description = "ensure #{@attribute} has a length "
71
+ if @minimum && @maximum
72
+ if @minimum == @maximum
73
+ description << "of exactly #{@minimum}"
74
+ else
75
+ description << "between #{@minimum} and #{@maximum}"
76
+ end
77
+ else
78
+ description << "of at least #{@minimum}" if @minimum
79
+ description << "of at most #{@maximum}" if @maximum
80
+ end
81
+ description
82
+ end
83
+
84
+ def matches?(subject)
85
+ super(subject)
86
+ translate_messages!
87
+ disallows_lower_length &&
88
+ allows_minimum_length &&
89
+ ((@minimum == @maximum) ||
90
+ (disallows_higher_length &&
91
+ allows_maximum_length))
92
+ end
93
+
94
+ private
95
+
96
+ def translate_messages!
97
+ if Symbol === @short_message
98
+ @short_message = default_error_message(@short_message,
99
+ :count => @minimum)
100
+ end
101
+
102
+ if Symbol === @long_message
103
+ @long_message = default_error_message(@long_message,
104
+ :count => @maximum)
105
+ end
106
+ end
107
+
108
+ def disallows_lower_length
109
+ @minimum == 0 ||
110
+ @minimum.nil? ||
111
+ disallows_length_of(@minimum - 1, @short_message)
112
+ end
113
+
114
+ def disallows_higher_length
115
+ @maximum.nil? || disallows_length_of(@maximum + 1, @long_message)
116
+ end
117
+
118
+ def allows_minimum_length
119
+ allows_length_of(@minimum, @short_message)
120
+ end
121
+
122
+ def allows_maximum_length
123
+ allows_length_of(@maximum, @long_message)
124
+ end
125
+
126
+ def allows_length_of(length, message)
127
+ length.nil? || allows_value_of(string_of_length(length), message)
128
+ end
129
+
130
+ def disallows_length_of(length, message)
131
+ length.nil? || disallows_value_of(string_of_length(length), message)
132
+ end
133
+
134
+ def string_of_length(length)
135
+ 'x' * length
136
+ end
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,169 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ # Ensures the database column exists.
6
+ #
7
+ # Options:
8
+ # * <tt>of_type</tt> - db column type (:integer, :string, etc.)
9
+ # * <tt>with_options</tt> - same options available in migrations
10
+ # (:default, :null, :limit, :precision, :scale)
11
+ #
12
+ # Examples:
13
+ # it { should_not have_db_column(:admin).of_type(:boolean) }
14
+ # it { should have_db_column(:salary).
15
+ # of_type(:decimal).
16
+ # with_options(:precision => 10, :scale => 2) }
17
+ #
18
+ def have_db_column(column)
19
+ HaveDbColumnMatcher.new(:have_db_column, column)
20
+ end
21
+
22
+ class HaveDbColumnMatcher # :nodoc:
23
+ def initialize(macro, column)
24
+ @macro = macro
25
+ @column = column
26
+ end
27
+
28
+ def of_type(column_type)
29
+ @column_type = column_type
30
+ self
31
+ end
32
+
33
+ def with_options(opts = {})
34
+ @precision = opts[:precision]
35
+ @limit = opts[:limit]
36
+ @default = opts[:default]
37
+ @null = opts[:null]
38
+ @scale = opts[:scale]
39
+ self
40
+ end
41
+
42
+ def matches?(subject)
43
+ @subject = subject
44
+ column_exists? &&
45
+ correct_column_type? &&
46
+ correct_precision? &&
47
+ correct_limit? &&
48
+ correct_default? &&
49
+ correct_null? &&
50
+ correct_scale?
51
+ end
52
+
53
+ def failure_message
54
+ "Expected #{expectation} (#{@missing})"
55
+ end
56
+
57
+ def negative_failure_message
58
+ "Did not expect #{expectation}"
59
+ end
60
+
61
+ def description
62
+ desc = "have db column named #{@column}"
63
+ desc << " of type #{@column_type}" unless @column_type.nil?
64
+ desc << " of precision #{@precision}" unless @precision.nil?
65
+ desc << " of limit #{@limit}" unless @limit.nil?
66
+ desc << " of default #{@default}" unless @default.nil?
67
+ desc << " of null #{@null}" unless @null.nil?
68
+ desc << " of primary #{@primary}" unless @primary.nil?
69
+ desc << " of scale #{@scale}" unless @scale.nil?
70
+ desc
71
+ end
72
+
73
+ protected
74
+
75
+ def column_exists?
76
+ if model_class.column_names.include?(@column.to_s)
77
+ true
78
+ else
79
+ @missing = "#{model_class} does not have a db column named #{@column}."
80
+ false
81
+ end
82
+ end
83
+
84
+ def correct_column_type?
85
+ return true if @column_type.nil?
86
+ if matched_column.type.to_s == @column_type.to_s
87
+ true
88
+ else
89
+ @missing = "#{model_class} has a db column named #{@column} " <<
90
+ "of type #{matched_column.type}, not #{@column_type}."
91
+ false
92
+ end
93
+ end
94
+
95
+ def correct_precision?
96
+ return true if @precision.nil?
97
+ if matched_column.precision.to_s == @precision.to_s
98
+ true
99
+ else
100
+ @missing = "#{model_class} has a db column named #{@column} " <<
101
+ "of precision #{matched_column.precision}, " <<
102
+ "not #{@precision}."
103
+ false
104
+ end
105
+ end
106
+
107
+ def correct_limit?
108
+ return true if @limit.nil?
109
+ if matched_column.limit.to_s == @limit.to_s
110
+ true
111
+ else
112
+ @missing = "#{model_class} has a db column named #{@column} " <<
113
+ "of limit #{matched_column.limit}, " <<
114
+ "not #{@limit}."
115
+ false
116
+ end
117
+ end
118
+
119
+ def correct_default?
120
+ return true if @default.nil?
121
+ if matched_column.default.to_s == @default.to_s
122
+ true
123
+ else
124
+ @missing = "#{model_class} has a db column named #{@column} " <<
125
+ "of default #{matched_column.default}, " <<
126
+ "not #{@default}."
127
+ false
128
+ end
129
+ end
130
+
131
+ def correct_null?
132
+ return true if @null.nil?
133
+ if matched_column.null.to_s == @null.to_s
134
+ true
135
+ else
136
+ @missing = "#{model_class} has a db column named #{@column} " <<
137
+ "of null #{matched_column.null}, " <<
138
+ "not #{@null}."
139
+ false
140
+ end
141
+ end
142
+
143
+ def correct_scale?
144
+ return true if @scale.nil?
145
+ if matched_column.scale.to_s == @scale.to_s
146
+ true
147
+ else
148
+ @missing = "#{model_class} has a db column named #{@column} " <<
149
+ "of scale #{matched_column.scale}, not #{@scale}."
150
+ false
151
+ end
152
+ end
153
+
154
+ def matched_column
155
+ model_class.columns.detect { |each| each.name == @column.to_s }
156
+ end
157
+
158
+ def model_class
159
+ @subject.class
160
+ end
161
+
162
+ def expectation
163
+ expected = "#{model_class.name} to #{description}"
164
+ end
165
+ end
166
+
167
+ end
168
+ end
169
+ end