activerecord 1.15.6 → 2.0.0

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

Potentially problematic release.


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

Files changed (185) hide show
  1. data/CHANGELOG +2454 -34
  2. data/README +1 -1
  3. data/RUNNING_UNIT_TESTS +3 -34
  4. data/Rakefile +98 -77
  5. data/install.rb +1 -1
  6. data/lib/active_record.rb +13 -22
  7. data/lib/active_record/aggregations.rb +38 -49
  8. data/lib/active_record/associations.rb +452 -333
  9. data/lib/active_record/associations/association_collection.rb +66 -20
  10. data/lib/active_record/associations/association_proxy.rb +9 -8
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +46 -51
  12. data/lib/active_record/associations/has_many_association.rb +21 -57
  13. data/lib/active_record/associations/has_many_through_association.rb +38 -18
  14. data/lib/active_record/associations/has_one_association.rb +30 -14
  15. data/lib/active_record/attribute_methods.rb +253 -0
  16. data/lib/active_record/base.rb +719 -494
  17. data/lib/active_record/calculations.rb +62 -63
  18. data/lib/active_record/callbacks.rb +57 -83
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +38 -9
  20. data/lib/active_record/connection_adapters/abstract/database_statements.rb +56 -15
  21. data/lib/active_record/connection_adapters/abstract/query_cache.rb +87 -0
  22. data/lib/active_record/connection_adapters/abstract/quoting.rb +23 -12
  23. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +191 -62
  24. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +37 -34
  25. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -17
  26. data/lib/active_record/connection_adapters/mysql_adapter.rb +119 -37
  27. data/lib/active_record/connection_adapters/postgresql_adapter.rb +473 -210
  28. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  29. data/lib/active_record/connection_adapters/sqlite_adapter.rb +91 -107
  30. data/lib/active_record/fixtures.rb +503 -113
  31. data/lib/active_record/locking/optimistic.rb +72 -34
  32. data/lib/active_record/migration.rb +80 -57
  33. data/lib/active_record/observer.rb +13 -10
  34. data/lib/active_record/query_cache.rb +16 -57
  35. data/lib/active_record/reflection.rb +35 -38
  36. data/lib/active_record/schema.rb +5 -5
  37. data/lib/active_record/schema_dumper.rb +35 -13
  38. data/lib/active_record/serialization.rb +98 -0
  39. data/lib/active_record/serializers/json_serializer.rb +71 -0
  40. data/lib/active_record/{xml_serialization.rb → serializers/xml_serializer.rb} +90 -83
  41. data/lib/active_record/timestamp.rb +20 -21
  42. data/lib/active_record/transactions.rb +39 -43
  43. data/lib/active_record/validations.rb +256 -107
  44. data/lib/active_record/version.rb +3 -3
  45. data/lib/activerecord.rb +1 -0
  46. data/test/aaa_create_tables_test.rb +15 -2
  47. data/test/abstract_unit.rb +24 -17
  48. data/test/active_schema_test_mysql.rb +20 -8
  49. data/test/adapter_test.rb +23 -5
  50. data/test/adapter_test_sqlserver.rb +15 -1
  51. data/test/aggregations_test.rb +16 -1
  52. data/test/all.sh +2 -2
  53. data/test/associations/ar_joins_test.rb +0 -0
  54. data/test/associations/callbacks_test.rb +51 -30
  55. data/test/associations/cascaded_eager_loading_test.rb +1 -29
  56. data/test/associations/eager_singularization_test.rb +145 -0
  57. data/test/associations/eager_test.rb +42 -6
  58. data/test/associations/extension_test.rb +6 -1
  59. data/test/associations/inner_join_association_test.rb +88 -0
  60. data/test/associations/join_model_test.rb +47 -16
  61. data/test/associations_test.rb +449 -226
  62. data/test/attribute_methods_test.rb +97 -0
  63. data/test/base_test.rb +251 -105
  64. data/test/binary_test.rb +22 -27
  65. data/test/calculations_test.rb +37 -5
  66. data/test/callbacks_test.rb +23 -0
  67. data/test/connection_test_firebird.rb +2 -2
  68. data/test/connection_test_mysql.rb +30 -0
  69. data/test/connections/native_mysql/connection.rb +3 -0
  70. data/test/connections/native_sqlite/connection.rb +5 -14
  71. data/test/connections/native_sqlite3/connection.rb +5 -14
  72. data/test/connections/native_sqlite3/in_memory_connection.rb +1 -1
  73. data/test/{copy_table_sqlite.rb → copy_table_test_sqlite.rb} +8 -3
  74. data/test/datatype_test_postgresql.rb +178 -27
  75. data/test/{empty_date_time_test.rb → date_time_test.rb} +13 -1
  76. data/test/defaults_test.rb +8 -1
  77. data/test/deprecated_finder_test.rb +7 -128
  78. data/test/finder_test.rb +192 -54
  79. data/test/fixtures/all/developers.yml +0 -0
  80. data/test/fixtures/all/people.csv +0 -0
  81. data/test/fixtures/all/tasks.yml +0 -0
  82. data/test/fixtures/author.rb +12 -5
  83. data/test/fixtures/binaries.yml +130 -435
  84. data/test/fixtures/category.rb +6 -0
  85. data/test/fixtures/company.rb +8 -1
  86. data/test/fixtures/computer.rb +1 -0
  87. data/test/fixtures/contact.rb +16 -0
  88. data/test/fixtures/customer.rb +2 -2
  89. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  90. data/test/fixtures/db_definitions/db2.sql +4 -0
  91. data/test/fixtures/db_definitions/firebird.drop.sql +3 -1
  92. data/test/fixtures/db_definitions/firebird.sql +6 -0
  93. data/test/fixtures/db_definitions/frontbase.drop.sql +1 -0
  94. data/test/fixtures/db_definitions/frontbase.sql +5 -0
  95. data/test/fixtures/db_definitions/openbase.sql +41 -25
  96. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  97. data/test/fixtures/db_definitions/oracle.sql +5 -0
  98. data/test/fixtures/db_definitions/postgresql.drop.sql +7 -0
  99. data/test/fixtures/db_definitions/postgresql.sql +87 -58
  100. data/test/fixtures/db_definitions/postgresql2.sql +1 -2
  101. data/test/fixtures/db_definitions/schema.rb +280 -0
  102. data/test/fixtures/db_definitions/schema2.rb +11 -0
  103. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  104. data/test/fixtures/db_definitions/sqlite.sql +4 -0
  105. data/test/fixtures/db_definitions/sybase.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/sybase.sql +4 -0
  107. data/test/fixtures/developer.rb +10 -0
  108. data/test/fixtures/example.log +1 -0
  109. data/test/fixtures/flowers.jpg +0 -0
  110. data/test/fixtures/item.rb +7 -0
  111. data/test/fixtures/items.yml +4 -0
  112. data/test/fixtures/joke.rb +0 -3
  113. data/test/fixtures/matey.rb +4 -0
  114. data/test/fixtures/mateys.yml +4 -0
  115. data/test/fixtures/minimalistic.rb +2 -0
  116. data/test/fixtures/minimalistics.yml +2 -0
  117. data/test/fixtures/mixins.yml +2 -100
  118. data/test/fixtures/parrot.rb +13 -0
  119. data/test/fixtures/parrots.yml +27 -0
  120. data/test/fixtures/parrots_pirates.yml +7 -0
  121. data/test/fixtures/pirate.rb +5 -0
  122. data/test/fixtures/pirates.yml +9 -0
  123. data/test/fixtures/post.rb +1 -0
  124. data/test/fixtures/project.rb +3 -2
  125. data/test/fixtures/reserved_words/distinct.yml +5 -0
  126. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  127. data/test/fixtures/reserved_words/group.yml +14 -0
  128. data/test/fixtures/reserved_words/select.yml +8 -0
  129. data/test/fixtures/reserved_words/values.yml +7 -0
  130. data/test/fixtures/ship.rb +3 -0
  131. data/test/fixtures/ships.yml +5 -0
  132. data/test/fixtures/tagging.rb +4 -0
  133. data/test/fixtures/taggings.yml +8 -1
  134. data/test/fixtures/topic.rb +13 -1
  135. data/test/fixtures/treasure.rb +4 -0
  136. data/test/fixtures/treasures.yml +10 -0
  137. data/test/fixtures_test.rb +205 -24
  138. data/test/inheritance_test.rb +7 -1
  139. data/test/json_serialization_test.rb +180 -0
  140. data/test/lifecycle_test.rb +1 -1
  141. data/test/locking_test.rb +85 -2
  142. data/test/migration_test.rb +206 -40
  143. data/test/mixin_test.rb +13 -515
  144. data/test/pk_test.rb +3 -6
  145. data/test/query_cache_test.rb +104 -0
  146. data/test/reflection_test.rb +16 -0
  147. data/test/reserved_word_test_mysql.rb +177 -0
  148. data/test/schema_dumper_test.rb +38 -3
  149. data/test/serialization_test.rb +47 -0
  150. data/test/transactions_test.rb +74 -23
  151. data/test/unconnected_test.rb +1 -1
  152. data/test/validations_test.rb +322 -32
  153. data/test/xml_serialization_test.rb +121 -44
  154. metadata +48 -41
  155. data/examples/associations.rb +0 -87
  156. data/examples/shared_setup.rb +0 -15
  157. data/examples/validation.rb +0 -85
  158. data/lib/active_record/acts/list.rb +0 -256
  159. data/lib/active_record/acts/nested_set.rb +0 -211
  160. data/lib/active_record/acts/tree.rb +0 -96
  161. data/lib/active_record/connection_adapters/db2_adapter.rb +0 -228
  162. data/lib/active_record/connection_adapters/firebird_adapter.rb +0 -728
  163. data/lib/active_record/connection_adapters/frontbase_adapter.rb +0 -861
  164. data/lib/active_record/connection_adapters/openbase_adapter.rb +0 -350
  165. data/lib/active_record/connection_adapters/oracle_adapter.rb +0 -690
  166. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +0 -591
  167. data/lib/active_record/connection_adapters/sybase_adapter.rb +0 -662
  168. data/lib/active_record/deprecated_associations.rb +0 -104
  169. data/lib/active_record/deprecated_finders.rb +0 -44
  170. data/lib/active_record/vendor/simple.rb +0 -693
  171. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  172. data/lib/active_record/wrappings.rb +0 -58
  173. data/test/connections/native_sqlserver/connection.rb +0 -23
  174. data/test/connections/native_sqlserver_odbc/connection.rb +0 -25
  175. data/test/deprecated_associations_test.rb +0 -396
  176. data/test/fixtures/db_definitions/mysql.drop.sql +0 -32
  177. data/test/fixtures/db_definitions/mysql.sql +0 -234
  178. data/test/fixtures/db_definitions/mysql2.drop.sql +0 -2
  179. data/test/fixtures/db_definitions/mysql2.sql +0 -5
  180. data/test/fixtures/db_definitions/sqlserver.drop.sql +0 -34
  181. data/test/fixtures/db_definitions/sqlserver.sql +0 -243
  182. data/test/fixtures/db_definitions/sqlserver2.drop.sql +0 -2
  183. data/test/fixtures/db_definitions/sqlserver2.sql +0 -5
  184. data/test/fixtures/mixin.rb +0 -63
  185. data/test/mixin_nested_set_test.rb +0 -196
@@ -1,104 +0,0 @@
1
- module ActiveRecord
2
- module Associations # :nodoc:
3
- module ClassMethods
4
- def deprecated_collection_count_method(collection_name)# :nodoc:
5
- module_eval <<-"end_eval", __FILE__, __LINE__
6
- def #{collection_name}_count(force_reload = false)
7
- unless has_attribute?(:#{collection_name}_count)
8
- ActiveSupport::Deprecation.warn :#{collection_name}_count
9
- end
10
- #{collection_name}.reload if force_reload
11
- #{collection_name}.size
12
- end
13
- end_eval
14
- end
15
-
16
- def deprecated_add_association_relation(association_name)# :nodoc:
17
- module_eval <<-"end_eval", __FILE__, __LINE__
18
- def add_#{association_name}(*items)
19
- #{association_name}.concat(items)
20
- end
21
- deprecate :add_#{association_name} => "use #{association_name}.concat instead"
22
- end_eval
23
- end
24
-
25
- def deprecated_remove_association_relation(association_name)# :nodoc:
26
- module_eval <<-"end_eval", __FILE__, __LINE__
27
- def remove_#{association_name}(*items)
28
- #{association_name}.delete(items)
29
- end
30
- deprecate :remove_#{association_name} => "use #{association_name}.delete instead"
31
- end_eval
32
- end
33
-
34
- def deprecated_has_collection_method(collection_name)# :nodoc:
35
- module_eval <<-"end_eval", __FILE__, __LINE__
36
- def has_#{collection_name}?(force_reload = false)
37
- !#{collection_name}(force_reload).empty?
38
- end
39
- deprecate :has_#{collection_name}? => "use !#{collection_name}.empty? instead"
40
- end_eval
41
- end
42
-
43
- def deprecated_find_in_collection_method(collection_name)# :nodoc:
44
- module_eval <<-"end_eval", __FILE__, __LINE__
45
- def find_in_#{collection_name}(association_id)
46
- #{collection_name}.find(association_id)
47
- end
48
- deprecate :find_in_#{collection_name} => "use #{collection_name}.find instead"
49
- end_eval
50
- end
51
-
52
- def deprecated_find_all_in_collection_method(collection_name)# :nodoc:
53
- module_eval <<-"end_eval", __FILE__, __LINE__
54
- def find_all_in_#{collection_name}(runtime_conditions = nil, orderings = nil, limit = nil, joins = nil)
55
- ActiveSupport::Deprecation.silence do
56
- #{collection_name}.find_all(runtime_conditions, orderings, limit, joins)
57
- end
58
- end
59
- deprecate :find_all_in_#{collection_name} => "use #{collection_name}.find(:all, ...) instead"
60
- end_eval
61
- end
62
-
63
- def deprecated_collection_create_method(collection_name)# :nodoc:
64
- module_eval <<-"end_eval", __FILE__, __LINE__
65
- def create_in_#{collection_name}(attributes = {})
66
- #{collection_name}.create(attributes)
67
- end
68
- deprecate :create_in_#{collection_name} => "use #{collection_name}.create instead"
69
- end_eval
70
- end
71
-
72
- def deprecated_collection_build_method(collection_name)# :nodoc:
73
- module_eval <<-"end_eval", __FILE__, __LINE__
74
- def build_to_#{collection_name}(attributes = {})
75
- #{collection_name}.build(attributes)
76
- end
77
- deprecate :build_to_#{collection_name} => "use #{collection_name}.build instead"
78
- end_eval
79
- end
80
-
81
- def deprecated_association_comparison_method(association_name, association_class_name) # :nodoc:
82
- module_eval <<-"end_eval", __FILE__, __LINE__
83
- def #{association_name}?(comparison_object, force_reload = false)
84
- if comparison_object.kind_of?(#{association_class_name})
85
- #{association_name}(force_reload) == comparison_object
86
- else
87
- raise "Comparison object is a #{association_class_name}, should have been \#{comparison_object.class.name}"
88
- end
89
- end
90
- deprecate :#{association_name}? => :==
91
- end_eval
92
- end
93
-
94
- def deprecated_has_association_method(association_name) # :nodoc:
95
- module_eval <<-"end_eval", __FILE__, __LINE__
96
- def has_#{association_name}?(force_reload = false)
97
- !#{association_name}(force_reload).nil?
98
- end
99
- deprecate :has_#{association_name}? => "use !#{association_name} insead"
100
- end_eval
101
- end
102
- end
103
- end
104
- end
@@ -1,44 +0,0 @@
1
- module ActiveRecord
2
- class Base
3
- class << self
4
- # DEPRECATION NOTICE: This method is deprecated in favor of find with the :conditions option.
5
- #
6
- # Works like find, but the record matching +id+ must also meet the +conditions+.
7
- # +RecordNotFound+ is raised if no record can be found matching the +id+ or meeting the condition.
8
- # Example:
9
- # Person.find_on_conditions 5, "first_name LIKE '%dav%' AND last_name = 'heinemeier'"
10
- def find_on_conditions(ids, conditions) # :nodoc:
11
- find(ids, :conditions => conditions)
12
- end
13
- deprecate :find_on_conditions => "use find(ids, :conditions => conditions)"
14
-
15
- # DEPRECATION NOTICE: This method is deprecated in favor of find(:first, options).
16
- #
17
- # Returns the object for the first record responding to the conditions in +conditions+,
18
- # such as "group = 'master'". If more than one record is returned from the query, it's the first that'll
19
- # be used to create the object. In such cases, it might be beneficial to also specify
20
- # +orderings+, like "income DESC, name", to control exactly which record is to be used. Example:
21
- # Employee.find_first "income > 50000", "income DESC, name"
22
- def find_first(conditions = nil, orderings = nil, joins = nil) # :nodoc:
23
- find(:first, :conditions => conditions, :order => orderings, :joins => joins)
24
- end
25
- deprecate :find_first => "use find(:first, ...)"
26
-
27
- # DEPRECATION NOTICE: This method is deprecated in favor of find(:all, options).
28
- #
29
- # Returns an array of all the objects that could be instantiated from the associated
30
- # table in the database. The +conditions+ can be used to narrow the selection of objects (WHERE-part),
31
- # such as by "color = 'red'", and arrangement of the selection can be done through +orderings+ (ORDER BY-part),
32
- # such as by "last_name, first_name DESC". A maximum of returned objects and their offset can be specified in
33
- # +limit+ with either just a single integer as the limit or as an array with the first element as the limit,
34
- # the second as the offset. Examples:
35
- # Project.find_all "category = 'accounts'", "last_accessed DESC", 15
36
- # Project.find_all ["category = ?", category_name], "created ASC", [15, 20]
37
- def find_all(conditions = nil, orderings = nil, limit = nil, joins = nil) # :nodoc:
38
- limit, offset = limit.is_a?(Array) ? limit : [ limit, nil ]
39
- find(:all, :conditions => conditions, :order => orderings, :joins => joins, :limit => limit, :offset => offset)
40
- end
41
- deprecate :find_all => "use find(:all, ...)"
42
- end
43
- end
44
- end
@@ -1,693 +0,0 @@
1
- # :title: Transaction::Simple -- Active Object Transaction Support for Ruby
2
- # :main: Transaction::Simple
3
- #
4
- # == Licence
5
- #
6
- # Permission is hereby granted, free of charge, to any person obtaining a
7
- # copy of this software and associated documentation files (the "Software"),
8
- # to deal in the Software without restriction, including without limitation
9
- # the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
- # and/or sell copies of the Software, and to permit persons to whom the
11
- # Software is furnished to do so, subject to the following conditions:
12
- #
13
- # The above copyright notice and this permission notice shall be included in
14
- # all copies or substantial portions of the Software.
15
- #
16
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
- # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
- # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
- # DEALINGS IN THE SOFTWARE.
23
- #--
24
- # Transaction::Simple
25
- # Simple object transaction support for Ruby
26
- # Version 1.3.0
27
- #
28
- # Copyright (c) 2003 - 2005 Austin Ziegler
29
- #
30
- # $Id: simple.rb,v 1.5 2005/05/05 16:16:49 austin Exp $
31
- #++
32
- # The "Transaction" namespace can be used for additional transaction
33
- # support objects and modules.
34
- module Transaction
35
- # A standard exception for transaction errors.
36
- class TransactionError < StandardError; end
37
- # The TransactionAborted exception is used to indicate when a
38
- # transaction has been aborted in the block form.
39
- class TransactionAborted < Exception; end
40
- # The TransactionCommitted exception is used to indicate when a
41
- # transaction has been committed in the block form.
42
- class TransactionCommitted < Exception; end
43
-
44
- te = "Transaction Error: %s"
45
-
46
- Messages = {
47
- :bad_debug_object =>
48
- te % "the transaction debug object must respond to #<<.",
49
- :unique_names =>
50
- te % "named transactions must be unique.",
51
- :no_transaction_open =>
52
- te % "no transaction open.",
53
- :cannot_rewind_no_transaction =>
54
- te % "cannot rewind; there is no current transaction.",
55
- :cannot_rewind_named_transaction =>
56
- te % "cannot rewind to transaction %s because it does not exist.",
57
- :cannot_rewind_transaction_before_block =>
58
- te % "cannot rewind a transaction started before the execution block.",
59
- :cannot_abort_no_transaction =>
60
- te % "cannot abort; there is no current transaction.",
61
- :cannot_abort_transaction_before_block =>
62
- te % "cannot abort a transaction started before the execution block.",
63
- :cannot_abort_named_transaction =>
64
- te % "cannot abort nonexistant transaction %s.",
65
- :cannot_commit_no_transaction =>
66
- te % "cannot commit; there is no current transaction.",
67
- :cannot_commit_transaction_before_block =>
68
- te % "cannot commit a transaction started before the execution block.",
69
- :cannot_commit_named_transaction =>
70
- te % "cannot commit nonexistant transaction %s.",
71
- :cannot_start_empty_block_transaction =>
72
- te % "cannot start a block transaction with no objects.",
73
- :cannot_obtain_transaction_lock =>
74
- te % "cannot obtain transaction lock for #%s.",
75
- }
76
-
77
- # = Transaction::Simple for Ruby
78
- # Simple object transaction support for Ruby
79
- #
80
- # == Introduction
81
- # Transaction::Simple provides a generic way to add active transaction
82
- # support to objects. The transaction methods added by this module will
83
- # work with most objects, excluding those that cannot be
84
- # <i>Marshal</i>ed (bindings, procedure objects, IO instances, or
85
- # singleton objects).
86
- #
87
- # The transactions supported by Transaction::Simple are not backed
88
- # transactions; they are not associated with any sort of data store.
89
- # They are "live" transactions occurring in memory and in the object
90
- # itself. This is to allow "test" changes to be made to an object
91
- # before making the changes permanent.
92
- #
93
- # Transaction::Simple can handle an "infinite" number of transaction
94
- # levels (limited only by memory). If I open two transactions, commit
95
- # the second, but abort the first, the object will revert to the
96
- # original version.
97
- #
98
- # Transaction::Simple supports "named" transactions, so that multiple
99
- # levels of transactions can be committed, aborted, or rewound by
100
- # referring to the appropriate name of the transaction. Names may be any
101
- # object *except* +nil+. As with Hash keys, String names will be
102
- # duplicated and frozen before using.
103
- #
104
- # Copyright:: Copyright � 2003 - 2005 by Austin Ziegler
105
- # Version:: 1.3.0
106
- # Licence:: MIT-Style
107
- #
108
- # Thanks to David Black for help with the initial concept that led to
109
- # this library.
110
- #
111
- # == Usage
112
- # include 'transaction/simple'
113
- #
114
- # v = "Hello, you." # -> "Hello, you."
115
- # v.extend(Transaction::Simple) # -> "Hello, you."
116
- #
117
- # v.start_transaction # -> ... (a Marshal string)
118
- # v.transaction_open? # -> true
119
- # v.gsub!(/you/, "world") # -> "Hello, world."
120
- #
121
- # v.rewind_transaction # -> "Hello, you."
122
- # v.transaction_open? # -> true
123
- #
124
- # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
125
- # v.abort_transaction # -> "Hello, you."
126
- # v.transaction_open? # -> false
127
- #
128
- # v.start_transaction # -> ... (a Marshal string)
129
- # v.start_transaction # -> ... (a Marshal string)
130
- #
131
- # v.transaction_open? # -> true
132
- # v.gsub!(/you/, "HAL") # -> "Hello, HAL."
133
- #
134
- # v.commit_transaction # -> "Hello, HAL."
135
- # v.transaction_open? # -> true
136
- # v.abort_transaction # -> "Hello, you."
137
- # v.transaction_open? # -> false
138
- #
139
- # == Named Transaction Usage
140
- # v = "Hello, you." # -> "Hello, you."
141
- # v.extend(Transaction::Simple) # -> "Hello, you."
142
- #
143
- # v.start_transaction(:first) # -> ... (a Marshal string)
144
- # v.transaction_open? # -> true
145
- # v.transaction_open?(:first) # -> true
146
- # v.transaction_open?(:second) # -> false
147
- # v.gsub!(/you/, "world") # -> "Hello, world."
148
- #
149
- # v.start_transaction(:second) # -> ... (a Marshal string)
150
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
151
- # v.rewind_transaction(:first) # -> "Hello, you."
152
- # v.transaction_open? # -> true
153
- # v.transaction_open?(:first) # -> true
154
- # v.transaction_open?(:second) # -> false
155
- #
156
- # v.gsub!(/you/, "world") # -> "Hello, world."
157
- # v.start_transaction(:second) # -> ... (a Marshal string)
158
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
159
- # v.transaction_name # -> :second
160
- # v.abort_transaction(:first) # -> "Hello, you."
161
- # v.transaction_open? # -> false
162
- #
163
- # v.start_transaction(:first) # -> ... (a Marshal string)
164
- # v.gsub!(/you/, "world") # -> "Hello, world."
165
- # v.start_transaction(:second) # -> ... (a Marshal string)
166
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
167
- #
168
- # v.commit_transaction(:first) # -> "Hello, HAL."
169
- # v.transaction_open? # -> false
170
- #
171
- # == Block Usage
172
- # v = "Hello, you." # -> "Hello, you."
173
- # Transaction::Simple.start(v) do |tv|
174
- # # v has been extended with Transaction::Simple and an unnamed
175
- # # transaction has been started.
176
- # tv.transaction_open? # -> true
177
- # tv.gsub!(/you/, "world") # -> "Hello, world."
178
- #
179
- # tv.rewind_transaction # -> "Hello, you."
180
- # tv.transaction_open? # -> true
181
- #
182
- # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
183
- # # The following breaks out of the transaction block after
184
- # # aborting the transaction.
185
- # tv.abort_transaction # -> "Hello, you."
186
- # end
187
- # # v still has Transaction::Simple applied from here on out.
188
- # v.transaction_open? # -> false
189
- #
190
- # Transaction::Simple.start(v) do |tv|
191
- # tv.start_transaction # -> ... (a Marshal string)
192
- #
193
- # tv.transaction_open? # -> true
194
- # tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
195
- #
196
- # # If #commit_transaction were called without having started a
197
- # # second transaction, then it would break out of the transaction
198
- # # block after committing the transaction.
199
- # tv.commit_transaction # -> "Hello, HAL."
200
- # tv.transaction_open? # -> true
201
- # tv.abort_transaction # -> "Hello, you."
202
- # end
203
- # v.transaction_open? # -> false
204
- #
205
- # == Named Transaction Usage
206
- # v = "Hello, you." # -> "Hello, you."
207
- # v.extend(Transaction::Simple) # -> "Hello, you."
208
- #
209
- # v.start_transaction(:first) # -> ... (a Marshal string)
210
- # v.transaction_open? # -> true
211
- # v.transaction_open?(:first) # -> true
212
- # v.transaction_open?(:second) # -> false
213
- # v.gsub!(/you/, "world") # -> "Hello, world."
214
- #
215
- # v.start_transaction(:second) # -> ... (a Marshal string)
216
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
217
- # v.rewind_transaction(:first) # -> "Hello, you."
218
- # v.transaction_open? # -> true
219
- # v.transaction_open?(:first) # -> true
220
- # v.transaction_open?(:second) # -> false
221
- #
222
- # v.gsub!(/you/, "world") # -> "Hello, world."
223
- # v.start_transaction(:second) # -> ... (a Marshal string)
224
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
225
- # v.transaction_name # -> :second
226
- # v.abort_transaction(:first) # -> "Hello, you."
227
- # v.transaction_open? # -> false
228
- #
229
- # v.start_transaction(:first) # -> ... (a Marshal string)
230
- # v.gsub!(/you/, "world") # -> "Hello, world."
231
- # v.start_transaction(:second) # -> ... (a Marshal string)
232
- # v.gsub!(/world/, "HAL") # -> "Hello, HAL."
233
- #
234
- # v.commit_transaction(:first) # -> "Hello, HAL."
235
- # v.transaction_open? # -> false
236
- #
237
- # == Thread Safety
238
- # Threadsafe version of Transaction::Simple and
239
- # Transaction::Simple::Group exist; these are loaded from
240
- # 'transaction/simple/threadsafe' and
241
- # 'transaction/simple/threadsafe/group', respectively, and are
242
- # represented in Ruby code as Transaction::Simple::ThreadSafe and
243
- # Transaction::Simple::ThreadSafe::Group, respectively.
244
- #
245
- # == Contraindications
246
- # While Transaction::Simple is very useful, it has some severe
247
- # limitations that must be understood. Transaction::Simple:
248
- #
249
- # * uses Marshal. Thus, any object which cannot be <i>Marshal</i>ed
250
- # cannot use Transaction::Simple. In my experience, this affects
251
- # singleton objects more often than any other object. It may be that
252
- # Ruby 2.0 will solve this problem.
253
- # * does not manage resources. Resources external to the object and its
254
- # instance variables are not managed at all. However, all instance
255
- # variables and objects "belonging" to those instance variables are
256
- # managed. If there are object reference counts to be handled,
257
- # Transaction::Simple will probably cause problems.
258
- # * is not inherently thread-safe. In the ACID ("atomic, consistent,
259
- # isolated, durable") test, Transaction::Simple provides CD, but it is
260
- # up to the user of Transaction::Simple to provide isolation and
261
- # atomicity. Transactions should be considered "critical sections" in
262
- # multi-threaded applications. If thread safety and atomicity is
263
- # absolutely required, use Transaction::Simple::ThreadSafe, which uses
264
- # a Mutex object to synchronize the accesses on the object during the
265
- # transaction operations.
266
- # * does not necessarily maintain Object#__id__ values on rewind or
267
- # abort. This may change for future versions that will be Ruby 1.8 or
268
- # better *only*. Certain objects that support #replace will maintain
269
- # Object#__id__.
270
- # * Can be a memory hog if you use many levels of transactions on many
271
- # objects.
272
- #
273
- module Simple
274
- TRANSACTION_SIMPLE_VERSION = '1.3.0'
275
-
276
- # Sets the Transaction::Simple debug object. It must respond to #<<.
277
- # Sets the transaction debug object. Debugging will be performed
278
- # automatically if there's a debug object. The generic transaction
279
- # error class.
280
- def self.debug_io=(io)
281
- if io.nil?
282
- @tdi = nil
283
- @debugging = false
284
- else
285
- unless io.respond_to?(:<<)
286
- raise TransactionError, Messages[:bad_debug_object]
287
- end
288
- @tdi = io
289
- @debugging = true
290
- end
291
- end
292
-
293
- # Returns +true+ if we are debugging.
294
- def self.debugging?
295
- @debugging
296
- end
297
-
298
- # Returns the Transaction::Simple debug object. It must respond to
299
- # #<<.
300
- def self.debug_io
301
- @tdi ||= ""
302
- @tdi
303
- end
304
-
305
- # If +name+ is +nil+ (default), then returns +true+ if there is
306
- # currently a transaction open.
307
- #
308
- # If +name+ is specified, then returns +true+ if there is currently a
309
- # transaction that responds to +name+ open.
310
- def transaction_open?(name = nil)
311
- if name.nil?
312
- if Transaction::Simple.debugging?
313
- Transaction::Simple.debug_io << "Transaction " <<
314
- "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
315
- end
316
- return (not @__transaction_checkpoint__.nil?)
317
- else
318
- if Transaction::Simple.debugging?
319
- Transaction::Simple.debug_io << "Transaction(#{name.inspect}) " <<
320
- "[#{(@__transaction_checkpoint__.nil?) ? 'closed' : 'open'}]\n"
321
- end
322
- return ((not @__transaction_checkpoint__.nil?) and @__transaction_names__.include?(name))
323
- end
324
- end
325
-
326
- # Returns the current name of the transaction. Transactions not
327
- # explicitly named are named +nil+.
328
- def transaction_name
329
- if @__transaction_checkpoint__.nil?
330
- raise TransactionError, Messages[:no_transaction_open]
331
- end
332
- if Transaction::Simple.debugging?
333
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
334
- "Transaction Name: #{@__transaction_names__[-1].inspect}\n"
335
- end
336
- if @__transaction_names__[-1].kind_of?(String)
337
- @__transaction_names__[-1].dup
338
- else
339
- @__transaction_names__[-1]
340
- end
341
- end
342
-
343
- # Starts a transaction. Stores the current object state. If a
344
- # transaction name is specified, the transaction will be named.
345
- # Transaction names must be unique. Transaction names of +nil+ will be
346
- # treated as unnamed transactions.
347
- def start_transaction(name = nil)
348
- @__transaction_level__ ||= 0
349
- @__transaction_names__ ||= []
350
-
351
- if name.nil?
352
- @__transaction_names__ << nil
353
- ss = "" if Transaction::Simple.debugging?
354
- else
355
- if @__transaction_names__.include?(name)
356
- raise TransactionError, Messages[:unique_names]
357
- end
358
- name = name.dup.freeze if name.kind_of?(String)
359
- @__transaction_names__ << name
360
- ss = "(#{name.inspect})" if Transaction::Simple.debugging?
361
- end
362
-
363
- @__transaction_level__ += 1
364
-
365
- if Transaction::Simple.debugging?
366
- Transaction::Simple.debug_io << "#{'>' * @__transaction_level__} " <<
367
- "Start Transaction#{ss}\n"
368
- end
369
-
370
- @__transaction_checkpoint__ = Marshal.dump(self)
371
- end
372
-
373
- # Rewinds the transaction. If +name+ is specified, then the
374
- # intervening transactions will be aborted and the named transaction
375
- # will be rewound. Otherwise, only the current transaction is rewound.
376
- def rewind_transaction(name = nil)
377
- if @__transaction_checkpoint__.nil?
378
- raise TransactionError, Messages[:cannot_rewind_no_transaction]
379
- end
380
-
381
- # Check to see if we are trying to rewind a transaction that is
382
- # outside of the current transaction block.
383
- if @__transaction_block__ and name
384
- nix = @__transaction_names__.index(name) + 1
385
- if nix < @__transaction_block__
386
- raise TransactionError, Messages[:cannot_rewind_transaction_before_block]
387
- end
388
- end
389
-
390
- if name.nil?
391
- __rewind_this_transaction
392
- ss = "" if Transaction::Simple.debugging?
393
- else
394
- unless @__transaction_names__.include?(name)
395
- raise TransactionError, Messages[:cannot_rewind_named_transaction] % name.inspect
396
- end
397
- ss = "(#{name})" if Transaction::Simple.debugging?
398
-
399
- while @__transaction_names__[-1] != name
400
- @__transaction_checkpoint__ = __rewind_this_transaction
401
- if Transaction::Simple.debugging?
402
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
403
- "Rewind Transaction#{ss}\n"
404
- end
405
- @__transaction_level__ -= 1
406
- @__transaction_names__.pop
407
- end
408
- __rewind_this_transaction
409
- end
410
- if Transaction::Simple.debugging?
411
- Transaction::Simple.debug_io << "#{'|' * @__transaction_level__} " <<
412
- "Rewind Transaction#{ss}\n"
413
- end
414
- self
415
- end
416
-
417
- # Aborts the transaction. Resets the object state to what it was
418
- # before the transaction was started and closes the transaction. If
419
- # +name+ is specified, then the intervening transactions and the named
420
- # transaction will be aborted. Otherwise, only the current transaction
421
- # is aborted.
422
- #
423
- # If the current or named transaction has been started by a block
424
- # (Transaction::Simple.start), then the execution of the block will be
425
- # halted with +break+ +self+.
426
- def abort_transaction(name = nil)
427
- if @__transaction_checkpoint__.nil?
428
- raise TransactionError, Messages[:cannot_abort_no_transaction]
429
- end
430
-
431
- # Check to see if we are trying to abort a transaction that is
432
- # outside of the current transaction block. Otherwise, raise
433
- # TransactionAborted if they are the same.
434
- if @__transaction_block__ and name
435
- nix = @__transaction_names__.index(name) + 1
436
- if nix < @__transaction_block__
437
- raise TransactionError, Messages[:cannot_abort_transaction_before_block]
438
- end
439
-
440
- raise TransactionAborted if @__transaction_block__ == nix
441
- end
442
-
443
- raise TransactionAborted if @__transaction_block__ == @__transaction_level__
444
-
445
- if name.nil?
446
- __abort_transaction(name)
447
- else
448
- unless @__transaction_names__.include?(name)
449
- raise TransactionError, Messages[:cannot_abort_named_transaction] % name.inspect
450
- end
451
- __abort_transaction(name) while @__transaction_names__.include?(name)
452
- end
453
- self
454
- end
455
-
456
- # If +name+ is +nil+ (default), the current transaction level is
457
- # closed out and the changes are committed.
458
- #
459
- # If +name+ is specified and +name+ is in the list of named
460
- # transactions, then all transactions are closed and committed until
461
- # the named transaction is reached.
462
- def commit_transaction(name = nil)
463
- if @__transaction_checkpoint__.nil?
464
- raise TransactionError, Messages[:cannot_commit_no_transaction]
465
- end
466
- @__transaction_block__ ||= nil
467
-
468
- # Check to see if we are trying to commit a transaction that is
469
- # outside of the current transaction block. Otherwise, raise
470
- # TransactionCommitted if they are the same.
471
- if @__transaction_block__ and name
472
- nix = @__transaction_names__.index(name) + 1
473
- if nix < @__transaction_block__
474
- raise TransactionError, Messages[:cannot_commit_transaction_before_block]
475
- end
476
-
477
- raise TransactionCommitted if @__transaction_block__ == nix
478
- end
479
-
480
- raise TransactionCommitted if @__transaction_block__ == @__transaction_level__
481
-
482
- if name.nil?
483
- ss = "" if Transaction::Simple.debugging?
484
- __commit_transaction
485
- if Transaction::Simple.debugging?
486
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
487
- "Commit Transaction#{ss}\n"
488
- end
489
- else
490
- unless @__transaction_names__.include?(name)
491
- raise TransactionError, Messages[:cannot_commit_named_transaction] % name.inspect
492
- end
493
- ss = "(#{name})" if Transaction::Simple.debugging?
494
-
495
- while @__transaction_names__[-1] != name
496
- if Transaction::Simple.debugging?
497
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
498
- "Commit Transaction#{ss}\n"
499
- end
500
- __commit_transaction
501
- end
502
- if Transaction::Simple.debugging?
503
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
504
- "Commit Transaction#{ss}\n"
505
- end
506
- __commit_transaction
507
- end
508
-
509
- self
510
- end
511
-
512
- # Alternative method for calling the transaction methods. An optional
513
- # name can be specified for named transaction support.
514
- #
515
- # #transaction(:start):: #start_transaction
516
- # #transaction(:rewind):: #rewind_transaction
517
- # #transaction(:abort):: #abort_transaction
518
- # #transaction(:commit):: #commit_transaction
519
- # #transaction(:name):: #transaction_name
520
- # #transaction:: #transaction_open?
521
- def transaction(action = nil, name = nil)
522
- case action
523
- when :start
524
- start_transaction(name)
525
- when :rewind
526
- rewind_transaction(name)
527
- when :abort
528
- abort_transaction(name)
529
- when :commit
530
- commit_transaction(name)
531
- when :name
532
- transaction_name
533
- when nil
534
- transaction_open?(name)
535
- end
536
- end
537
-
538
- # Allows specific variables to be excluded from transaction support.
539
- # Must be done after extending the object but before starting the
540
- # first transaction on the object.
541
- #
542
- # vv.transaction_exclusions << "@io"
543
- def transaction_exclusions
544
- @transaction_exclusions ||= []
545
- end
546
-
547
- class << self
548
- def __common_start(name, vars, &block)
549
- if vars.empty?
550
- raise TransactionError, Messages[:cannot_start_empty_block_transaction]
551
- end
552
-
553
- if block
554
- begin
555
- vlevel = {}
556
-
557
- vars.each do |vv|
558
- vv.extend(Transaction::Simple)
559
- vv.start_transaction(name)
560
- vlevel[vv.__id__] = vv.instance_variable_get(:@__transaction_level__)
561
- vv.instance_variable_set(:@__transaction_block__, vlevel[vv.__id__])
562
- end
563
-
564
- yield(*vars)
565
- rescue TransactionAborted
566
- vars.each do |vv|
567
- if name.nil? and vv.transaction_open?
568
- loop do
569
- tlevel = vv.instance_variable_get(:@__transaction_level__) || -1
570
- vv.instance_variable_set(:@__transaction_block__, -1)
571
- break if tlevel < vlevel[vv.__id__]
572
- vv.abort_transaction if vv.transaction_open?
573
- end
574
- elsif vv.transaction_open?(name)
575
- vv.instance_variable_set(:@__transaction_block__, -1)
576
- vv.abort_transaction(name)
577
- end
578
- end
579
- rescue TransactionCommitted
580
- nil
581
- ensure
582
- vars.each do |vv|
583
- if name.nil? and vv.transaction_open?
584
- loop do
585
- tlevel = vv.instance_variable_get(:@__transaction_level__) || -1
586
- break if tlevel < vlevel[vv.__id__]
587
- vv.instance_variable_set(:@__transaction_block__, -1)
588
- vv.commit_transaction if vv.transaction_open?
589
- end
590
- elsif vv.transaction_open?(name)
591
- vv.instance_variable_set(:@__transaction_block__, -1)
592
- vv.commit_transaction(name)
593
- end
594
- end
595
- end
596
- else
597
- vars.each do |vv|
598
- vv.extend(Transaction::Simple)
599
- vv.start_transaction(name)
600
- end
601
- end
602
- end
603
- private :__common_start
604
-
605
- def start_named(name, *vars, &block)
606
- __common_start(name, vars, &block)
607
- end
608
-
609
- def start(*vars, &block)
610
- __common_start(nil, vars, &block)
611
- end
612
- end
613
-
614
- def __abort_transaction(name = nil) #:nodoc:
615
- @__transaction_checkpoint__ = __rewind_this_transaction
616
-
617
- if name.nil?
618
- ss = "" if Transaction::Simple.debugging?
619
- else
620
- ss = "(#{name.inspect})" if Transaction::Simple.debugging?
621
- end
622
-
623
- if Transaction::Simple.debugging?
624
- Transaction::Simple.debug_io << "#{'<' * @__transaction_level__} " <<
625
- "Abort Transaction#{ss}\n"
626
- end
627
- @__transaction_level__ -= 1
628
- @__transaction_names__.pop
629
- if @__transaction_level__ < 1
630
- @__transaction_level__ = 0
631
- @__transaction_names__ = []
632
- end
633
- end
634
-
635
- TRANSACTION_CHECKPOINT = "@__transaction_checkpoint__" #:nodoc:
636
- SKIP_TRANSACTION_VARS = [TRANSACTION_CHECKPOINT, "@__transaction_level__"] #:nodoc:
637
-
638
- def __rewind_this_transaction #:nodoc:
639
- rr = Marshal.restore(@__transaction_checkpoint__)
640
-
641
- begin
642
- self.replace(rr) if respond_to?(:replace)
643
- rescue
644
- nil
645
- end
646
-
647
- rr.instance_variables.each do |vv|
648
- next if SKIP_TRANSACTION_VARS.include?(vv)
649
- next if self.transaction_exclusions.include?(vv)
650
- if respond_to?(:instance_variable_get)
651
- instance_variable_set(vv, rr.instance_variable_get(vv))
652
- else
653
- instance_eval(%q|#{vv} = rr.instance_eval("#{vv}")|)
654
- end
655
- end
656
-
657
- new_ivar = instance_variables - rr.instance_variables - SKIP_TRANSACTION_VARS
658
- new_ivar.each do |vv|
659
- if respond_to?(:instance_variable_set)
660
- instance_variable_set(vv, nil)
661
- else
662
- instance_eval(%q|#{vv} = nil|)
663
- end
664
- end
665
-
666
- if respond_to?(:instance_variable_get)
667
- rr.instance_variable_get(TRANSACTION_CHECKPOINT)
668
- else
669
- rr.instance_eval(TRANSACTION_CHECKPOINT)
670
- end
671
- end
672
-
673
- def __commit_transaction #:nodoc:
674
- if respond_to?(:instance_variable_get)
675
- @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_variable_get(TRANSACTION_CHECKPOINT)
676
- else
677
- @__transaction_checkpoint__ = Marshal.restore(@__transaction_checkpoint__).instance_eval(TRANSACTION_CHECKPOINT)
678
- end
679
-
680
- @__transaction_level__ -= 1
681
- @__transaction_names__.pop
682
-
683
- if @__transaction_level__ < 1
684
- @__transaction_level__ = 0
685
- @__transaction_names__ = []
686
- end
687
- end
688
-
689
- private :__abort_transaction
690
- private :__rewind_this_transaction
691
- private :__commit_transaction
692
- end
693
- end