activerecord_csi 2.3.5.p6

Sign up to get free protection for your applications and to get access to all the features.
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