activerecord 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (311) hide show
  1. data/CHANGELOG +4928 -3
  2. data/README +45 -46
  3. data/RUNNING_UNIT_TESTS +8 -11
  4. data/Rakefile +247 -0
  5. data/install.rb +8 -38
  6. data/lib/active_record/aggregations.rb +64 -49
  7. data/lib/active_record/associations/association_collection.rb +217 -47
  8. data/lib/active_record/associations/association_proxy.rb +159 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +155 -37
  12. data/lib/active_record/associations/has_many_association.rb +145 -75
  13. data/lib/active_record/associations/has_many_through_association.rb +283 -0
  14. data/lib/active_record/associations/has_one_association.rb +96 -0
  15. data/lib/active_record/associations.rb +1537 -304
  16. data/lib/active_record/attribute_methods.rb +328 -0
  17. data/lib/active_record/base.rb +2001 -588
  18. data/lib/active_record/calculations.rb +269 -0
  19. data/lib/active_record/callbacks.rb +169 -165
  20. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +308 -0
  21. data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -0
  22. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  23. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +472 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +306 -0
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +125 -279
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +442 -77
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +805 -135
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +353 -69
  31. data/lib/active_record/fixtures.rb +946 -100
  32. data/lib/active_record/locking/optimistic.rb +144 -0
  33. data/lib/active_record/locking/pessimistic.rb +77 -0
  34. data/lib/active_record/migration.rb +417 -0
  35. data/lib/active_record/observer.rb +142 -32
  36. data/lib/active_record/query_cache.rb +23 -0
  37. data/lib/active_record/reflection.rb +163 -70
  38. data/lib/active_record/schema.rb +58 -0
  39. data/lib/active_record/schema_dumper.rb +171 -0
  40. data/lib/active_record/serialization.rb +98 -0
  41. data/lib/active_record/serializers/json_serializer.rb +71 -0
  42. data/lib/active_record/serializers/xml_serializer.rb +315 -0
  43. data/lib/active_record/timestamp.rb +41 -0
  44. data/lib/active_record/transactions.rb +87 -57
  45. data/lib/active_record/validations.rb +909 -122
  46. data/lib/active_record/vendor/db2.rb +362 -0
  47. data/lib/active_record/vendor/mysql.rb +126 -29
  48. data/lib/active_record/version.rb +9 -0
  49. data/lib/active_record.rb +35 -7
  50. data/lib/activerecord.rb +1 -0
  51. data/test/aaa_create_tables_test.rb +72 -0
  52. data/test/abstract_unit.rb +73 -5
  53. data/test/active_schema_test_mysql.rb +43 -0
  54. data/test/adapter_test.rb +105 -0
  55. data/test/adapter_test_sqlserver.rb +95 -0
  56. data/test/aggregations_test.rb +110 -16
  57. data/test/all.sh +2 -2
  58. data/test/ar_schema_test.rb +33 -0
  59. data/test/association_inheritance_reload.rb +14 -0
  60. data/test/associations/ar_joins_test.rb +0 -0
  61. data/test/associations/callbacks_test.rb +147 -0
  62. data/test/associations/cascaded_eager_loading_test.rb +110 -0
  63. data/test/associations/eager_singularization_test.rb +145 -0
  64. data/test/associations/eager_test.rb +442 -0
  65. data/test/associations/extension_test.rb +47 -0
  66. data/test/associations/inner_join_association_test.rb +88 -0
  67. data/test/associations/join_model_test.rb +553 -0
  68. data/test/associations_test.rb +1930 -267
  69. data/test/attribute_methods_test.rb +146 -0
  70. data/test/base_test.rb +1316 -84
  71. data/test/binary_test.rb +32 -0
  72. data/test/calculations_test.rb +251 -0
  73. data/test/callbacks_test.rb +400 -0
  74. data/test/class_inheritable_attributes_test.rb +3 -4
  75. data/test/column_alias_test.rb +17 -0
  76. data/test/connection_test_firebird.rb +8 -0
  77. data/test/connection_test_mysql.rb +30 -0
  78. data/test/connections/native_db2/connection.rb +25 -0
  79. data/test/connections/native_firebird/connection.rb +26 -0
  80. data/test/connections/native_frontbase/connection.rb +27 -0
  81. data/test/connections/native_mysql/connection.rb +21 -18
  82. data/test/connections/native_openbase/connection.rb +21 -0
  83. data/test/connections/native_oracle/connection.rb +27 -0
  84. data/test/connections/native_postgresql/connection.rb +17 -18
  85. data/test/connections/native_sqlite/connection.rb +17 -16
  86. data/test/connections/native_sqlite3/connection.rb +25 -0
  87. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  88. data/test/connections/native_sybase/connection.rb +23 -0
  89. data/test/copy_table_test_sqlite.rb +69 -0
  90. data/test/datatype_test_postgresql.rb +203 -0
  91. data/test/date_time_test.rb +37 -0
  92. data/test/default_test_firebird.rb +16 -0
  93. data/test/defaults_test.rb +67 -0
  94. data/test/deprecated_finder_test.rb +30 -0
  95. data/test/finder_test.rb +607 -32
  96. data/test/fixtures/accounts.yml +28 -0
  97. data/test/fixtures/all/developers.yml +0 -0
  98. data/test/fixtures/all/people.csv +0 -0
  99. data/test/fixtures/all/tasks.yml +0 -0
  100. data/test/fixtures/author.rb +107 -0
  101. data/test/fixtures/author_favorites.yml +4 -0
  102. data/test/fixtures/authors.yml +7 -0
  103. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  104. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  105. data/test/fixtures/bad_fixtures/blank_line +3 -0
  106. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  107. data/test/fixtures/bad_fixtures/missing_value +1 -0
  108. data/test/fixtures/binaries.yml +132 -0
  109. data/test/fixtures/binary.rb +2 -0
  110. data/test/fixtures/book.rb +4 -0
  111. data/test/fixtures/books.yml +7 -0
  112. data/test/fixtures/categories/special_categories.yml +9 -0
  113. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  114. data/test/fixtures/categories.yml +14 -0
  115. data/test/fixtures/categories_ordered.yml +7 -0
  116. data/test/fixtures/categories_posts.yml +23 -0
  117. data/test/fixtures/categorization.rb +5 -0
  118. data/test/fixtures/categorizations.yml +17 -0
  119. data/test/fixtures/category.rb +26 -0
  120. data/test/fixtures/citation.rb +6 -0
  121. data/test/fixtures/comment.rb +23 -0
  122. data/test/fixtures/comments.yml +59 -0
  123. data/test/fixtures/companies.yml +55 -0
  124. data/test/fixtures/company.rb +81 -4
  125. data/test/fixtures/company_in_module.rb +32 -6
  126. data/test/fixtures/computer.rb +4 -0
  127. data/test/fixtures/computers.yml +4 -0
  128. data/test/fixtures/contact.rb +16 -0
  129. data/test/fixtures/courses.yml +7 -0
  130. data/test/fixtures/customer.rb +28 -3
  131. data/test/fixtures/customers.yml +17 -0
  132. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  133. data/test/fixtures/db_definitions/db2.sql +235 -0
  134. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  135. data/test/fixtures/db_definitions/db22.sql +5 -0
  136. data/test/fixtures/db_definitions/firebird.drop.sql +65 -0
  137. data/test/fixtures/db_definitions/firebird.sql +310 -0
  138. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  139. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  140. data/test/fixtures/db_definitions/frontbase.drop.sql +33 -0
  141. data/test/fixtures/db_definitions/frontbase.sql +273 -0
  142. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  143. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  144. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  145. data/test/fixtures/db_definitions/openbase.sql +318 -0
  146. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  148. data/test/fixtures/db_definitions/oracle.drop.sql +67 -0
  149. data/test/fixtures/db_definitions/oracle.sql +330 -0
  150. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  151. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  152. data/test/fixtures/db_definitions/postgresql.drop.sql +44 -0
  153. data/test/fixtures/db_definitions/postgresql.sql +217 -38
  154. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/postgresql2.sql +2 -2
  156. data/test/fixtures/db_definitions/schema.rb +354 -0
  157. data/test/fixtures/db_definitions/schema2.rb +11 -0
  158. data/test/fixtures/db_definitions/sqlite.drop.sql +33 -0
  159. data/test/fixtures/db_definitions/sqlite.sql +139 -5
  160. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  161. data/test/fixtures/db_definitions/sqlite2.sql +1 -0
  162. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  163. data/test/fixtures/db_definitions/sybase.sql +222 -0
  164. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  165. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  166. data/test/fixtures/developer.rb +70 -6
  167. data/test/fixtures/developers.yml +21 -0
  168. data/test/fixtures/developers_projects/david_action_controller +2 -1
  169. data/test/fixtures/developers_projects/david_active_record +2 -1
  170. data/test/fixtures/developers_projects.yml +17 -0
  171. data/test/fixtures/edge.rb +5 -0
  172. data/test/fixtures/edges.yml +6 -0
  173. data/test/fixtures/entrants.yml +14 -0
  174. data/test/fixtures/example.log +1 -0
  175. data/test/fixtures/fk_test_has_fk.yml +3 -0
  176. data/test/fixtures/fk_test_has_pk.yml +2 -0
  177. data/test/fixtures/flowers.jpg +0 -0
  178. data/test/fixtures/funny_jokes.yml +10 -0
  179. data/test/fixtures/item.rb +7 -0
  180. data/test/fixtures/items.yml +4 -0
  181. data/test/fixtures/joke.rb +3 -0
  182. data/test/fixtures/keyboard.rb +3 -0
  183. data/test/fixtures/legacy_thing.rb +3 -0
  184. data/test/fixtures/legacy_things.yml +3 -0
  185. data/test/fixtures/matey.rb +4 -0
  186. data/test/fixtures/mateys.yml +4 -0
  187. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  188. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  189. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  190. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  191. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  192. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  193. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  194. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  195. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  196. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  197. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  198. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  199. data/test/fixtures/minimalistic.rb +2 -0
  200. data/test/fixtures/minimalistics.yml +2 -0
  201. data/test/fixtures/mixed_case_monkey.rb +3 -0
  202. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  203. data/test/fixtures/mixins.yml +29 -0
  204. data/test/fixtures/movies.yml +7 -0
  205. data/test/fixtures/naked/csv/accounts.csv +1 -0
  206. data/test/fixtures/naked/yml/accounts.yml +1 -0
  207. data/test/fixtures/naked/yml/companies.yml +1 -0
  208. data/test/fixtures/naked/yml/courses.yml +1 -0
  209. data/test/fixtures/order.rb +4 -0
  210. data/test/fixtures/parrot.rb +13 -0
  211. data/test/fixtures/parrots.yml +27 -0
  212. data/test/fixtures/parrots_pirates.yml +7 -0
  213. data/test/fixtures/people.yml +3 -0
  214. data/test/fixtures/person.rb +4 -0
  215. data/test/fixtures/pirate.rb +5 -0
  216. data/test/fixtures/pirates.yml +9 -0
  217. data/test/fixtures/post.rb +59 -0
  218. data/test/fixtures/posts.yml +48 -0
  219. data/test/fixtures/project.rb +27 -2
  220. data/test/fixtures/projects.yml +7 -0
  221. data/test/fixtures/reader.rb +4 -0
  222. data/test/fixtures/readers.yml +4 -0
  223. data/test/fixtures/reply.rb +18 -2
  224. data/test/fixtures/reserved_words/distinct.yml +5 -0
  225. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  226. data/test/fixtures/reserved_words/group.yml +14 -0
  227. data/test/fixtures/reserved_words/select.yml +8 -0
  228. data/test/fixtures/reserved_words/values.yml +7 -0
  229. data/test/fixtures/ship.rb +3 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/subject.rb +4 -0
  232. data/test/fixtures/subscriber.rb +4 -3
  233. data/test/fixtures/tag.rb +7 -0
  234. data/test/fixtures/tagging.rb +10 -0
  235. data/test/fixtures/taggings.yml +25 -0
  236. data/test/fixtures/tags.yml +7 -0
  237. data/test/fixtures/task.rb +3 -0
  238. data/test/fixtures/tasks.yml +7 -0
  239. data/test/fixtures/topic.rb +20 -3
  240. data/test/fixtures/topics.yml +22 -0
  241. data/test/fixtures/treasure.rb +4 -0
  242. data/test/fixtures/treasures.yml +10 -0
  243. data/test/fixtures/vertex.rb +9 -0
  244. data/test/fixtures/vertices.yml +4 -0
  245. data/test/fixtures_test.rb +574 -8
  246. data/test/inheritance_test.rb +113 -27
  247. data/test/json_serialization_test.rb +180 -0
  248. data/test/lifecycle_test.rb +56 -29
  249. data/test/locking_test.rb +273 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +933 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_test.rb +95 -0
  254. data/test/modules_test.rb +23 -10
  255. data/test/multiple_db_test.rb +17 -3
  256. data/test/pk_test.rb +59 -15
  257. data/test/query_cache_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +124 -27
  260. data/test/reserved_word_test_mysql.rb +177 -0
  261. data/test/schema_authorization_test_postgresql.rb +75 -0
  262. data/test/schema_dumper_test.rb +131 -0
  263. data/test/schema_test_postgresql.rb +64 -0
  264. data/test/serialization_test.rb +47 -0
  265. data/test/synonym_test_oracle.rb +17 -0
  266. data/test/table_name_test_sqlserver.rb +23 -0
  267. data/test/threaded_connections_test.rb +48 -0
  268. data/test/transactions_test.rb +227 -29
  269. data/test/unconnected_test.rb +14 -6
  270. data/test/validations_test.rb +1293 -32
  271. data/test/xml_serialization_test.rb +202 -0
  272. metadata +347 -143
  273. data/dev-utils/eval_debugger.rb +0 -9
  274. data/examples/associations.rb +0 -87
  275. data/examples/shared_setup.rb +0 -15
  276. data/examples/validation.rb +0 -88
  277. data/lib/active_record/deprecated_associations.rb +0 -70
  278. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  279. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  280. data/lib/active_record/support/clean_logger.rb +0 -10
  281. data/lib/active_record/support/inflector.rb +0 -70
  282. data/lib/active_record/vendor/simple.rb +0 -702
  283. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  284. data/lib/active_record/wrappings.rb +0 -59
  285. data/rakefile +0 -122
  286. data/test/deprecated_associations_test.rb +0 -336
  287. data/test/fixtures/accounts/signals37 +0 -3
  288. data/test/fixtures/accounts/unknown +0 -2
  289. data/test/fixtures/companies/first_client +0 -6
  290. data/test/fixtures/companies/first_firm +0 -4
  291. data/test/fixtures/companies/second_client +0 -6
  292. data/test/fixtures/courses/java +0 -2
  293. data/test/fixtures/courses/ruby +0 -2
  294. data/test/fixtures/customers/david +0 -6
  295. data/test/fixtures/db_definitions/mysql.sql +0 -96
  296. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  297. data/test/fixtures/developers/david +0 -2
  298. data/test/fixtures/developers/jamis +0 -2
  299. data/test/fixtures/entrants/first +0 -3
  300. data/test/fixtures/entrants/second +0 -3
  301. data/test/fixtures/entrants/third +0 -3
  302. data/test/fixtures/fixture_database.sqlite +0 -0
  303. data/test/fixtures/fixture_database_2.sqlite +0 -0
  304. data/test/fixtures/movies/first +0 -2
  305. data/test/fixtures/movies/second +0 -2
  306. data/test/fixtures/projects/action_controller +0 -2
  307. data/test/fixtures/projects/active_record +0 -2
  308. data/test/fixtures/topics/first +0 -9
  309. data/test/fixtures/topics/second +0 -8
  310. data/test/inflector_test.rb +0 -104
  311. data/test/thread_safety_test.rb +0 -33
@@ -1,28 +1,80 @@
1
1
  require 'abstract_unit'
2
2
  require 'fixtures/topic'
3
3
  require 'fixtures/reply'
4
+ require 'fixtures/person'
4
5
  require 'fixtures/developer'
5
6
 
7
+ # The following methods in Topic are used in test_conditional_validation_*
8
+ class Topic
9
+ def condition_is_true
10
+ return true
11
+ end
12
+
13
+ def condition_is_true_but_its_not
14
+ return false
15
+ end
16
+ end
17
+
18
+ class ProtectedPerson < ActiveRecord::Base
19
+ set_table_name 'people'
20
+ attr_accessor :addon
21
+ attr_protected :first_name
22
+ end
23
+
24
+ class UniqueReply < Reply
25
+ validates_uniqueness_of :content, :scope => 'parent_id'
26
+ end
27
+
28
+ class PlagiarizedReply < Reply
29
+ validates_acceptance_of :author_name
30
+ end
31
+
32
+ class SillyUniqueReply < UniqueReply
33
+ end
34
+
35
+ class Topic < ActiveRecord::Base
36
+ has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
37
+ has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
38
+ end
39
+
40
+ class Wizard < ActiveRecord::Base
41
+ self.abstract_class = true
42
+
43
+ validates_uniqueness_of :name
44
+ end
45
+
46
+ class IneptWizard < Wizard
47
+ validates_uniqueness_of :city
48
+ end
49
+
50
+ class Conjurer < IneptWizard
51
+ end
52
+
53
+ class Thaumaturgist < IneptWizard
54
+ end
6
55
 
7
56
  class ValidationsTest < Test::Unit::TestCase
57
+ fixtures :topics, :developers
58
+
8
59
  def setup
9
- @topic_fixtures = create_fixtures("topics")
10
- @developers = create_fixtures("developers")
60
+ Topic.write_inheritable_attribute(:validate, nil)
61
+ Topic.write_inheritable_attribute(:validate_on_create, nil)
62
+ Topic.write_inheritable_attribute(:validate_on_update, nil)
11
63
  end
12
64
 
13
65
  def test_single_field_validation
14
66
  r = Reply.new
15
67
  r.title = "There's no content!"
16
- assert !r.save, "A reply without content shouldn't be saveable"
68
+ assert !r.valid?, "A reply without content shouldn't be saveable"
17
69
 
18
70
  r.content = "Messa content!"
19
- assert r.save, "A reply with content should be saveable"
71
+ assert r.valid?, "A reply with content should be saveable"
20
72
  end
21
-
73
+
22
74
  def test_single_attr_validation_and_error_msg
23
75
  r = Reply.new
24
76
  r.title = "There's no content!"
25
- r.save
77
+ assert !r.valid?
26
78
  assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
27
79
  assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
28
80
  assert_equal 1, r.errors.count
@@ -30,7 +82,7 @@ class ValidationsTest < Test::Unit::TestCase
30
82
 
31
83
  def test_double_attr_validation_and_error_msg
32
84
  r = Reply.new
33
- assert !r.save
85
+ assert !r.valid?
34
86
 
35
87
  assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
36
88
  assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
@@ -40,66 +92,107 @@ class ValidationsTest < Test::Unit::TestCase
40
92
 
41
93
  assert_equal 2, r.errors.count
42
94
  end
43
-
95
+
44
96
  def test_error_on_create
45
97
  r = Reply.new
46
98
  r.title = "Wrong Create"
47
- assert !r.save
99
+ assert !r.valid?
48
100
  assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
49
101
  assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
50
102
  end
51
103
 
52
-
53
104
  def test_error_on_update
54
105
  r = Reply.new
55
106
  r.title = "Bad"
56
107
  r.content = "Good"
57
-
58
108
  assert r.save, "First save should be successful"
59
-
109
+
60
110
  r.title = "Wrong Update"
61
111
  assert !r.save, "Second save should fail"
62
-
112
+
63
113
  assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
64
114
  assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
65
115
  end
66
-
116
+
117
+ def test_invalid_record_exception
118
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
119
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.new.save! }
120
+
121
+ begin
122
+ r = Reply.new
123
+ r.save!
124
+ flunk
125
+ rescue ActiveRecord::RecordInvalid => invalid
126
+ assert_equal r, invalid.record
127
+ end
128
+ end
129
+
130
+ def test_exception_on_create_bang_many
131
+ assert_raises(ActiveRecord::RecordInvalid) do
132
+ Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
133
+ end
134
+ end
135
+
136
+ def test_scoped_create_without_attributes
137
+ Reply.with_scope(:create => {}) do
138
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
139
+ end
140
+ end
141
+
142
+ def test_create_with_exceptions_using_scope_for_protected_attributes
143
+ assert_nothing_raised do
144
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
145
+ person = ProtectedPerson.create! :addon => "Addon"
146
+ assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
147
+ end
148
+ end
149
+ end
150
+
151
+ def test_create_with_exceptions_using_scope_and_empty_attributes
152
+ assert_nothing_raised do
153
+ ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
154
+ person = ProtectedPerson.create!
155
+ assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
156
+ end
157
+ end
158
+ end
159
+
67
160
  def test_single_error_per_attr_iteration
68
161
  r = Reply.new
69
162
  r.save
70
-
163
+
71
164
  errors = []
72
165
  r.errors.each { |attr, msg| errors << [attr, msg] }
73
-
166
+
74
167
  assert errors.include?(["title", "Empty"])
75
168
  assert errors.include?(["content", "Empty"])
76
169
  end
77
-
170
+
78
171
  def test_multiple_errors_per_attr_iteration_with_full_error_composition
79
172
  r = Reply.new
80
173
  r.title = "Wrong Create"
81
174
  r.content = "Mismatch"
82
175
  r.save
83
-
176
+
84
177
  errors = []
85
178
  r.errors.each_full { |error| errors << error }
86
-
179
+
87
180
  assert_equal "Title is Wrong Create", errors[0]
88
181
  assert_equal "Title is Content Mismatch", errors[1]
89
182
  assert_equal 2, r.errors.count
90
183
  end
91
-
184
+
92
185
  def test_errors_on_base
93
186
  r = Reply.new
94
187
  r.content = "Mismatch"
95
188
  r.save
96
189
  r.errors.add_to_base "Reply is not dignifying"
97
-
190
+
98
191
  errors = []
99
192
  r.errors.each_full { |error| errors << error }
100
-
193
+
101
194
  assert_equal "Reply is not dignifying", r.errors.on_base
102
-
195
+
103
196
  assert errors.include?("Title Empty")
104
197
  assert errors.include?("Reply is not dignifying")
105
198
  assert_equal 2, r.errors.count
@@ -110,17 +203,1185 @@ class ValidationsTest < Test::Unit::TestCase
110
203
  assert !reply.save
111
204
  assert reply.save(false)
112
205
  end
206
+
207
+ def test_create_without_validation_bang
208
+ count = Reply.count
209
+ assert_nothing_raised { Reply.new.save_without_validation! }
210
+ assert count+1, Reply.count
211
+ end
212
+
213
+ def test_validates_each
214
+ perform = true
215
+ hits = 0
216
+ Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
217
+ if perform
218
+ record.errors.add attr, 'gotcha'
219
+ hits += 1
220
+ end
221
+ end
222
+ t = Topic.new("title" => "valid", "content" => "whatever")
223
+ assert !t.save
224
+ assert_equal 4, hits
225
+ assert_equal %w(gotcha gotcha), t.errors.on(:title)
226
+ assert_equal %w(gotcha gotcha), t.errors.on(:content)
227
+ ensure
228
+ perform = false
229
+ end
230
+
231
+ def test_no_title_confirmation
232
+ Topic.validates_confirmation_of(:title)
233
+
234
+ t = Topic.new(:author_name => "Plutarch")
235
+ assert t.valid?
236
+
237
+ t.title_confirmation = "Parallel Lives"
238
+ assert !t.valid?
239
+
240
+ t.title_confirmation = nil
241
+ t.title = "Parallel Lives"
242
+ assert t.valid?
243
+
244
+ t.title_confirmation = "Parallel Lives"
245
+ assert t.valid?
246
+ end
247
+
248
+ def test_title_confirmation
249
+ Topic.validates_confirmation_of(:title)
250
+
251
+ t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
252
+ assert !t.save
253
+
254
+ t.title_confirmation = "We should be confirmed"
255
+ assert t.save
256
+ end
257
+
258
+ def test_terms_of_service_agreement_no_acceptance
259
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
260
+
261
+ t = Topic.create("title" => "We should not be confirmed")
262
+ assert t.save
263
+ end
264
+
265
+ def test_terms_of_service_agreement
266
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
267
+
268
+ t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
269
+ assert !t.save
270
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
271
+
272
+ t.terms_of_service = "1"
273
+ assert t.save
274
+ end
275
+
276
+
277
+ def test_eula
278
+ Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
279
+
280
+ t = Topic.create("title" => "We should be confirmed","eula" => "")
281
+ assert !t.save
282
+ assert_equal "must be abided", t.errors.on(:eula)
283
+
284
+ t.eula = "1"
285
+ assert t.save
286
+ end
287
+
288
+ def test_terms_of_service_agreement_with_accept_value
289
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
290
+
291
+ t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
292
+ assert !t.save
293
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
294
+
295
+ t.terms_of_service = "I agree."
296
+ assert t.save
297
+ end
298
+
299
+ def test_validates_acceptance_of_as_database_column
300
+ reply = PlagiarizedReply.create("author_name" => "Dan Brown")
301
+ assert_equal "Dan Brown", reply["author_name"]
302
+ end
303
+
304
+ def test_validate_presences
305
+ Topic.validates_presence_of(:title, :content)
306
+
307
+ t = Topic.create
308
+ assert !t.save
309
+ assert_equal "can't be blank", t.errors.on(:title)
310
+ assert_equal "can't be blank", t.errors.on(:content)
311
+
312
+ t.title = "something"
313
+ t.content = " "
314
+
315
+ assert !t.save
316
+ assert_equal "can't be blank", t.errors.on(:content)
317
+
318
+ t.content = "like stuff"
319
+
320
+ assert t.save
321
+ end
322
+
323
+ def test_validate_uniqueness
324
+ Topic.validates_uniqueness_of(:title)
325
+
326
+ t = Topic.new("title" => "I'm unique!")
327
+ assert t.save, "Should save t as unique"
328
+
329
+ t.content = "Remaining unique"
330
+ assert t.save, "Should still save t as unique"
331
+
332
+ t2 = Topic.new("title" => "I'm unique!")
333
+ assert !t2.valid?, "Shouldn't be valid"
334
+ assert !t2.save, "Shouldn't save t2 as unique"
335
+ assert_equal "has already been taken", t2.errors.on(:title)
336
+
337
+ t2.title = "Now Im really also unique"
338
+ assert t2.save, "Should now save t2 as unique"
339
+ end
340
+
341
+ def test_validate_uniqueness_with_scope
342
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
343
+
344
+ t = Topic.create("title" => "I'm unique!")
345
+
346
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
347
+ assert r1.valid?, "Saving r1"
348
+
349
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
350
+ assert !r2.valid?, "Saving r2 first time"
351
+
352
+ r2.content = "something else"
353
+ assert r2.save, "Saving r2 second time"
354
+
355
+ t2 = Topic.create("title" => "I'm unique too!")
356
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
357
+ assert r3.valid?, "Saving r3"
358
+ end
359
+
360
+ def test_validate_uniqueness_scoped_to_defining_class
361
+ t = Topic.create("title" => "What, me worry?")
362
+
363
+ r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
364
+ assert r1.valid?, "Saving r1"
365
+
366
+ r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
367
+ assert !r2.valid?, "Saving r2"
368
+
369
+ # Should succeed as validates_uniqueness_of only applies to
370
+ # UniqueReply and its subclasses
371
+ r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
372
+ assert r3.valid?, "Saving r3"
373
+ end
374
+
375
+ def test_validate_uniqueness_with_scope_array
376
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
377
+
378
+ t = Topic.create("title" => "The earth is actually flat!")
379
+
380
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
381
+ assert r1.valid?, "Saving r1"
382
+
383
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
384
+ assert !r2.valid?, "Saving r2. Double reply by same author."
385
+
386
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
387
+ assert r2.save, "Saving r2 the second time."
388
+
389
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
390
+ assert !r3.valid?, "Saving r3"
391
+
392
+ r3.author_name = "jj"
393
+ assert r3.save, "Saving r3 the second time."
394
+
395
+ r3.author_name = "jeremy"
396
+ assert !r3.save, "Saving r3 the third time."
397
+ end
398
+
399
+ def test_validate_case_insensitive_uniqueness
400
+ Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
401
+
402
+ t = Topic.new("title" => "I'm unique!", :parent_id => 2)
403
+ assert t.save, "Should save t as unique"
404
+
405
+ t.content = "Remaining unique"
406
+ assert t.save, "Should still save t as unique"
407
+
408
+ t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
409
+ assert !t2.valid?, "Shouldn't be valid"
410
+ assert !t2.save, "Shouldn't save t2 as unique"
411
+ assert t2.errors.on(:title)
412
+ assert t2.errors.on(:parent_id)
413
+ assert_equal "has already been taken", t2.errors.on(:title)
414
+
415
+ t2.title = "I'm truly UNIQUE!"
416
+ assert !t2.valid?, "Shouldn't be valid"
417
+ assert !t2.save, "Shouldn't save t2 as unique"
418
+ assert_nil t2.errors.on(:title)
419
+ assert t2.errors.on(:parent_id)
420
+
421
+ t2.parent_id = 3
422
+ assert t2.save, "Should now save t2 as unique"
423
+
424
+ t2.parent_id = nil
425
+ t2.title = nil
426
+ assert t2.valid?, "should validate with nil"
427
+ assert t2.save, "should save with nil"
428
+ end
429
+
430
+ def test_validate_straight_inheritance_uniqueness
431
+ w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
432
+ assert w1.valid?, "Saving w1"
433
+
434
+ # Should use validation from base class (which is abstract)
435
+ w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
436
+ assert !w2.valid?, "w2 shouldn't be valid"
437
+ assert w2.errors.on(:name), "Should have errors for name"
438
+ assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
439
+
440
+ w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
441
+ assert !w3.valid?, "w3 shouldn't be valid"
442
+ assert w3.errors.on(:name), "Should have errors for name"
443
+ assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
444
+
445
+ w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
446
+ assert w4.valid?, "Saving w4"
447
+
448
+ w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
449
+ assert !w5.valid?, "w5 shouldn't be valid"
450
+ assert w5.errors.on(:name), "Should have errors for name"
451
+ assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
452
+
453
+ w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
454
+ assert !w6.valid?, "w6 shouldn't be valid"
455
+ assert w6.errors.on(:city), "Should have errors for city"
456
+ assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
457
+ end
458
+
459
+ def test_validate_format
460
+ Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
461
+
462
+ t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
463
+ assert !t.valid?, "Shouldn't be valid"
464
+ assert !t.save, "Shouldn't save because it's invalid"
465
+ assert_equal "is bad data", t.errors.on(:title)
466
+ assert_nil t.errors.on(:content)
467
+
468
+ t.title = "Validation macros rule!"
469
+
470
+ assert t.save
471
+ assert_nil t.errors.on(:title)
472
+
473
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
474
+ end
113
475
 
114
- def test_errors_on_boundary_breaking
115
- developer = Developer.new("name" => "xs")
116
- assert !developer.save
117
- assert_equal "is too short (min is 3 characters)", developer.errors.on("name")
476
+ # testing ticket #3142
477
+ def test_validate_format_numeric
478
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
479
+
480
+ t = Topic.create("title" => "72x", "content" => "6789")
481
+ assert !t.valid?, "Shouldn't be valid"
482
+ assert !t.save, "Shouldn't save because it's invalid"
483
+ assert_equal "is bad data", t.errors.on(:title)
484
+ assert_nil t.errors.on(:content)
485
+
486
+ t.title = "-11"
487
+ assert !t.valid?, "Shouldn't be valid"
488
+
489
+ t.title = "03"
490
+ assert !t.valid?, "Shouldn't be valid"
491
+
492
+ t.title = "z44"
493
+ assert !t.valid?, "Shouldn't be valid"
494
+
495
+ t.title = "5v7"
496
+ assert !t.valid?, "Shouldn't be valid"
497
+
498
+ t.title = "1"
499
+
500
+ assert t.save
501
+ assert_nil t.errors.on(:title)
502
+ end
503
+
504
+ def test_validates_inclusion_of
505
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
506
+
507
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
508
+ assert !Topic.create("title" => "a b", "content" => "abc").valid?
509
+ assert !Topic.create("title" => nil, "content" => "def").valid?
510
+
511
+ t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
512
+ assert t.valid?
513
+ t.title = "uhoh"
514
+ assert !t.valid?
515
+ assert t.errors.on(:title)
516
+ assert_equal "is not included in the list", t.errors["title"]
517
+
518
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
519
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
520
+
521
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
522
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
523
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
524
+ end
525
+
526
+ def test_validates_inclusion_of_with_allow_nil
527
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
528
+
529
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
530
+ assert !Topic.create("title" => "", "content" => "abc").valid?
531
+ assert Topic.create("title" => nil, "content" => "abc").valid?
532
+ end
533
+
534
+ def test_numericality_with_getter_method
535
+ Developer.validates_numericality_of( :salary )
536
+ developer = Developer.new("name" => "michael", "salary" => nil)
537
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
538
+ assert developer.valid?
539
+ end
540
+
541
+ def test_validates_length_of_with_allow_nil
542
+ Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
543
+
544
+ assert !Topic.create("title" => "ab").valid?
545
+ assert !Topic.create("title" => "").valid?
546
+ assert Topic.create("title" => nil).valid?
547
+ assert Topic.create("title" => "abcde").valid?
548
+ end
549
+
550
+ def test_validates_length_of_with_allow_blank
551
+ Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
552
+
553
+ assert !Topic.create("title" => "ab").valid?
554
+ assert Topic.create("title" => "").valid?
555
+ assert Topic.create("title" => nil).valid?
556
+ assert Topic.create("title" => "abcde").valid?
557
+ end
558
+
559
+ def test_validates_inclusion_of_with_formatted_message
560
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
561
+
562
+ assert Topic.create("title" => "a", "content" => "abc").valid?
563
+
564
+ t = Topic.create("title" => "uhoh", "content" => "abc")
565
+ assert !t.valid?
566
+ assert t.errors.on(:title)
567
+ assert_equal "option uhoh is not in the list", t.errors["title"]
568
+ end
569
+
570
+ def test_numericality_with_allow_nil_and_getter_method
571
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
572
+ developer = Developer.new("name" => "michael", "salary" => nil)
573
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
574
+ assert developer.valid?
575
+ end
576
+
577
+ def test_validates_exclusion_of
578
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
579
+
580
+ assert Topic.create("title" => "something", "content" => "abc").valid?
581
+ assert !Topic.create("title" => "monkey", "content" => "abc").valid?
582
+ end
583
+
584
+ def test_validates_exclusion_of_with_formatted_message
585
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %s is restricted" )
586
+
587
+ assert Topic.create("title" => "something", "content" => "abc")
588
+
589
+ t = Topic.create("title" => "monkey")
590
+ assert !t.valid?
591
+ assert t.errors.on(:title)
592
+ assert_equal "option monkey is restricted", t.errors["title"]
593
+ end
594
+
595
+ def test_validates_length_of_using_minimum
596
+ Topic.validates_length_of :title, :minimum => 5
597
+
598
+ t = Topic.create("title" => "valid", "content" => "whatever")
599
+ assert t.valid?
600
+
601
+ t.title = "not"
602
+ assert !t.valid?
603
+ assert t.errors.on(:title)
604
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
605
+
606
+ t.title = ""
607
+ assert !t.valid?
608
+ assert t.errors.on(:title)
609
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
610
+
611
+ t.title = nil
612
+ assert !t.valid?
613
+ assert t.errors.on(:title)
614
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
615
+ end
616
+
617
+ def test_optionally_validates_length_of_using_minimum
618
+ Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
619
+
620
+ t = Topic.create("title" => "valid", "content" => "whatever")
621
+ assert t.valid?
622
+
623
+ t.title = nil
624
+ assert t.valid?
625
+ end
626
+
627
+ def test_validates_length_of_using_maximum
628
+ Topic.validates_length_of :title, :maximum => 5
629
+
630
+ t = Topic.create("title" => "valid", "content" => "whatever")
631
+ assert t.valid?
632
+
633
+ t.title = "notvalid"
634
+ assert !t.valid?
635
+ assert t.errors.on(:title)
636
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
637
+
638
+ t.title = ""
639
+ assert t.valid?
640
+
641
+ t.title = nil
642
+ assert !t.valid?
643
+ end
644
+
645
+ def test_optionally_validates_length_of_using_maximum
646
+ Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
647
+
648
+ t = Topic.create("title" => "valid", "content" => "whatever")
649
+ assert t.valid?
650
+
651
+ t.title = nil
652
+ assert t.valid?
653
+ end
654
+
655
+ def test_validates_length_of_using_within
656
+ Topic.validates_length_of(:title, :content, :within => 3..5)
657
+
658
+ t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
659
+ assert !t.valid?
660
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
661
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
662
+
663
+ t.title = nil
664
+ t.content = nil
665
+ assert !t.valid?
666
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
667
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
668
+
669
+ t.title = "abe"
670
+ t.content = "mad"
671
+ assert t.valid?
672
+ end
673
+
674
+ def test_optionally_validates_length_of_using_within
675
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
676
+
677
+ t = Topic.create('title' => 'abc', 'content' => 'abcd')
678
+ assert t.valid?
679
+
680
+ t.title = nil
681
+ assert t.valid?
682
+ end
683
+
684
+ def test_optionally_validates_length_of_using_within_on_create
685
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %d"
686
+
687
+ t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
688
+ assert !t.save
689
+ assert t.errors.on(:title)
690
+ assert_equal "my string is too long: 10", t.errors[:title]
691
+
692
+ t.title = "butthisis"
693
+ assert t.save
694
+
695
+ t.title = "few"
696
+ assert t.save
697
+
698
+ t.content = "andthisislong"
699
+ assert t.save
700
+
701
+ t.content = t.title = "iamfine"
702
+ assert t.save
703
+ end
704
+
705
+ def test_optionally_validates_length_of_using_within_on_update
706
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %d"
707
+
708
+ t = Topic.create("title" => "vali", "content" => "whatever")
709
+ assert !t.save
710
+ assert t.errors.on(:title)
711
+
712
+ t.title = "not"
713
+ assert !t.save
714
+ assert t.errors.on(:title)
715
+ assert_equal "my string is too short: 5", t.errors[:title]
716
+
717
+ t.title = "valid"
718
+ t.content = "andthisistoolong"
719
+ assert !t.save
720
+ assert t.errors.on(:content)
721
+
722
+ t.content = "iamfine"
723
+ assert t.save
724
+ end
725
+
726
+ def test_validates_length_of_using_is
727
+ Topic.validates_length_of :title, :is => 5
728
+
729
+ t = Topic.create("title" => "valid", "content" => "whatever")
730
+ assert t.valid?
731
+
732
+ t.title = "notvalid"
733
+ assert !t.valid?
734
+ assert t.errors.on(:title)
735
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
736
+
737
+ t.title = ""
738
+ assert !t.valid?
739
+
740
+ t.title = nil
741
+ assert !t.valid?
742
+ end
743
+
744
+ def test_optionally_validates_length_of_using_is
745
+ Topic.validates_length_of :title, :is => 5, :allow_nil => true
746
+
747
+ t = Topic.create("title" => "valid", "content" => "whatever")
748
+ assert t.valid?
749
+
750
+ t.title = nil
751
+ assert t.valid?
752
+ end
753
+
754
+ def test_validates_length_of_using_bignum
755
+ bigmin = 2 ** 30
756
+ bigmax = 2 ** 32
757
+ bigrange = bigmin...bigmax
758
+ assert_nothing_raised do
759
+ Topic.validates_length_of :title, :is => bigmin + 5
760
+ Topic.validates_length_of :title, :within => bigrange
761
+ Topic.validates_length_of :title, :in => bigrange
762
+ Topic.validates_length_of :title, :minimum => bigmin
763
+ Topic.validates_length_of :title, :maximum => bigmax
764
+ end
765
+ end
766
+
767
+ def test_validates_length_with_globally_modified_error_message
768
+ ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
769
+ Topic.validates_length_of :title, :minimum => 10
770
+ t = Topic.create(:title => 'too short')
771
+ assert !t.valid?
772
+
773
+ assert_equal 'tu est trops petit hombre 10', t.errors['title']
774
+ end
775
+
776
+ def test_validates_size_of_association
777
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
778
+ t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
779
+ assert !t.save
780
+ assert t.errors.on(:replies)
781
+ reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
782
+ assert t.valid?
783
+ end
784
+
785
+ def test_validates_length_of_nasty_params
786
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
787
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
788
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
789
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
790
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
791
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
792
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
793
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
794
+ end
795
+
796
+ def test_validates_length_of_custom_errors_for_minimum_with_message
797
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %d" )
798
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
799
+ assert !t.valid?
800
+ assert t.errors.on(:title)
801
+ assert_equal "boo 5", t.errors["title"]
802
+ end
803
+
804
+ def test_validates_length_of_custom_errors_for_minimum_with_too_short
805
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %d" )
806
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
807
+ assert !t.valid?
808
+ assert t.errors.on(:title)
809
+ assert_equal "hoo 5", t.errors["title"]
810
+ end
811
+
812
+ def test_validates_length_of_custom_errors_for_maximum_with_message
813
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %d" )
814
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
815
+ assert !t.valid?
816
+ assert t.errors.on(:title)
817
+ assert_equal "boo 5", t.errors["title"]
818
+ end
819
+
820
+ def test_validates_length_of_custom_errors_for_maximum_with_too_long
821
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d" )
822
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
823
+ assert !t.valid?
824
+ assert t.errors.on(:title)
825
+ assert_equal "hoo 5", t.errors["title"]
826
+ end
827
+
828
+ def test_validates_length_of_custom_errors_for_is_with_message
829
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo %d" )
830
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
831
+ assert !t.valid?
832
+ assert t.errors.on(:title)
833
+ assert_equal "boo 5", t.errors["title"]
834
+ end
835
+
836
+ def test_validates_length_of_custom_errors_for_is_with_wrong_length
837
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %d" )
838
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
839
+ assert !t.valid?
840
+ assert t.errors.on(:title)
841
+ assert_equal "hoo 5", t.errors["title"]
842
+ end
843
+
844
+ def kcode_scope(kcode)
845
+ orig_kcode = $KCODE
846
+ $KCODE = kcode
847
+ begin
848
+ yield
849
+ ensure
850
+ $KCODE = orig_kcode
851
+ end
852
+ end
853
+
854
+ def test_validates_length_of_using_minimum_utf8
855
+ kcode_scope('UTF8') do
856
+ Topic.validates_length_of :title, :minimum => 5
857
+
858
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
859
+ assert t.valid?
860
+
861
+ t.title = "一二三四"
862
+ assert !t.valid?
863
+ assert t.errors.on(:title)
864
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
865
+ end
866
+ end
867
+
868
+ def test_validates_length_of_using_maximum_utf8
869
+ kcode_scope('UTF8') do
870
+ Topic.validates_length_of :title, :maximum => 5
871
+
872
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
873
+ assert t.valid?
874
+
875
+ t.title = "一二34五六"
876
+ assert !t.valid?
877
+ assert t.errors.on(:title)
878
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
879
+ end
880
+ end
881
+
882
+ def test_validates_length_of_using_within_utf8
883
+ kcode_scope('UTF8') do
884
+ Topic.validates_length_of(:title, :content, :within => 3..5)
885
+
886
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
887
+ assert !t.valid?
888
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
889
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
890
+ t.title = "一二三"
891
+ t.content = "12三"
892
+ assert t.valid?
893
+ end
894
+ end
895
+
896
+ def test_optionally_validates_length_of_using_within_utf8
897
+ kcode_scope('UTF8') do
898
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
899
+
900
+ t = Topic.create('title' => '一二三', 'content' => '一二三四五')
901
+ assert t.valid?
902
+
903
+ t.title = nil
904
+ assert t.valid?
905
+ end
906
+ end
907
+
908
+ def test_optionally_validates_length_of_using_within_on_create_utf8
909
+ kcode_scope('UTF8') do
910
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
911
+
912
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
913
+ assert !t.save
914
+ assert t.errors.on(:title)
915
+ assert_equal "長すぎます: 10", t.errors[:title]
916
+
917
+ t.title = "一二三四五六七八九"
918
+ assert t.save
919
+
920
+ t.title = "一二3"
921
+ assert t.save
922
+
923
+ t.content = "一二三四五六七八九十"
924
+ assert t.save
925
+
926
+ t.content = t.title = "一二三四五六"
927
+ assert t.save
928
+ end
929
+ end
930
+
931
+ def test_optionally_validates_length_of_using_within_on_update_utf8
932
+ kcode_scope('UTF8') do
933
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
934
+
935
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
936
+ assert !t.save
937
+ assert t.errors.on(:title)
938
+
939
+ t.title = "1二三4"
940
+ assert !t.save
941
+ assert t.errors.on(:title)
942
+ assert_equal "短すぎます: 5", t.errors[:title]
943
+
944
+ t.title = "valid"
945
+ t.content = "一二三四五六七八九十A"
946
+ assert !t.save
947
+ assert t.errors.on(:content)
948
+
949
+ t.content = "一二345"
950
+ assert t.save
951
+ end
952
+ end
953
+
954
+ def test_validates_length_of_using_is_utf8
955
+ kcode_scope('UTF8') do
956
+ Topic.validates_length_of :title, :is => 5
957
+
958
+ t = Topic.create("title" => "一二345", "content" => "whatever")
959
+ assert t.valid?
960
+
961
+ t.title = "一二345六"
962
+ assert !t.valid?
963
+ assert t.errors.on(:title)
964
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
965
+ end
966
+ end
967
+
968
+ def test_validates_size_of_association_utf8
969
+ kcode_scope('UTF8') do
970
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
971
+ t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
972
+ assert !t.save
973
+ assert t.errors.on(:replies)
974
+ t.replies.build('title' => 'あいうえお', 'content' => 'かきくけこ')
975
+ assert t.valid?
976
+ end
977
+ end
978
+
979
+ def test_validates_associated_many
980
+ Topic.validates_associated( :replies )
981
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
982
+ 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")]
983
+ assert !t.valid?
984
+ assert t.errors.on(:replies)
985
+ assert_equal 1, r.errors.count # make sure all associated objects have been validated
986
+ assert_equal 0, r2.errors.count
987
+ assert_equal 1, r3.errors.count
988
+ assert_equal 0, r4.errors.count
989
+ r.content = r3.content = "non-empty"
990
+ assert t.valid?
991
+ end
992
+
993
+ def test_validates_associated_one
994
+ Reply.validates_associated( :topic )
995
+ Topic.validates_presence_of( :content )
996
+ r = Reply.new("title" => "A reply", "content" => "with content!")
997
+ r.topic = Topic.create("title" => "uhohuhoh")
998
+ assert !r.valid?
999
+ assert r.errors.on(:topic)
1000
+ r.topic.content = "non-empty"
1001
+ assert r.valid?
1002
+ end
1003
+
1004
+ def test_validate_block
1005
+ Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
1006
+ t = Topic.create("title" => "Title", "content" => "whatever")
1007
+ assert !t.valid?
1008
+ assert t.errors.on(:title)
1009
+ assert_equal "will never be valid", t.errors["title"]
1010
+ end
1011
+
1012
+ def test_invalid_validator
1013
+ Topic.validate 3
1014
+ assert_raise(ActiveRecord::ActiveRecordError) { t = Topic.create }
1015
+ end
1016
+
1017
+ def test_throw_away_typing
1018
+ d = Developer.new("name" => "David", "salary" => "100,000")
1019
+ assert !d.valid?
1020
+ assert_equal 100, d.salary
1021
+ assert_equal "100,000", d.salary_before_type_cast
1022
+ end
1023
+
1024
+ def test_validates_acceptance_of_with_custom_error_using_quotes
1025
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
1026
+ d = Developer.new
1027
+ d.salary = "0"
1028
+ assert !d.valid?
1029
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1030
+ end
1031
+
1032
+ def test_validates_confirmation_of_with_custom_error_using_quotes
1033
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
1034
+ d = Developer.new
1035
+ d.name = "John"
1036
+ d.name_confirmation = "Johnny"
1037
+ assert !d.valid?
1038
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
1039
+ end
1040
+
1041
+ def test_validates_format_of_with_custom_error_using_quotes
1042
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
1043
+ d = Developer.new
1044
+ d.name = d.name_confirmation = "John 32"
1045
+ assert !d.valid?
1046
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
1047
+ end
1048
+
1049
+ def test_validates_inclusion_of_with_custom_error_using_quotes
1050
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
1051
+ d = Developer.new
1052
+ d.salary = "90,000"
1053
+ assert !d.valid?
1054
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1055
+ end
1056
+
1057
+ def test_validates_length_of_with_custom_too_long_using_quotes
1058
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
1059
+ d = Developer.new
1060
+ d.name = "Jeffrey"
1061
+ assert !d.valid?
1062
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1063
+ end
1064
+
1065
+ def test_validates_length_of_with_custom_too_short_using_quotes
1066
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
1067
+ d = Developer.new
1068
+ d.name = "Joe"
1069
+ assert !d.valid?
1070
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1071
+ end
1072
+
1073
+ def test_validates_length_of_with_custom_message_using_quotes
1074
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
1075
+ d = Developer.new
1076
+ d.name = "Joe"
1077
+ assert !d.valid?
1078
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1079
+ end
1080
+
1081
+ def test_validates_presence_of_with_custom_message_using_quotes
1082
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
1083
+ d = Developer.new
1084
+ d.name = "Joe"
1085
+ assert !d.valid?
1086
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
1087
+ end
1088
+
1089
+ def test_validates_uniqueness_of_with_custom_message_using_quotes
1090
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
1091
+ d = Developer.new
1092
+ d.name = "David"
1093
+ assert !d.valid?
1094
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1095
+ end
1096
+
1097
+ def test_validates_associated_with_custom_message_using_quotes
1098
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
1099
+ Topic.validates_presence_of :content
1100
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1101
+ r.topic = Topic.create("title" => "uhohuhoh")
1102
+ assert !r.valid?
1103
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
1104
+ end
1105
+
1106
+ def test_if_validation_using_method_true
1107
+ # When the method returns true
1108
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
1109
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1110
+ assert !t.valid?
1111
+ assert t.errors.on(:title)
1112
+ assert_equal "hoo 5", t.errors["title"]
1113
+ end
1114
+
1115
+ def test_unless_validation_using_method_true
1116
+ # When the method returns true
1117
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
1118
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1119
+ assert t.valid?
1120
+ assert !t.errors.on(:title)
1121
+ end
1122
+
1123
+ def test_if_validation_using_method_false
1124
+ # When the method returns false
1125
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
1126
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1127
+ assert t.valid?
1128
+ assert !t.errors.on(:title)
1129
+ end
1130
+
1131
+ def test_unless_validation_using_method_false
1132
+ # When the method returns false
1133
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
1134
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1135
+ assert !t.valid?
1136
+ assert t.errors.on(:title)
1137
+ assert_equal "hoo 5", t.errors["title"]
1138
+ end
1139
+
1140
+ def test_if_validation_using_string_true
1141
+ # When the evaluated string returns true
1142
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
1143
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1144
+ assert !t.valid?
1145
+ assert t.errors.on(:title)
1146
+ assert_equal "hoo 5", t.errors["title"]
1147
+ end
1148
+
1149
+ def test_unless_validation_using_string_true
1150
+ # When the evaluated string returns true
1151
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
1152
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1153
+ assert t.valid?
1154
+ assert !t.errors.on(:title)
1155
+ end
1156
+
1157
+ def test_if_validation_using_string_false
1158
+ # When the evaluated string returns false
1159
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
1160
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1161
+ assert t.valid?
1162
+ assert !t.errors.on(:title)
1163
+ end
1164
+
1165
+ def test_unless_validation_using_string_false
1166
+ # When the evaluated string returns false
1167
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
1168
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1169
+ assert !t.valid?
1170
+ assert t.errors.on(:title)
1171
+ assert_equal "hoo 5", t.errors["title"]
1172
+ end
1173
+
1174
+ def test_if_validation_using_block_true
1175
+ # When the block returns true
1176
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1177
+ :if => Proc.new { |r| r.content.size > 4 } )
1178
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1179
+ assert !t.valid?
1180
+ assert t.errors.on(:title)
1181
+ assert_equal "hoo 5", t.errors["title"]
1182
+ end
1183
+
1184
+ def test_unless_validation_using_block_true
1185
+ # When the block returns true
1186
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1187
+ :unless => Proc.new { |r| r.content.size > 4 } )
1188
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1189
+ assert t.valid?
1190
+ assert !t.errors.on(:title)
1191
+ end
1192
+
1193
+ def test_if_validation_using_block_false
1194
+ # When the block returns false
1195
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1196
+ :if => Proc.new { |r| r.title != "uhohuhoh"} )
1197
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1198
+ assert t.valid?
1199
+ assert !t.errors.on(:title)
1200
+ end
1201
+
1202
+ def test_unless_validation_using_block_false
1203
+ # When the block returns false
1204
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1205
+ :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1206
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1207
+ assert !t.valid?
1208
+ assert t.errors.on(:title)
1209
+ assert_equal "hoo 5", t.errors["title"]
1210
+ end
1211
+
1212
+ def test_validates_associated_missing
1213
+ Reply.validates_presence_of(:topic)
1214
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1215
+ assert !r.valid?
1216
+ assert r.errors.on(:topic)
118
1217
 
119
- developer.name = "All too very long for this boundary, it really is"
120
- assert !developer.save
121
- assert_equal "is too long (max is 20 characters)", developer.errors.on("name")
1218
+ r.topic = Topic.find :first
1219
+ assert r.valid?
1220
+ end
122
1221
 
123
- developer.name = "Just right"
124
- assert developer.save
1222
+ def test_errors_to_xml
1223
+ r = Reply.new :title => "Wrong Create"
1224
+ assert !r.valid?
1225
+ xml = r.errors.to_xml(:skip_instruct => true)
1226
+ assert_equal "<errors>", xml.first(8)
1227
+ assert xml.include?("<error>Title is Wrong Create</error>")
1228
+ assert xml.include?("<error>Content Empty</error>")
125
1229
  end
1230
+
1231
+ def test_validation_order
1232
+ Topic.validates_presence_of :title
1233
+ Topic.validates_length_of :title, :minimum => 2
1234
+
1235
+ t = Topic.new("title" => "")
1236
+ assert !t.valid?
1237
+ assert_equal "can't be blank", t.errors.on("title").first
1238
+ end
1239
+
1240
+ # previous implementation of validates_presence_of eval'd the
1241
+ # string with the wrong binding, this regression test is to
1242
+ # ensure that it works correctly
1243
+ def test_validation_with_if_as_string
1244
+ Topic.validates_presence_of(:title)
1245
+ Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1246
+
1247
+ t = Topic.new
1248
+ assert !t.valid?, "A topic without a title should not be valid"
1249
+ assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1250
+
1251
+ t.title = "Just a title"
1252
+ assert t.valid?, "A topic with a basic title should be valid"
1253
+
1254
+ t.title = "A very important title"
1255
+ assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1256
+ assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1257
+
1258
+ t.author_name = "Hubert J. Farnsworth"
1259
+ assert t.valid?, "A topic with an important title and author should be valid"
1260
+ end
1261
+ end
1262
+
1263
+
1264
+ class ValidatesNumericalityTest < Test::Unit::TestCase
1265
+ NIL = [nil]
1266
+ BLANK = ["", " ", " \t \r \n"]
1267
+ BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1268
+ 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)
1269
+ INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1270
+ FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1271
+ INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1272
+ BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1273
+ 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"]
1274
+
1275
+ def setup
1276
+ Topic.write_inheritable_attribute(:validate, nil)
1277
+ Topic.write_inheritable_attribute(:validate_on_create, nil)
1278
+ Topic.write_inheritable_attribute(:validate_on_update, nil)
1279
+ end
1280
+
1281
+ def test_default_validates_numericality_of
1282
+ Topic.validates_numericality_of :approved
1283
+
1284
+ invalid!(NIL + BLANK + JUNK)
1285
+ valid!(FLOATS + INTEGERS + BIGDECIMAL)
1286
+ end
1287
+
1288
+ def test_validates_numericality_of_with_nil_allowed
1289
+ Topic.validates_numericality_of :approved, :allow_nil => true
1290
+
1291
+ invalid!(BLANK + JUNK)
1292
+ valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL)
1293
+ end
1294
+
1295
+ def test_validates_numericality_of_with_integer_only
1296
+ Topic.validates_numericality_of :approved, :only_integer => true
1297
+
1298
+ invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL)
1299
+ valid!(INTEGERS)
1300
+ end
1301
+
1302
+ def test_validates_numericality_of_with_integer_only_and_nil_allowed
1303
+ Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1304
+
1305
+ invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL)
1306
+ valid!(NIL + INTEGERS)
1307
+ end
1308
+
1309
+ def test_validates_numericality_with_greater_than
1310
+ Topic.validates_numericality_of :approved, :greater_than => 10
1311
+
1312
+ invalid!([-10, 10], 'must be greater than 10')
1313
+ valid!([11])
1314
+ end
1315
+
1316
+ def test_validates_numericality_with_greater_than_or_equal
1317
+ Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1318
+
1319
+ invalid!([-9, 9], 'must be greater than or equal to 10')
1320
+ valid!([10])
1321
+ end
1322
+
1323
+ def test_validates_numericality_with_equal_to
1324
+ Topic.validates_numericality_of :approved, :equal_to => 10
1325
+
1326
+ invalid!([-10, 11], 'must be equal to 10')
1327
+ valid!([10])
1328
+ end
1329
+
1330
+ def test_validates_numericality_with_less_than
1331
+ Topic.validates_numericality_of :approved, :less_than => 10
1332
+
1333
+ invalid!([10], 'must be less than 10')
1334
+ valid!([-9, 9])
1335
+ end
1336
+
1337
+ def test_validates_numericality_with_less_than_or_equal_to
1338
+ Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1339
+
1340
+ invalid!([11], 'must be less than or equal to 10')
1341
+ valid!([-10, 10])
1342
+ end
1343
+
1344
+ def test_validates_numericality_with_odd
1345
+ Topic.validates_numericality_of :approved, :odd => true
1346
+
1347
+ invalid!([-2, 2], 'must be odd')
1348
+ valid!([-1, 1])
1349
+ end
1350
+
1351
+ def test_validates_numericality_with_even
1352
+ Topic.validates_numericality_of :approved, :even => true
1353
+
1354
+ invalid!([-1, 1], 'must be even')
1355
+ valid!([-2, 2])
1356
+ end
1357
+
1358
+ def test_validates_numericality_with_greater_than_less_than_and_even
1359
+ Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1360
+
1361
+ invalid!([1, 3, 4])
1362
+ valid!([2])
1363
+ end
1364
+
1365
+ private
1366
+ def invalid!(values, error=nil)
1367
+ with_each_topic_approved_value(values) do |topic, value|
1368
+ assert !topic.valid?, "#{value.inspect} not rejected as a number"
1369
+ assert topic.errors.on(:approved)
1370
+ assert_equal error, topic.errors.on(:approved) if error
1371
+ end
1372
+ end
1373
+
1374
+ def valid!(values)
1375
+ with_each_topic_approved_value(values) do |topic, value|
1376
+ assert topic.valid?, "#{value.inspect} not accepted as a number"
1377
+ end
1378
+ end
1379
+
1380
+ def with_each_topic_approved_value(values)
1381
+ topic = Topic.new("title" => "numeric test", "content" => "whatever")
1382
+ values.each do |value|
1383
+ topic.approved = value
1384
+ yield topic, value
1385
+ end
1386
+ end
126
1387
  end