datamapper-dm-core 0.9.11 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -39
  5. data/Manifest.txt +67 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +16 -15
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/dm-core.gemspec +11 -15
  12. data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
  13. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  14. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  15. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  16. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  19. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  20. data/lib/dm-core/adapters.rb +135 -16
  21. data/lib/dm-core/associations/many_to_many.rb +372 -90
  22. data/lib/dm-core/associations/many_to_one.rb +220 -73
  23. data/lib/dm-core/associations/one_to_many.rb +319 -255
  24. data/lib/dm-core/associations/one_to_one.rb +66 -53
  25. data/lib/dm-core/associations/relationship.rb +560 -158
  26. data/lib/dm-core/collection.rb +1104 -381
  27. data/lib/dm-core/core_ext/kernel.rb +12 -0
  28. data/lib/dm-core/core_ext/symbol.rb +10 -0
  29. data/lib/dm-core/identity_map.rb +4 -34
  30. data/lib/dm-core/migrations.rb +1283 -0
  31. data/lib/dm-core/model/descendant_set.rb +81 -0
  32. data/lib/dm-core/model/hook.rb +45 -0
  33. data/lib/dm-core/model/is.rb +32 -0
  34. data/lib/dm-core/model/property.rb +248 -0
  35. data/lib/dm-core/model/relationship.rb +335 -0
  36. data/lib/dm-core/model/scope.rb +90 -0
  37. data/lib/dm-core/model.rb +570 -369
  38. data/lib/dm-core/property.rb +753 -280
  39. data/lib/dm-core/property_set.rb +141 -98
  40. data/lib/dm-core/query/conditions/comparison.rb +814 -0
  41. data/lib/dm-core/query/conditions/operation.rb +247 -0
  42. data/lib/dm-core/query/direction.rb +43 -0
  43. data/lib/dm-core/query/operator.rb +42 -0
  44. data/lib/dm-core/query/path.rb +102 -0
  45. data/lib/dm-core/query/sort.rb +45 -0
  46. data/lib/dm-core/query.rb +974 -492
  47. data/lib/dm-core/repository.rb +147 -107
  48. data/lib/dm-core/resource.rb +644 -429
  49. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  50. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  51. data/lib/dm-core/support/chainable.rb +20 -0
  52. data/lib/dm-core/support/deprecate.rb +12 -0
  53. data/lib/dm-core/support/equalizer.rb +23 -0
  54. data/lib/dm-core/support/logger.rb +13 -0
  55. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  56. data/lib/dm-core/transaction.rb +333 -92
  57. data/lib/dm-core/type.rb +98 -60
  58. data/lib/dm-core/types/boolean.rb +1 -1
  59. data/lib/dm-core/types/discriminator.rb +34 -20
  60. data/lib/dm-core/types/object.rb +7 -4
  61. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  62. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  63. data/lib/dm-core/types/serial.rb +3 -3
  64. data/lib/dm-core/types/text.rb +3 -4
  65. data/lib/dm-core/version.rb +1 -1
  66. data/lib/dm-core.rb +106 -110
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/model/relationship_spec.rb +924 -0
  80. data/spec/public/model_spec.rb +159 -0
  81. data/spec/public/property_spec.rb +829 -0
  82. data/spec/public/resource_spec.rb +71 -0
  83. data/spec/public/sel_spec.rb +44 -0
  84. data/spec/public/setup_spec.rb +145 -0
  85. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  86. data/spec/public/shared/collection_shared_spec.rb +1723 -0
  87. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  88. data/spec/public/shared/resource_shared_spec.rb +924 -0
  89. data/spec/public/shared/sel_shared_spec.rb +112 -0
  90. data/spec/public/transaction_spec.rb +129 -0
  91. data/spec/public/types/discriminator_spec.rb +130 -0
  92. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  93. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  94. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  95. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  96. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  97. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  99. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  100. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  101. data/spec/semipublic/associations_spec.rb +177 -0
  102. data/spec/semipublic/collection_spec.rb +142 -0
  103. data/spec/semipublic/property_spec.rb +61 -0
  104. data/spec/semipublic/query/conditions_spec.rb +528 -0
  105. data/spec/semipublic/query/path_spec.rb +443 -0
  106. data/spec/semipublic/query_spec.rb +2626 -0
  107. data/spec/semipublic/resource_spec.rb +47 -0
  108. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  109. data/spec/spec.opts +3 -1
  110. data/spec/spec_helper.rb +80 -57
  111. data/tasks/ci.rb +19 -31
  112. data/tasks/dm.rb +43 -48
  113. data/tasks/doc.rb +8 -11
  114. data/tasks/gemspec.rb +5 -5
  115. data/tasks/hoe.rb +15 -16
  116. data/tasks/install.rb +8 -10
  117. metadata +72 -93
  118. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  119. data/lib/dm-core/associations.rb +0 -207
  120. data/lib/dm-core/auto_migrations.rb +0 -105
  121. data/lib/dm-core/dependency_queue.rb +0 -32
  122. data/lib/dm-core/hook.rb +0 -11
  123. data/lib/dm-core/is.rb +0 -16
  124. data/lib/dm-core/logger.rb +0 -232
  125. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  126. data/lib/dm-core/migrator.rb +0 -29
  127. data/lib/dm-core/scope.rb +0 -58
  128. data/lib/dm-core/support/array.rb +0 -13
  129. data/lib/dm-core/support/assertions.rb +0 -8
  130. data/lib/dm-core/support/errors.rb +0 -23
  131. data/lib/dm-core/support/kernel.rb +0 -11
  132. data/lib/dm-core/support/symbol.rb +0 -41
  133. data/lib/dm-core/support.rb +0 -7
  134. data/lib/dm-core/type_map.rb +0 -80
  135. data/lib/dm-core/types.rb +0 -19
  136. data/script/all +0 -4
  137. data/spec/integration/association_spec.rb +0 -1382
  138. data/spec/integration/association_through_spec.rb +0 -203
  139. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  140. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  141. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  142. data/spec/integration/auto_migrations_spec.rb +0 -413
  143. data/spec/integration/collection_spec.rb +0 -1073
  144. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  145. data/spec/integration/dependency_queue_spec.rb +0 -46
  146. data/spec/integration/model_spec.rb +0 -197
  147. data/spec/integration/mysql_adapter_spec.rb +0 -85
  148. data/spec/integration/postgres_adapter_spec.rb +0 -731
  149. data/spec/integration/property_spec.rb +0 -253
  150. data/spec/integration/query_spec.rb +0 -514
  151. data/spec/integration/repository_spec.rb +0 -61
  152. data/spec/integration/resource_spec.rb +0 -513
  153. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  154. data/spec/integration/sti_spec.rb +0 -273
  155. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  156. data/spec/integration/transaction_spec.rb +0 -75
  157. data/spec/integration/type_spec.rb +0 -275
  158. data/spec/lib/logging_helper.rb +0 -18
  159. data/spec/lib/mock_adapter.rb +0 -27
  160. data/spec/lib/model_loader.rb +0 -100
  161. data/spec/lib/publicize_methods.rb +0 -28
  162. data/spec/models/content.rb +0 -16
  163. data/spec/models/vehicles.rb +0 -34
  164. data/spec/models/zoo.rb +0 -48
  165. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  166. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  167. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  168. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  169. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  170. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  171. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  172. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  173. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  174. data/spec/unit/associations/relationship_spec.rb +0 -71
  175. data/spec/unit/associations_spec.rb +0 -242
  176. data/spec/unit/auto_migrations_spec.rb +0 -111
  177. data/spec/unit/collection_spec.rb +0 -182
  178. data/spec/unit/data_mapper_spec.rb +0 -35
  179. data/spec/unit/identity_map_spec.rb +0 -126
  180. data/spec/unit/is_spec.rb +0 -80
  181. data/spec/unit/migrator_spec.rb +0 -33
  182. data/spec/unit/model_spec.rb +0 -321
  183. data/spec/unit/naming_conventions_spec.rb +0 -36
  184. data/spec/unit/property_set_spec.rb +0 -90
  185. data/spec/unit/property_spec.rb +0 -753
  186. data/spec/unit/query_spec.rb +0 -571
  187. data/spec/unit/repository_spec.rb +0 -93
  188. data/spec/unit/resource_spec.rb +0 -649
  189. data/spec/unit/scope_spec.rb +0 -142
  190. data/spec/unit/transaction_spec.rb +0 -493
  191. data/spec/unit/type_map_spec.rb +0 -114
  192. data/spec/unit/type_spec.rb +0 -119
@@ -1,141 +1,170 @@
1
- # TODO: move to dm-more/dm-transactions
1
+ # TODO: move to dm-more/dm-transaction
2
2
 
3
3
  module DataMapper
4
4
  class Transaction
5
+ extend Chainable
5
6
 
6
- attr_reader :transaction_primitives, :adapters, :state
7
-
8
- #
9
- # Create a new DataMapper::Transaction
7
+ # Create a new Transaction
10
8
  #
11
- # @see DataMapper::Transaction#link
9
+ # @see Transaction#link
12
10
  #
13
11
  # In fact, it just calls #link with the given arguments at the end of the
14
12
  # constructor.
15
13
  #
14
+ # @api public
16
15
  def initialize(*things)
17
16
  @transaction_primitives = {}
18
17
  @state = :none
19
18
  @adapters = {}
20
19
  link(*things)
21
- commit { |*block_args| yield(*block_args) } if block_given?
20
+ if block_given?
21
+ warn "Passing block to #{self.class.name}.new is deprecated (#{caller[0]})"
22
+ commit { |*block_args| yield(*block_args) }
23
+ end
22
24
  end
23
25
 
24
- #
25
26
  # Associate this Transaction with some things.
26
27
  #
27
- # @param things<any number of Object> the things you want this Transaction
28
- # associated with
29
- # @details [things a Transaction may be associatied with]
30
- # DataMapper::Adapters::AbstractAdapter subclasses will be added as
28
+ # @param [Object] things
29
+ # the things you want this Transaction associated with:
30
+ #
31
+ # Adapters::AbstractAdapter subclasses will be added as
31
32
  # adapters as is.
32
33
  # Arrays will have their elements added.
33
- # DataMapper::Repositories will have their @adapters added.
34
- # DataMapper::Resource subclasses will have all the repositories of all
34
+ # Repository will have it's own @adapters added.
35
+ # Resource subclasses will have all the repositories of all
35
36
  # their properties added.
36
- # DataMapper::Resource instances will have all repositories of all their
37
+ # Resource instances will have all repositories of all their
37
38
  # properties added.
38
- # @param block<Block> a block (taking one argument, the Transaction) to execute
39
- # within this transaction. The transaction will begin and commit around
39
+ #
40
+ # @param [Proc] block
41
+ # a block (taking one argument, the Transaction) to execute within
42
+ # this transaction. The transaction will begin and commit around
40
43
  # the block, and rollback if an exception is raised.
41
44
  #
45
+ # @api private
42
46
  def link(*things)
43
- raise "Illegal state for link: #{@state}" unless @state == :none
47
+ unless @state == :none
48
+ raise "Illegal state for link: #{@state}"
49
+ end
50
+
44
51
  things.each do |thing|
45
- if thing.is_a?(Array)
46
- link(*thing)
47
- elsif thing.is_a?(DataMapper::Adapters::AbstractAdapter)
48
- @adapters[thing] = :none
49
- elsif thing.is_a?(DataMapper::Repository)
50
- link(thing.adapter)
51
- elsif thing.is_a?(Class) && thing.ancestors.include?(DataMapper::Resource)
52
- link(*thing.repositories)
53
- elsif thing.is_a?(DataMapper::Resource)
54
- link(thing.model)
55
- else
56
- raise "Unknown argument to #{self}#link: #{thing.inspect}"
52
+ case thing
53
+ when DataMapper::Adapters::AbstractAdapter
54
+ @adapters[thing] = :none
55
+ when DataMapper::Repository
56
+ link(thing.adapter)
57
+ when DataMapper::Model
58
+ link(*thing.repositories)
59
+ when DataMapper::Resource
60
+ link(thing.model)
61
+ when Array
62
+ link(*thing)
63
+ else
64
+ raise "Unknown argument to #{self.class}#link: #{thing.inspect} (#{thing.class})"
57
65
  end
58
66
  end
59
- return commit { |*block_args| yield(*block_args) } if block_given?
60
- return self
67
+
68
+ if block_given?
69
+ commit { |*block_args| yield(*block_args) }
70
+ else
71
+ self
72
+ end
61
73
  end
62
74
 
63
- #
64
75
  # Begin the transaction
65
76
  #
66
77
  # Before #begin is called, the transaction is not valid and can not be used.
67
78
  #
79
+ # @api private
68
80
  def begin
69
- raise "Illegal state for begin: #{@state}" unless @state == :none
81
+ unless @state == :none
82
+ raise "Illegal state for begin: #{@state}"
83
+ end
84
+
70
85
  each_adapter(:connect_adapter, [:log_fatal_transaction_breakage])
71
86
  each_adapter(:begin_adapter, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
72
87
  @state = :begin
73
88
  end
74
89
 
75
- #
76
90
  # Commit the transaction
77
91
  #
92
+ # If no block is given, it will simply commit any changes made since the
93
+ # Transaction did #begin.
94
+ #
78
95
  # @param block<Block> a block (taking the one argument, the Transaction) to
79
96
  # execute within this transaction. The transaction will begin and commit
80
97
  # around the block, and roll back if an exception is raised.
81
98
  #
82
- # @note
83
- # If no block is given, it will simply commit any changes made since the
84
- # Transaction did #begin.
85
- #
99
+ # @api private
86
100
  def commit
87
101
  if block_given?
88
- raise "Illegal state for commit with block: #{@state}" unless @state == :none
102
+ unless @state == :none
103
+ raise "Illegal state for commit with block: #{@state}"
104
+ end
105
+
89
106
  begin
90
107
  self.begin
91
108
  rval = within { |*block_args| yield(*block_args) }
92
- self.commit if @state == :begin
109
+ if @state == :begin
110
+ self.commit
111
+ end
93
112
  return rval
94
- rescue Exception => e
95
- self.rollback if @state == :begin
96
- raise e
113
+ rescue Exception => exception
114
+ if @state == :begin
115
+ self.rollback
116
+ end
117
+ raise exception
97
118
  end
98
119
  else
99
- raise "Illegal state for commit without block: #{@state}" unless @state == :begin
100
- each_adapter(:prepare_adapter, [:rollback_and_close_adapter_if_begin, :rollback_prepared_and_close_adapter_if_prepare])
120
+ unless @state == :begin
121
+ raise "Illegal state for commit without block: #{@state}"
122
+ end
101
123
  each_adapter(:commit_adapter, [:log_fatal_transaction_breakage])
102
124
  each_adapter(:close_adapter, [:log_fatal_transaction_breakage])
103
125
  @state = :commit
104
126
  end
105
127
  end
106
128
 
107
- #
108
129
  # Rollback the transaction
109
130
  #
110
131
  # Will undo all changes made during the transaction.
111
132
  #
133
+ # @api private
112
134
  def rollback
113
- raise "Illegal state for rollback: #{@state}" unless @state == :begin
135
+ unless @state == :begin
136
+ raise "Illegal state for rollback: #{@state}"
137
+ end
114
138
  each_adapter(:rollback_adapter_if_begin, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
115
- each_adapter(:rollback_prepared_adapter_if_prepare, [:rollback_prepared_and_close_adapter_if_begin, :close_adapter_if_none])
116
139
  each_adapter(:close_adapter_if_open, [:log_fatal_transaction_breakage])
117
140
  @state = :rollback
118
141
  end
119
142
 
120
- #
121
143
  # Execute a block within this Transaction.
122
144
  #
123
- # @param block<Block> the block of code to execute.
145
+ # No #begin, #commit or #rollback is performed in #within, but this
146
+ # Transaction will pushed on the per thread stack of transactions for each
147
+ # adapter it is associated with, and it will ensures that it will pop the
148
+ # Transaction away again after the block is finished.
124
149
  #
125
- # @note
126
- # No #begin, #commit or #rollback is performed in #within, but this
127
- # Transaction will pushed on the per thread stack of transactions for each
128
- # adapter it is associated with, and it will ensures that it will pop the
129
- # Transaction away again after the block is finished.
150
+ # @param block<Block> the block of code to execute.
130
151
  #
152
+ # @api private
131
153
  def within
132
- raise "No block provided" unless block_given?
133
- raise "Illegal state for within: #{@state}" unless @state == :begin
154
+ unless block_given?
155
+ raise 'No block provided'
156
+ end
157
+
158
+ unless @state == :begin
159
+ raise "Illegal state for within: #{@state}"
160
+ end
161
+
134
162
  @adapters.each do |adapter, state|
135
163
  adapter.push_transaction(self)
136
164
  end
165
+
137
166
  begin
138
- return yield(self)
167
+ yield self
139
168
  ensure
140
169
  @adapters.each do |adapter, state|
141
170
  adapter.pop_transaction
@@ -143,17 +172,23 @@ module DataMapper
143
172
  end
144
173
  end
145
174
 
175
+ # TODO: document
176
+ # @api private
146
177
  def method_missing(meth, *args, &block)
147
- if args.size == 1 && args.first.is_a?(DataMapper::Adapters::AbstractAdapter)
148
- if (match = meth.to_s.match(/^(.*)_if_(none|begin|prepare|rollback|commit)$/))
178
+ if args.size == 1 && args.first.kind_of?(Adapters::AbstractAdapter)
179
+ if (match = meth.to_s.match(/^(.*)_if_(none|begin|rollback|commit)$/))
149
180
  if self.respond_to?(match[1], true)
150
- self.send(match[1], args.first) if state_for(args.first).to_s == match[2]
181
+ if state_for(args.first).to_s == match[2]
182
+ self.send(match[1], args.first)
183
+ end
151
184
  else
152
185
  super
153
186
  end
154
- elsif (match = meth.to_s.match(/^(.*)_unless_(none|begin|prepare|rollback|commit)$/))
187
+ elsif (match = meth.to_s.match(/^(.*)_unless_(none|begin|rollback|commit)$/))
155
188
  if self.respond_to?(match[1], true)
156
- self.send(match[1], args.first) unless state_for(args.first).to_s == match[2]
189
+ unless state_for(args.first).to_s == match[2]
190
+ self.send(match[1], args.first)
191
+ end
157
192
  else
158
193
  super
159
194
  end
@@ -165,103 +200,309 @@ module DataMapper
165
200
  end
166
201
  end
167
202
 
203
+ # TODO: document
204
+ # @api private
168
205
  def primitive_for(adapter)
169
- raise "Unknown adapter #{adapter}" unless @adapters.include?(adapter)
170
- raise "No primitive for #{adapter}" unless @transaction_primitives.include?(adapter)
206
+ unless @adapters.include?(adapter)
207
+ raise "Unknown adapter #{adapter}"
208
+ end
209
+
210
+ unless @transaction_primitives.include?(adapter)
211
+ raise "No primitive for #{adapter}"
212
+ end
213
+
171
214
  @transaction_primitives[adapter]
172
215
  end
173
216
 
174
217
  private
175
218
 
219
+ # TODO: document
220
+ # @api private
176
221
  def validate_primitive(primitive)
177
- [:close, :begin, :prepare, :rollback, :rollback_prepared, :commit].each do |meth|
178
- raise "Invalid primitive #{primitive}: doesnt respond_to?(#{meth.inspect})" unless primitive.respond_to?(meth)
222
+ [:close, :begin, :rollback, :commit].each do |meth|
223
+ unless primitive.respond_to?(meth)
224
+ raise "Invalid primitive #{primitive}: doesnt respond_to?(#{meth.inspect})"
225
+ end
179
226
  end
180
- return primitive
227
+
228
+ primitive
181
229
  end
182
230
 
231
+ # TODO: document
232
+ # @api private
183
233
  def each_adapter(method, on_fail)
184
234
  begin
185
235
  @adapters.each do |adapter, state|
186
236
  self.send(method, adapter)
187
237
  end
188
- rescue Exception => e
238
+ rescue Exception => exception
189
239
  @adapters.each do |adapter, state|
190
240
  on_fail.each do |fail_handler|
191
241
  begin
192
242
  self.send(fail_handler, adapter)
193
- rescue Exception => e2
194
- DataMapper.logger.fatal("#{self}#each_adapter(#{method.inspect}, #{on_fail.inspect}) failed with #{e.inspect}: #{e.backtrace.join("\n")} - and when sending #{fail_handler} to #{adapter} we failed again with #{e2.inspect}: #{e2.backtrace.join("\n")}")
243
+ rescue Exception => inner_exception
244
+ DataMapper.logger.fatal("#{self}#each_adapter(#{method.inspect}, #{on_fail.inspect}) failed with #{exception.inspect}: #{exception.backtrace.join("\n")} - and when sending #{fail_handler} to #{adapter} we failed again with #{inner_exception.inspect}: #{inner_exception.backtrace.join("\n")}")
195
245
  end
196
246
  end
197
247
  end
198
- raise e
248
+ raise exception
199
249
  end
200
250
  end
201
251
 
252
+ # TODO: document
253
+ # @api private
202
254
  def state_for(adapter)
203
- raise "Unknown adapter #{adapter}" unless @adapters.include?(adapter)
255
+ unless @adapters.include?(adapter)
256
+ raise "Unknown adapter #{adapter}"
257
+ end
258
+
204
259
  @adapters[adapter]
205
260
  end
206
261
 
262
+ # TODO: document
263
+ # @api private
207
264
  def do_adapter(adapter, what, prerequisite)
208
- raise "No primitive for #{adapter}" unless @transaction_primitives.include?(adapter)
209
- raise "Illegal state for #{what}: #{state_for(adapter)}" unless state_for(adapter) == prerequisite
265
+ unless @transaction_primitives.include?(adapter)
266
+ raise "No primitive for #{adapter}"
267
+ end
268
+
269
+ unless state_for(adapter) == prerequisite
270
+ raise "Illegal state for #{what}: #{state_for(adapter)}"
271
+ end
272
+
210
273
  DataMapper.logger.debug("#{adapter.name}: #{what}")
211
274
  @transaction_primitives[adapter].send(what)
212
275
  @adapters[adapter] = what
213
276
  end
214
277
 
278
+ # TODO: document
279
+ # @api private
215
280
  def log_fatal_transaction_breakage(adapter)
216
281
  DataMapper.logger.fatal("#{self} experienced a totally broken transaction execution. Presenting member #{adapter.inspect}.")
217
282
  end
218
283
 
284
+ # TODO: document
285
+ # @api private
219
286
  def connect_adapter(adapter)
220
- raise "Already a primitive for adapter #{adapter}" unless @transaction_primitives[adapter].nil?
287
+ if @transaction_primitives.key?(adapter)
288
+ raise "Already a primitive for adapter #{adapter}"
289
+ end
290
+
221
291
  @transaction_primitives[adapter] = validate_primitive(adapter.transaction_primitive)
222
292
  end
223
293
 
294
+ # TODO: document
295
+ # @api private
224
296
  def close_adapter_if_open(adapter)
225
297
  if @transaction_primitives.include?(adapter)
226
298
  close_adapter(adapter)
227
299
  end
228
300
  end
229
301
 
302
+ # TODO: document
303
+ # @api private
230
304
  def close_adapter(adapter)
231
- raise "No primitive for adapter" unless @transaction_primitives.include?(adapter)
305
+ unless @transaction_primitives.include?(adapter)
306
+ raise 'No primitive for adapter'
307
+ end
308
+
232
309
  @transaction_primitives[adapter].close
233
310
  @transaction_primitives.delete(adapter)
234
311
  end
235
312
 
313
+ # TODO: document
314
+ # @api private
236
315
  def begin_adapter(adapter)
237
316
  do_adapter(adapter, :begin, :none)
238
317
  end
239
318
 
240
- def prepare_adapter(adapter)
241
- do_adapter(adapter, :prepare, :begin);
242
- end
243
-
319
+ # TODO: document
320
+ # @api private
244
321
  def commit_adapter(adapter)
245
- do_adapter(adapter, :commit, :prepare)
322
+ do_adapter(adapter, :commit, :begin)
246
323
  end
247
324
 
325
+ # TODO: document
326
+ # @api private
248
327
  def rollback_adapter(adapter)
249
328
  do_adapter(adapter, :rollback, :begin)
250
329
  end
251
330
 
252
- def rollback_prepared_adapter(adapter)
253
- do_adapter(adapter, :rollback_prepared, :prepare)
254
- end
255
-
256
- def rollback_prepared_and_close_adapter(adapter)
257
- rollback_prepared_adapter(adapter)
258
- close_adapter(adapter)
259
- end
260
-
331
+ # TODO: document
332
+ # @api private
261
333
  def rollback_and_close_adapter(adapter)
262
334
  rollback_adapter(adapter)
263
335
  close_adapter(adapter)
264
336
  end
265
337
 
338
+ module Adapter
339
+ extend Chainable
340
+
341
+ # TODO: document
342
+ # @api private
343
+ def self.included(base)
344
+ [ :Repository, :Model, :Resource ].each do |name|
345
+ DataMapper.const_get(name).send(:include, Transaction.const_get(name))
346
+ end
347
+ end
348
+
349
+ # Produces a fresh transaction primitive for this Adapter
350
+ #
351
+ # Used by Transaction to perform its various tasks.
352
+ #
353
+ # @return [Object]
354
+ # a new Object that responds to :close, :begin, :commit,
355
+ # and :rollback,
356
+ #
357
+ # @api private
358
+ def transaction_primitive
359
+ DataObjects::Transaction.create_for_uri(normalized_uri)
360
+ end
361
+
362
+ # Pushes the given Transaction onto the per thread Transaction stack so
363
+ # that everything done by this Adapter is done within the context of said
364
+ # Transaction.
365
+ #
366
+ # @param [Transaction] transaction
367
+ # a Transaction to be the 'current' transaction until popped.
368
+ #
369
+ # @return [Array(Transaction)]
370
+ # the stack of active transactions for the current thread
371
+ #
372
+ # @api private
373
+ def push_transaction(transaction)
374
+ transactions << transaction
375
+ end
376
+
377
+ # Pop the 'current' Transaction from the per thread Transaction stack so
378
+ # that everything done by this Adapter is no longer necessarily within the
379
+ # context of said Transaction.
380
+ #
381
+ # @return [Transaction]
382
+ # the former 'current' transaction.
383
+ #
384
+ # @api private
385
+ def pop_transaction
386
+ transactions.pop
387
+ end
388
+
389
+ # Retrieve the current transaction for this Adapter.
390
+ #
391
+ # Everything done by this Adapter is done within the context of this
392
+ # Transaction.
393
+ #
394
+ # @return [Transaction]
395
+ # the 'current' transaction for this Adapter.
396
+ #
397
+ # @api private
398
+ def current_transaction
399
+ transactions.last
400
+ end
401
+
402
+ chainable do
403
+ protected
404
+
405
+ # TODO: document
406
+ # @api semipublic
407
+ def open_connection
408
+ current_connection || super
409
+ end
410
+
411
+ # TODO: document
412
+ # @api semipublic
413
+ def close_connection(connection)
414
+ unless current_connection == connection
415
+ super
416
+ end
417
+ end
418
+ end
419
+
420
+ private
421
+
422
+ # TODO: document
423
+ # @api private
424
+ def transactions
425
+ Thread.current[:dm_transactions] ||= []
426
+ end
427
+
428
+ # Retrieve the current connection for this Adapter.
429
+ #
430
+ # @return [Transaction]
431
+ # the 'current' connection for this Adapter.
432
+ #
433
+ # @api private
434
+ def current_connection
435
+ if transaction = current_transaction
436
+ transaction.primitive_for(self).connection
437
+ end
438
+ end
439
+ end # module Adapter
440
+
441
+ # alias the MySQL, PostgreSQL, Sqlite3 and Oracle adapters to use transactions
442
+ MysqlAdapter = PostgresAdapter = Sqlite3Adapter = OracleAdapter = Adapter
443
+
444
+ module Repository
445
+
446
+ # Produce a new Transaction for this Repository
447
+ #
448
+ # @return [Adapters::Transaction]
449
+ # a new Transaction (in state :none) that can be used
450
+ # to execute code #with_transaction
451
+ #
452
+ # @api public
453
+ def transaction
454
+ Transaction.new(self)
455
+ end
456
+ end # module Repository
457
+
458
+ module Model
459
+ # TODO: document
460
+ # @api private
461
+ def self.included(mod)
462
+ mod.descendants.each { |model| model.extend self }
463
+ end
464
+
465
+ # Produce a new Transaction for this Resource class
466
+ #
467
+ # @return <Adapters::Transaction
468
+ # a new Adapters::Transaction with all Repositories
469
+ # of the class of this Resource added.
470
+ #
471
+ # @api public
472
+ def transaction
473
+ transaction = Transaction.new(self)
474
+ transaction.commit { |block_args| yield(*block_args) }
475
+ end
476
+ end # module Model
477
+
478
+ module Resource
479
+
480
+ # Produce a new Transaction for the class of this Resource
481
+ #
482
+ # @return [Adapters::Transaction]
483
+ # a new Adapters::Transaction for the Repository
484
+ # of the class of this Resource added.
485
+ #
486
+ # @api public
487
+ def transaction
488
+ model.transaction { |*block_args| yield(*block_args) }
489
+ end
490
+ end # module Resource
266
491
  end # class Transaction
492
+
493
+ module Adapters
494
+ extendable do
495
+
496
+ # TODO: document
497
+ # @api private
498
+ def const_added(const_name)
499
+ if Transaction.const_defined?(const_name)
500
+ adapter = const_get(const_name)
501
+ adapter.send(:include, Transaction.const_get(const_name))
502
+ end
503
+
504
+ super
505
+ end
506
+ end
507
+ end # module Adapters
267
508
  end # module DataMapper