activerecord_authorails 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. data/CHANGELOG +3043 -0
  2. data/README +360 -0
  3. data/RUNNING_UNIT_TESTS +64 -0
  4. data/Rakefile +226 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/associations.rb +87 -0
  7. data/examples/shared_setup.rb +15 -0
  8. data/examples/validation.rb +85 -0
  9. data/install.rb +30 -0
  10. data/lib/active_record.rb +85 -0
  11. data/lib/active_record/acts/list.rb +244 -0
  12. data/lib/active_record/acts/nested_set.rb +211 -0
  13. data/lib/active_record/acts/tree.rb +89 -0
  14. data/lib/active_record/aggregations.rb +191 -0
  15. data/lib/active_record/associations.rb +1637 -0
  16. data/lib/active_record/associations/association_collection.rb +190 -0
  17. data/lib/active_record/associations/association_proxy.rb +158 -0
  18. data/lib/active_record/associations/belongs_to_association.rb +56 -0
  19. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
  20. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +169 -0
  21. data/lib/active_record/associations/has_many_association.rb +210 -0
  22. data/lib/active_record/associations/has_many_through_association.rb +247 -0
  23. data/lib/active_record/associations/has_one_association.rb +80 -0
  24. data/lib/active_record/attribute_methods.rb +75 -0
  25. data/lib/active_record/base.rb +2164 -0
  26. data/lib/active_record/calculations.rb +270 -0
  27. data/lib/active_record/callbacks.rb +367 -0
  28. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +279 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +130 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +58 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +343 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +161 -0
  34. data/lib/active_record/connection_adapters/db2_adapter.rb +228 -0
  35. data/lib/active_record/connection_adapters/firebird_adapter.rb +728 -0
  36. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +414 -0
  38. data/lib/active_record/connection_adapters/openbase_adapter.rb +350 -0
  39. data/lib/active_record/connection_adapters/oracle_adapter.rb +689 -0
  40. data/lib/active_record/connection_adapters/postgresql_adapter.rb +584 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +407 -0
  42. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +591 -0
  43. data/lib/active_record/connection_adapters/sybase_adapter.rb +662 -0
  44. data/lib/active_record/deprecated_associations.rb +104 -0
  45. data/lib/active_record/deprecated_finders.rb +44 -0
  46. data/lib/active_record/fixtures.rb +628 -0
  47. data/lib/active_record/locking/optimistic.rb +106 -0
  48. data/lib/active_record/locking/pessimistic.rb +77 -0
  49. data/lib/active_record/migration.rb +394 -0
  50. data/lib/active_record/observer.rb +178 -0
  51. data/lib/active_record/query_cache.rb +64 -0
  52. data/lib/active_record/reflection.rb +222 -0
  53. data/lib/active_record/schema.rb +58 -0
  54. data/lib/active_record/schema_dumper.rb +149 -0
  55. data/lib/active_record/timestamp.rb +51 -0
  56. data/lib/active_record/transactions.rb +136 -0
  57. data/lib/active_record/validations.rb +843 -0
  58. data/lib/active_record/vendor/db2.rb +362 -0
  59. data/lib/active_record/vendor/mysql.rb +1214 -0
  60. data/lib/active_record/vendor/simple.rb +693 -0
  61. data/lib/active_record/version.rb +9 -0
  62. data/lib/active_record/wrappers/yaml_wrapper.rb +15 -0
  63. data/lib/active_record/wrappings.rb +58 -0
  64. data/lib/active_record/xml_serialization.rb +308 -0
  65. data/test/aaa_create_tables_test.rb +59 -0
  66. data/test/abstract_unit.rb +77 -0
  67. data/test/active_schema_test_mysql.rb +31 -0
  68. data/test/adapter_test.rb +87 -0
  69. data/test/adapter_test_sqlserver.rb +81 -0
  70. data/test/aggregations_test.rb +95 -0
  71. data/test/all.sh +8 -0
  72. data/test/ar_schema_test.rb +33 -0
  73. data/test/association_inheritance_reload.rb +14 -0
  74. data/test/associations/callbacks_test.rb +126 -0
  75. data/test/associations/cascaded_eager_loading_test.rb +138 -0
  76. data/test/associations/eager_test.rb +393 -0
  77. data/test/associations/extension_test.rb +42 -0
  78. data/test/associations/join_model_test.rb +497 -0
  79. data/test/associations_test.rb +1809 -0
  80. data/test/attribute_methods_test.rb +49 -0
  81. data/test/base_test.rb +1586 -0
  82. data/test/binary_test.rb +37 -0
  83. data/test/calculations_test.rb +219 -0
  84. data/test/callbacks_test.rb +377 -0
  85. data/test/class_inheritable_attributes_test.rb +32 -0
  86. data/test/column_alias_test.rb +17 -0
  87. data/test/connection_test_firebird.rb +8 -0
  88. data/test/connections/native_db2/connection.rb +25 -0
  89. data/test/connections/native_firebird/connection.rb +26 -0
  90. data/test/connections/native_frontbase/connection.rb +27 -0
  91. data/test/connections/native_mysql/connection.rb +24 -0
  92. data/test/connections/native_openbase/connection.rb +21 -0
  93. data/test/connections/native_oracle/connection.rb +27 -0
  94. data/test/connections/native_postgresql/connection.rb +23 -0
  95. data/test/connections/native_sqlite/connection.rb +34 -0
  96. data/test/connections/native_sqlite3/connection.rb +34 -0
  97. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  98. data/test/connections/native_sqlserver/connection.rb +23 -0
  99. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  100. data/test/connections/native_sybase/connection.rb +23 -0
  101. data/test/copy_table_sqlite.rb +64 -0
  102. data/test/datatype_test_postgresql.rb +52 -0
  103. data/test/default_test_firebird.rb +16 -0
  104. data/test/defaults_test.rb +60 -0
  105. data/test/deprecated_associations_test.rb +396 -0
  106. data/test/deprecated_finder_test.rb +151 -0
  107. data/test/empty_date_time_test.rb +25 -0
  108. data/test/finder_test.rb +504 -0
  109. data/test/fixtures/accounts.yml +28 -0
  110. data/test/fixtures/author.rb +99 -0
  111. data/test/fixtures/author_favorites.yml +4 -0
  112. data/test/fixtures/authors.yml +7 -0
  113. data/test/fixtures/auto_id.rb +4 -0
  114. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +1 -0
  115. data/test/fixtures/bad_fixtures/attr_with_spaces +1 -0
  116. data/test/fixtures/bad_fixtures/blank_line +3 -0
  117. data/test/fixtures/bad_fixtures/duplicate_attributes +3 -0
  118. data/test/fixtures/bad_fixtures/missing_value +1 -0
  119. data/test/fixtures/binary.rb +2 -0
  120. data/test/fixtures/categories.yml +14 -0
  121. data/test/fixtures/categories/special_categories.yml +9 -0
  122. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  123. data/test/fixtures/categories_ordered.yml +7 -0
  124. data/test/fixtures/categories_posts.yml +23 -0
  125. data/test/fixtures/categorization.rb +5 -0
  126. data/test/fixtures/categorizations.yml +17 -0
  127. data/test/fixtures/category.rb +20 -0
  128. data/test/fixtures/column_name.rb +3 -0
  129. data/test/fixtures/comment.rb +23 -0
  130. data/test/fixtures/comments.yml +59 -0
  131. data/test/fixtures/companies.yml +55 -0
  132. data/test/fixtures/company.rb +107 -0
  133. data/test/fixtures/company_in_module.rb +59 -0
  134. data/test/fixtures/computer.rb +3 -0
  135. data/test/fixtures/computers.yml +4 -0
  136. data/test/fixtures/course.rb +3 -0
  137. data/test/fixtures/courses.yml +7 -0
  138. data/test/fixtures/customer.rb +55 -0
  139. data/test/fixtures/customers.yml +17 -0
  140. data/test/fixtures/db_definitions/db2.drop.sql +32 -0
  141. data/test/fixtures/db_definitions/db2.sql +231 -0
  142. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  143. data/test/fixtures/db_definitions/db22.sql +5 -0
  144. data/test/fixtures/db_definitions/firebird.drop.sql +63 -0
  145. data/test/fixtures/db_definitions/firebird.sql +304 -0
  146. data/test/fixtures/db_definitions/firebird2.drop.sql +2 -0
  147. data/test/fixtures/db_definitions/firebird2.sql +6 -0
  148. data/test/fixtures/db_definitions/frontbase.drop.sql +32 -0
  149. data/test/fixtures/db_definitions/frontbase.sql +268 -0
  150. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  151. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  152. data/test/fixtures/db_definitions/mysql.drop.sql +32 -0
  153. data/test/fixtures/db_definitions/mysql.sql +234 -0
  154. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  155. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  156. data/test/fixtures/db_definitions/openbase.drop.sql +2 -0
  157. data/test/fixtures/db_definitions/openbase.sql +302 -0
  158. data/test/fixtures/db_definitions/openbase2.drop.sql +2 -0
  159. data/test/fixtures/db_definitions/openbase2.sql +7 -0
  160. data/test/fixtures/db_definitions/oracle.drop.sql +65 -0
  161. data/test/fixtures/db_definitions/oracle.sql +325 -0
  162. data/test/fixtures/db_definitions/oracle2.drop.sql +2 -0
  163. data/test/fixtures/db_definitions/oracle2.sql +6 -0
  164. data/test/fixtures/db_definitions/postgresql.drop.sql +37 -0
  165. data/test/fixtures/db_definitions/postgresql.sql +263 -0
  166. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  167. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  168. data/test/fixtures/db_definitions/schema.rb +60 -0
  169. data/test/fixtures/db_definitions/sqlite.drop.sql +32 -0
  170. data/test/fixtures/db_definitions/sqlite.sql +215 -0
  171. data/test/fixtures/db_definitions/sqlite2.drop.sql +2 -0
  172. data/test/fixtures/db_definitions/sqlite2.sql +5 -0
  173. data/test/fixtures/db_definitions/sqlserver.drop.sql +34 -0
  174. data/test/fixtures/db_definitions/sqlserver.sql +243 -0
  175. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  176. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  177. data/test/fixtures/db_definitions/sybase.drop.sql +34 -0
  178. data/test/fixtures/db_definitions/sybase.sql +218 -0
  179. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  180. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  181. data/test/fixtures/default.rb +2 -0
  182. data/test/fixtures/developer.rb +52 -0
  183. data/test/fixtures/developers.yml +21 -0
  184. data/test/fixtures/developers_projects.yml +17 -0
  185. data/test/fixtures/developers_projects/david_action_controller +3 -0
  186. data/test/fixtures/developers_projects/david_active_record +3 -0
  187. data/test/fixtures/developers_projects/jamis_active_record +2 -0
  188. data/test/fixtures/edge.rb +5 -0
  189. data/test/fixtures/edges.yml +6 -0
  190. data/test/fixtures/entrant.rb +3 -0
  191. data/test/fixtures/entrants.yml +14 -0
  192. data/test/fixtures/fk_test_has_fk.yml +3 -0
  193. data/test/fixtures/fk_test_has_pk.yml +2 -0
  194. data/test/fixtures/flowers.jpg +0 -0
  195. data/test/fixtures/funny_jokes.yml +10 -0
  196. data/test/fixtures/joke.rb +6 -0
  197. data/test/fixtures/keyboard.rb +3 -0
  198. data/test/fixtures/legacy_thing.rb +3 -0
  199. data/test/fixtures/legacy_things.yml +3 -0
  200. data/test/fixtures/migrations/1_people_have_last_names.rb +9 -0
  201. data/test/fixtures/migrations/2_we_need_reminders.rb +12 -0
  202. data/test/fixtures/migrations/3_innocent_jointable.rb +12 -0
  203. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  204. data/test/fixtures/migrations_with_duplicate/1_people_have_last_names.rb +9 -0
  205. data/test/fixtures/migrations_with_duplicate/2_we_need_reminders.rb +12 -0
  206. data/test/fixtures/migrations_with_duplicate/3_foo.rb +7 -0
  207. data/test/fixtures/migrations_with_duplicate/3_innocent_jointable.rb +12 -0
  208. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  209. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  210. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  211. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  212. data/test/fixtures/mixed_case_monkey.rb +3 -0
  213. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  214. data/test/fixtures/mixin.rb +63 -0
  215. data/test/fixtures/mixins.yml +127 -0
  216. data/test/fixtures/movie.rb +5 -0
  217. data/test/fixtures/movies.yml +7 -0
  218. data/test/fixtures/naked/csv/accounts.csv +1 -0
  219. data/test/fixtures/naked/yml/accounts.yml +1 -0
  220. data/test/fixtures/naked/yml/companies.yml +1 -0
  221. data/test/fixtures/naked/yml/courses.yml +1 -0
  222. data/test/fixtures/order.rb +4 -0
  223. data/test/fixtures/people.yml +3 -0
  224. data/test/fixtures/person.rb +4 -0
  225. data/test/fixtures/post.rb +58 -0
  226. data/test/fixtures/posts.yml +48 -0
  227. data/test/fixtures/project.rb +27 -0
  228. data/test/fixtures/projects.yml +7 -0
  229. data/test/fixtures/reader.rb +4 -0
  230. data/test/fixtures/readers.yml +4 -0
  231. data/test/fixtures/reply.rb +37 -0
  232. data/test/fixtures/subject.rb +4 -0
  233. data/test/fixtures/subscriber.rb +6 -0
  234. data/test/fixtures/subscribers/first +2 -0
  235. data/test/fixtures/subscribers/second +2 -0
  236. data/test/fixtures/tag.rb +7 -0
  237. data/test/fixtures/tagging.rb +6 -0
  238. data/test/fixtures/taggings.yml +18 -0
  239. data/test/fixtures/tags.yml +7 -0
  240. data/test/fixtures/task.rb +3 -0
  241. data/test/fixtures/tasks.yml +7 -0
  242. data/test/fixtures/topic.rb +25 -0
  243. data/test/fixtures/topics.yml +22 -0
  244. data/test/fixtures/vertex.rb +9 -0
  245. data/test/fixtures/vertices.yml +4 -0
  246. data/test/fixtures_test.rb +401 -0
  247. data/test/inheritance_test.rb +205 -0
  248. data/test/lifecycle_test.rb +137 -0
  249. data/test/locking_test.rb +190 -0
  250. data/test/method_scoping_test.rb +416 -0
  251. data/test/migration_test.rb +768 -0
  252. data/test/migration_test_firebird.rb +124 -0
  253. data/test/mixin_nested_set_test.rb +196 -0
  254. data/test/mixin_test.rb +550 -0
  255. data/test/modules_test.rb +34 -0
  256. data/test/multiple_db_test.rb +60 -0
  257. data/test/pk_test.rb +104 -0
  258. data/test/readonly_test.rb +107 -0
  259. data/test/reflection_test.rb +159 -0
  260. data/test/schema_authorization_test_postgresql.rb +75 -0
  261. data/test/schema_dumper_test.rb +96 -0
  262. data/test/schema_test_postgresql.rb +64 -0
  263. data/test/synonym_test_oracle.rb +17 -0
  264. data/test/table_name_test_sqlserver.rb +23 -0
  265. data/test/threaded_connections_test.rb +48 -0
  266. data/test/transactions_test.rb +230 -0
  267. data/test/unconnected_test.rb +32 -0
  268. data/test/validations_test.rb +1097 -0
  269. data/test/xml_serialization_test.rb +125 -0
  270. metadata +365 -0
@@ -0,0 +1,32 @@
1
+ require 'abstract_unit'
2
+
3
+ class TestRecord < ActiveRecord::Base
4
+ end
5
+
6
+ class TestUnconnectedAdaptor < Test::Unit::TestCase
7
+ self.use_transactional_fixtures = false
8
+
9
+ def setup
10
+ @underlying = ActiveRecord::Base.connection
11
+ @specification = ActiveRecord::Base.remove_connection
12
+ end
13
+
14
+ def teardown
15
+ @underlying = nil
16
+ ActiveRecord::Base.establish_connection(@specification)
17
+ end
18
+
19
+ def test_connection_no_longer_established
20
+ assert_raise(ActiveRecord::ConnectionNotEstablished) do
21
+ TestRecord.find(1)
22
+ end
23
+
24
+ assert_raise(ActiveRecord::ConnectionNotEstablished) do
25
+ TestRecord.new.save
26
+ end
27
+ end
28
+
29
+ def test_underlying_adapter_no_longer_active
30
+ assert !@underlying.active?, "Removed adapter should no longer be active"
31
+ end
32
+ end
@@ -0,0 +1,1097 @@
1
+ require 'abstract_unit'
2
+ require 'fixtures/topic'
3
+ require 'fixtures/reply'
4
+ require 'fixtures/developer'
5
+
6
+ # The following methods in Topic are used in test_conditional_validation_*
7
+ class Topic
8
+ def condition_is_true
9
+ return true
10
+ end
11
+
12
+ def condition_is_true_but_its_not
13
+ return false
14
+ end
15
+ end
16
+
17
+ class ValidationsTest < Test::Unit::TestCase
18
+ fixtures :topics, :developers
19
+
20
+ def setup
21
+ Topic.write_inheritable_attribute(:validate, nil)
22
+ Topic.write_inheritable_attribute(:validate_on_create, nil)
23
+ Topic.write_inheritable_attribute(:validate_on_update, nil)
24
+ end
25
+
26
+ def test_single_field_validation
27
+ r = Reply.new
28
+ r.title = "There's no content!"
29
+ assert !r.valid?, "A reply without content shouldn't be saveable"
30
+
31
+ r.content = "Messa content!"
32
+ assert r.valid?, "A reply with content should be saveable"
33
+ end
34
+
35
+ def test_single_attr_validation_and_error_msg
36
+ r = Reply.new
37
+ r.title = "There's no content!"
38
+ assert !r.valid?
39
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
40
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
41
+ assert_equal 1, r.errors.count
42
+ end
43
+
44
+ def test_double_attr_validation_and_error_msg
45
+ r = Reply.new
46
+ assert !r.valid?
47
+
48
+ assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
49
+ assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
50
+
51
+ assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
52
+ assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
53
+
54
+ assert_equal 2, r.errors.count
55
+ end
56
+
57
+ def test_error_on_create
58
+ r = Reply.new
59
+ r.title = "Wrong Create"
60
+ assert !r.valid?
61
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
62
+ assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
63
+ end
64
+
65
+ def test_error_on_update
66
+ r = Reply.new
67
+ r.title = "Bad"
68
+ r.content = "Good"
69
+ assert r.save, "First save should be successful"
70
+
71
+ r.title = "Wrong Update"
72
+ assert !r.save, "Second save should fail"
73
+
74
+ assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
75
+ assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
76
+ end
77
+
78
+ def test_invalid_record_exception
79
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
80
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.new.save! }
81
+
82
+ begin
83
+ r = Reply.new
84
+ r.save!
85
+ flunk
86
+ rescue ActiveRecord::RecordInvalid => invalid
87
+ assert_equal r, invalid.record
88
+ end
89
+ end
90
+
91
+ def test_scoped_create_without_attributes
92
+ Reply.with_scope(:create => {}) do
93
+ assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
94
+ end
95
+ end
96
+
97
+ def test_single_error_per_attr_iteration
98
+ r = Reply.new
99
+ r.save
100
+
101
+ errors = []
102
+ r.errors.each { |attr, msg| errors << [attr, msg] }
103
+
104
+ assert errors.include?(["title", "Empty"])
105
+ assert errors.include?(["content", "Empty"])
106
+ end
107
+
108
+ def test_multiple_errors_per_attr_iteration_with_full_error_composition
109
+ r = Reply.new
110
+ r.title = "Wrong Create"
111
+ r.content = "Mismatch"
112
+ r.save
113
+
114
+ errors = []
115
+ r.errors.each_full { |error| errors << error }
116
+
117
+ assert_equal "Title is Wrong Create", errors[0]
118
+ assert_equal "Title is Content Mismatch", errors[1]
119
+ assert_equal 2, r.errors.count
120
+ end
121
+
122
+ def test_errors_on_base
123
+ r = Reply.new
124
+ r.content = "Mismatch"
125
+ r.save
126
+ r.errors.add_to_base "Reply is not dignifying"
127
+
128
+ errors = []
129
+ r.errors.each_full { |error| errors << error }
130
+
131
+ assert_equal "Reply is not dignifying", r.errors.on_base
132
+
133
+ assert errors.include?("Title Empty")
134
+ assert errors.include?("Reply is not dignifying")
135
+ assert_equal 2, r.errors.count
136
+ end
137
+
138
+ def test_create_without_validation
139
+ reply = Reply.new
140
+ assert !reply.save
141
+ assert reply.save(false)
142
+ end
143
+
144
+ def test_create_without_validation_bang
145
+ count = Reply.count
146
+ assert_nothing_raised { Reply.new.save_without_validation! }
147
+ assert count+1, Reply.count
148
+ end
149
+
150
+ def test_validates_each
151
+ perform = true
152
+ hits = 0
153
+ Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
154
+ if perform
155
+ record.errors.add attr, 'gotcha'
156
+ hits += 1
157
+ end
158
+ end
159
+ t = Topic.new("title" => "valid", "content" => "whatever")
160
+ assert !t.save
161
+ assert_equal 4, hits
162
+ assert_equal %w(gotcha gotcha), t.errors.on(:title)
163
+ assert_equal %w(gotcha gotcha), t.errors.on(:content)
164
+ ensure
165
+ perform = false
166
+ end
167
+
168
+ def test_no_title_confirmation
169
+ Topic.validates_confirmation_of(:title)
170
+
171
+ t = Topic.new(:author_name => "Plutarch")
172
+ assert t.valid?
173
+
174
+ t.title_confirmation = "Parallel Lives"
175
+ assert !t.valid?
176
+
177
+ t.title_confirmation = nil
178
+ t.title = "Parallel Lives"
179
+ assert t.valid?
180
+
181
+ t.title_confirmation = "Parallel Lives"
182
+ assert t.valid?
183
+ end
184
+
185
+ def test_title_confirmation
186
+ Topic.validates_confirmation_of(:title)
187
+
188
+ t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
189
+ assert !t.save
190
+
191
+ t.title_confirmation = "We should be confirmed"
192
+ assert t.save
193
+ end
194
+
195
+ def test_terms_of_service_agreement_no_acceptance
196
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
197
+
198
+ t = Topic.create("title" => "We should not be confirmed")
199
+ assert t.save
200
+ end
201
+
202
+ def test_terms_of_service_agreement
203
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create)
204
+
205
+ t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
206
+ assert !t.save
207
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
208
+
209
+ t.terms_of_service = "1"
210
+ assert t.save
211
+ end
212
+
213
+
214
+ def test_eula
215
+ Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
216
+
217
+ t = Topic.create("title" => "We should be confirmed","eula" => "")
218
+ assert !t.save
219
+ assert_equal "must be abided", t.errors.on(:eula)
220
+
221
+ t.eula = "1"
222
+ assert t.save
223
+ end
224
+
225
+ def test_terms_of_service_agreement_with_accept_value
226
+ Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
227
+
228
+ t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
229
+ assert !t.save
230
+ assert_equal "must be accepted", t.errors.on(:terms_of_service)
231
+
232
+ t.terms_of_service = "I agree."
233
+ assert t.save
234
+ end
235
+
236
+ def test_validate_presences
237
+ Topic.validates_presence_of(:title, :content)
238
+
239
+ t = Topic.create
240
+ assert !t.save
241
+ assert_equal "can't be blank", t.errors.on(:title)
242
+ assert_equal "can't be blank", t.errors.on(:content)
243
+
244
+ t.title = "something"
245
+ t.content = " "
246
+
247
+ assert !t.save
248
+ assert_equal "can't be blank", t.errors.on(:content)
249
+
250
+ t.content = "like stuff"
251
+
252
+ assert t.save
253
+ end
254
+
255
+ def test_validate_uniqueness
256
+ Topic.validates_uniqueness_of(:title)
257
+
258
+ t = Topic.new("title" => "I'm unique!")
259
+ assert t.save, "Should save t as unique"
260
+
261
+ t.content = "Remaining unique"
262
+ assert t.save, "Should still save t as unique"
263
+
264
+ t2 = Topic.new("title" => "I'm unique!")
265
+ assert !t2.valid?, "Shouldn't be valid"
266
+ assert !t2.save, "Shouldn't save t2 as unique"
267
+ assert_equal "has already been taken", t2.errors.on(:title)
268
+
269
+ t2.title = "Now Im really also unique"
270
+ assert t2.save, "Should now save t2 as unique"
271
+ end
272
+
273
+ def test_validate_uniqueness_with_scope
274
+ Reply.validates_uniqueness_of(:content, :scope => "parent_id")
275
+
276
+ t = Topic.create("title" => "I'm unique!")
277
+
278
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
279
+ assert r1.valid?, "Saving r1"
280
+
281
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
282
+ assert !r2.valid?, "Saving r2 first time"
283
+
284
+ r2.content = "something else"
285
+ assert r2.save, "Saving r2 second time"
286
+
287
+ t2 = Topic.create("title" => "I'm unique too!")
288
+ r3 = t2.replies.create "title" => "r3", "content" => "hello world"
289
+ assert r3.valid?, "Saving r3"
290
+ end
291
+
292
+ def test_validate_uniqueness_with_scope_array
293
+ Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
294
+
295
+ t = Topic.create("title" => "The earth is actually flat!")
296
+
297
+ r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
298
+ assert r1.valid?, "Saving r1"
299
+
300
+ r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
301
+ assert !r2.valid?, "Saving r2. Double reply by same author."
302
+
303
+ r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
304
+ assert r2.save, "Saving r2 the second time."
305
+
306
+ r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
307
+ assert !r3.valid?, "Saving r3"
308
+
309
+ r3.author_name = "jj"
310
+ assert r3.save, "Saving r3 the second time."
311
+
312
+ r3.author_name = "jeremy"
313
+ assert !r3.save, "Saving r3 the third time."
314
+ end
315
+
316
+ def test_validate_case_insensitive_uniqueness
317
+ Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
318
+
319
+ t = Topic.new("title" => "I'm unique!", :parent_id => 2)
320
+ assert t.save, "Should save t as unique"
321
+
322
+ t.content = "Remaining unique"
323
+ assert t.save, "Should still save t as unique"
324
+
325
+ t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
326
+ assert !t2.valid?, "Shouldn't be valid"
327
+ assert !t2.save, "Shouldn't save t2 as unique"
328
+ assert t2.errors.on(:title)
329
+ assert t2.errors.on(:parent_id)
330
+ assert_equal "has already been taken", t2.errors.on(:title)
331
+
332
+ t2.title = "I'm truly UNIQUE!"
333
+ assert !t2.valid?, "Shouldn't be valid"
334
+ assert !t2.save, "Shouldn't save t2 as unique"
335
+ assert_nil t2.errors.on(:title)
336
+ assert t2.errors.on(:parent_id)
337
+
338
+ t2.parent_id = 3
339
+ assert t2.save, "Should now save t2 as unique"
340
+
341
+ t2.parent_id = nil
342
+ t2.title = nil
343
+ assert t2.valid?, "should validate with nil"
344
+ assert t2.save, "should save with nil"
345
+ end
346
+
347
+ def test_validate_format
348
+ Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
349
+
350
+ t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
351
+ assert !t.valid?, "Shouldn't be valid"
352
+ assert !t.save, "Shouldn't save because it's invalid"
353
+ assert_equal "is bad data", t.errors.on(:title)
354
+ assert_nil t.errors.on(:content)
355
+
356
+ t.title = "Validation macros rule!"
357
+
358
+ assert t.save
359
+ assert_nil t.errors.on(:title)
360
+
361
+ assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
362
+ end
363
+
364
+ # testing ticket #3142
365
+ def test_validate_format_numeric
366
+ Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
367
+
368
+ t = Topic.create("title" => "72x", "content" => "6789")
369
+ assert !t.valid?, "Shouldn't be valid"
370
+ assert !t.save, "Shouldn't save because it's invalid"
371
+ assert_equal "is bad data", t.errors.on(:title)
372
+ assert_nil t.errors.on(:content)
373
+
374
+ t.title = "-11"
375
+ assert !t.valid?, "Shouldn't be valid"
376
+
377
+ t.title = "03"
378
+ assert !t.valid?, "Shouldn't be valid"
379
+
380
+ t.title = "z44"
381
+ assert !t.valid?, "Shouldn't be valid"
382
+
383
+ t.title = "5v7"
384
+ assert !t.valid?, "Shouldn't be valid"
385
+
386
+ t.title = "1"
387
+
388
+ assert t.save
389
+ assert_nil t.errors.on(:title)
390
+ end
391
+
392
+ def test_validates_inclusion_of
393
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
394
+
395
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
396
+ assert !Topic.create("title" => "a b", "content" => "abc").valid?
397
+ assert !Topic.create("title" => nil, "content" => "def").valid?
398
+
399
+ t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
400
+ assert t.valid?
401
+ t.title = "uhoh"
402
+ assert !t.valid?
403
+ assert t.errors.on(:title)
404
+ assert_equal "is not included in the list", t.errors["title"]
405
+
406
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
407
+ assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
408
+
409
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
410
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
411
+ assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
412
+ end
413
+
414
+ def test_validates_inclusion_of_with_allow_nil
415
+ Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
416
+
417
+ assert !Topic.create("title" => "a!", "content" => "abc").valid?
418
+ assert !Topic.create("title" => "", "content" => "abc").valid?
419
+ assert Topic.create("title" => nil, "content" => "abc").valid?
420
+ end
421
+
422
+ def test_numericality_with_allow_nil_and_getter_method
423
+ Developer.validates_numericality_of( :salary, :allow_nil => true)
424
+ developer = Developer.new("name" => "michael", "salary" => nil)
425
+ developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
426
+ assert developer.valid?
427
+ end
428
+
429
+ def test_validates_exclusion_of
430
+ Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
431
+
432
+ assert Topic.create("title" => "something", "content" => "abc").valid?
433
+ assert !Topic.create("title" => "monkey", "content" => "abc").valid?
434
+ end
435
+
436
+ def test_validates_length_of_using_minimum
437
+ Topic.validates_length_of :title, :minimum => 5
438
+
439
+ t = Topic.create("title" => "valid", "content" => "whatever")
440
+ assert t.valid?
441
+
442
+ t.title = "not"
443
+ assert !t.valid?
444
+ assert t.errors.on(:title)
445
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
446
+
447
+ t.title = ""
448
+ assert !t.valid?
449
+ assert t.errors.on(:title)
450
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
451
+
452
+ t.title = nil
453
+ assert !t.valid?
454
+ assert t.errors.on(:title)
455
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
456
+ end
457
+
458
+ def test_optionally_validates_length_of_using_minimum
459
+ Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
460
+
461
+ t = Topic.create("title" => "valid", "content" => "whatever")
462
+ assert t.valid?
463
+
464
+ t.title = nil
465
+ assert t.valid?
466
+ end
467
+
468
+ def test_validates_length_of_using_maximum
469
+ Topic.validates_length_of :title, :maximum => 5
470
+
471
+ t = Topic.create("title" => "valid", "content" => "whatever")
472
+ assert t.valid?
473
+
474
+ t.title = "notvalid"
475
+ assert !t.valid?
476
+ assert t.errors.on(:title)
477
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
478
+
479
+ t.title = ""
480
+ assert t.valid?
481
+
482
+ t.title = nil
483
+ assert !t.valid?
484
+ end
485
+
486
+ def test_optionally_validates_length_of_using_maximum
487
+ Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
488
+
489
+ t = Topic.create("title" => "valid", "content" => "whatever")
490
+ assert t.valid?
491
+
492
+ t.title = nil
493
+ assert t.valid?
494
+ end
495
+
496
+ def test_validates_length_of_using_within
497
+ Topic.validates_length_of(:title, :content, :within => 3..5)
498
+
499
+ t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
500
+ assert !t.valid?
501
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
502
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
503
+
504
+ t.title = nil
505
+ t.content = nil
506
+ assert !t.valid?
507
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
508
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
509
+
510
+ t.title = "abe"
511
+ t.content = "mad"
512
+ assert t.valid?
513
+ end
514
+
515
+ def test_optionally_validates_length_of_using_within
516
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
517
+
518
+ t = Topic.create('title' => 'abc', 'content' => 'abcd')
519
+ assert t.valid?
520
+
521
+ t.title = nil
522
+ assert t.valid?
523
+ end
524
+
525
+ def test_optionally_validates_length_of_using_within_on_create
526
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %d"
527
+
528
+ t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
529
+ assert !t.save
530
+ assert t.errors.on(:title)
531
+ assert_equal "my string is too long: 10", t.errors[:title]
532
+
533
+ t.title = "butthisis"
534
+ assert t.save
535
+
536
+ t.title = "few"
537
+ assert t.save
538
+
539
+ t.content = "andthisislong"
540
+ assert t.save
541
+
542
+ t.content = t.title = "iamfine"
543
+ assert t.save
544
+ end
545
+
546
+ def test_optionally_validates_length_of_using_within_on_update
547
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %d"
548
+
549
+ t = Topic.create("title" => "vali", "content" => "whatever")
550
+ assert !t.save
551
+ assert t.errors.on(:title)
552
+
553
+ t.title = "not"
554
+ assert !t.save
555
+ assert t.errors.on(:title)
556
+ assert_equal "my string is too short: 5", t.errors[:title]
557
+
558
+ t.title = "valid"
559
+ t.content = "andthisistoolong"
560
+ assert !t.save
561
+ assert t.errors.on(:content)
562
+
563
+ t.content = "iamfine"
564
+ assert t.save
565
+ end
566
+
567
+ def test_validates_length_of_using_is
568
+ Topic.validates_length_of :title, :is => 5
569
+
570
+ t = Topic.create("title" => "valid", "content" => "whatever")
571
+ assert t.valid?
572
+
573
+ t.title = "notvalid"
574
+ assert !t.valid?
575
+ assert t.errors.on(:title)
576
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
577
+
578
+ t.title = ""
579
+ assert !t.valid?
580
+
581
+ t.title = nil
582
+ assert !t.valid?
583
+ end
584
+
585
+ def test_optionally_validates_length_of_using_is
586
+ Topic.validates_length_of :title, :is => 5, :allow_nil => true
587
+
588
+ t = Topic.create("title" => "valid", "content" => "whatever")
589
+ assert t.valid?
590
+
591
+ t.title = nil
592
+ assert t.valid?
593
+ end
594
+
595
+ def test_validates_length_of_using_bignum
596
+ bigmin = 2 ** 30
597
+ bigmax = 2 ** 32
598
+ bigrange = bigmin...bigmax
599
+ assert_nothing_raised do
600
+ Topic.validates_length_of :title, :is => bigmin + 5
601
+ Topic.validates_length_of :title, :within => bigrange
602
+ Topic.validates_length_of :title, :in => bigrange
603
+ Topic.validates_length_of :title, :minimum => bigmin
604
+ Topic.validates_length_of :title, :maximum => bigmax
605
+ end
606
+ end
607
+
608
+ def test_validates_length_with_globaly_modified_error_message
609
+ ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
610
+ Topic.validates_length_of :title, :minimum => 10
611
+ t = Topic.create(:title => 'too short')
612
+ assert !t.valid?
613
+
614
+ assert_equal 'tu est trops petit hombre 10', t.errors['title']
615
+ end
616
+
617
+ def test_add_on_boundary_breaking_is_deprecated
618
+ t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
619
+ class << t
620
+ def validate
621
+ errors.add_on_boundary_breaking('title', 1..6)
622
+ end
623
+ end
624
+ assert_deprecated 'add_on_boundary_breaking' do
625
+ assert !t.valid?
626
+ end
627
+ end
628
+
629
+ def test_validates_size_of_association
630
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
631
+ t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
632
+ assert !t.save
633
+ assert t.errors.on(:replies)
634
+ t.replies.create('title' => 'areply', 'content' => 'whateveragain')
635
+ assert t.valid?
636
+ end
637
+
638
+ def test_validates_length_of_nasty_params
639
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
640
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
641
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
642
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
643
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
644
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
645
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
646
+ assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
647
+ end
648
+
649
+ def test_validates_length_of_custom_errors_for_minimum_with_message
650
+ Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %d" )
651
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
652
+ assert !t.valid?
653
+ assert t.errors.on(:title)
654
+ assert_equal "boo 5", t.errors["title"]
655
+ end
656
+
657
+ def test_validates_length_of_custom_errors_for_minimum_with_too_short
658
+ Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %d" )
659
+ t = Topic.create("title" => "uhoh", "content" => "whatever")
660
+ assert !t.valid?
661
+ assert t.errors.on(:title)
662
+ assert_equal "hoo 5", t.errors["title"]
663
+ end
664
+
665
+ def test_validates_length_of_custom_errors_for_maximum_with_message
666
+ Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %d" )
667
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
668
+ assert !t.valid?
669
+ assert t.errors.on(:title)
670
+ assert_equal "boo 5", t.errors["title"]
671
+ end
672
+
673
+ def test_validates_length_of_custom_errors_for_maximum_with_too_long
674
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d" )
675
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
676
+ assert !t.valid?
677
+ assert t.errors.on(:title)
678
+ assert_equal "hoo 5", t.errors["title"]
679
+ end
680
+
681
+ def test_validates_length_of_custom_errors_for_is_with_message
682
+ Topic.validates_length_of( :title, :is=>5, :message=>"boo %d" )
683
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
684
+ assert !t.valid?
685
+ assert t.errors.on(:title)
686
+ assert_equal "boo 5", t.errors["title"]
687
+ end
688
+
689
+ def test_validates_length_of_custom_errors_for_is_with_wrong_length
690
+ Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %d" )
691
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
692
+ assert !t.valid?
693
+ assert t.errors.on(:title)
694
+ assert_equal "hoo 5", t.errors["title"]
695
+ end
696
+
697
+ def kcode_scope(kcode)
698
+ orig_kcode = $KCODE
699
+ $KCODE = kcode
700
+ begin
701
+ yield
702
+ ensure
703
+ $KCODE = orig_kcode
704
+ end
705
+ end
706
+
707
+ def test_validates_length_of_using_minimum_utf8
708
+ kcode_scope('UTF8') do
709
+ Topic.validates_length_of :title, :minimum => 5
710
+
711
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
712
+ assert t.valid?
713
+
714
+ t.title = "一二三四"
715
+ assert !t.valid?
716
+ assert t.errors.on(:title)
717
+ assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
718
+ end
719
+ end
720
+
721
+ def test_validates_length_of_using_maximum_utf8
722
+ kcode_scope('UTF8') do
723
+ Topic.validates_length_of :title, :maximum => 5
724
+
725
+ t = Topic.create("title" => "一二三四五", "content" => "whatever")
726
+ assert t.valid?
727
+
728
+ t.title = "一二34五六"
729
+ assert !t.valid?
730
+ assert t.errors.on(:title)
731
+ assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
732
+ end
733
+ end
734
+
735
+ def test_validates_length_of_using_within_utf8
736
+ kcode_scope('UTF8') do
737
+ Topic.validates_length_of(:title, :content, :within => 3..5)
738
+
739
+ t = Topic.new("title" => "一二", "content" => "12三四五六七")
740
+ assert !t.valid?
741
+ assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
742
+ assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
743
+ t.title = "一二三"
744
+ t.content = "12三"
745
+ assert t.valid?
746
+ end
747
+ end
748
+
749
+ def test_optionally_validates_length_of_using_within_utf8
750
+ kcode_scope('UTF8') do
751
+ Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
752
+
753
+ t = Topic.create('title' => '一二三', 'content' => '一二三四五')
754
+ assert t.valid?
755
+
756
+ t.title = nil
757
+ assert t.valid?
758
+ end
759
+ end
760
+
761
+ def test_optionally_validates_length_of_using_within_on_create_utf8
762
+ kcode_scope('UTF8') do
763
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
764
+
765
+ t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
766
+ assert !t.save
767
+ assert t.errors.on(:title)
768
+ assert_equal "長すぎます: 10", t.errors[:title]
769
+
770
+ t.title = "一二三四五六七八九"
771
+ assert t.save
772
+
773
+ t.title = "一二3"
774
+ assert t.save
775
+
776
+ t.content = "一二三四五六七八九十"
777
+ assert t.save
778
+
779
+ t.content = t.title = "一二三四五六"
780
+ assert t.save
781
+ end
782
+ end
783
+
784
+ def test_optionally_validates_length_of_using_within_on_update_utf8
785
+ kcode_scope('UTF8') do
786
+ Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
787
+
788
+ t = Topic.create("title" => "一二三4", "content" => "whatever")
789
+ assert !t.save
790
+ assert t.errors.on(:title)
791
+
792
+ t.title = "1二三4"
793
+ assert !t.save
794
+ assert t.errors.on(:title)
795
+ assert_equal "短すぎます: 5", t.errors[:title]
796
+
797
+ t.title = "valid"
798
+ t.content = "一二三四五六七八九十A"
799
+ assert !t.save
800
+ assert t.errors.on(:content)
801
+
802
+ t.content = "一二345"
803
+ assert t.save
804
+ end
805
+ end
806
+
807
+ def test_validates_length_of_using_is_utf8
808
+ kcode_scope('UTF8') do
809
+ Topic.validates_length_of :title, :is => 5
810
+
811
+ t = Topic.create("title" => "一二345", "content" => "whatever")
812
+ assert t.valid?
813
+
814
+ t.title = "一二345六"
815
+ assert !t.valid?
816
+ assert t.errors.on(:title)
817
+ assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
818
+ end
819
+ end
820
+
821
+ def test_validates_size_of_association_utf8
822
+ kcode_scope('UTF8') do
823
+ assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
824
+ t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
825
+ assert !t.save
826
+ assert t.errors.on(:replies)
827
+ t.replies.create('title' => 'あいうえお', 'content' => 'かきくけこ')
828
+ assert t.valid?
829
+ end
830
+ end
831
+
832
+ def test_validates_associated_many
833
+ Topic.validates_associated( :replies )
834
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
835
+ t.replies << [r = Reply.create("title" => "A reply"), r2 = Reply.create("title" => "Another reply")]
836
+ assert !t.valid?
837
+ assert t.errors.on(:replies)
838
+ assert_equal 1, r.errors.count # make sure all associated objects have been validated
839
+ assert_equal 1, r2.errors.count
840
+ r.content = r2.content = "non-empty"
841
+ assert t.valid?
842
+ end
843
+
844
+ def test_validates_associated_one
845
+ Reply.validates_associated( :topic )
846
+ Topic.validates_presence_of( :content )
847
+ r = Reply.create("title" => "A reply", "content" => "with content!")
848
+ r.topic = Topic.create("title" => "uhohuhoh")
849
+ assert !r.valid?
850
+ assert r.errors.on(:topic)
851
+ r.topic.content = "non-empty"
852
+ assert r.valid?
853
+ end
854
+
855
+ def test_validate_block
856
+ Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
857
+ t = Topic.create("title" => "Title", "content" => "whatever")
858
+ assert !t.valid?
859
+ assert t.errors.on(:title)
860
+ assert_equal "will never be valid", t.errors["title"]
861
+ end
862
+
863
+ def test_invalid_validator
864
+ Topic.validate 3
865
+ assert_raise(ActiveRecord::ActiveRecordError) { t = Topic.create }
866
+ end
867
+
868
+ def test_throw_away_typing
869
+ d = Developer.new("name" => "David", "salary" => "100,000")
870
+ assert !d.valid?
871
+ assert_equal 100, d.salary
872
+ assert_equal "100,000", d.salary_before_type_cast
873
+ end
874
+
875
+ def test_validates_acceptance_of_with_custom_error_using_quotes
876
+ Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
877
+ d = Developer.new
878
+ d.salary = "0"
879
+ assert !d.valid?
880
+ assert_equal d.errors.on(:salary).first, "This string contains 'single' and \"double\" quotes"
881
+ end
882
+
883
+ def test_validates_confirmation_of_with_custom_error_using_quotes
884
+ Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
885
+ d = Developer.new
886
+ d.name = "John"
887
+ d.name_confirmation = "Johnny"
888
+ assert !d.valid?
889
+ assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
890
+ end
891
+
892
+ def test_validates_format_of_with_custom_error_using_quotes
893
+ Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
894
+ d = Developer.new
895
+ d.name = d.name_confirmation = "John 32"
896
+ assert !d.valid?
897
+ assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
898
+ end
899
+
900
+ def test_validates_inclusion_of_with_custom_error_using_quotes
901
+ Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
902
+ d = Developer.new
903
+ d.salary = "90,000"
904
+ assert !d.valid?
905
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).first
906
+ end
907
+
908
+ def test_validates_length_of_with_custom_too_long_using_quotes
909
+ Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
910
+ d = Developer.new
911
+ d.name = "Jeffrey"
912
+ assert !d.valid?
913
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
914
+ end
915
+
916
+ def test_validates_length_of_with_custom_too_short_using_quotes
917
+ Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
918
+ d = Developer.new
919
+ d.name = "Joe"
920
+ assert !d.valid?
921
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
922
+ end
923
+
924
+ def test_validates_length_of_with_custom_message_using_quotes
925
+ Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
926
+ d = Developer.new
927
+ d.name = "Joe"
928
+ assert !d.valid?
929
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
930
+ end
931
+
932
+ def test_validates_presence_of_with_custom_message_using_quotes
933
+ Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
934
+ d = Developer.new
935
+ d.name = "Joe"
936
+ assert !d.valid?
937
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
938
+ end
939
+
940
+ def test_validates_uniqueness_of_with_custom_message_using_quotes
941
+ Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
942
+ d = Developer.new
943
+ d.name = "David"
944
+ assert !d.valid?
945
+ assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).first
946
+ end
947
+
948
+ def test_validates_associated_with_custom_message_using_quotes
949
+ Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
950
+ Topic.validates_presence_of :content
951
+ r = Reply.create("title" => "A reply", "content" => "with content!")
952
+ r.topic = Topic.create("title" => "uhohuhoh")
953
+ assert !r.valid?
954
+ assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).first
955
+ end
956
+
957
+ def test_conditional_validation_using_method_true
958
+ # When the method returns true
959
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
960
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
961
+ assert !t.valid?
962
+ assert t.errors.on(:title)
963
+ assert_equal "hoo 5", t.errors["title"]
964
+ end
965
+
966
+ def test_conditional_validation_using_method_false
967
+ # When the method returns false
968
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
969
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
970
+ assert t.valid?
971
+ assert !t.errors.on(:title)
972
+ end
973
+
974
+ def test_conditional_validation_using_string_true
975
+ # When the evaluated string returns true
976
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
977
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
978
+ assert !t.valid?
979
+ assert t.errors.on(:title)
980
+ assert_equal "hoo 5", t.errors["title"]
981
+ end
982
+
983
+ def test_conditional_validation_using_string_false
984
+ # When the evaluated string returns false
985
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
986
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
987
+ assert t.valid?
988
+ assert !t.errors.on(:title)
989
+ end
990
+
991
+ def test_conditional_validation_using_block_true
992
+ # When the block returns true
993
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
994
+ :if => Proc.new { |r| r.content.size > 4 } )
995
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
996
+ assert !t.valid?
997
+ assert t.errors.on(:title)
998
+ assert_equal "hoo 5", t.errors["title"]
999
+ end
1000
+
1001
+ def test_conditional_validation_using_block_false
1002
+ # When the block returns false
1003
+ Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1004
+ :if => Proc.new { |r| r.title != "uhohuhoh"} )
1005
+ t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1006
+ assert t.valid?
1007
+ assert !t.errors.on(:title)
1008
+ end
1009
+
1010
+ def test_validates_associated_missing
1011
+ Reply.validates_presence_of(:topic)
1012
+ r = Reply.create("title" => "A reply", "content" => "with content!")
1013
+ assert !r.valid?
1014
+ assert r.errors.on(:topic)
1015
+
1016
+ r.topic = Topic.find :first
1017
+ assert r.valid?
1018
+ end
1019
+
1020
+ def test_errors_to_xml
1021
+ r = Reply.new :title => "Wrong Create"
1022
+ assert !r.valid?
1023
+ xml = r.errors.to_xml(:skip_instruct => true)
1024
+ assert_equal "<errors>", xml.first(8)
1025
+ assert xml.include?("<error>Title is Wrong Create</error>")
1026
+ assert xml.include?("<error>Content Empty</error>")
1027
+ end
1028
+ end
1029
+
1030
+
1031
+ class ValidatesNumericalityTest < Test::Unit::TestCase
1032
+ NIL = [nil]
1033
+ BLANK = ["", " ", " \t \r \n"]
1034
+ BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1035
+ 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)
1036
+ INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1037
+ FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1038
+ INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1039
+ BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1040
+ 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"]
1041
+
1042
+ def setup
1043
+ Topic.write_inheritable_attribute(:validate, nil)
1044
+ Topic.write_inheritable_attribute(:validate_on_create, nil)
1045
+ Topic.write_inheritable_attribute(:validate_on_update, nil)
1046
+ end
1047
+
1048
+ def test_default_validates_numericality_of
1049
+ Topic.validates_numericality_of :approved
1050
+
1051
+ invalid!(NIL + BLANK + JUNK)
1052
+ valid!(FLOATS + INTEGERS + BIGDECIMAL)
1053
+ end
1054
+
1055
+ def test_validates_numericality_of_with_nil_allowed
1056
+ Topic.validates_numericality_of :approved, :allow_nil => true
1057
+
1058
+ invalid!(BLANK + JUNK)
1059
+ valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL)
1060
+ end
1061
+
1062
+ def test_validates_numericality_of_with_integer_only
1063
+ Topic.validates_numericality_of :approved, :only_integer => true
1064
+
1065
+ invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL)
1066
+ valid!(INTEGERS)
1067
+ end
1068
+
1069
+ def test_validates_numericality_of_with_integer_only_and_nil_allowed
1070
+ Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1071
+
1072
+ invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL)
1073
+ valid!(NIL + INTEGERS)
1074
+ end
1075
+
1076
+ private
1077
+ def invalid!(values)
1078
+ with_each_topic_approved_value(values) do |topic, value|
1079
+ assert !topic.valid?, "#{value.inspect} not rejected as a number"
1080
+ assert topic.errors.on(:approved)
1081
+ end
1082
+ end
1083
+
1084
+ def valid!(values)
1085
+ with_each_topic_approved_value(values) do |topic, value|
1086
+ assert topic.valid?, "#{value.inspect} not accepted as a number"
1087
+ end
1088
+ end
1089
+
1090
+ def with_each_topic_approved_value(values)
1091
+ topic = Topic.new("title" => "numeric test", "content" => "whatever")
1092
+ values.each do |value|
1093
+ topic.approved = value
1094
+ yield topic, value
1095
+ end
1096
+ end
1097
+ end