dm-core 0.9.11 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (194) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -50
  5. data/Manifest.txt +66 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +6 -7
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/deps.rip +2 -0
  12. data/dm-core.gemspec +11 -15
  13. data/lib/dm-core.rb +105 -110
  14. data/lib/dm-core/adapters.rb +135 -16
  15. data/lib/dm-core/adapters/abstract_adapter.rb +251 -181
  16. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  17. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  18. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  19. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  20. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  21. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  22. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  23. data/lib/dm-core/associations/many_to_many.rb +372 -90
  24. data/lib/dm-core/associations/many_to_one.rb +220 -73
  25. data/lib/dm-core/associations/one_to_many.rb +319 -255
  26. data/lib/dm-core/associations/one_to_one.rb +66 -53
  27. data/lib/dm-core/associations/relationship.rb +561 -156
  28. data/lib/dm-core/collection.rb +1101 -379
  29. data/lib/dm-core/core_ext/kernel.rb +12 -0
  30. data/lib/dm-core/core_ext/symbol.rb +10 -0
  31. data/lib/dm-core/identity_map.rb +4 -34
  32. data/lib/dm-core/migrations.rb +1283 -0
  33. data/lib/dm-core/model.rb +570 -369
  34. data/lib/dm-core/model/descendant_set.rb +81 -0
  35. data/lib/dm-core/model/hook.rb +45 -0
  36. data/lib/dm-core/model/is.rb +32 -0
  37. data/lib/dm-core/model/property.rb +247 -0
  38. data/lib/dm-core/model/relationship.rb +335 -0
  39. data/lib/dm-core/model/scope.rb +90 -0
  40. data/lib/dm-core/property.rb +808 -273
  41. data/lib/dm-core/property_set.rb +141 -98
  42. data/lib/dm-core/query.rb +1037 -483
  43. data/lib/dm-core/query/conditions/comparison.rb +872 -0
  44. data/lib/dm-core/query/conditions/operation.rb +221 -0
  45. data/lib/dm-core/query/direction.rb +43 -0
  46. data/lib/dm-core/query/operator.rb +84 -0
  47. data/lib/dm-core/query/path.rb +138 -0
  48. data/lib/dm-core/query/sort.rb +45 -0
  49. data/lib/dm-core/repository.rb +210 -94
  50. data/lib/dm-core/resource.rb +641 -421
  51. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  52. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  53. data/lib/dm-core/support/chainable.rb +22 -0
  54. data/lib/dm-core/support/deprecate.rb +12 -0
  55. data/lib/dm-core/support/logger.rb +13 -0
  56. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  57. data/lib/dm-core/transaction.rb +333 -92
  58. data/lib/dm-core/type.rb +98 -60
  59. data/lib/dm-core/types/boolean.rb +1 -1
  60. data/lib/dm-core/types/discriminator.rb +34 -20
  61. data/lib/dm-core/types/object.rb +7 -4
  62. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  63. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  64. data/lib/dm-core/types/serial.rb +3 -3
  65. data/lib/dm-core/types/text.rb +3 -4
  66. data/lib/dm-core/version.rb +1 -1
  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/migrations_spec.rb +359 -0
  80. data/spec/public/model/relationship_spec.rb +924 -0
  81. data/spec/public/model_spec.rb +159 -0
  82. data/spec/public/property_spec.rb +829 -0
  83. data/spec/public/resource_spec.rb +71 -0
  84. data/spec/public/sel_spec.rb +44 -0
  85. data/spec/public/setup_spec.rb +145 -0
  86. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  87. data/spec/public/shared/collection_shared_spec.rb +1670 -0
  88. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  89. data/spec/public/shared/resource_shared_spec.rb +924 -0
  90. data/spec/public/shared/sel_shared_spec.rb +112 -0
  91. data/spec/public/transaction_spec.rb +129 -0
  92. data/spec/public/types/discriminator_spec.rb +130 -0
  93. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  94. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  95. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  96. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  97. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  99. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  100. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  101. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  102. data/spec/semipublic/associations_spec.rb +177 -0
  103. data/spec/semipublic/collection_spec.rb +142 -0
  104. data/spec/semipublic/property_spec.rb +61 -0
  105. data/spec/semipublic/query/conditions_spec.rb +528 -0
  106. data/spec/semipublic/query/path_spec.rb +443 -0
  107. data/spec/semipublic/query_spec.rb +2626 -0
  108. data/spec/semipublic/resource_spec.rb +47 -0
  109. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  110. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  111. data/spec/spec.opts +3 -1
  112. data/spec/spec_helper.rb +80 -57
  113. data/tasks/ci.rb +19 -31
  114. data/tasks/dm.rb +43 -48
  115. data/tasks/doc.rb +8 -11
  116. data/tasks/gemspec.rb +5 -5
  117. data/tasks/hoe.rb +15 -16
  118. data/tasks/install.rb +8 -10
  119. metadata +74 -111
  120. data/lib/dm-core/associations.rb +0 -207
  121. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  122. data/lib/dm-core/auto_migrations.rb +0 -105
  123. data/lib/dm-core/dependency_queue.rb +0 -32
  124. data/lib/dm-core/hook.rb +0 -11
  125. data/lib/dm-core/is.rb +0 -16
  126. data/lib/dm-core/logger.rb +0 -232
  127. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  128. data/lib/dm-core/migrator.rb +0 -29
  129. data/lib/dm-core/scope.rb +0 -58
  130. data/lib/dm-core/support.rb +0 -7
  131. data/lib/dm-core/support/array.rb +0 -13
  132. data/lib/dm-core/support/assertions.rb +0 -8
  133. data/lib/dm-core/support/errors.rb +0 -23
  134. data/lib/dm-core/support/kernel.rb +0 -11
  135. data/lib/dm-core/support/symbol.rb +0 -41
  136. data/lib/dm-core/type_map.rb +0 -80
  137. data/lib/dm-core/types.rb +0 -19
  138. data/script/all +0 -4
  139. data/spec/integration/association_spec.rb +0 -1382
  140. data/spec/integration/association_through_spec.rb +0 -203
  141. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  142. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  143. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  144. data/spec/integration/auto_migrations_spec.rb +0 -413
  145. data/spec/integration/collection_spec.rb +0 -1073
  146. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  147. data/spec/integration/dependency_queue_spec.rb +0 -46
  148. data/spec/integration/model_spec.rb +0 -197
  149. data/spec/integration/mysql_adapter_spec.rb +0 -85
  150. data/spec/integration/postgres_adapter_spec.rb +0 -731
  151. data/spec/integration/property_spec.rb +0 -253
  152. data/spec/integration/query_spec.rb +0 -514
  153. data/spec/integration/repository_spec.rb +0 -61
  154. data/spec/integration/resource_spec.rb +0 -513
  155. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  156. data/spec/integration/sti_spec.rb +0 -273
  157. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  158. data/spec/integration/transaction_spec.rb +0 -75
  159. data/spec/integration/type_spec.rb +0 -275
  160. data/spec/lib/logging_helper.rb +0 -18
  161. data/spec/lib/mock_adapter.rb +0 -27
  162. data/spec/lib/model_loader.rb +0 -100
  163. data/spec/lib/publicize_methods.rb +0 -28
  164. data/spec/models/content.rb +0 -16
  165. data/spec/models/vehicles.rb +0 -34
  166. data/spec/models/zoo.rb +0 -48
  167. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  168. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  169. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  170. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  171. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  172. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  173. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  174. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  175. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  176. data/spec/unit/associations/relationship_spec.rb +0 -71
  177. data/spec/unit/associations_spec.rb +0 -242
  178. data/spec/unit/auto_migrations_spec.rb +0 -111
  179. data/spec/unit/collection_spec.rb +0 -182
  180. data/spec/unit/data_mapper_spec.rb +0 -35
  181. data/spec/unit/identity_map_spec.rb +0 -126
  182. data/spec/unit/is_spec.rb +0 -80
  183. data/spec/unit/migrator_spec.rb +0 -33
  184. data/spec/unit/model_spec.rb +0 -321
  185. data/spec/unit/naming_conventions_spec.rb +0 -36
  186. data/spec/unit/property_set_spec.rb +0 -90
  187. data/spec/unit/property_spec.rb +0 -753
  188. data/spec/unit/query_spec.rb +0 -571
  189. data/spec/unit/repository_spec.rb +0 -93
  190. data/spec/unit/resource_spec.rb +0 -649
  191. data/spec/unit/scope_spec.rb +0 -142
  192. data/spec/unit/transaction_spec.rb +0 -493
  193. data/spec/unit/type_map_spec.rb +0 -114
  194. data/spec/unit/type_spec.rb +0 -119
@@ -20,15 +20,15 @@ module DataMapper
20
20
  # DataMapper.setup(name, uri) returns the Adapter for convenience, so you can
21
21
  # use code like this:
22
22
  #
23
- # adapter = DataMapper.setup(:default, "mock://localhost/mock")
24
- # adapter.resource_naming_convention = DataMapper::NamingConventions::Resource::Underscored
23
+ # adapter = DataMapper.setup(:default, 'mock://localhost/mock')
24
+ # adapter.resource_naming_convention = NamingConventions::Resource::Underscored
25
25
  module NamingConventions
26
26
 
27
27
  module Resource
28
28
 
29
29
  module UnderscoredAndPluralized
30
30
  def self.call(name)
31
- Extlib::Inflection.pluralize(Extlib::Inflection.underscore(name)).gsub('/','_')
31
+ Extlib::Inflection.pluralize(Extlib::Inflection.underscore(name)).gsub('/', '_')
32
32
  end
33
33
  end # module UnderscoredAndPluralized
34
34
 
@@ -46,7 +46,7 @@ module DataMapper
46
46
 
47
47
  module Yaml
48
48
  def self.call(name)
49
- Extlib::Inflection.pluralize(Extlib::Inflection.underscore(name)) + ".yaml"
49
+ "#{Extlib::Inflection.pluralize(Extlib::Inflection.underscore(name))}.yaml"
50
50
  end
51
51
  end # module Yaml
52
52
 
@@ -56,7 +56,7 @@ module DataMapper
56
56
 
57
57
  module UnderscoredAndPluralized
58
58
  def self.call(property)
59
- Extlib::Inflection.pluralize(Extlib::Inflection.underscore(property.name.to_s)).gsub('/','_')
59
+ Extlib::Inflection.pluralize(Extlib::Inflection.underscore(property.name.to_s)).gsub('/', '_')
60
60
  end
61
61
  end # module UnderscoredAndPluralized
62
62
 
@@ -74,7 +74,7 @@ module DataMapper
74
74
 
75
75
  module Yaml
76
76
  def self.call(property)
77
- Extlib::Inflection.pluralize(Extlib::Inflection.underscore(property.name.to_s)) + ".yaml"
77
+ "#{Extlib::Inflection.pluralize(Extlib::Inflection.underscore(property.name.to_s))}.yaml"
78
78
  end
79
79
  end # module Yaml
80
80
 
@@ -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