activerecord_csi 2.3.5.p6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,66 @@
1
+ require "active_support/test_case"
2
+
3
+ module ActiveRecord
4
+ class TestCase < ActiveSupport::TestCase #:nodoc:
5
+ def assert_date_from_db(expected, actual, message = nil)
6
+ # SybaseAdapter doesn't have a separate column type just for dates,
7
+ # so the time is in the string and incorrectly formatted
8
+ if current_adapter?(:SybaseAdapter)
9
+ assert_equal expected.to_s, actual.to_date.to_s, message
10
+ else
11
+ assert_equal expected.to_s, actual.to_s, message
12
+ end
13
+ end
14
+
15
+ def assert_sql(*patterns_to_match)
16
+ $queries_executed = []
17
+ yield
18
+ ensure
19
+ failed_patterns = []
20
+ patterns_to_match.each do |pattern|
21
+ failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
22
+ end
23
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found."
24
+ end
25
+
26
+ def assert_queries(num = 1)
27
+ $queries_executed = []
28
+ yield
29
+ ensure
30
+ %w{ BEGIN COMMIT }.each { |x| $queries_executed.delete(x) }
31
+ assert_equal num, $queries_executed.size, "#{$queries_executed.size} instead of #{num} queries were executed.#{$queries_executed.size == 0 ? '' : "\nQueries:\n#{$queries_executed.join("\n")}"}"
32
+ end
33
+
34
+ def assert_no_queries(&block)
35
+ assert_queries(0, &block)
36
+ end
37
+
38
+ def self.use_concurrent_connections
39
+ setup :connection_allow_concurrency_setup
40
+ teardown :connection_allow_concurrency_teardown
41
+ end
42
+
43
+ def connection_allow_concurrency_setup
44
+ @connection = ActiveRecord::Base.remove_connection
45
+ ActiveRecord::Base.establish_connection(@connection.merge({:allow_concurrency => true}))
46
+ end
47
+
48
+ def connection_allow_concurrency_teardown
49
+ ActiveRecord::Base.clear_all_connections!
50
+ ActiveRecord::Base.establish_connection(@connection)
51
+ end
52
+
53
+ def with_kcode(kcode)
54
+ if RUBY_VERSION < '1.9'
55
+ orig_kcode, $KCODE = $KCODE, kcode
56
+ begin
57
+ yield
58
+ ensure
59
+ $KCODE = orig_kcode
60
+ end
61
+ else
62
+ yield
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,71 @@
1
+ module ActiveRecord
2
+ # Active Record automatically timestamps create and update operations if the table has fields
3
+ # named created_at/created_on or updated_at/updated_on.
4
+ #
5
+ # Timestamping can be turned off by setting
6
+ # <tt>ActiveRecord::Base.record_timestamps = false</tt>
7
+ #
8
+ # Timestamps are in the local timezone by default but you can use UTC by setting
9
+ # <tt>ActiveRecord::Base.default_timezone = :utc</tt>
10
+ module Timestamp
11
+ def self.included(base) #:nodoc:
12
+ base.alias_method_chain :create, :timestamps
13
+ base.alias_method_chain :update, :timestamps
14
+
15
+ base.class_inheritable_accessor :record_timestamps, :instance_writer => false
16
+ base.record_timestamps = true
17
+ end
18
+
19
+ # Saves the record with the updated_at/on attributes set to the current time.
20
+ # If the save fails because of validation errors, an ActiveRecord::RecordInvalid exception is raised.
21
+ # If an attribute name is passed, that attribute is used for the touch instead of the updated_at/on attributes.
22
+ #
23
+ # Examples:
24
+ #
25
+ # product.touch # updates updated_at
26
+ # product.touch(:designed_at) # updates the designed_at attribute
27
+ def touch(attribute = nil)
28
+ current_time = current_time_from_proper_timezone
29
+
30
+ if attribute
31
+ write_attribute(attribute, current_time)
32
+ else
33
+ write_attribute('updated_at', current_time) if respond_to?(:updated_at)
34
+ write_attribute('updated_on', current_time) if respond_to?(:updated_on)
35
+ end
36
+
37
+ save!
38
+ end
39
+
40
+
41
+ private
42
+ def create_with_timestamps #:nodoc:
43
+ if record_timestamps
44
+ current_time = current_time_from_proper_timezone
45
+
46
+ write_attribute('created_at', current_time) if respond_to?(:created_at) && created_at.nil?
47
+ write_attribute('created_on', current_time) if respond_to?(:created_on) && created_on.nil?
48
+
49
+ write_attribute('updated_at', current_time) if respond_to?(:updated_at) && updated_at.nil?
50
+ write_attribute('updated_on', current_time) if respond_to?(:updated_on) && updated_on.nil?
51
+ end
52
+
53
+ create_without_timestamps
54
+ end
55
+
56
+ def update_with_timestamps(*args) #:nodoc:
57
+ if record_timestamps && (!partial_updates? || changed?)
58
+ current_time = current_time_from_proper_timezone
59
+
60
+ write_attribute('updated_at', current_time) if respond_to?(:updated_at)
61
+ write_attribute('updated_on', current_time) if respond_to?(:updated_on)
62
+ end
63
+
64
+ update_without_timestamps(*args)
65
+ end
66
+
67
+ def current_time_from_proper_timezone
68
+ self.class.default_timezone == :utc ? Time.now.utc : Time.now
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,235 @@
1
+ require 'thread'
2
+
3
+ module ActiveRecord
4
+ # See ActiveRecord::Transactions::ClassMethods for documentation.
5
+ module Transactions
6
+ class TransactionError < ActiveRecordError # :nodoc:
7
+ end
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+
12
+ base.class_eval do
13
+ [:destroy, :save, :save!].each do |method|
14
+ alias_method_chain method, :transactions
15
+ end
16
+ end
17
+ end
18
+
19
+ # Transactions are protective blocks where SQL statements are only permanent
20
+ # if they can all succeed as one atomic action. The classic example is a
21
+ # transfer between two accounts where you can only have a deposit if the
22
+ # withdrawal succeeded and vice versa. Transactions enforce the integrity of
23
+ # the database and guard the data against program errors or database
24
+ # break-downs. So basically you should use transaction blocks whenever you
25
+ # have a number of statements that must be executed together or not at all.
26
+ # Example:
27
+ #
28
+ # ActiveRecord::Base.transaction do
29
+ # david.withdrawal(100)
30
+ # mary.deposit(100)
31
+ # end
32
+ #
33
+ # This example will only take money from David and give to Mary if neither
34
+ # +withdrawal+ nor +deposit+ raises an exception. Exceptions will force a
35
+ # ROLLBACK that returns the database to the state before the transaction was
36
+ # begun. Be aware, though, that the objects will _not_ have their instance
37
+ # data returned to their pre-transactional state.
38
+ #
39
+ # == Different Active Record classes in a single transaction
40
+ #
41
+ # Though the transaction class method is called on some Active Record class,
42
+ # the objects within the transaction block need not all be instances of
43
+ # that class. This is because transactions are per-database connection, not
44
+ # per-model.
45
+ #
46
+ # In this example a <tt>Balance</tt> record is transactionally saved even
47
+ # though <tt>transaction</tt> is called on the <tt>Account</tt> class:
48
+ #
49
+ # Account.transaction do
50
+ # balance.save!
51
+ # account.save!
52
+ # end
53
+ #
54
+ # Note that the +transaction+ method is also available as a model instance
55
+ # method. For example, you can also do this:
56
+ #
57
+ # balance.transaction do
58
+ # balance.save!
59
+ # account.save!
60
+ # end
61
+ #
62
+ # == Transactions are not distributed across database connections
63
+ #
64
+ # A transaction acts on a single database connection. If you have
65
+ # multiple class-specific databases, the transaction will not protect
66
+ # interaction among them. One workaround is to begin a transaction
67
+ # on each class whose models you alter:
68
+ #
69
+ # Student.transaction do
70
+ # Course.transaction do
71
+ # course.enroll(student)
72
+ # student.units += course.units
73
+ # end
74
+ # end
75
+ #
76
+ # This is a poor solution, but full distributed transactions are beyond
77
+ # the scope of Active Record.
78
+ #
79
+ # == Save and destroy are automatically wrapped in a transaction
80
+ #
81
+ # Both Base#save and Base#destroy come wrapped in a transaction that ensures
82
+ # that whatever you do in validations or callbacks will happen under the
83
+ # protected cover of a transaction. So you can use validations to check for
84
+ # values that the transaction depends on or you can raise exceptions in the
85
+ # callbacks to rollback, including <tt>after_*</tt> callbacks.
86
+ #
87
+ # == Exception handling and rolling back
88
+ #
89
+ # Also have in mind that exceptions thrown within a transaction block will
90
+ # be propagated (after triggering the ROLLBACK), so you should be ready to
91
+ # catch those in your application code.
92
+ #
93
+ # One exception is the ActiveRecord::Rollback exception, which will trigger
94
+ # a ROLLBACK when raised, but not be re-raised by the transaction block.
95
+ #
96
+ # *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
97
+ # inside a transaction block. StatementInvalid exceptions indicate that an
98
+ # error occurred at the database level, for example when a unique constraint
99
+ # is violated. On some database systems, such as PostgreSQL, database errors
100
+ # inside a transaction causes the entire transaction to become unusable
101
+ # until it's restarted from the beginning. Here is an example which
102
+ # demonstrates the problem:
103
+ #
104
+ # # Suppose that we have a Number model with a unique column called 'i'.
105
+ # Number.transaction do
106
+ # Number.create(:i => 0)
107
+ # begin
108
+ # # This will raise a unique constraint error...
109
+ # Number.create(:i => 0)
110
+ # rescue ActiveRecord::StatementInvalid
111
+ # # ...which we ignore.
112
+ # end
113
+ #
114
+ # # On PostgreSQL, the transaction is now unusable. The following
115
+ # # statement will cause a PostgreSQL error, even though the unique
116
+ # # constraint is no longer violated:
117
+ # Number.create(:i => 1)
118
+ # # => "PGError: ERROR: current transaction is aborted, commands
119
+ # # ignored until end of transaction block"
120
+ # end
121
+ #
122
+ # One should restart the entire transaction if a StatementError occurred.
123
+ #
124
+ # == Nested transactions
125
+ #
126
+ # #transaction calls can be nested. By default, this makes all database
127
+ # statements in the nested transaction block become part of the parent
128
+ # transaction. For example:
129
+ #
130
+ # User.transaction do
131
+ # User.create(:username => 'Kotori')
132
+ # User.transaction do
133
+ # User.create(:username => 'Nemu')
134
+ # raise ActiveRecord::Rollback
135
+ # end
136
+ # end
137
+ #
138
+ # User.find(:all) # => empty
139
+ #
140
+ # It is also possible to requires a sub-transaction by passing
141
+ # <tt>:requires_new => true</tt>. If anything goes wrong, the
142
+ # database rolls back to the beginning of the sub-transaction
143
+ # without rolling back the parent transaction. For example:
144
+ #
145
+ # User.transaction do
146
+ # User.create(:username => 'Kotori')
147
+ # User.transaction(:requires_new => true) do
148
+ # User.create(:username => 'Nemu')
149
+ # raise ActiveRecord::Rollback
150
+ # end
151
+ # end
152
+ #
153
+ # User.find(:all) # => Returns only Kotori
154
+ #
155
+ # Most databases don't support true nested transactions. At the time of
156
+ # writing, the only database that we're aware of that supports true nested
157
+ # transactions, is MS-SQL. Because of this, Active Record emulates nested
158
+ # transactions by using savepoints. See
159
+ # http://dev.mysql.com/doc/refman/5.0/en/savepoints.html
160
+ # for more information about savepoints.
161
+ #
162
+ # === Caveats
163
+ #
164
+ # If you're on MySQL, then do not use DDL operations in nested transactions
165
+ # blocks that are emulated with savepoints. That is, do not execute statements
166
+ # like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
167
+ # releases all savepoints upon executing a DDL operation. When #transaction
168
+ # is finished and tries to release the savepoint it created earlier, a
169
+ # database error will occur because the savepoint has already been
170
+ # automatically released. The following example demonstrates the problem:
171
+ #
172
+ # Model.connection.transaction do # BEGIN
173
+ # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
174
+ # Model.connection.create_table(...) # active_record_1 now automatically released
175
+ # end # RELEASE savepoint active_record_1
176
+ # # ^^^^ BOOM! database error!
177
+ # end
178
+ module ClassMethods
179
+ # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
180
+ def transaction(options = {}, &block)
181
+ # See the ConnectionAdapters::DatabaseStatements#transaction API docs.
182
+ connection.transaction(options, &block)
183
+ end
184
+ end
185
+
186
+ # See ActiveRecord::Transactions::ClassMethods for detailed documentation.
187
+ def transaction(&block)
188
+ self.class.transaction(&block)
189
+ end
190
+
191
+ def destroy_with_transactions #:nodoc:
192
+ with_transaction_returning_status(:destroy_without_transactions)
193
+ end
194
+
195
+ def save_with_transactions(perform_validation = true) #:nodoc:
196
+ rollback_active_record_state! { with_transaction_returning_status(:save_without_transactions, perform_validation) }
197
+ end
198
+
199
+ def save_with_transactions! #:nodoc:
200
+ rollback_active_record_state! { self.class.transaction { save_without_transactions! } }
201
+ end
202
+
203
+ # Reset id and @new_record if the transaction rolls back.
204
+ def rollback_active_record_state!
205
+ id_present = has_attribute?(self.class.primary_key)
206
+ previous_id = id
207
+ previous_new_record = new_record?
208
+ yield
209
+ rescue Exception
210
+ @new_record = previous_new_record
211
+ if id_present
212
+ self.id = previous_id
213
+ else
214
+ @attributes.delete(self.class.primary_key)
215
+ @attributes_cache.delete(self.class.primary_key)
216
+ end
217
+ raise
218
+ end
219
+
220
+ # Executes +method+ within a transaction and captures its return value as a
221
+ # status flag. If the status is true the transaction is committed, otherwise
222
+ # a ROLLBACK is issued. In any case the status flag is returned.
223
+ #
224
+ # This method is available within the context of an ActiveRecord::Base
225
+ # instance.
226
+ def with_transaction_returning_status(method, *args)
227
+ status = nil
228
+ self.class.transaction do
229
+ status = send(method, *args)
230
+ raise ActiveRecord::Rollback unless status
231
+ end
232
+ status
233
+ end
234
+ end
235
+ end