activerecord_csi 2.3.5.p6

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 (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,1640 @@
1
+ # encoding: utf-8
2
+ require "cases/helper"
3
+ require 'models/topic'
4
+ require 'models/reply'
5
+ require 'models/person'
6
+ require 'models/developer'
7
+ require 'models/warehouse_thing'
8
+ require 'models/guid'
9
+ require 'models/owner'
10
+ require 'models/pet'
11
+ require 'models/event'
12
+
13
+ # The following methods in Topic are used in test_conditional_validation_*
14
+ class Topic
15
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
16
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
17
+
18
+ def condition_is_true
19
+ true
20
+ end
21
+
22
+ def condition_is_true_but_its_not
23
+ false
24
+ end
25
+ end
26
+
27
+ class ProtectedPerson < ActiveRecord::Base
28
+ set_table_name 'people'
29
+ attr_accessor :addon
30
+ attr_protected :first_name
31
+
32
+ def special_error
33
+ this_method_does_not_exist!
34
+ rescue
35
+ errors.add(:special_error, "This method does not exist")
36
+ end
37
+ end
38
+
39
+ class UniqueReply < Reply
40
+ validates_uniqueness_of :content, :scope => 'parent_id'
41
+ end
42
+
43
+ class SillyUniqueReply < UniqueReply
44
+ end
45
+
46
+ class Wizard < ActiveRecord::Base
47
+ self.abstract_class = true
48
+
49
+ validates_uniqueness_of :name
50
+ end
51
+
52
+ class IneptWizard < Wizard
53
+ validates_uniqueness_of :city
54
+ end
55
+
56
+ class Conjurer < IneptWizard
57
+ end
58
+
59
+ class Thaumaturgist < IneptWizard
60
+ end
61
+
62
+
63
+ class ValidationsTest < ActiveRecord::TestCase
64
+ fixtures :topics, :developers, 'warehouse-things'
65
+
66
+ # Most of the tests mess with the validations of Topic, so lets repair it all the time.
67
+ # Other classes we mess with will be dealt with in the specific tests
68
+ repair_validations(Topic)
69
+
70
+ def test_single_field_validation
71
+ r = Reply.new
72
+ r.title = "There's no content!"
73
+ assert !r.valid?, "A reply without content shouldn't be saveable"
74
+
75
+ r.content = "Messa content!"
76
+ assert r.valid?, "A reply with content should be saveable"
77
+ end
78
+
79
+ def test_single_attr_validation_and_error_msg
80
+ r = Reply.new
81
+ r.title = "There's no content!"
82
+ assert !r.valid?
83
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
84
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
85
+ assert_equal 1, r.errors.count
86
+ end
87
+
88
+ def test_double_attr_validation_and_error_msg
89
+ r = Reply.new
90
+ assert !r.valid?
91
+
92
+ assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
93
+ assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
94
+
95
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
96
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
97
+
98
+ assert_equal 2, r.errors.count
99
+ end
100
+
101
+ def test_error_on_create
102
+ r = Reply.new
103
+ r.title = "Wrong Create"
104
+ assert !r.valid?
105
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
106
+ assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
107
+ end
108
+
109
+ def test_error_on_update
110
+ r = Reply.new
111
+ r.title = "Bad"
112
+ r.content = "Good"
113
+ assert r.save, "First save should be successful"
114
+
115
+ r.title = "Wrong Update"
116
+ assert !r.save, "Second save should fail"
117
+
118
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
119
+ assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
120
+ end
121
+
122
+ def test_invalid_record_exception
123
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
124
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.new.save! }
125
+
126
+ begin
127
+ r = Reply.new
128
+ r.save!
129
+ flunk
130
+ rescue ActiveRecord::RecordInvalid => invalid
131
+ assert_equal r, invalid.record
132
+ end
133
+ end
134
+
135
+ def test_exception_on_create_bang_many
136
+ assert_raise(ActiveRecord::RecordInvalid) do
137
+ Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
138
+ end
139
+ end
140
+
141
+ def test_exception_on_create_bang_with_block
142
+ assert_raise(ActiveRecord::RecordInvalid) do
143
+ Reply.create!({ "title" => "OK" }) do |r|
144
+ r.content = nil
145
+ end
146
+ end
147
+ end
148
+
149
+ def test_exception_on_create_bang_many_with_block
150
+ assert_raise(ActiveRecord::RecordInvalid) do
151
+ Reply.create!([{ "title" => "OK" }, { "title" => "Wrong Create" }]) do |r|
152
+ r.content = nil
153
+ end
154
+ end
155
+ end
156
+
157
+ def test_scoped_create_without_attributes
158
+ Reply.with_scope(:create => {}) do
159
+ assert_raise(ActiveRecord::RecordInvalid) { Reply.create! }
160
+ end
161
+ end
162
+
163
+ def test_create_with_exceptions_using_scope_for_protected_attributes
164
+ assert_nothing_raised do
165
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
166
+ person = ProtectedPerson.create! :addon => "Addon"
167
+ assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
168
+ end
169
+ end
170
+ end
171
+
172
+ def test_create_with_exceptions_using_scope_and_empty_attributes
173
+ assert_nothing_raised do
174
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
175
+ person = ProtectedPerson.create!
176
+ assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
177
+ end
178
+ end
179
+ end
180
+
181
+ def test_values_are_not_retrieved_unless_needed
182
+ assert_nothing_raised do
183
+ person = ProtectedPerson.new
184
+ person.special_error
185
+ assert_equal "This method does not exist", person.errors[:special_error]
186
+ end
187
+ end
188
+
189
+ def test_single_error_per_attr_iteration
190
+ r = Reply.new
191
+ r.save
192
+
193
+ errors = []
194
+ r.errors.each { |attr, msg| errors << [attr, msg] }
195
+
196
+ assert errors.include?(["title", "Empty"])
197
+ assert errors.include?(["content", "Empty"])
198
+ end
199
+
200
+ def test_multiple_errors_per_attr_iteration_with_full_error_composition
201
+ r = Reply.new
202
+ r.title = "Wrong Create"
203
+ r.content = "Mismatch"
204
+ r.save
205
+
206
+ errors = []
207
+ r.errors.each_full { |error| errors << error }
208
+
209
+ assert_equal "Title is Wrong Create", errors[0]
210
+ assert_equal "Title is Content Mismatch", errors[1]
211
+ assert_equal 2, r.errors.count
212
+ end
213
+
214
+ def test_errors_on_base
215
+ r = Reply.new
216
+ r.content = "Mismatch"
217
+ r.save
218
+ r.errors.add_to_base "Reply is not dignifying"
219
+
220
+ errors = []
221
+ r.errors.each_full { |error| errors << error }
222
+
223
+ assert_equal "Reply is not dignifying", r.errors.on_base
224
+
225
+ assert errors.include?("Title Empty")
226
+ assert errors.include?("Reply is not dignifying")
227
+ assert_equal 2, r.errors.count
228
+ end
229
+
230
+ def test_create_without_validation
231
+ reply = Reply.new
232
+ assert !reply.save
233
+ assert reply.save(false)
234
+ end
235
+
236
+ def test_create_without_validation_bang
237
+ count = Reply.count
238
+ assert_nothing_raised { Reply.new.save_without_validation! }
239
+ assert count+1, Reply.count
240
+ end
241
+
242
+ def test_validates_each
243
+ hits = 0
244
+ Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
245
+ record.errors.add attr, 'gotcha'
246
+ hits += 1
247
+ end
248
+ t = Topic.new("title" => "valid", "content" => "whatever")
249
+ assert !t.save
250
+ assert_equal 4, hits
251
+ assert_equal %w(gotcha gotcha), t.errors.on(:title)
252
+ assert_equal %w(gotcha gotcha), t.errors.on(:content)
253
+ end
254
+
255
+ def test_no_title_confirmation
256
+ Topic.validates_confirmation_of(:title)
257
+
258
+ t = Topic.new(:author_name => "Plutarch")
259
+ assert t.valid?
260
+
261
+ t.title_confirmation = "Parallel Lives"
262
+ assert !t.valid?
263
+
264
+ t.title_confirmation = nil
265
+ t.title = "Parallel Lives"
266
+ assert t.valid?
267
+
268
+ t.title_confirmation = "Parallel Lives"
269
+ assert t.valid?
270
+ end
271
+
272
+ def test_title_confirmation
273
+ Topic.validates_confirmation_of(:title)
274
+
275
+ t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
276
+ assert !t.save
277
+
278
+ t.title_confirmation = "We should be confirmed"
279
+ assert t.save
280
+ end
281
+
282
+ def test_terms_of_service_agreement_no_acceptance
283
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
284
+
285
+ t = Topic.create("title" => "We should not be confirmed")
286
+ assert t.save
287
+ end
288
+
289
+ def test_terms_of_service_agreement
290
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
291
+
292
+ t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
293
+ assert !t.save
294
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
295
+
296
+ t.terms_of_service = "1"
297
+ assert t.save
298
+ end
299
+
300
+
301
+ def test_eula
302
+ Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
303
+
304
+ t = Topic.create("title" => "We should be confirmed","eula" => "")
305
+ assert !t.save
306
+ assert_equal "must be abided", t.errors.on(:eula)
307
+
308
+ t.eula = "1"
309
+ assert t.save
310
+ end
311
+
312
+ def test_terms_of_service_agreement_with_accept_value
313
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
314
+
315
+ t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
316
+ assert !t.save
317
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
318
+
319
+ t.terms_of_service = "I agree."
320
+ assert t.save
321
+ end
322
+
323
+ def test_validates_acceptance_of_as_database_column
324
+ repair_validations(Reply) do
325
+ Reply.validates_acceptance_of(:author_name)
326
+
327
+ reply = Reply.create("author_name" => "Dan Brown")
328
+ assert_equal "Dan Brown", reply["author_name"]
329
+ end
330
+ end
331
+
332
+ def test_validates_acceptance_of_with_non_existant_table
333
+ Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base)
334
+
335
+ assert_nothing_raised ActiveRecord::StatementInvalid do
336
+ IncorporealModel.validates_acceptance_of(:incorporeal_column)
337
+ end
338
+ end
339
+
340
+ def test_validate_presences
341
+ Topic.validates_presence_of(:title, :content)
342
+
343
+ t = Topic.create
344
+ assert !t.save
345
+ assert_equal "can't be blank", t.errors.on(:title)
346
+ assert_equal "can't be blank", t.errors.on(:content)
347
+
348
+ t.title = "something"
349
+ t.content = " "
350
+
351
+ assert !t.save
352
+ assert_equal "can't be blank", t.errors.on(:content)
353
+
354
+ t.content = "like stuff"
355
+
356
+ assert t.save
357
+ end
358
+
359
+ def test_validate_uniqueness
360
+ Topic.validates_uniqueness_of(:title)
361
+
362
+ t = Topic.new("title" => "I'm uniqué!")
363
+ assert t.save, "Should save t as unique"
364
+
365
+ t.content = "Remaining unique"
366
+ assert t.save, "Should still save t as unique"
367
+
368
+ t2 = Topic.new("title" => "I'm uniqué!")
369
+ assert !t2.valid?, "Shouldn't be valid"
370
+ assert !t2.save, "Shouldn't save t2 as unique"
371
+ assert_equal "has already been taken", t2.errors.on(:title)
372
+
373
+ t2.title = "Now Im really also unique"
374
+ assert t2.save, "Should now save t2 as unique"
375
+ end
376
+
377
+ def test_validates_uniquness_with_newline_chars
378
+ Topic.validates_uniqueness_of(:title, :case_sensitive => false)
379
+
380
+ t = Topic.new("title" => "new\nline")
381
+ assert t.save, "Should save t as unique"
382
+ end
383
+
384
+ def test_validate_uniqueness_with_scope
385
+ repair_validations(Reply) do
386
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
387
+
388
+ t = Topic.create("title" => "I'm unique!")
389
+
390
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
391
+ assert r1.valid?, "Saving r1"
392
+
393
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
394
+ assert !r2.valid?, "Saving r2 first time"
395
+
396
+ r2.content = "something else"
397
+ assert r2.save, "Saving r2 second time"
398
+
399
+ t2 = Topic.create("title" => "I'm unique too!")
400
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
401
+ assert r3.valid?, "Saving r3"
402
+ end
403
+ end
404
+
405
+ def test_validate_uniqueness_scoped_to_defining_class
406
+ t = Topic.create("title" => "What, me worry?")
407
+
408
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
409
+ assert r1.valid?, "Saving r1"
410
+
411
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
412
+ assert !r2.valid?, "Saving r2"
413
+
414
+ # Should succeed as validates_uniqueness_of only applies to
415
+ # UniqueReply and its subclasses
416
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
417
+ assert r3.valid?, "Saving r3"
418
+ end
419
+
420
+ def test_validate_uniqueness_with_scope_array
421
+ repair_validations(Reply) do
422
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
423
+
424
+ t = Topic.create("title" => "The earth is actually flat!")
425
+
426
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
427
+ assert r1.valid?, "Saving r1"
428
+
429
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
430
+ assert !r2.valid?, "Saving r2. Double reply by same author."
431
+
432
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
433
+ assert r2.save, "Saving r2 the second time."
434
+
435
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
436
+ assert !r3.valid?, "Saving r3"
437
+
438
+ r3.author_name = "jj"
439
+ assert r3.save, "Saving r3 the second time."
440
+
441
+ r3.author_name = "jeremy"
442
+ assert !r3.save, "Saving r3 the third time."
443
+ end
444
+ end
445
+
446
+ def test_validate_case_insensitive_uniqueness
447
+ Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
448
+
449
+ t = Topic.new("title" => "I'm unique!", :parent_id => 2)
450
+ assert t.save, "Should save t as unique"
451
+
452
+ t.content = "Remaining unique"
453
+ assert t.save, "Should still save t as unique"
454
+
455
+ t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
456
+ assert !t2.valid?, "Shouldn't be valid"
457
+ assert !t2.save, "Shouldn't save t2 as unique"
458
+ assert t2.errors.on(:title)
459
+ assert t2.errors.on(:parent_id)
460
+ assert_equal "has already been taken", t2.errors.on(:title)
461
+
462
+ t2.title = "I'm truly UNIQUE!"
463
+ assert !t2.valid?, "Shouldn't be valid"
464
+ assert !t2.save, "Shouldn't save t2 as unique"
465
+ assert_nil t2.errors.on(:title)
466
+ assert t2.errors.on(:parent_id)
467
+
468
+ t2.parent_id = 4
469
+ assert t2.save, "Should now save t2 as unique"
470
+
471
+ t2.parent_id = nil
472
+ t2.title = nil
473
+ assert t2.valid?, "should validate with nil"
474
+ assert t2.save, "should save with nil"
475
+
476
+ with_kcode('UTF8') do
477
+ t_utf8 = Topic.new("title" => "Я тоже уникальный!")
478
+ assert t_utf8.save, "Should save t_utf8 as unique"
479
+
480
+ # If database hasn't UTF-8 character set, this test fails
481
+ if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
482
+ t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
483
+ assert !t2_utf8.valid?, "Shouldn't be valid"
484
+ assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
485
+ end
486
+ end
487
+ end
488
+
489
+ def test_validate_case_sensitive_uniqueness
490
+ Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
491
+
492
+ t = Topic.new("title" => "I'm unique!")
493
+ assert t.save, "Should save t as unique"
494
+
495
+ t.content = "Remaining unique"
496
+ assert t.save, "Should still save t as unique"
497
+
498
+ t2 = Topic.new("title" => "I'M UNIQUE!")
499
+ assert t2.valid?, "Should be valid"
500
+ assert t2.save, "Should save t2 as unique"
501
+ assert !t2.errors.on(:title)
502
+ assert !t2.errors.on(:parent_id)
503
+ assert_not_equal "has already been taken", t2.errors.on(:title)
504
+
505
+ t3 = Topic.new("title" => "I'M uNiQUe!")
506
+ assert t3.valid?, "Should be valid"
507
+ assert t3.save, "Should save t2 as unique"
508
+ assert !t3.errors.on(:title)
509
+ assert !t3.errors.on(:parent_id)
510
+ assert_not_equal "has already been taken", t3.errors.on(:title)
511
+ end
512
+
513
+ def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
514
+ Topic.validates_uniqueness_of(:title, :case_sensitve => true)
515
+ t = Topic.create!('title' => 101)
516
+
517
+ t2 = Topic.new('title' => 101)
518
+ assert !t2.valid?
519
+ assert t2.errors.on(:title)
520
+ end
521
+
522
+ def test_validate_uniqueness_with_non_standard_table_names
523
+ i1 = WarehouseThing.create(:value => 1000)
524
+ assert !i1.valid?, "i1 should not be valid"
525
+ assert i1.errors.on(:value), "Should not be empty"
526
+ end
527
+
528
+ def test_validates_uniqueness_inside_with_scope
529
+ Topic.validates_uniqueness_of(:title)
530
+
531
+ Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do
532
+ t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary")
533
+ assert t1.save
534
+ t2 = Topic.new("title" => "I'm unique!", "author_name" => "David")
535
+ assert !t2.valid?
536
+ end
537
+ end
538
+
539
+ def test_validate_uniqueness_with_columns_which_are_sql_keywords
540
+ repair_validations(Guid) do
541
+ Guid.validates_uniqueness_of :key
542
+ g = Guid.new
543
+ g.key = "foo"
544
+ assert_nothing_raised { !g.valid? }
545
+ end
546
+ end
547
+
548
+ def test_validate_uniqueness_with_limit
549
+ # Event.title is limited to 5 characters
550
+ e1 = Event.create(:title => "abcde")
551
+ assert e1.valid?, "Could not create an event with a unique, 5 character title"
552
+ e2 = Event.create(:title => "abcdefgh")
553
+ assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
554
+ end
555
+
556
+ def test_validate_uniqueness_with_limit_and_utf8
557
+ with_kcode('UTF8') do
558
+ # Event.title is limited to 5 characters
559
+ e1 = Event.create(:title => "一二三四五")
560
+ assert e1.valid?, "Could not create an event with a unique, 5 character title"
561
+ e2 = Event.create(:title => "一二三四五六七八")
562
+ assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
563
+ end
564
+ end
565
+
566
+ def test_validate_straight_inheritance_uniqueness
567
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
568
+ assert w1.valid?, "Saving w1"
569
+
570
+ # Should use validation from base class (which is abstract)
571
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
572
+ assert !w2.valid?, "w2 shouldn't be valid"
573
+ assert w2.errors.on(:name), "Should have errors for name"
574
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
575
+
576
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
577
+ assert !w3.valid?, "w3 shouldn't be valid"
578
+ assert w3.errors.on(:name), "Should have errors for name"
579
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
580
+
581
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
582
+ assert w4.valid?, "Saving w4"
583
+
584
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
585
+ assert !w5.valid?, "w5 shouldn't be valid"
586
+ assert w5.errors.on(:name), "Should have errors for name"
587
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
588
+
589
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
590
+ assert !w6.valid?, "w6 shouldn't be valid"
591
+ assert w6.errors.on(:city), "Should have errors for city"
592
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
593
+ end
594
+
595
+ def test_validate_format
596
+ Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
597
+
598
+ t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
599
+ assert !t.valid?, "Shouldn't be valid"
600
+ assert !t.save, "Shouldn't save because it's invalid"
601
+ assert_equal "is bad data", t.errors.on(:title)
602
+ assert_nil t.errors.on(:content)
603
+
604
+ t.title = "Validation macros rule!"
605
+
606
+ assert t.save
607
+ assert_nil t.errors.on(:title)
608
+
609
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
610
+ end
611
+
612
+ def test_validate_format_with_allow_blank
613
+ Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true)
614
+ assert !Topic.create("title" => "Shouldn't be valid").valid?
615
+ assert Topic.create("title" => "").valid?
616
+ assert Topic.create("title" => nil).valid?
617
+ assert Topic.create("title" => "Validation macros rule!").valid?
618
+ end
619
+
620
+ # testing ticket #3142
621
+ def test_validate_format_numeric
622
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
623
+
624
+ t = Topic.create("title" => "72x", "content" => "6789")
625
+ assert !t.valid?, "Shouldn't be valid"
626
+ assert !t.save, "Shouldn't save because it's invalid"
627
+ assert_equal "is bad data", t.errors.on(:title)
628
+ assert_nil t.errors.on(:content)
629
+
630
+ t.title = "-11"
631
+ assert !t.valid?, "Shouldn't be valid"
632
+
633
+ t.title = "03"
634
+ assert !t.valid?, "Shouldn't be valid"
635
+
636
+ t.title = "z44"
637
+ assert !t.valid?, "Shouldn't be valid"
638
+
639
+ t.title = "5v7"
640
+ assert !t.valid?, "Shouldn't be valid"
641
+
642
+ t.title = "1"
643
+
644
+ assert t.save
645
+ assert_nil t.errors.on(:title)
646
+ end
647
+
648
+ def test_validate_format_with_formatted_message
649
+ Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}")
650
+ t = Topic.create(:title => 'Invalid title')
651
+ assert_equal "can't be Invalid title", t.errors.on(:title)
652
+ end
653
+
654
+ def test_validates_inclusion_of
655
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
656
+
657
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
658
+ assert !Topic.create("title" => "a b", "content" => "abc").valid?
659
+ assert !Topic.create("title" => nil, "content" => "def").valid?
660
+
661
+ t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
662
+ assert t.valid?
663
+ t.title = "uhoh"
664
+ assert !t.valid?
665
+ assert t.errors.on(:title)
666
+ assert_equal "is not included in the list", t.errors["title"]
667
+
668
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
669
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
670
+
671
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
672
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
673
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
674
+ end
675
+
676
+ def test_validates_inclusion_of_with_allow_nil
677
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
678
+
679
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
680
+ assert !Topic.create("title" => "", "content" => "abc").valid?
681
+ assert Topic.create("title" => nil, "content" => "abc").valid?
682
+ end
683
+
684
+ def test_numericality_with_getter_method
685
+ repair_validations(Developer) do
686
+ Developer.validates_numericality_of( :salary )
687
+ developer = Developer.new("name" => "michael", "salary" => nil)
688
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
689
+ assert developer.valid?
690
+ end
691
+ end
692
+
693
+ def test_validates_length_of_with_allow_nil
694
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
695
+
696
+ assert !Topic.create("title" => "ab").valid?
697
+ assert !Topic.create("title" => "").valid?
698
+ assert Topic.create("title" => nil).valid?
699
+ assert Topic.create("title" => "abcde").valid?
700
+ end
701
+
702
+ def test_validates_length_of_with_allow_blank
703
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
704
+
705
+ assert !Topic.create("title" => "ab").valid?
706
+ assert Topic.create("title" => "").valid?
707
+ assert Topic.create("title" => nil).valid?
708
+ assert Topic.create("title" => "abcde").valid?
709
+ end
710
+
711
+ def test_validates_inclusion_of_with_formatted_message
712
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" )
713
+
714
+ assert Topic.create("title" => "a", "content" => "abc").valid?
715
+
716
+ t = Topic.create("title" => "uhoh", "content" => "abc")
717
+ assert !t.valid?
718
+ assert t.errors.on(:title)
719
+ assert_equal "option uhoh is not in the list", t.errors["title"]
720
+ end
721
+
722
+ def test_numericality_with_allow_nil_and_getter_method
723
+ repair_validations(Developer) do
724
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
725
+ developer = Developer.new("name" => "michael", "salary" => nil)
726
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
727
+ assert developer.valid?
728
+ end
729
+ end
730
+
731
+ def test_validates_exclusion_of
732
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
733
+
734
+ assert Topic.create("title" => "something", "content" => "abc").valid?
735
+ assert !Topic.create("title" => "monkey", "content" => "abc").valid?
736
+ end
737
+
738
+ def test_validates_exclusion_of_with_formatted_message
739
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" )
740
+
741
+ assert Topic.create("title" => "something", "content" => "abc")
742
+
743
+ t = Topic.create("title" => "monkey")
744
+ assert !t.valid?
745
+ assert t.errors.on(:title)
746
+ assert_equal "option monkey is restricted", t.errors["title"]
747
+ end
748
+
749
+ def test_validates_length_of_using_minimum
750
+ Topic.validates_length_of :title, :minimum => 5
751
+
752
+ t = Topic.create("title" => "valid", "content" => "whatever")
753
+ assert t.valid?
754
+
755
+ t.title = "not"
756
+ assert !t.valid?
757
+ assert t.errors.on(:title)
758
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
759
+
760
+ t.title = ""
761
+ assert !t.valid?
762
+ assert t.errors.on(:title)
763
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
764
+
765
+ t.title = nil
766
+ assert !t.valid?
767
+ assert t.errors.on(:title)
768
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
769
+ end
770
+
771
+ def test_optionally_validates_length_of_using_minimum
772
+ Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
773
+
774
+ t = Topic.create("title" => "valid", "content" => "whatever")
775
+ assert t.valid?
776
+
777
+ t.title = nil
778
+ assert t.valid?
779
+ end
780
+
781
+ def test_validates_length_of_using_maximum
782
+ Topic.validates_length_of :title, :maximum => 5
783
+
784
+ t = Topic.create("title" => "valid", "content" => "whatever")
785
+ assert t.valid?
786
+
787
+ t.title = "notvalid"
788
+ assert !t.valid?
789
+ assert t.errors.on(:title)
790
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
791
+
792
+ t.title = ""
793
+ assert t.valid?
794
+
795
+ t.title = nil
796
+ assert !t.valid?
797
+ end
798
+
799
+ def test_optionally_validates_length_of_using_maximum
800
+ Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
801
+
802
+ t = Topic.create("title" => "valid", "content" => "whatever")
803
+ assert t.valid?
804
+
805
+ t.title = nil
806
+ assert t.valid?
807
+ end
808
+
809
+ def test_validates_length_of_using_within
810
+ Topic.validates_length_of(:title, :content, :within => 3..5)
811
+
812
+ t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
813
+ assert !t.valid?
814
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
815
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
816
+
817
+ t.title = nil
818
+ t.content = nil
819
+ assert !t.valid?
820
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
821
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
822
+
823
+ t.title = "abe"
824
+ t.content = "mad"
825
+ assert t.valid?
826
+ end
827
+
828
+ def test_optionally_validates_length_of_using_within
829
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
830
+
831
+ t = Topic.create('title' => 'abc', 'content' => 'abcd')
832
+ assert t.valid?
833
+
834
+ t.title = nil
835
+ assert t.valid?
836
+ end
837
+
838
+ def test_optionally_validates_length_of_using_within_on_create
839
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}"
840
+
841
+ t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
842
+ assert !t.save
843
+ assert t.errors.on(:title)
844
+ assert_equal "my string is too long: 10", t.errors[:title]
845
+
846
+ t.title = "butthisis"
847
+ assert t.save
848
+
849
+ t.title = "few"
850
+ assert t.save
851
+
852
+ t.content = "andthisislong"
853
+ assert t.save
854
+
855
+ t.content = t.title = "iamfine"
856
+ assert t.save
857
+ end
858
+
859
+ def test_optionally_validates_length_of_using_within_on_update
860
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}"
861
+
862
+ t = Topic.create("title" => "vali", "content" => "whatever")
863
+ assert !t.save
864
+ assert t.errors.on(:title)
865
+
866
+ t.title = "not"
867
+ assert !t.save
868
+ assert t.errors.on(:title)
869
+ assert_equal "my string is too short: 5", t.errors[:title]
870
+
871
+ t.title = "valid"
872
+ t.content = "andthisistoolong"
873
+ assert !t.save
874
+ assert t.errors.on(:content)
875
+
876
+ t.content = "iamfine"
877
+ assert t.save
878
+ end
879
+
880
+ def test_validates_length_of_using_is
881
+ Topic.validates_length_of :title, :is => 5
882
+
883
+ t = Topic.create("title" => "valid", "content" => "whatever")
884
+ assert t.valid?
885
+
886
+ t.title = "notvalid"
887
+ assert !t.valid?
888
+ assert t.errors.on(:title)
889
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
890
+
891
+ t.title = ""
892
+ assert !t.valid?
893
+
894
+ t.title = nil
895
+ assert !t.valid?
896
+ end
897
+
898
+ def test_optionally_validates_length_of_using_is
899
+ Topic.validates_length_of :title, :is => 5, :allow_nil => true
900
+
901
+ t = Topic.create("title" => "valid", "content" => "whatever")
902
+ assert t.valid?
903
+
904
+ t.title = nil
905
+ assert t.valid?
906
+ end
907
+
908
+ def test_validates_length_of_using_bignum
909
+ bigmin = 2 ** 30
910
+ bigmax = 2 ** 32
911
+ bigrange = bigmin...bigmax
912
+ assert_nothing_raised do
913
+ Topic.validates_length_of :title, :is => bigmin + 5
914
+ Topic.validates_length_of :title, :within => bigrange
915
+ Topic.validates_length_of :title, :in => bigrange
916
+ Topic.validates_length_of :title, :minimum => bigmin
917
+ Topic.validates_length_of :title, :maximum => bigmax
918
+ end
919
+ end
920
+
921
+ def test_validates_length_with_globally_modified_error_message
922
+ defaults = ActiveSupport::Deprecation.silence { ActiveRecord::Errors.default_error_messages }
923
+ original_message = defaults[:too_short]
924
+ defaults[:too_short] = 'tu est trops petit hombre {{count}}'
925
+
926
+ Topic.validates_length_of :title, :minimum => 10
927
+ t = Topic.create(:title => 'too short')
928
+ assert !t.valid?
929
+
930
+ assert_equal 'tu est trops petit hombre 10', t.errors['title']
931
+
932
+ ensure
933
+ defaults[:too_short] = original_message
934
+ end
935
+
936
+ def test_validates_size_of_association
937
+ repair_validations(Owner) do
938
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
939
+ o = Owner.new('name' => 'nopets')
940
+ assert !o.save
941
+ assert o.errors.on(:pets)
942
+ pet = o.pets.build('name' => 'apet')
943
+ assert o.valid?
944
+ end
945
+ end
946
+
947
+ def test_validates_size_of_association_using_within
948
+ repair_validations(Owner) do
949
+ assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 }
950
+ o = Owner.new('name' => 'nopets')
951
+ assert !o.save
952
+ assert o.errors.on(:pets)
953
+
954
+ pet = o.pets.build('name' => 'apet')
955
+ assert o.valid?
956
+
957
+ 2.times { o.pets.build('name' => 'apet') }
958
+ assert !o.save
959
+ assert o.errors.on(:pets)
960
+ end
961
+ end
962
+
963
+ def test_validates_length_of_nasty_params
964
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
965
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
966
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
967
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
968
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
969
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
970
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
971
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
972
+ end
973
+
974
+ def test_validates_length_of_custom_errors_for_minimum_with_message
975
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" )
976
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
977
+ assert !t.valid?
978
+ assert t.errors.on(:title)
979
+ assert_equal "boo 5", t.errors["title"]
980
+ end
981
+
982
+ def test_validates_length_of_custom_errors_for_minimum_with_too_short
983
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" )
984
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
985
+ assert !t.valid?
986
+ assert t.errors.on(:title)
987
+ assert_equal "hoo 5", t.errors["title"]
988
+ end
989
+
990
+ def test_validates_length_of_custom_errors_for_maximum_with_message
991
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" )
992
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
993
+ assert !t.valid?
994
+ assert t.errors.on(:title)
995
+ assert_equal "boo 5", t.errors["title"]
996
+ end
997
+
998
+ def test_validates_length_of_custom_errors_for_in
999
+ Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}")
1000
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1001
+ assert !t.valid?
1002
+ assert t.errors.on(:title)
1003
+ assert_equal "hoo 10", t.errors["title"]
1004
+
1005
+ t = Topic.create("title" => "uhohuhohuhohuhohuhohuhohuhohuhoh", "content" => "whatever")
1006
+ assert !t.valid?
1007
+ assert t.errors.on(:title)
1008
+ assert_equal "hoo 20", t.errors["title"]
1009
+ end
1010
+
1011
+ def test_validates_length_of_custom_errors_for_maximum_with_too_long
1012
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" )
1013
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1014
+ assert !t.valid?
1015
+ assert t.errors.on(:title)
1016
+ assert_equal "hoo 5", t.errors["title"]
1017
+ end
1018
+
1019
+ def test_validates_length_of_custom_errors_for_is_with_message
1020
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" )
1021
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1022
+ assert !t.valid?
1023
+ assert t.errors.on(:title)
1024
+ assert_equal "boo 5", t.errors["title"]
1025
+ end
1026
+
1027
+ def test_validates_length_of_custom_errors_for_is_with_wrong_length
1028
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" )
1029
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1030
+ assert !t.valid?
1031
+ assert t.errors.on(:title)
1032
+ assert_equal "hoo 5", t.errors["title"]
1033
+ end
1034
+
1035
+ def test_validates_length_of_using_minimum_utf8
1036
+ with_kcode('UTF8') do
1037
+ Topic.validates_length_of :title, :minimum => 5
1038
+
1039
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
1040
+ assert t.valid?
1041
+
1042
+ t.title = "一二三四"
1043
+ assert !t.valid?
1044
+ assert t.errors.on(:title)
1045
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
1046
+ end
1047
+ end
1048
+
1049
+ def test_validates_length_of_using_maximum_utf8
1050
+ with_kcode('UTF8') do
1051
+ Topic.validates_length_of :title, :maximum => 5
1052
+
1053
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
1054
+ assert t.valid?
1055
+
1056
+ t.title = "一二34五六"
1057
+ assert !t.valid?
1058
+ assert t.errors.on(:title)
1059
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
1060
+ end
1061
+ end
1062
+
1063
+ def test_validates_length_of_using_within_utf8
1064
+ with_kcode('UTF8') do
1065
+ Topic.validates_length_of(:title, :content, :within => 3..5)
1066
+
1067
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
1068
+ assert !t.valid?
1069
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
1070
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
1071
+ t.title = "一二三"
1072
+ t.content = "12三"
1073
+ assert t.valid?
1074
+ end
1075
+ end
1076
+
1077
+ def test_optionally_validates_length_of_using_within_utf8
1078
+ with_kcode('UTF8') do
1079
+ Topic.validates_length_of :title, :within => 3..5, :allow_nil => true
1080
+
1081
+ t = Topic.create(:title => "一二三四五")
1082
+ assert t.valid?, t.errors.inspect
1083
+
1084
+ t = Topic.create(:title => "一二三")
1085
+ assert t.valid?, t.errors.inspect
1086
+
1087
+ t.title = nil
1088
+ assert t.valid?, t.errors.inspect
1089
+ end
1090
+ end
1091
+
1092
+ def test_optionally_validates_length_of_using_within_on_create_utf8
1093
+ with_kcode('UTF8') do
1094
+ Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}"
1095
+
1096
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
1097
+ assert !t.save
1098
+ assert t.errors.on(:title)
1099
+ assert_equal "長すぎます: 10", t.errors[:title]
1100
+
1101
+ t.title = "一二三四五六七八九"
1102
+ assert t.save
1103
+
1104
+ t.title = "一二3"
1105
+ assert t.save
1106
+
1107
+ t.content = "一二三四五六七八九十"
1108
+ assert t.save
1109
+
1110
+ t.content = t.title = "一二三四五六"
1111
+ assert t.save
1112
+ end
1113
+ end
1114
+
1115
+ def test_optionally_validates_length_of_using_within_on_update_utf8
1116
+ with_kcode('UTF8') do
1117
+ Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}"
1118
+
1119
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
1120
+ assert !t.save
1121
+ assert t.errors.on(:title)
1122
+
1123
+ t.title = "1二三4"
1124
+ assert !t.save
1125
+ assert t.errors.on(:title)
1126
+ assert_equal "短すぎます: 5", t.errors[:title]
1127
+
1128
+ t.title = "一二三四五六七八九十A"
1129
+ assert !t.save
1130
+ assert t.errors.on(:title)
1131
+
1132
+ t.title = "一二345"
1133
+ assert t.save
1134
+ end
1135
+ end
1136
+
1137
+ def test_validates_length_of_using_is_utf8
1138
+ with_kcode('UTF8') do
1139
+ Topic.validates_length_of :title, :is => 5
1140
+
1141
+ t = Topic.create("title" => "一二345", "content" => "whatever")
1142
+ assert t.valid?
1143
+
1144
+ t.title = "一二345六"
1145
+ assert !t.valid?
1146
+ assert t.errors.on(:title)
1147
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
1148
+ end
1149
+ end
1150
+
1151
+ def test_validates_length_of_with_block
1152
+ Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.",
1153
+ :tokenizer => lambda {|str| str.scan(/\w+/) }
1154
+ t = Topic.create!(:content => "this content should be long enough")
1155
+ assert t.valid?
1156
+
1157
+ t.content = "not long enough"
1158
+ assert !t.valid?
1159
+ assert t.errors.on(:content)
1160
+ assert_equal "Your essay must be at least 5 words.", t.errors[:content]
1161
+ end
1162
+
1163
+ def test_validates_size_of_association_utf8
1164
+ repair_validations(Owner) do
1165
+ with_kcode('UTF8') do
1166
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
1167
+ o = Owner.new('name' => 'あいうえおかきくけこ')
1168
+ assert !o.save
1169
+ assert o.errors.on(:pets)
1170
+ o.pets.build('name' => 'あいうえおかきくけこ')
1171
+ assert o.valid?
1172
+ end
1173
+ end
1174
+ end
1175
+
1176
+ def test_validates_associated_many
1177
+ Topic.validates_associated( :replies )
1178
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1179
+ t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
1180
+ assert !t.valid?
1181
+ assert t.errors.on(:replies)
1182
+ assert_equal 1, r.errors.count # make sure all associated objects have been validated
1183
+ assert_equal 0, r2.errors.count
1184
+ assert_equal 1, r3.errors.count
1185
+ assert_equal 0, r4.errors.count
1186
+ r.content = r3.content = "non-empty"
1187
+ assert t.valid?
1188
+ end
1189
+
1190
+ def test_validates_associated_one
1191
+ repair_validations(Reply) do
1192
+ Reply.validates_associated( :topic )
1193
+ Topic.validates_presence_of( :content )
1194
+ r = Reply.new("title" => "A reply", "content" => "with content!")
1195
+ r.topic = Topic.create("title" => "uhohuhoh")
1196
+ assert !r.valid?
1197
+ assert r.errors.on(:topic)
1198
+ r.topic.content = "non-empty"
1199
+ assert r.valid?
1200
+ end
1201
+ end
1202
+
1203
+ def test_validate_block
1204
+ Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
1205
+ t = Topic.create("title" => "Title", "content" => "whatever")
1206
+ assert !t.valid?
1207
+ assert t.errors.on(:title)
1208
+ assert_equal "will never be valid", t.errors["title"]
1209
+ end
1210
+
1211
+ def test_invalid_validator
1212
+ Topic.validate 3
1213
+ assert_raise(ArgumentError) { t = Topic.create }
1214
+ end
1215
+
1216
+ def test_throw_away_typing
1217
+ d = Developer.new("name" => "David", "salary" => "100,000")
1218
+ assert !d.valid?
1219
+ assert_equal 100, d.salary
1220
+ assert_equal "100,000", d.salary_before_type_cast
1221
+ end
1222
+
1223
+ def test_validates_acceptance_of_with_custom_error_using_quotes
1224
+ repair_validations(Developer) do
1225
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
1226
+ d = Developer.new
1227
+ d.salary = "0"
1228
+ assert !d.valid?
1229
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1230
+ end
1231
+ end
1232
+
1233
+ def test_validates_confirmation_of_with_custom_error_using_quotes
1234
+ repair_validations(Developer) do
1235
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
1236
+ d = Developer.new
1237
+ d.name = "John"
1238
+ d.name_confirmation = "Johnny"
1239
+ assert !d.valid?
1240
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
1241
+ end
1242
+ end
1243
+
1244
+ def test_validates_format_of_with_custom_error_using_quotes
1245
+ repair_validations(Developer) do
1246
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
1247
+ d = Developer.new
1248
+ d.name = d.name_confirmation = "John 32"
1249
+ assert !d.valid?
1250
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
1251
+ end
1252
+ end
1253
+
1254
+ def test_validates_inclusion_of_with_custom_error_using_quotes
1255
+ repair_validations(Developer) do
1256
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
1257
+ d = Developer.new
1258
+ d.salary = "90,000"
1259
+ assert !d.valid?
1260
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1261
+ end
1262
+ end
1263
+
1264
+ def test_validates_length_of_with_custom_too_long_using_quotes
1265
+ repair_validations(Developer) do
1266
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
1267
+ d = Developer.new
1268
+ d.name = "Jeffrey"
1269
+ assert !d.valid?
1270
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1271
+ end
1272
+ end
1273
+
1274
+ def test_validates_length_of_with_custom_too_short_using_quotes
1275
+ repair_validations(Developer) do
1276
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
1277
+ d = Developer.new
1278
+ d.name = "Joe"
1279
+ assert !d.valid?
1280
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1281
+ end
1282
+ end
1283
+
1284
+ def test_validates_length_of_with_custom_message_using_quotes
1285
+ repair_validations(Developer) do
1286
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
1287
+ d = Developer.new
1288
+ d.name = "Joe"
1289
+ assert !d.valid?
1290
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1291
+ end
1292
+ end
1293
+
1294
+ def test_validates_presence_of_with_custom_message_using_quotes
1295
+ repair_validations(Developer) do
1296
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
1297
+ d = Developer.new
1298
+ d.name = "Joe"
1299
+ assert !d.valid?
1300
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
1301
+ end
1302
+ end
1303
+
1304
+ def test_validates_uniqueness_of_with_custom_message_using_quotes
1305
+ repair_validations(Developer) do
1306
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
1307
+ d = Developer.new
1308
+ d.name = "David"
1309
+ assert !d.valid?
1310
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name)
1311
+ end
1312
+ end
1313
+
1314
+ def test_validates_associated_with_custom_message_using_quotes
1315
+ repair_validations(Reply) do
1316
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
1317
+ Topic.validates_presence_of :content
1318
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1319
+ r.topic = Topic.create("title" => "uhohuhoh")
1320
+ assert !r.valid?
1321
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic)
1322
+ end
1323
+ end
1324
+
1325
+ def test_if_validation_using_method_true
1326
+ # When the method returns true
1327
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true )
1328
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1329
+ assert !t.valid?
1330
+ assert t.errors.on(:title)
1331
+ assert_equal "hoo 5", t.errors["title"]
1332
+ end
1333
+
1334
+ def test_unless_validation_using_method_true
1335
+ # When the method returns true
1336
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true )
1337
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1338
+ assert t.valid?
1339
+ assert !t.errors.on(:title)
1340
+ end
1341
+
1342
+ def test_if_validation_using_method_false
1343
+ # When the method returns false
1344
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not )
1345
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1346
+ assert t.valid?
1347
+ assert !t.errors.on(:title)
1348
+ end
1349
+
1350
+ def test_unless_validation_using_method_false
1351
+ # When the method returns false
1352
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not )
1353
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1354
+ assert !t.valid?
1355
+ assert t.errors.on(:title)
1356
+ assert_equal "hoo 5", t.errors["title"]
1357
+ end
1358
+
1359
+ def test_if_validation_using_string_true
1360
+ # When the evaluated string returns true
1361
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" )
1362
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1363
+ assert !t.valid?
1364
+ assert t.errors.on(:title)
1365
+ assert_equal "hoo 5", t.errors["title"]
1366
+ end
1367
+
1368
+ def test_unless_validation_using_string_true
1369
+ # When the evaluated string returns true
1370
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" )
1371
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1372
+ assert t.valid?
1373
+ assert !t.errors.on(:title)
1374
+ end
1375
+
1376
+ def test_if_validation_using_string_false
1377
+ # When the evaluated string returns false
1378
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false")
1379
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1380
+ assert t.valid?
1381
+ assert !t.errors.on(:title)
1382
+ end
1383
+
1384
+ def test_unless_validation_using_string_false
1385
+ # When the evaluated string returns false
1386
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false")
1387
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1388
+ assert !t.valid?
1389
+ assert t.errors.on(:title)
1390
+ assert_equal "hoo 5", t.errors["title"]
1391
+ end
1392
+
1393
+ def test_if_validation_using_block_true
1394
+ # When the block returns true
1395
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1396
+ :if => Proc.new { |r| r.content.size > 4 } )
1397
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1398
+ assert !t.valid?
1399
+ assert t.errors.on(:title)
1400
+ assert_equal "hoo 5", t.errors["title"]
1401
+ end
1402
+
1403
+ def test_unless_validation_using_block_true
1404
+ # When the block returns true
1405
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1406
+ :unless => Proc.new { |r| r.content.size > 4 } )
1407
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1408
+ assert t.valid?
1409
+ assert !t.errors.on(:title)
1410
+ end
1411
+
1412
+ def test_if_validation_using_block_false
1413
+ # When the block returns false
1414
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1415
+ :if => Proc.new { |r| r.title != "uhohuhoh"} )
1416
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1417
+ assert t.valid?
1418
+ assert !t.errors.on(:title)
1419
+ end
1420
+
1421
+ def test_unless_validation_using_block_false
1422
+ # When the block returns false
1423
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}",
1424
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1425
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1426
+ assert !t.valid?
1427
+ assert t.errors.on(:title)
1428
+ assert_equal "hoo 5", t.errors["title"]
1429
+ end
1430
+
1431
+ def test_validates_associated_missing
1432
+ repair_validations(Reply) do
1433
+ Reply.validates_presence_of(:topic)
1434
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1435
+ assert !r.valid?
1436
+ assert r.errors.on(:topic)
1437
+
1438
+ r.topic = Topic.find :first
1439
+ assert r.valid?
1440
+ end
1441
+ end
1442
+
1443
+ def test_errors_to_xml
1444
+ r = Reply.new :title => "Wrong Create"
1445
+ assert !r.valid?
1446
+ xml = r.errors.to_xml(:skip_instruct => true)
1447
+ assert_equal "<errors>", xml.first(8)
1448
+ assert xml.include?("<error>Title is Wrong Create</error>")
1449
+ assert xml.include?("<error>Content Empty</error>")
1450
+ end
1451
+
1452
+ def test_validation_order
1453
+ Topic.validates_presence_of :title, :author_name
1454
+ Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')}
1455
+ Topic.validates_length_of :title, :content, :minimum => 2
1456
+
1457
+ t = Topic.new :title => ''
1458
+ t.valid?
1459
+ e = t.errors.instance_variable_get '@errors'
1460
+ assert_equal 'title', key = e.keys.first
1461
+ assert_equal "can't be blank", t.errors.on(key).first
1462
+ assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key).second
1463
+ assert_equal 'author_name', key = e.keys.second
1464
+ assert_equal "can't be blank", t.errors.on(key)
1465
+ assert_equal 'author_email_address', key = e.keys.third
1466
+ assert_equal 'will never be valid', t.errors.on(key)
1467
+ assert_equal 'content', key = e.keys.fourth
1468
+ assert_equal 'is too short (minimum is 2 characters)', t.errors.on(key)
1469
+ end
1470
+
1471
+ def test_invalid_should_be_the_opposite_of_valid
1472
+ Topic.validates_presence_of :title
1473
+
1474
+ t = Topic.new
1475
+ assert t.invalid?
1476
+ assert t.errors.invalid?(:title)
1477
+
1478
+ t.title = 'Things are going to change'
1479
+ assert !t.invalid?
1480
+ end
1481
+
1482
+ # previous implementation of validates_presence_of eval'd the
1483
+ # string with the wrong binding, this regression test is to
1484
+ # ensure that it works correctly
1485
+ def test_validation_with_if_as_string
1486
+ Topic.validates_presence_of(:title)
1487
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1488
+
1489
+ t = Topic.new
1490
+ assert !t.valid?, "A topic without a title should not be valid"
1491
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1492
+
1493
+ t.title = "Just a title"
1494
+ assert t.valid?, "A topic with a basic title should be valid"
1495
+
1496
+ t.title = "A very important title"
1497
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1498
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1499
+
1500
+ t.author_name = "Hubert J. Farnsworth"
1501
+ assert t.valid?, "A topic with an important title and author should be valid"
1502
+ end
1503
+ end
1504
+
1505
+
1506
+ class ValidatesNumericalityTest < ActiveRecord::TestCase
1507
+ NIL = [nil]
1508
+ BLANK = ["", " ", " \t \r \n"]
1509
+ BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1510
+ FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5)
1511
+ INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1512
+ FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1513
+ INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1514
+ BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1515
+ JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
1516
+ INFINITY = [1.0/0.0]
1517
+
1518
+ repair_validations(Topic)
1519
+
1520
+ def test_default_validates_numericality_of
1521
+ Topic.validates_numericality_of :approved
1522
+
1523
+ invalid!(NIL + BLANK + JUNK)
1524
+ valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1525
+ end
1526
+
1527
+ def test_validates_numericality_of_with_nil_allowed
1528
+ Topic.validates_numericality_of :approved, :allow_nil => true
1529
+
1530
+ invalid!(JUNK)
1531
+ valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY)
1532
+ end
1533
+
1534
+ def test_validates_numericality_of_with_integer_only
1535
+ Topic.validates_numericality_of :approved, :only_integer => true
1536
+
1537
+ invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY)
1538
+ valid!(INTEGERS)
1539
+ end
1540
+
1541
+ def test_validates_numericality_of_with_integer_only_and_nil_allowed
1542
+ Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1543
+
1544
+ invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY)
1545
+ valid!(NIL + BLANK + INTEGERS)
1546
+ end
1547
+
1548
+ def test_validates_numericality_with_greater_than
1549
+ Topic.validates_numericality_of :approved, :greater_than => 10
1550
+
1551
+ invalid!([-10, 10], 'must be greater than 10')
1552
+ valid!([11])
1553
+ end
1554
+
1555
+ def test_validates_numericality_with_greater_than_or_equal
1556
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1557
+
1558
+ invalid!([-9, 9], 'must be greater than or equal to 10')
1559
+ valid!([10])
1560
+ end
1561
+
1562
+ def test_validates_numericality_with_equal_to
1563
+ Topic.validates_numericality_of :approved, :equal_to => 10
1564
+
1565
+ invalid!([-10, 11] + INFINITY, 'must be equal to 10')
1566
+ valid!([10])
1567
+ end
1568
+
1569
+ def test_validates_numericality_with_less_than
1570
+ Topic.validates_numericality_of :approved, :less_than => 10
1571
+
1572
+ invalid!([10], 'must be less than 10')
1573
+ valid!([-9, 9])
1574
+ end
1575
+
1576
+ def test_validates_numericality_with_less_than_or_equal_to
1577
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1578
+
1579
+ invalid!([11], 'must be less than or equal to 10')
1580
+ valid!([-10, 10])
1581
+ end
1582
+
1583
+ def test_validates_numericality_with_odd
1584
+ Topic.validates_numericality_of :approved, :odd => true
1585
+
1586
+ invalid!([-2, 2], 'must be odd')
1587
+ valid!([-1, 1])
1588
+ end
1589
+
1590
+ def test_validates_numericality_with_even
1591
+ Topic.validates_numericality_of :approved, :even => true
1592
+
1593
+ invalid!([-1, 1], 'must be even')
1594
+ valid!([-2, 2])
1595
+ end
1596
+
1597
+ def test_validates_numericality_with_greater_than_less_than_and_even
1598
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1599
+
1600
+ invalid!([1, 3, 4])
1601
+ valid!([2])
1602
+ end
1603
+
1604
+ def test_validates_numericality_with_numeric_message
1605
+ Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}"
1606
+ topic = Topic.new("title" => "numeric test", "approved" => 10)
1607
+
1608
+ assert !topic.valid?
1609
+ assert_equal "smaller than 4", topic.errors.on(:approved)
1610
+
1611
+ Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}"
1612
+ topic = Topic.new("title" => "numeric test", "approved" => 1)
1613
+
1614
+ assert !topic.valid?
1615
+ assert_equal "greater than 4", topic.errors.on(:approved)
1616
+ end
1617
+
1618
+ private
1619
+ def invalid!(values, error=nil)
1620
+ with_each_topic_approved_value(values) do |topic, value|
1621
+ assert !topic.valid?, "#{value.inspect} not rejected as a number"
1622
+ assert topic.errors.on(:approved)
1623
+ assert_equal error, topic.errors.on(:approved) if error
1624
+ end
1625
+ end
1626
+
1627
+ def valid!(values)
1628
+ with_each_topic_approved_value(values) do |topic, value|
1629
+ assert topic.valid?, "#{value.inspect} not accepted as a number"
1630
+ end
1631
+ end
1632
+
1633
+ def with_each_topic_approved_value(values)
1634
+ topic = Topic.new("title" => "numeric test", "content" => "whatever")
1635
+ values.each do |value|
1636
+ topic.approved = value
1637
+ yield topic, value
1638
+ end
1639
+ end
1640
+ end