activerecord 2.0.5 → 2.1.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 (289) hide show
  1. data/CHANGELOG +168 -6
  2. data/README +27 -22
  3. data/RUNNING_UNIT_TESTS +7 -4
  4. data/Rakefile +22 -25
  5. data/lib/active_record.rb +8 -2
  6. data/lib/active_record/aggregations.rb +21 -12
  7. data/lib/active_record/association_preload.rb +277 -0
  8. data/lib/active_record/associations.rb +481 -295
  9. data/lib/active_record/associations/association_collection.rb +162 -37
  10. data/lib/active_record/associations/association_proxy.rb +71 -7
  11. data/lib/active_record/associations/belongs_to_association.rb +5 -3
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +5 -6
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +12 -64
  14. data/lib/active_record/associations/has_many_association.rb +8 -73
  15. data/lib/active_record/associations/has_many_through_association.rb +68 -117
  16. data/lib/active_record/associations/has_one_association.rb +7 -5
  17. data/lib/active_record/associations/has_one_through_association.rb +28 -0
  18. data/lib/active_record/attribute_methods.rb +69 -19
  19. data/lib/active_record/base.rb +496 -275
  20. data/lib/active_record/calculations.rb +28 -21
  21. data/lib/active_record/callbacks.rb +9 -38
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +3 -2
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/query_cache.rb +6 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +232 -45
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +141 -27
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +9 -13
  28. data/lib/active_record/connection_adapters/mysql_adapter.rb +57 -24
  29. data/lib/active_record/connection_adapters/postgresql_adapter.rb +143 -42
  30. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +1 -1
  31. data/lib/active_record/connection_adapters/sqlite_adapter.rb +18 -10
  32. data/lib/active_record/dirty.rb +158 -0
  33. data/lib/active_record/fixtures.rb +121 -156
  34. data/lib/active_record/locking/optimistic.rb +14 -11
  35. data/lib/active_record/locking/pessimistic.rb +2 -2
  36. data/lib/active_record/migration.rb +157 -77
  37. data/lib/active_record/named_scope.rb +163 -0
  38. data/lib/active_record/observer.rb +19 -5
  39. data/lib/active_record/reflection.rb +34 -14
  40. data/lib/active_record/schema.rb +7 -14
  41. data/lib/active_record/schema_dumper.rb +4 -4
  42. data/lib/active_record/serialization.rb +5 -5
  43. data/lib/active_record/serializers/json_serializer.rb +37 -28
  44. data/lib/active_record/serializers/xml_serializer.rb +52 -29
  45. data/lib/active_record/test_case.rb +36 -0
  46. data/lib/active_record/timestamp.rb +4 -4
  47. data/lib/active_record/transactions.rb +3 -3
  48. data/lib/active_record/validations.rb +182 -248
  49. data/lib/active_record/version.rb +2 -2
  50. data/test/{fixtures → assets}/example.log +0 -0
  51. data/test/{fixtures → assets}/flowers.jpg +0 -0
  52. data/test/cases/aaa_create_tables_test.rb +24 -0
  53. data/test/cases/active_schema_test_mysql.rb +95 -0
  54. data/test/cases/active_schema_test_postgresql.rb +24 -0
  55. data/test/{adapter_test.rb → cases/adapter_test.rb} +15 -14
  56. data/test/{adapter_test_sqlserver.rb → cases/adapter_test_sqlserver.rb} +95 -95
  57. data/test/{aggregations_test.rb → cases/aggregations_test.rb} +20 -20
  58. data/test/{ar_schema_test.rb → cases/ar_schema_test.rb} +6 -6
  59. data/test/cases/associations/belongs_to_associations_test.rb +412 -0
  60. data/test/{associations → cases/associations}/callbacks_test.rb +24 -10
  61. data/test/{associations → cases/associations}/cascaded_eager_loading_test.rb +18 -17
  62. data/test/cases/associations/eager_load_nested_include_test.rb +83 -0
  63. data/test/{associations → cases/associations}/eager_singularization_test.rb +5 -5
  64. data/test/{associations → cases/associations}/eager_test.rb +216 -51
  65. data/test/{associations → cases/associations}/extension_test.rb +8 -8
  66. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +684 -0
  67. data/test/cases/associations/has_many_associations_test.rb +932 -0
  68. data/test/cases/associations/has_many_through_associations_test.rb +190 -0
  69. data/test/cases/associations/has_one_associations_test.rb +323 -0
  70. data/test/cases/associations/has_one_through_associations_test.rb +74 -0
  71. data/test/{associations → cases/associations}/inner_join_association_test.rb +20 -20
  72. data/test/{associations → cases/associations}/join_model_test.rb +175 -35
  73. data/test/cases/associations_test.rb +262 -0
  74. data/test/{attribute_methods_test.rb → cases/attribute_methods_test.rb} +103 -11
  75. data/test/{base_test.rb → cases/base_test.rb} +338 -191
  76. data/test/{binary_test.rb → cases/binary_test.rb} +6 -4
  77. data/test/{calculations_test.rb → cases/calculations_test.rb} +35 -23
  78. data/test/{callbacks_test.rb → cases/callbacks_test.rb} +7 -7
  79. data/test/{class_inheritable_attributes_test.rb → cases/class_inheritable_attributes_test.rb} +3 -3
  80. data/test/{column_alias_test.rb → cases/column_alias_test.rb} +3 -3
  81. data/test/{connection_test_firebird.rb → cases/connection_test_firebird.rb} +2 -2
  82. data/test/{connection_test_mysql.rb → cases/connection_test_mysql.rb} +2 -2
  83. data/test/{copy_table_test_sqlite.rb → cases/copy_table_test_sqlite.rb} +13 -13
  84. data/test/{datatype_test_postgresql.rb → cases/datatype_test_postgresql.rb} +8 -8
  85. data/test/{date_time_test.rb → cases/date_time_test.rb} +5 -5
  86. data/test/{default_test_firebird.rb → cases/default_test_firebird.rb} +3 -3
  87. data/test/{defaults_test.rb → cases/defaults_test.rb} +8 -6
  88. data/test/{deprecated_finder_test.rb → cases/deprecated_finder_test.rb} +3 -3
  89. data/test/cases/dirty_test.rb +163 -0
  90. data/test/cases/finder_respond_to_test.rb +76 -0
  91. data/test/{finder_test.rb → cases/finder_test.rb} +266 -33
  92. data/test/{fixtures_test.rb → cases/fixtures_test.rb} +88 -72
  93. data/test/cases/helper.rb +47 -0
  94. data/test/{inheritance_test.rb → cases/inheritance_test.rb} +61 -17
  95. data/test/cases/invalid_date_test.rb +24 -0
  96. data/test/{json_serialization_test.rb → cases/json_serialization_test.rb} +36 -11
  97. data/test/{lifecycle_test.rb → cases/lifecycle_test.rb} +16 -13
  98. data/test/{locking_test.rb → cases/locking_test.rb} +17 -10
  99. data/test/{method_scoping_test.rb → cases/method_scoping_test.rb} +75 -39
  100. data/test/{migration_test.rb → cases/migration_test.rb} +420 -80
  101. data/test/{migration_test_firebird.rb → cases/migration_test_firebird.rb} +3 -3
  102. data/test/{mixin_test.rb → cases/mixin_test.rb} +7 -6
  103. data/test/{modules_test.rb → cases/modules_test.rb} +11 -6
  104. data/test/{multiple_db_test.rb → cases/multiple_db_test.rb} +5 -5
  105. data/test/cases/named_scope_test.rb +157 -0
  106. data/test/{pk_test.rb → cases/pk_test.rb} +10 -10
  107. data/test/{query_cache_test.rb → cases/query_cache_test.rb} +12 -10
  108. data/test/{readonly_test.rb → cases/readonly_test.rb} +11 -11
  109. data/test/{reflection_test.rb → cases/reflection_test.rb} +15 -14
  110. data/test/{reserved_word_test_mysql.rb → cases/reserved_word_test_mysql.rb} +4 -5
  111. data/test/{schema_authorization_test_postgresql.rb → cases/schema_authorization_test_postgresql.rb} +5 -5
  112. data/test/cases/schema_dumper_test.rb +138 -0
  113. data/test/cases/schema_test_postgresql.rb +102 -0
  114. data/test/{serialization_test.rb → cases/serialization_test.rb} +7 -7
  115. data/test/{synonym_test_oracle.rb → cases/synonym_test_oracle.rb} +5 -5
  116. data/test/{table_name_test_sqlserver.rb → cases/table_name_test_sqlserver.rb} +3 -3
  117. data/test/{threaded_connections_test.rb → cases/threaded_connections_test.rb} +7 -7
  118. data/test/{transactions_test.rb → cases/transactions_test.rb} +31 -5
  119. data/test/{unconnected_test.rb → cases/unconnected_test.rb} +2 -2
  120. data/test/{validations_test.rb → cases/validations_test.rb} +141 -39
  121. data/test/{xml_serialization_test.rb → cases/xml_serialization_test.rb} +12 -12
  122. data/test/config.rb +5 -0
  123. data/test/connections/native_db2/connection.rb +1 -1
  124. data/test/connections/native_firebird/connection.rb +1 -1
  125. data/test/connections/native_frontbase/connection.rb +1 -1
  126. data/test/connections/native_mysql/connection.rb +1 -1
  127. data/test/connections/native_openbase/connection.rb +1 -1
  128. data/test/connections/native_oracle/connection.rb +1 -1
  129. data/test/connections/native_postgresql/connection.rb +1 -3
  130. data/test/connections/native_sqlite/connection.rb +2 -2
  131. data/test/connections/native_sqlite3/connection.rb +2 -2
  132. data/test/connections/native_sqlite3/in_memory_connection.rb +3 -3
  133. data/test/connections/native_sybase/connection.rb +1 -1
  134. data/test/fixtures/author_addresses.yml +5 -0
  135. data/test/fixtures/authors.yml +2 -0
  136. data/test/fixtures/clubs.yml +6 -0
  137. data/test/fixtures/jobs.yml +7 -0
  138. data/test/fixtures/members.yml +4 -0
  139. data/test/fixtures/memberships.yml +20 -0
  140. data/test/fixtures/owners.yml +7 -0
  141. data/test/fixtures/people.yml +4 -1
  142. data/test/fixtures/pets.yml +14 -0
  143. data/test/fixtures/posts.yml +1 -0
  144. data/test/fixtures/price_estimates.yml +7 -0
  145. data/test/fixtures/readers.yml +5 -0
  146. data/test/fixtures/references.yml +17 -0
  147. data/test/fixtures/sponsors.yml +9 -0
  148. data/test/fixtures/subscribers.yml +7 -0
  149. data/test/fixtures/subscriptions.yml +12 -0
  150. data/test/fixtures/taggings.yml +4 -1
  151. data/test/fixtures/topics.yml +22 -2
  152. data/test/fixtures/warehouse-things.yml +3 -0
  153. data/test/{fixtures/migrations_with_decimal → migrations/decimal}/1_give_me_big_numbers.rb +0 -0
  154. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/1_people_have_last_names.rb +1 -1
  155. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/2_we_need_reminders.rb +1 -1
  156. data/test/{fixtures/migrations_with_duplicate → migrations/duplicate}/3_foo.rb +0 -0
  157. data/test/{fixtures/migrations → migrations/duplicate}/3_innocent_jointable.rb +0 -0
  158. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  159. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  160. data/test/{fixtures/migrations_with_duplicate → migrations/interleaved/pass_1}/3_innocent_jointable.rb +0 -0
  161. data/test/{fixtures/migrations → migrations/interleaved/pass_2}/1_people_have_last_names.rb +1 -1
  162. data/test/{fixtures/migrations_with_missing_versions/4_innocent_jointable.rb → migrations/interleaved/pass_2/3_innocent_jointable.rb} +0 -0
  163. data/test/{fixtures/migrations_with_missing_versions → migrations/interleaved/pass_3}/1_people_have_last_names.rb +1 -1
  164. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  165. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  166. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/1000_people_have_middle_names.rb +1 -1
  167. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  168. data/test/{fixtures/migrations_with_missing_versions → migrations/missing}/3_we_need_reminders.rb +1 -1
  169. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  170. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  171. data/test/{fixtures/migrations → migrations/valid}/2_we_need_reminders.rb +1 -1
  172. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  173. data/test/{fixtures → models}/author.rb +28 -4
  174. data/test/{fixtures → models}/auto_id.rb +0 -0
  175. data/test/{fixtures → models}/binary.rb +0 -0
  176. data/test/{fixtures → models}/book.rb +0 -0
  177. data/test/{fixtures → models}/categorization.rb +0 -0
  178. data/test/{fixtures → models}/category.rb +8 -5
  179. data/test/{fixtures → models}/citation.rb +0 -0
  180. data/test/models/club.rb +7 -0
  181. data/test/{fixtures → models}/column_name.rb +0 -0
  182. data/test/{fixtures → models}/comment.rb +5 -3
  183. data/test/{fixtures → models}/company.rb +15 -6
  184. data/test/{fixtures → models}/company_in_module.rb +5 -3
  185. data/test/{fixtures → models}/computer.rb +0 -1
  186. data/test/{fixtures → models}/contact.rb +1 -1
  187. data/test/{fixtures → models}/course.rb +0 -0
  188. data/test/{fixtures → models}/customer.rb +8 -8
  189. data/test/{fixtures → models}/default.rb +0 -0
  190. data/test/{fixtures → models}/developer.rb +14 -10
  191. data/test/{fixtures → models}/edge.rb +0 -0
  192. data/test/{fixtures → models}/entrant.rb +0 -0
  193. data/test/models/guid.rb +2 -0
  194. data/test/{fixtures → models}/item.rb +0 -0
  195. data/test/models/job.rb +5 -0
  196. data/test/{fixtures → models}/joke.rb +0 -0
  197. data/test/{fixtures → models}/keyboard.rb +0 -0
  198. data/test/{fixtures → models}/legacy_thing.rb +0 -0
  199. data/test/{fixtures → models}/matey.rb +0 -0
  200. data/test/models/member.rb +9 -0
  201. data/test/models/membership.rb +9 -0
  202. data/test/{fixtures → models}/minimalistic.rb +0 -0
  203. data/test/{fixtures → models}/mixed_case_monkey.rb +0 -0
  204. data/test/{fixtures → models}/movie.rb +0 -0
  205. data/test/{fixtures → models}/order.rb +2 -2
  206. data/test/models/owner.rb +4 -0
  207. data/test/{fixtures → models}/parrot.rb +0 -0
  208. data/test/models/person.rb +10 -0
  209. data/test/models/pet.rb +4 -0
  210. data/test/models/pirate.rb +9 -0
  211. data/test/{fixtures → models}/post.rb +23 -2
  212. data/test/models/price_estimate.rb +3 -0
  213. data/test/{fixtures → models}/project.rb +1 -0
  214. data/test/{fixtures → models}/reader.rb +0 -0
  215. data/test/models/reference.rb +4 -0
  216. data/test/{fixtures → models}/reply.rb +7 -5
  217. data/test/{fixtures → models}/ship.rb +0 -0
  218. data/test/models/sponsor.rb +4 -0
  219. data/test/{fixtures → models}/subject.rb +0 -0
  220. data/test/{fixtures → models}/subscriber.rb +2 -0
  221. data/test/models/subscription.rb +4 -0
  222. data/test/{fixtures → models}/tag.rb +0 -0
  223. data/test/{fixtures → models}/tagging.rb +0 -0
  224. data/test/{fixtures → models}/task.rb +0 -0
  225. data/test/{fixtures → models}/topic.rb +32 -4
  226. data/test/{fixtures → models}/treasure.rb +2 -0
  227. data/test/{fixtures → models}/vertex.rb +0 -0
  228. data/test/models/warehouse_thing.rb +5 -0
  229. data/test/schema/mysql_specific_schema.rb +12 -0
  230. data/test/schema/postgresql_specific_schema.rb +103 -0
  231. data/test/schema/schema.rb +421 -0
  232. data/test/schema/schema2.rb +6 -0
  233. data/test/schema/sqlite_specific_schema.rb +25 -0
  234. data/test/schema/sqlserver_specific_schema.rb +5 -0
  235. metadata +192 -176
  236. data/test/aaa_create_tables_test.rb +0 -72
  237. data/test/abstract_unit.rb +0 -84
  238. data/test/active_schema_test_mysql.rb +0 -46
  239. data/test/all.sh +0 -8
  240. data/test/association_inheritance_reload.rb +0 -14
  241. data/test/associations_test.rb +0 -2177
  242. data/test/fixtures/bad_fixtures/attr_with_numeric_first_char +0 -1
  243. data/test/fixtures/bad_fixtures/attr_with_spaces +0 -1
  244. data/test/fixtures/bad_fixtures/blank_line +0 -3
  245. data/test/fixtures/bad_fixtures/duplicate_attributes +0 -3
  246. data/test/fixtures/bad_fixtures/missing_value +0 -1
  247. data/test/fixtures/db_definitions/db2.drop.sql +0 -33
  248. data/test/fixtures/db_definitions/db2.sql +0 -235
  249. data/test/fixtures/db_definitions/db22.drop.sql +0 -2
  250. data/test/fixtures/db_definitions/db22.sql +0 -5
  251. data/test/fixtures/db_definitions/firebird.drop.sql +0 -65
  252. data/test/fixtures/db_definitions/firebird.sql +0 -310
  253. data/test/fixtures/db_definitions/firebird2.drop.sql +0 -2
  254. data/test/fixtures/db_definitions/firebird2.sql +0 -6
  255. data/test/fixtures/db_definitions/frontbase.drop.sql +0 -33
  256. data/test/fixtures/db_definitions/frontbase.sql +0 -273
  257. data/test/fixtures/db_definitions/frontbase2.drop.sql +0 -1
  258. data/test/fixtures/db_definitions/frontbase2.sql +0 -4
  259. data/test/fixtures/db_definitions/openbase.drop.sql +0 -2
  260. data/test/fixtures/db_definitions/openbase.sql +0 -318
  261. data/test/fixtures/db_definitions/openbase2.drop.sql +0 -2
  262. data/test/fixtures/db_definitions/openbase2.sql +0 -7
  263. data/test/fixtures/db_definitions/oracle.drop.sql +0 -67
  264. data/test/fixtures/db_definitions/oracle.sql +0 -330
  265. data/test/fixtures/db_definitions/oracle2.drop.sql +0 -2
  266. data/test/fixtures/db_definitions/oracle2.sql +0 -6
  267. data/test/fixtures/db_definitions/postgresql.drop.sql +0 -44
  268. data/test/fixtures/db_definitions/postgresql.sql +0 -292
  269. data/test/fixtures/db_definitions/postgresql2.drop.sql +0 -2
  270. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  271. data/test/fixtures/db_definitions/schema.rb +0 -354
  272. data/test/fixtures/db_definitions/schema2.rb +0 -11
  273. data/test/fixtures/db_definitions/sqlite.drop.sql +0 -33
  274. data/test/fixtures/db_definitions/sqlite.sql +0 -219
  275. data/test/fixtures/db_definitions/sqlite2.drop.sql +0 -2
  276. data/test/fixtures/db_definitions/sqlite2.sql +0 -5
  277. data/test/fixtures/db_definitions/sybase.drop.sql +0 -35
  278. data/test/fixtures/db_definitions/sybase.sql +0 -222
  279. data/test/fixtures/db_definitions/sybase2.drop.sql +0 -4
  280. data/test/fixtures/db_definitions/sybase2.sql +0 -5
  281. data/test/fixtures/developers_projects/david_action_controller +0 -3
  282. data/test/fixtures/developers_projects/david_active_record +0 -3
  283. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  284. data/test/fixtures/person.rb +0 -4
  285. data/test/fixtures/pirate.rb +0 -5
  286. data/test/fixtures/subscribers/first +0 -2
  287. data/test/fixtures/subscribers/second +0 -2
  288. data/test/schema_dumper_test.rb +0 -131
  289. data/test/schema_test_postgresql.rb +0 -64
@@ -0,0 +1,36 @@
1
+ require "active_support/test_case"
2
+
3
+ module ActiveRecord
4
+ class TestCase < ActiveSupport::TestCase #:nodoc:
5
+ self.fixture_path = FIXTURES_ROOT
6
+ self.use_instantiated_fixtures = false
7
+ self.use_transactional_fixtures = true
8
+
9
+ def create_fixtures(*table_names, &block)
10
+ Fixtures.create_fixtures(FIXTURES_ROOT, table_names, {}, &block)
11
+ end
12
+
13
+ def assert_date_from_db(expected, actual, message = nil)
14
+ # SQL Server doesn't have a separate column type just for dates,
15
+ # so the time is in the string and incorrectly formatted
16
+ if current_adapter?(:SQLServerAdapter)
17
+ assert_equal expected.strftime("%Y/%m/%d 00:00:00"), actual.strftime("%Y/%m/%d 00:00:00")
18
+ elsif current_adapter?(:SybaseAdapter)
19
+ assert_equal expected.to_s, actual.to_date.to_s, message
20
+ else
21
+ assert_equal expected.to_s, actual.to_s, message
22
+ end
23
+ end
24
+
25
+ def assert_queries(num = 1)
26
+ $query_count = 0
27
+ yield
28
+ ensure
29
+ assert_equal num, $query_count, "#{$query_count} instead of #{num} queries were executed."
30
+ end
31
+
32
+ def assert_no_queries(&block)
33
+ assert_queries(0, &block)
34
+ end
35
+ end
36
+ end
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  # Timestamping can be turned off by setting
6
6
  # <tt>ActiveRecord::Base.record_timestamps = false</tt>
7
7
  #
8
- # Timestamps are in the local timezone by default but can use UTC by setting
8
+ # Timestamps are in the local timezone by default but you can use UTC by setting
9
9
  # <tt>ActiveRecord::Base.default_timezone = :utc</tt>
10
10
  module Timestamp
11
11
  def self.included(base) #:nodoc:
@@ -29,13 +29,13 @@ module ActiveRecord
29
29
  create_without_timestamps
30
30
  end
31
31
 
32
- def update_with_timestamps #:nodoc:
33
- if record_timestamps
32
+ def update_with_timestamps(*args) #:nodoc:
33
+ if record_timestamps && (!partial_updates? || changed?)
34
34
  t = self.class.default_timezone == :utc ? Time.now.utc : Time.now
35
35
  write_attribute('updated_at', t) if respond_to?(:updated_at)
36
36
  write_attribute('updated_on', t) if respond_to?(:updated_on)
37
37
  end
38
- update_without_timestamps
38
+ update_without_timestamps(*args)
39
39
  end
40
40
  end
41
41
  end
@@ -28,11 +28,11 @@ module ActiveRecord
28
28
  #
29
29
  # This example will only take money from David and give to Mary if neither +withdrawal+ nor +deposit+ raises an exception.
30
30
  # Exceptions will force a ROLLBACK that returns the database to the state before the transaction was begun. Be aware, though,
31
- # that the objects by default will _not_ have their instance data returned to their pre-transactional state.
31
+ # that the objects will _not_ have their instance data returned to their pre-transactional state.
32
32
  #
33
- # == Different ActiveRecord classes in a single transaction
33
+ # == Different Active Record classes in a single transaction
34
34
  #
35
- # Though the transaction class method is called on some ActiveRecord class,
35
+ # Though the transaction class method is called on some Active Record class,
36
36
  # the objects within the transaction block need not all be instances of
37
37
  # that class.
38
38
  # In this example a <tt>Balance</tt> record is transactionally saved even
@@ -201,7 +201,7 @@ module ActiveRecord
201
201
  alias_method :count, :size
202
202
  alias_method :length, :size
203
203
 
204
- # Return an XML representation of this error object.
204
+ # Returns an XML representation of this error object.
205
205
  #
206
206
  # class Company < ActiveRecord::Base
207
207
  # validates_presence_of :name, :address, :email
@@ -266,7 +266,7 @@ module ActiveRecord
266
266
  # person.attributes = { "last_name" => "Heinemeier", "phone_number" => "555-555" }
267
267
  # person.save # => true (and person is now saved in the database)
268
268
  #
269
- # An +Errors+ object is automatically created for every Active Record.
269
+ # An Errors object is automatically created for every Active Record.
270
270
  #
271
271
  # Please do have a look at ActiveRecord::Validations::ClassMethods for a higher level of validations.
272
272
  module Validations
@@ -279,11 +279,14 @@ module ActiveRecord
279
279
  alias_method_chain :save!, :validation
280
280
  alias_method_chain :update_attribute, :validation_skipping
281
281
  end
282
+
283
+ base.send :include, ActiveSupport::Callbacks
284
+ base.define_callbacks *VALIDATIONS
282
285
  end
283
286
 
284
287
  # All of the following validations are defined in the class scope of the model that you're interested in validating.
285
288
  # They offer a more declarative way of specifying when the model is valid and when it is not. It is recommended to use
286
- # these over the low-level calls to validate and validate_on_create when possible.
289
+ # these over the low-level calls to +validate+ and +validate_on_create+ when possible.
287
290
  module ClassMethods
288
291
  DEFAULT_VALIDATION_OPTIONS = {
289
292
  :on => :save,
@@ -298,7 +301,7 @@ module ActiveRecord
298
301
  :odd => 'odd?', :even => 'even?' }.freeze
299
302
 
300
303
  # Adds a validation method or block to the class. This is useful when
301
- # overriding the #validate instance method becomes too unwieldly and
304
+ # overriding the +validate+ instance method becomes too unwieldly and
302
305
  # you're looking for more descriptive declaration of your validations.
303
306
  #
304
307
  # This can be done with a symbol pointing to a method:
@@ -323,44 +326,7 @@ module ActiveRecord
323
326
  # end
324
327
  # end
325
328
  #
326
- # This usage applies to #validate_on_create and #validate_on_update as well.
327
- def validate(*methods, &block)
328
- methods << block if block_given?
329
- write_inheritable_set(:validate, methods)
330
- end
331
-
332
- def validate_on_create(*methods, &block)
333
- methods << block if block_given?
334
- write_inheritable_set(:validate_on_create, methods)
335
- end
336
-
337
- def validate_on_update(*methods, &block)
338
- methods << block if block_given?
339
- write_inheritable_set(:validate_on_update, methods)
340
- end
341
-
342
- def condition_block?(condition)
343
- condition.respond_to?("call") && (condition.arity == 1 || condition.arity == -1)
344
- end
345
-
346
- # Determine from the given condition (whether a block, procedure, method or string)
347
- # whether or not to validate the record. See #validates_each.
348
- def evaluate_condition(condition, record)
349
- case condition
350
- when Symbol; record.send(condition)
351
- when String; eval(condition, record.send(:binding))
352
- else
353
- if condition_block?(condition)
354
- condition.call(record)
355
- else
356
- raise(
357
- ActiveRecordError,
358
- "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
359
- "class implementing a static validation method"
360
- )
361
- end
362
- end
363
- end
329
+ # This usage applies to +validate_on_create+ and +validate_on_update+ as well.
364
330
 
365
331
  # Validates each attribute against a block.
366
332
  #
@@ -371,28 +337,25 @@ module ActiveRecord
371
337
  # end
372
338
  #
373
339
  # Options:
374
- # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
375
- # * <tt>allow_nil</tt> - Skip validation if attribute is nil.
376
- # * <tt>allow_blank</tt> - Skip validation if attribute is blank.
377
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
378
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
340
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
341
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
342
+ # * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
343
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
344
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
345
+ # method, proc or string should return or evaluate to a true or false value.
346
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
347
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
379
348
  # method, proc or string should return or evaluate to a true or false value.
380
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
381
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
382
- # method, proc or string should return or evaluate to a true or false value.
383
349
  def validates_each(*attrs)
384
350
  options = attrs.extract_options!.symbolize_keys
385
351
  attrs = attrs.flatten
386
352
 
387
353
  # Declare the validation.
388
- send(validation_method(options[:on] || :save)) do |record|
389
- # Don't validate when there is an :if condition and that condition is false or there is an :unless condition and that condition is true
390
- unless (options[:if] && !evaluate_condition(options[:if], record)) || (options[:unless] && evaluate_condition(options[:unless], record))
391
- attrs.each do |attr|
392
- value = record.send(attr)
393
- next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
394
- yield record, attr, value
395
- end
354
+ send(validation_method(options[:on] || :save), options) do |record|
355
+ attrs.each do |attr|
356
+ value = record.send(attr)
357
+ next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
358
+ yield record, attr, value
396
359
  end
397
360
  end
398
361
  end
@@ -410,21 +373,21 @@ module ActiveRecord
410
373
  # <%= password_field "person", "password_confirmation" %>
411
374
  #
412
375
  # The added +password_confirmation+ attribute is virtual; it exists only as an in-memory attribute for validating the password.
413
- # To achieve this, the validation adds acccessors to the model for the confirmation attribute. NOTE: This check is performed
414
- # only if +password_confirmation+ is not nil, and by default only on save. To require confirmation, make sure to add a presence
376
+ # To achieve this, the validation adds accessors to the model for the confirmation attribute. NOTE: This check is performed
377
+ # only if +password_confirmation+ is not +nil+, and by default only on save. To require confirmation, make sure to add a presence
415
378
  # check for the confirmation attribute:
416
379
  #
417
380
  # validates_presence_of :password_confirmation, :if => :password_changed?
418
381
  #
419
382
  # Configuration options:
420
- # * <tt>message</tt> - A custom error message (default is: "doesn't match confirmation")
421
- # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
422
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
423
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
383
+ # * <tt>:message</tt> - A custom error message (default is: "doesn't match confirmation").
384
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
385
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
386
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
387
+ # method, proc or string should return or evaluate to a true or false value.
388
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
389
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
424
390
  # method, proc or string should return or evaluate to a true or false value.
425
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
426
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
427
- # method, proc or string should return or evaluate to a true or false value.
428
391
  def validates_confirmation_of(*attr_names)
429
392
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:confirmation], :on => :save }
430
393
  configuration.update(attr_names.extract_options!)
@@ -443,22 +406,22 @@ module ActiveRecord
443
406
  # validates_acceptance_of :eula, :message => "must be abided"
444
407
  # end
445
408
  #
446
- # If the database column does not exist, the terms_of_service attribute is entirely virtual. This check is
447
- # performed only if terms_of_service is not nil and by default on save.
409
+ # If the database column does not exist, the +terms_of_service+ attribute is entirely virtual. This check is
410
+ # performed only if +terms_of_service+ is not +nil+ and by default on save.
448
411
  #
449
412
  # Configuration options:
450
- # * <tt>message</tt> - A custom error message (default is: "must be accepted")
451
- # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
452
- # * <tt>allow_nil</tt> - Skip validation if attribute is nil. (default is true)
453
- # * <tt>accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
454
- # makes it easy to relate to an HTML checkbox. This should be set to 'true' if you are validating a database
455
- # column, since the attribute is typecast from "1" to <tt>true</tt> before validation.
456
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
457
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
413
+ # * <tt>:message</tt> - A custom error message (default is: "must be accepted").
414
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
415
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is true).
416
+ # * <tt>:accept</tt> - Specifies value that is considered accepted. The default value is a string "1", which
417
+ # makes it easy to relate to an HTML checkbox. This should be set to +true+ if you are validating a database
418
+ # column, since the attribute is typecast from "1" to +true+ before validation.
419
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
420
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
421
+ # method, proc or string should return or evaluate to a true or false value.
422
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
423
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
458
424
  # method, proc or string should return or evaluate to a true or false value.
459
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
460
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
461
- # method, proc or string should return or evaluate to a true or false value.
462
425
  def validates_acceptance_of(*attr_names)
463
426
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:accepted], :on => :save, :allow_nil => true, :accept => "1" }
464
427
  configuration.update(attr_names.extract_options!)
@@ -489,8 +452,8 @@ module ActiveRecord
489
452
  # This is due to the way Object#blank? handles boolean values. false.blank? # => true
490
453
  #
491
454
  # Configuration options:
492
- # * <tt>message</tt> - A custom error message (default is: "can't be blank")
493
- # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
455
+ # * <tt>message</tt> - A custom error message (default is: "can't be blank").
456
+ # * <tt>on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
494
457
  # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
495
458
  # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
496
459
  # method, proc or string should return or evaluate to a true or false value.
@@ -498,28 +461,15 @@ module ActiveRecord
498
461
  # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
499
462
  # method, proc or string should return or evaluate to a true or false value.
500
463
  #
501
- # === Warning
502
- # Validate the presence of the foreign key, not the instance variable itself.
503
- # Do this:
504
- # validates_presence_of :invoice_id
505
- #
506
- # Not this:
507
- # validates_presence_of :invoice
508
- #
509
- # If you validate the presence of the associated object, you will get
510
- # failures on saves when both the parent object and the child object are
511
- # new.
512
464
  def validates_presence_of(*attr_names)
513
465
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:blank], :on => :save }
514
466
  configuration.update(attr_names.extract_options!)
515
467
 
516
468
  # can't use validates_each here, because it cannot cope with nonexistent attributes,
517
469
  # while errors.add_on_empty can
518
- send(validation_method(configuration[:on])) do |record|
519
- unless (configuration[:if] && !evaluate_condition(configuration[:if], record)) || (configuration[:unless] && evaluate_condition(configuration[:unless], record))
520
- record.errors.add_on_blank(attr_names, configuration[:message])
521
- end
522
- end
470
+ send(validation_method(configuration[:on]), configuration) do |record|
471
+ record.errors.add_on_blank(attr_names, configuration[:message])
472
+ end
523
473
  end
524
474
 
525
475
  # Validates that the specified attribute matches the length restrictions supplied. Only one option can be used at a time:
@@ -535,25 +485,25 @@ module ActiveRecord
535
485
  # end
536
486
  #
537
487
  # Configuration options:
538
- # * <tt>minimum</tt> - The minimum size of the attribute
539
- # * <tt>maximum</tt> - The maximum size of the attribute
540
- # * <tt>is</tt> - The exact size of the attribute
541
- # * <tt>within</tt> - A range specifying the minimum and maximum size of the attribute
542
- # * <tt>in</tt> - A synonym(or alias) for :within
543
- # * <tt>allow_nil</tt> - Attribute may be nil; skip validation.
544
- # * <tt>allow_blank</tt> - Attribute may be blank; skip validation.
545
- #
546
- # * <tt>too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)")
547
- # * <tt>too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)")
548
- # * <tt>wrong_length</tt> - The error message if using the :is method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)")
549
- # * <tt>message</tt> - The error message to use for a :minimum, :maximum, or :is violation. An alias of the appropriate too_long/too_short/wrong_length message
550
- # * <tt>on</tt> - Specifies when this validation is active (default is :save, other options :create, :update)
551
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
552
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
488
+ # * <tt>:minimum</tt> - The minimum size of the attribute.
489
+ # * <tt>:maximum</tt> - The maximum size of the attribute.
490
+ # * <tt>:is</tt> - The exact size of the attribute.
491
+ # * <tt>:within</tt> - A range specifying the minimum and maximum size of the attribute.
492
+ # * <tt>:in</tt> - A synonym(or alias) for <tt>:within</tt>.
493
+ # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
494
+ # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
495
+ #
496
+ # * <tt>:too_long</tt> - The error message if the attribute goes over the maximum (default is: "is too long (maximum is %d characters)").
497
+ # * <tt>:too_short</tt> - The error message if the attribute goes under the minimum (default is: "is too short (min is %d characters)").
498
+ # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt> method and the attribute is the wrong size (default is: "is the wrong length (should be %d characters)").
499
+ # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>, <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
500
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
501
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
502
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
503
+ # method, proc or string should return or evaluate to a true or false value.
504
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
505
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
553
506
  # method, proc or string should return or evaluate to a true or false value.
554
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
555
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
556
- # method, proc or string should return or evaluate to a true or false value.
557
507
  def validates_length_of(*attrs)
558
508
  # Merge given options with defaults.
559
509
  options = {
@@ -586,9 +536,10 @@ module ActiveRecord
586
536
  too_long = options[:too_long] % option_value.end
587
537
 
588
538
  validates_each(attrs, options) do |record, attr, value|
589
- if value.nil? or value.split(//).size < option_value.begin
539
+ value = value.split(//) if value.kind_of?(String)
540
+ if value.nil? or value.size < option_value.begin
590
541
  record.errors.add(attr, too_short)
591
- elsif value.split(//).size > option_value.end
542
+ elsif value.size > option_value.end
592
543
  record.errors.add(attr, too_long)
593
544
  end
594
545
  end
@@ -602,11 +553,8 @@ module ActiveRecord
602
553
  message = (options[:message] || options[message_options[option]]) % option_value
603
554
 
604
555
  validates_each(attrs, options) do |record, attr, value|
605
- if value.kind_of?(String)
606
- record.errors.add(attr, message) unless !value.nil? and value.split(//).size.method(validity_checks[option])[option_value]
607
- else
608
- record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
609
- end
556
+ value = value.split(//) if value.kind_of?(String)
557
+ record.errors.add(attr, message) unless !value.nil? and value.size.method(validity_checks[option])[option_value]
610
558
  end
611
559
  end
612
560
  end
@@ -632,63 +580,83 @@ module ActiveRecord
632
580
  # attribute (that maps to a column). When the record is updated, the same check is made but disregarding the record itself.
633
581
  #
634
582
  # Because this check is performed outside the database there is still a chance that duplicate values
635
- # will be inserted in two parallel transactions. To guarantee against this you should create a
583
+ # will be inserted in two parallel transactions. To guarantee against this you should create a
636
584
  # unique index on the field. See +add_index+ for more information.
637
585
  #
638
586
  # Configuration options:
639
- # * <tt>message</tt> - Specifies a custom error message (default is: "has already been taken")
640
- # * <tt>scope</tt> - One or more columns by which to limit the scope of the uniquness constraint.
641
- # * <tt>case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (true by default).
642
- # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
643
- # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
644
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
645
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
587
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "has already been taken").
588
+ # * <tt>:scope</tt> - One or more columns by which to limit the scope of the uniqueness constraint.
589
+ # * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by non-text columns (+false+ by default).
590
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
591
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
592
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
593
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
646
594
  # method, proc or string should return or evaluate to a true or false value.
647
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
648
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
595
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
596
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
649
597
  # method, proc or string should return or evaluate to a true or false value.
650
598
  def validates_uniqueness_of(*attr_names)
651
599
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:taken], :case_sensitive => true }
652
600
  configuration.update(attr_names.extract_options!)
653
601
 
654
602
  validates_each(attr_names,configuration) do |record, attr_name, value|
655
- if value.nil? || (configuration[:case_sensitive] || !columns_hash[attr_name.to_s].text?)
656
- condition_sql = "#{record.class.table_name}.#{attr_name} #{attribute_condition(value)}"
603
+ # The check for an existing value should be run from a class that
604
+ # isn't abstract. This means working down from the current class
605
+ # (self), to the first non-abstract class. Since classes don't know
606
+ # their subclasses, we have to build the hierarchy between self and
607
+ # the record's class.
608
+ class_hierarchy = [record.class]
609
+ while class_hierarchy.first != self
610
+ class_hierarchy.insert(0, class_hierarchy.first.superclass)
611
+ end
612
+
613
+ # Now we can work our way down the tree to the first non-abstract
614
+ # class (which has a database table to query from).
615
+ finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
616
+
617
+ if value.nil? || (configuration[:case_sensitive] || !finder_class.columns_hash[attr_name.to_s].text?)
618
+ condition_sql = "#{record.class.quoted_table_name}.#{attr_name} #{attribute_condition(value)}"
657
619
  condition_params = [value]
658
620
  else
659
- condition_sql = "LOWER(#{record.class.table_name}.#{attr_name}) #{attribute_condition(value)}"
621
+ # sqlite has case sensitive SELECT query, while MySQL/Postgresql don't.
622
+ # Hence, this is needed only for sqlite.
623
+ condition_sql = "LOWER(#{record.class.quoted_table_name}.#{attr_name}) #{attribute_condition(value)}"
660
624
  condition_params = [value.downcase]
661
625
  end
662
626
 
663
627
  if scope = configuration[:scope]
664
628
  Array(scope).map do |scope_item|
665
629
  scope_value = record.send(scope_item)
666
- condition_sql << " AND #{record.class.table_name}.#{scope_item} #{attribute_condition(scope_value)}"
630
+ condition_sql << " AND #{record.class.quoted_table_name}.#{scope_item} #{attribute_condition(scope_value)}"
667
631
  condition_params << scope_value
668
632
  end
669
633
  end
670
634
 
671
635
  unless record.new_record?
672
- condition_sql << " AND #{record.class.table_name}.#{record.class.primary_key} <> ?"
636
+ condition_sql << " AND #{record.class.quoted_table_name}.#{record.class.primary_key} <> ?"
673
637
  condition_params << record.send(:id)
674
638
  end
675
639
 
676
- # The check for an existing value should be run from a class that
677
- # isn't abstract. This means working down from the current class
678
- # (self), to the first non-abstract class. Since classes don't know
679
- # their subclasses, we have to build the hierarchy between self and
680
- # the record's class.
681
- class_hierarchy = [record.class]
682
- while class_hierarchy.first != self
683
- class_hierarchy.insert(0, class_hierarchy.first.superclass)
640
+ results = finder_class.with_exclusive_scope do
641
+ connection.select_all(
642
+ construct_finder_sql(
643
+ :select => "#{connection.quote_column_name(attr_name)}",
644
+ :from => "#{finder_class.quoted_table_name}",
645
+ :conditions => [condition_sql, *condition_params]
646
+ )
647
+ )
684
648
  end
685
649
 
686
- # Now we can work our way down the tree to the first non-abstract
687
- # class (which has a database table to query from).
688
- finder_class = class_hierarchy.detect { |klass| !klass.abstract_class? }
650
+ unless results.length.zero?
651
+ found = true
689
652
 
690
- if finder_class.find(:first, :conditions => [condition_sql, *condition_params])
691
- record.errors.add(attr_name, configuration[:message])
653
+ # As MySQL/Postgres don't have case sensitive SELECT queries, we try to find duplicate
654
+ # column in ruby when case sensitive option
655
+ if configuration[:case_sensitive] && finder_class.columns_hash[attr_name.to_s].text?
656
+ found = results.any? { |a| a[attr_name.to_s] == value }
657
+ end
658
+
659
+ record.errors.add(attr_name, configuration[:message]) if found
692
660
  end
693
661
  end
694
662
  end
@@ -701,21 +669,21 @@ module ActiveRecord
701
669
  # validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :on => :create
702
670
  # end
703
671
  #
704
- # Note: use \A and \Z to match the start and end of the string, ^ and $ match the start/end of a line.
672
+ # Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
705
673
  #
706
674
  # A regular expression must be provided or else an exception will be raised.
707
675
  #
708
676
  # Configuration options:
709
- # * <tt>message</tt> - A custom error message (default is: "is invalid")
710
- # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
711
- # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
712
- # * <tt>with</tt> - The regular expression used to validate the format with (note: must be supplied!)
713
- # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
714
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
715
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
677
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid").
678
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
679
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
680
+ # * <tt>:with</tt> - The regular expression used to validate the format with (note: must be supplied!).
681
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
682
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
683
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
716
684
  # method, proc or string should return or evaluate to a true or false value.
717
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
718
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
685
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
686
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
719
687
  # method, proc or string should return or evaluate to a true or false value.
720
688
  def validates_format_of(*attr_names)
721
689
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save, :with => nil }
@@ -724,7 +692,7 @@ module ActiveRecord
724
692
  raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp)
725
693
 
726
694
  validates_each(attr_names, configuration) do |record, attr_name, value|
727
- record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with]
695
+ record.errors.add(attr_name, configuration[:message] % value) unless value.to_s =~ configuration[:with]
728
696
  end
729
697
  end
730
698
 
@@ -737,15 +705,15 @@ module ActiveRecord
737
705
  # end
738
706
  #
739
707
  # Configuration options:
740
- # * <tt>in</tt> - An enumerable object of available items
741
- # * <tt>message</tt> - Specifies a customer error message (default is: "is not included in the list")
742
- # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
743
- # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
744
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
745
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
708
+ # * <tt>:in</tt> - An enumerable object of available items.
709
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is not included in the list").
710
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
711
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
712
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
713
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
746
714
  # method, proc or string should return or evaluate to a true or false value.
747
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
748
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
715
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
716
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
749
717
  # method, proc or string should return or evaluate to a true or false value.
750
718
  def validates_inclusion_of(*attr_names)
751
719
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:inclusion], :on => :save }
@@ -769,15 +737,15 @@ module ActiveRecord
769
737
  # end
770
738
  #
771
739
  # Configuration options:
772
- # * <tt>in</tt> - An enumerable object of items that the value shouldn't be part of
773
- # * <tt>message</tt> - Specifies a customer error message (default is: "is reserved")
774
- # * <tt>allow_nil</tt> - If set to true, skips this validation if the attribute is null (default is: false)
775
- # * <tt>allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is: false)
776
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
777
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
740
+ # * <tt>:in</tt> - An enumerable object of items that the value shouldn't be part of.
741
+ # * <tt>:message</tt> - Specifies a custom error message (default is: "is reserved").
742
+ # * <tt>:allow_nil</tt> - If set to true, skips this validation if the attribute is +nil+ (default is +false+).
743
+ # * <tt>:allow_blank</tt> - If set to true, skips this validation if the attribute is blank (default is +false+).
744
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
745
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
778
746
  # method, proc or string should return or evaluate to a true or false value.
779
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
780
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
747
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
748
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
781
749
  # method, proc or string should return or evaluate to a true or false value.
782
750
  def validates_exclusion_of(*attr_names)
783
751
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:exclusion], :on => :save }
@@ -809,19 +777,19 @@ module ActiveRecord
809
777
  # validates_associated :book
810
778
  # end
811
779
  #
812
- # ...this would specify a circular dependency and cause infinite recursion.
780
+ # this would specify a circular dependency and cause infinite recursion.
813
781
  #
814
782
  # NOTE: This validation will not fail if the association hasn't been assigned. If you want to ensure that the association
815
- # is both present and guaranteed to be valid, you also need to use validates_presence_of.
783
+ # is both present and guaranteed to be valid, you also need to use +validates_presence_of+.
816
784
  #
817
785
  # Configuration options:
818
- # * <tt>message</tt> - A custom error message (default is: "is invalid")
819
- # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
820
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
821
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
786
+ # * <tt>:message</tt> - A custom error message (default is: "is invalid")
787
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
788
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
789
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
822
790
  # method, proc or string should return or evaluate to a true or false value.
823
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
824
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
791
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
792
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
825
793
  # method, proc or string should return or evaluate to a true or false value.
826
794
  def validates_associated(*attr_names)
827
795
  configuration = { :message => ActiveRecord::Errors.default_error_messages[:invalid], :on => :save }
@@ -834,30 +802,30 @@ module ActiveRecord
834
802
  end
835
803
 
836
804
  # Validates whether the value of the specified attribute is numeric by trying to convert it to
837
- # a float with Kernel.Float (if <tt>integer</tt> is false) or applying it to the regular expression
838
- # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>integer</tt> is set to true).
805
+ # a float with Kernel.Float (if <tt>only_integer</tt> is false) or applying it to the regular expression
806
+ # <tt>/\A[\+\-]?\d+\Z/</tt> (if <tt>only_integer</tt> is set to true).
839
807
  #
840
808
  # class Person < ActiveRecord::Base
841
809
  # validates_numericality_of :value, :on => :create
842
810
  # end
843
811
  #
844
812
  # Configuration options:
845
- # * <tt>message</tt> - A custom error message (default is: "is not a number")
846
- # * <tt>on</tt> Specifies when this validation is active (default is :save, other options :create, :update)
847
- # * <tt>only_integer</tt> Specifies whether the value has to be an integer, e.g. an integral value (default is false)
848
- # * <tt>allow_nil</tt> Skip validation if attribute is nil (default is false). Notice that for fixnum and float columns empty strings are converted to nil
849
- # * <tt>greater_than</tt> Specifies the value must be greater than the supplied value
850
- # * <tt>greater_than_or_equal_to</tt> Specifies the value must be greater than or equal the supplied value
851
- # * <tt>equal_to</tt> Specifies the value must be equal to the supplied value
852
- # * <tt>less_than</tt> Specifies the value must be less than the supplied value
853
- # * <tt>less_than_or_equal_to</tt> Specifies the value must be less than or equal the supplied value
854
- # * <tt>odd</tt> Specifies the value must be an odd number
855
- # * <tt>even</tt> Specifies the value must be an even number
856
- # * <tt>if</tt> - Specifies a method, proc or string to call to determine if the validation should
857
- # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). The
813
+ # * <tt>:message</tt> - A custom error message (default is: "is not a number").
814
+ # * <tt>:on</tt> - Specifies when this validation is active (default is <tt>:save</tt>, other options <tt>:create</tt>, <tt>:update</tt>).
815
+ # * <tt>:only_integer</tt> - Specifies whether the value has to be an integer, e.g. an integral value (default is +false+).
816
+ # * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is +false+). Notice that for fixnum and float columns empty strings are converted to +nil+.
817
+ # * <tt>:greater_than</tt> - Specifies the value must be greater than the supplied value.
818
+ # * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be greater than or equal the supplied value.
819
+ # * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied value.
820
+ # * <tt>:less_than</tt> - Specifies the value must be less than the supplied value.
821
+ # * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less than or equal the supplied value.
822
+ # * <tt>:odd</tt> - Specifies the value must be an odd number.
823
+ # * <tt>:even</tt> - Specifies the value must be an even number.
824
+ # * <tt>:if</tt> - Specifies a method, proc or string to call to determine if the validation should
825
+ # occur (e.g. <tt>:if => :allow_validation</tt>, or <tt>:if => Proc.new { |user| user.signup_step > 2 }</tt>). The
858
826
  # method, proc or string should return or evaluate to a true or false value.
859
- # * <tt>unless</tt> - Specifies a method, proc or string to call to determine if the validation should
860
- # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The
827
+ # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should
828
+ # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The
861
829
  # method, proc or string should return or evaluate to a true or false value.
862
830
  def validates_numericality_of(*attr_names)
863
831
  configuration = { :on => :save, :only_integer => false, :allow_nil => false }
@@ -895,7 +863,9 @@ module ActiveRecord
895
863
  when :odd, :even
896
864
  record.errors.add(attr_name, configuration[:message] || ActiveRecord::Errors.default_error_messages[option]) unless raw_value.to_i.method(ALL_NUMERICALITY_CHECKS[option])[]
897
865
  else
898
- record.errors.add(attr_name, configuration[:message] || (ActiveRecord::Errors.default_error_messages[option] % configuration[option])) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
866
+ message = configuration[:message] || ActiveRecord::Errors.default_error_messages[option]
867
+ message = message % configuration[option] if configuration[option]
868
+ record.errors.add(attr_name, message) unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]]
899
869
  end
900
870
  end
901
871
  end
@@ -903,23 +873,18 @@ module ActiveRecord
903
873
 
904
874
  # Creates an object just like Base.create but calls save! instead of save
905
875
  # so an exception is raised if the record is invalid.
906
- def create!(attributes = nil)
876
+ def create!(attributes = nil, &block)
907
877
  if attributes.is_a?(Array)
908
- attributes.collect { |attr| create!(attr) }
878
+ attributes.collect { |attr| create!(attr, &block) }
909
879
  else
910
880
  object = new(attributes)
881
+ yield(object) if block_given?
911
882
  object.save!
912
883
  object
913
884
  end
914
885
  end
915
886
 
916
-
917
887
  private
918
- def write_inheritable_set(key, methods)
919
- existing_methods = read_inheritable_attribute(key) || []
920
- write_inheritable_attribute(key, existing_methods | methods)
921
- end
922
-
923
888
  def validation_method(on)
924
889
  case on
925
890
  when :save then :validate
@@ -957,18 +922,18 @@ module ActiveRecord
957
922
  save(false)
958
923
  end
959
924
 
960
- # Runs validate and validate_on_create or validate_on_update and returns true if no errors were added otherwise false.
925
+ # Runs +validate+ and +validate_on_create+ or +validate_on_update+ and returns true if no errors were added otherwise false.
961
926
  def valid?
962
927
  errors.clear
963
928
 
964
- run_validations(:validate)
929
+ run_callbacks(:validate)
965
930
  validate
966
931
 
967
932
  if new_record?
968
- run_validations(:validate_on_create)
933
+ run_callbacks(:validate_on_create)
969
934
  validate_on_create
970
935
  else
971
- run_validations(:validate_on_update)
936
+ run_callbacks(:validate_on_update)
972
937
  validate_on_update
973
938
  end
974
939
 
@@ -981,7 +946,7 @@ module ActiveRecord
981
946
  end
982
947
 
983
948
  protected
984
- # Overwrite this method for validation checks on all saves and use Errors.add(field, msg) for invalid attributes.
949
+ # Overwrite this method for validation checks on all saves and use <tt>Errors.add(field, msg)</tt> for invalid attributes.
985
950
  def validate #:doc:
986
951
  end
987
952
 
@@ -992,36 +957,5 @@ module ActiveRecord
992
957
  # Overwrite this method for validation checks used only on updates.
993
958
  def validate_on_update # :doc:
994
959
  end
995
-
996
- private
997
- def run_validations(validation_method)
998
- validations = self.class.read_inheritable_attribute(validation_method.to_sym)
999
- if validations.nil? then return end
1000
- validations.each do |validation|
1001
- if validation.is_a?(Symbol)
1002
- self.send(validation)
1003
- elsif validation.is_a?(String)
1004
- eval(validation, binding)
1005
- elsif validation_block?(validation)
1006
- validation.call(self)
1007
- elsif validation_class?(validation, validation_method)
1008
- validation.send(validation_method, self)
1009
- else
1010
- raise(
1011
- ActiveRecordError,
1012
- "Validations need to be either a symbol, string (to be eval'ed), proc/method, or " +
1013
- "class implementing a static validation method"
1014
- )
1015
- end
1016
- end
1017
- end
1018
-
1019
- def validation_block?(validation)
1020
- validation.respond_to?("call") && (validation.arity == 1 || validation.arity == -1)
1021
- end
1022
-
1023
- def validation_class?(validation, validation_method)
1024
- validation.respond_to?(validation_method)
1025
- end
1026
960
  end
1027
961
  end