sam-dm-core 0.9.6

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 (126) hide show
  1. data/.autotest +26 -0
  2. data/CONTRIBUTING +51 -0
  3. data/FAQ +92 -0
  4. data/History.txt +145 -0
  5. data/MIT-LICENSE +22 -0
  6. data/Manifest.txt +125 -0
  7. data/QUICKLINKS +12 -0
  8. data/README.txt +143 -0
  9. data/Rakefile +30 -0
  10. data/SPECS +63 -0
  11. data/TODO +1 -0
  12. data/lib/dm-core.rb +224 -0
  13. data/lib/dm-core/adapters.rb +4 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
  15. data/lib/dm-core/adapters/data_objects_adapter.rb +707 -0
  16. data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +188 -0
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
  19. data/lib/dm-core/associations.rb +199 -0
  20. data/lib/dm-core/associations/many_to_many.rb +147 -0
  21. data/lib/dm-core/associations/many_to_one.rb +107 -0
  22. data/lib/dm-core/associations/one_to_many.rb +309 -0
  23. data/lib/dm-core/associations/one_to_one.rb +61 -0
  24. data/lib/dm-core/associations/relationship.rb +218 -0
  25. data/lib/dm-core/associations/relationship_chain.rb +81 -0
  26. data/lib/dm-core/auto_migrations.rb +113 -0
  27. data/lib/dm-core/collection.rb +638 -0
  28. data/lib/dm-core/dependency_queue.rb +31 -0
  29. data/lib/dm-core/hook.rb +11 -0
  30. data/lib/dm-core/identity_map.rb +45 -0
  31. data/lib/dm-core/is.rb +16 -0
  32. data/lib/dm-core/logger.rb +232 -0
  33. data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
  34. data/lib/dm-core/migrator.rb +29 -0
  35. data/lib/dm-core/model.rb +471 -0
  36. data/lib/dm-core/naming_conventions.rb +84 -0
  37. data/lib/dm-core/property.rb +673 -0
  38. data/lib/dm-core/property_set.rb +162 -0
  39. data/lib/dm-core/query.rb +625 -0
  40. data/lib/dm-core/repository.rb +159 -0
  41. data/lib/dm-core/resource.rb +637 -0
  42. data/lib/dm-core/scope.rb +58 -0
  43. data/lib/dm-core/support.rb +7 -0
  44. data/lib/dm-core/support/array.rb +13 -0
  45. data/lib/dm-core/support/assertions.rb +8 -0
  46. data/lib/dm-core/support/errors.rb +23 -0
  47. data/lib/dm-core/support/kernel.rb +7 -0
  48. data/lib/dm-core/support/symbol.rb +41 -0
  49. data/lib/dm-core/transaction.rb +267 -0
  50. data/lib/dm-core/type.rb +160 -0
  51. data/lib/dm-core/type_map.rb +80 -0
  52. data/lib/dm-core/types.rb +19 -0
  53. data/lib/dm-core/types/boolean.rb +7 -0
  54. data/lib/dm-core/types/discriminator.rb +34 -0
  55. data/lib/dm-core/types/object.rb +24 -0
  56. data/lib/dm-core/types/paranoid_boolean.rb +34 -0
  57. data/lib/dm-core/types/paranoid_datetime.rb +33 -0
  58. data/lib/dm-core/types/serial.rb +9 -0
  59. data/lib/dm-core/types/text.rb +10 -0
  60. data/lib/dm-core/version.rb +3 -0
  61. data/script/all +5 -0
  62. data/script/performance.rb +203 -0
  63. data/script/profile.rb +87 -0
  64. data/spec/integration/association_spec.rb +1371 -0
  65. data/spec/integration/association_through_spec.rb +203 -0
  66. data/spec/integration/associations/many_to_many_spec.rb +449 -0
  67. data/spec/integration/associations/many_to_one_spec.rb +163 -0
  68. data/spec/integration/associations/one_to_many_spec.rb +151 -0
  69. data/spec/integration/auto_migrations_spec.rb +398 -0
  70. data/spec/integration/collection_spec.rb +1069 -0
  71. data/spec/integration/data_objects_adapter_spec.rb +32 -0
  72. data/spec/integration/dependency_queue_spec.rb +58 -0
  73. data/spec/integration/model_spec.rb +127 -0
  74. data/spec/integration/mysql_adapter_spec.rb +85 -0
  75. data/spec/integration/postgres_adapter_spec.rb +731 -0
  76. data/spec/integration/property_spec.rb +233 -0
  77. data/spec/integration/query_spec.rb +506 -0
  78. data/spec/integration/repository_spec.rb +57 -0
  79. data/spec/integration/resource_spec.rb +475 -0
  80. data/spec/integration/sqlite3_adapter_spec.rb +352 -0
  81. data/spec/integration/sti_spec.rb +208 -0
  82. data/spec/integration/strategic_eager_loading_spec.rb +138 -0
  83. data/spec/integration/transaction_spec.rb +75 -0
  84. data/spec/integration/type_spec.rb +271 -0
  85. data/spec/lib/logging_helper.rb +18 -0
  86. data/spec/lib/mock_adapter.rb +27 -0
  87. data/spec/lib/model_loader.rb +91 -0
  88. data/spec/lib/publicize_methods.rb +28 -0
  89. data/spec/models/vehicles.rb +34 -0
  90. data/spec/models/zoo.rb +47 -0
  91. data/spec/spec.opts +3 -0
  92. data/spec/spec_helper.rb +86 -0
  93. data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
  94. data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
  95. data/spec/unit/adapters/data_objects_adapter_spec.rb +628 -0
  96. data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
  97. data/spec/unit/associations/many_to_many_spec.rb +17 -0
  98. data/spec/unit/associations/many_to_one_spec.rb +152 -0
  99. data/spec/unit/associations/one_to_many_spec.rb +393 -0
  100. data/spec/unit/associations/one_to_one_spec.rb +7 -0
  101. data/spec/unit/associations/relationship_spec.rb +71 -0
  102. data/spec/unit/associations_spec.rb +242 -0
  103. data/spec/unit/auto_migrations_spec.rb +111 -0
  104. data/spec/unit/collection_spec.rb +182 -0
  105. data/spec/unit/data_mapper_spec.rb +35 -0
  106. data/spec/unit/identity_map_spec.rb +126 -0
  107. data/spec/unit/is_spec.rb +80 -0
  108. data/spec/unit/migrator_spec.rb +33 -0
  109. data/spec/unit/model_spec.rb +339 -0
  110. data/spec/unit/naming_conventions_spec.rb +36 -0
  111. data/spec/unit/property_set_spec.rb +83 -0
  112. data/spec/unit/property_spec.rb +753 -0
  113. data/spec/unit/query_spec.rb +530 -0
  114. data/spec/unit/repository_spec.rb +93 -0
  115. data/spec/unit/resource_spec.rb +626 -0
  116. data/spec/unit/scope_spec.rb +142 -0
  117. data/spec/unit/transaction_spec.rb +493 -0
  118. data/spec/unit/type_map_spec.rb +114 -0
  119. data/spec/unit/type_spec.rb +119 -0
  120. data/tasks/ci.rb +68 -0
  121. data/tasks/dm.rb +63 -0
  122. data/tasks/doc.rb +20 -0
  123. data/tasks/gemspec.rb +23 -0
  124. data/tasks/hoe.rb +46 -0
  125. data/tasks/install.rb +20 -0
  126. metadata +216 -0
@@ -0,0 +1,142 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Scope do
4
+ after do
5
+ Article.publicize_methods do
6
+ Article.scope_stack.clear # reset the stack before each spec
7
+ end
8
+ end
9
+
10
+ describe '.with_scope' do
11
+ it 'should be protected' do
12
+ klass = class << Article; self; end
13
+ klass.should be_protected_method_defined(:with_scope)
14
+ end
15
+
16
+ it 'should set the current scope for the block when given a Hash' do
17
+ Article.publicize_methods do
18
+ Article.with_scope :blog_id => 1 do
19
+ Article.query.should == DataMapper::Query.new(repository(:mock), Article, :blog_id => 1)
20
+ end
21
+ end
22
+ end
23
+
24
+ it 'should set the current scope for the block when given a DataMapper::Query' do
25
+ Article.publicize_methods do
26
+ Article.with_scope query = DataMapper::Query.new(repository(:mock), Article) do
27
+ Article.query.should == query
28
+ end
29
+ end
30
+ end
31
+
32
+ it 'should set the current scope for an inner block, merged with the outer scope' do
33
+ Article.publicize_methods do
34
+ Article.with_scope :blog_id => 1 do
35
+ Article.with_scope :author => 'dkubb' do
36
+ Article.query.should == DataMapper::Query.new(repository(:mock), Article, :blog_id => 1, :author => 'dkubb')
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ it 'should reset the stack on error' do
43
+ Article.publicize_methods do
44
+ Article.query.should be_nil
45
+ lambda {
46
+ Article.with_scope(:blog_id => 1) { raise 'There was a problem!' }
47
+ }.should raise_error(RuntimeError)
48
+ Article.query.should be_nil
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '.with_exclusive_scope' do
54
+ it 'should be protected' do
55
+ klass = class << Article; self; end
56
+ klass.should be_protected_method_defined(:with_exclusive_scope)
57
+ end
58
+
59
+ it 'should set the current scope for an inner block, ignoring the outer scope' do
60
+ Article.publicize_methods do
61
+ Article.with_scope :blog_id => 1 do
62
+ Article.with_exclusive_scope :author => 'dkubb' do
63
+ Article.query.should == DataMapper::Query.new(repository(:mock), Article, :author => 'dkubb')
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ it 'should reset the stack on error' do
70
+ Article.publicize_methods do
71
+ Article.query.should be_nil
72
+ lambda {
73
+ Article.with_exclusive_scope(:blog_id => 1) { raise 'There was a problem!' }
74
+ }.should raise_error(RuntimeError)
75
+ Article.query.should be_nil
76
+ end
77
+ end
78
+
79
+ it "should ignore the default_scope when using an exclusive scope" do
80
+ Article.default_scope.update(:blog_id => 1)
81
+ Article.publicize_methods do
82
+ Article.with_exclusive_scope(:author => 'dkubb') do
83
+ Article.query.should == DataMapper::Query.new(repository(:mock), Article, :author => 'dkubb')
84
+ end
85
+ end
86
+ Article.default_scope.delete(:blog_id)
87
+ end
88
+
89
+ end
90
+
91
+ describe '.scope_stack' do
92
+ it 'should be private' do
93
+ klass = class << Article; self; end
94
+ klass.should be_private_method_defined(:scope_stack)
95
+ end
96
+
97
+ it 'should provide an Array' do
98
+ Article.publicize_methods do
99
+ Article.scope_stack.should be_kind_of(Array)
100
+ end
101
+ end
102
+
103
+ it 'should be the same in a thread' do
104
+ Article.publicize_methods do
105
+ Article.scope_stack.object_id.should == Article.scope_stack.object_id
106
+ end
107
+ end
108
+
109
+ it 'should be different in each thread' do
110
+ Article.publicize_methods do
111
+ a = Thread.new { Article.scope_stack }
112
+ b = Thread.new { Article.scope_stack }
113
+
114
+ a.value.object_id.should_not == b.value.object_id
115
+ end
116
+ end
117
+ end
118
+
119
+ describe '.query' do
120
+ it 'should be public' do
121
+ klass = class << Article; self; end
122
+ klass.should be_public_method_defined(:query)
123
+ end
124
+
125
+ it 'should return nil if the scope stack is empty' do
126
+ Article.publicize_methods do
127
+ Article.scope_stack.should be_empty
128
+ Article.query.should be_nil
129
+ end
130
+ end
131
+
132
+ it 'should return the last element of the scope stack' do
133
+ Article.publicize_methods do
134
+ query = DataMapper::Query.new(repository(:mock), Article)
135
+ Article.scope_stack << query
136
+ Article.query.object_id.should == query.object_id
137
+ end
138
+ end
139
+ end
140
+
141
+ # TODO: specify the behavior of finders (all, first, get, []) when scope is in effect
142
+ end
@@ -0,0 +1,493 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe DataMapper::Transaction do
4
+
5
+ before :all do
6
+ class Smurf
7
+ include DataMapper::Resource
8
+ property :id, Integer, :key => true
9
+ end
10
+ end
11
+
12
+ before :each do
13
+ @adapter = mock("adapter", :name => 'mock_adapter')
14
+ @repository = mock("repository")
15
+ @repository_adapter = mock("repository adapter", :name => 'mock_repository_adapter')
16
+ @resource = Smurf.new
17
+ @transaction_primitive = mock("transaction primitive")
18
+ @repository_transaction_primitive = mock("repository transaction primitive")
19
+ @array = [@adapter, @repository]
20
+
21
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
22
+ @adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(true)
23
+ @adapter.should_receive(:transaction_primitive).any_number_of_times.and_return(@transaction_primitive)
24
+ @repository.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
25
+ @repository.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(false)
26
+ @repository.should_receive(:is_a?).any_number_of_times.with(DataMapper::Repository).and_return(true)
27
+ @repository.should_receive(:adapter).any_number_of_times.and_return(@repository_adapter)
28
+ @repository_adapter.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
29
+ @repository_adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(true)
30
+ @repository_adapter.should_receive(:transaction_primitive).any_number_of_times.and_return(@repository_transaction_primitive)
31
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:close).and_return(true)
32
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:begin).and_return(true)
33
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:prepare).and_return(true)
34
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:rollback).and_return(true)
35
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:rollback_prepared).and_return(true)
36
+ @transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:commit).and_return(true)
37
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:close).and_return(true)
38
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:begin).and_return(true)
39
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:prepare).and_return(true)
40
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:rollback).and_return(true)
41
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:rollback_prepared).and_return(true)
42
+ @repository_transaction_primitive.should_receive(:respond_to?).any_number_of_times.with(:commit).and_return(true)
43
+ end
44
+
45
+ it "should be able to initialize with an Array" do
46
+ DataMapper::Transaction.new(@array)
47
+ end
48
+ it "should be able to initialize with DataMapper::Adapters::AbstractAdapters" do
49
+ DataMapper::Transaction.new(@adapter)
50
+ end
51
+ it "should be able to initialize with DataMapper::Repositories" do
52
+ DataMapper::Transaction.new(@repository)
53
+ end
54
+ it "should be able to initialize with DataMapper::Resource subclasses" do
55
+ DataMapper::Transaction.new(Smurf)
56
+ end
57
+ it "should be able to initialize with DataMapper::Resources" do
58
+ DataMapper::Transaction.new(Smurf.new)
59
+ end
60
+ it "should initialize with no transaction_primitives" do
61
+ DataMapper::Transaction.new.transaction_primitives.empty?.should == true
62
+ end
63
+ it "should initialize with state :none" do
64
+ DataMapper::Transaction.new.state.should == :none
65
+ end
66
+ it "should initialize the adapters given on creation" do
67
+ DataMapper::Transaction.new(Smurf).adapters.should == {Smurf.repository.adapter => :none}
68
+ end
69
+ it "should be able receive multiple adapters on creation" do
70
+ DataMapper::Transaction.new(Smurf, @resource, @adapter, @repository)
71
+ end
72
+ it "should be able to initialize with a block" do
73
+ p = Proc.new do end
74
+ @transaction_primitive.stub!(:begin)
75
+ @transaction_primitive.stub!(:prepare)
76
+ @transaction_primitive.stub!(:commit)
77
+ @adapter.stub!(:push_transaction)
78
+ @adapter.stub!(:pop_transaction)
79
+ @transaction_primitive.stub!(:close)
80
+ DataMapper::Transaction.new(@adapter, &p)
81
+ end
82
+ it "should accept new adapters after creation" do
83
+ t = DataMapper::Transaction.new(@adapter, @repository)
84
+ t.adapters.should == {@adapter => :none, @repository_adapter => :none}
85
+ t.link(@resource)
86
+ t.adapters.should == {@adapter => :none, @repository_adapter => :none, Smurf.repository.adapter => :none}
87
+ end
88
+ it "should not accept new adapters after state is changed" do
89
+ t = DataMapper::Transaction.new(@adapter, @repository)
90
+ @transaction_primitive.stub!(:begin)
91
+ @repository_transaction_primitive.stub!(:begin)
92
+ t.begin
93
+ lambda do t.link(@resource) end.should raise_error(Exception, /Illegal state/)
94
+ end
95
+ describe "#begin" do
96
+ before :each do
97
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
98
+ end
99
+ it "should raise error if state is changed" do
100
+ @transaction_primitive.stub!(:begin)
101
+ @repository_transaction_primitive.stub!(:begin)
102
+ @transaction.begin
103
+ lambda do @transaction.begin end.should raise_error(Exception, /Illegal state/)
104
+ end
105
+ it "should try to connect each adapter (or log fatal error), then begin each adapter (or rollback and close)" do
106
+ @transaction.should_receive(:each_adapter).with(:connect_adapter, [:log_fatal_transaction_breakage])
107
+ @transaction.should_receive(:each_adapter).with(:begin_adapter, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
108
+ @transaction.begin
109
+ end
110
+ it "should leave with state :begin" do
111
+ @transaction_primitive.stub!(:begin)
112
+ @repository_transaction_primitive.stub!(:begin)
113
+ @transaction.begin
114
+ @transaction.state.should == :begin
115
+ end
116
+ end
117
+ describe "#rollback" do
118
+ before :each do
119
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
120
+ end
121
+ it "should raise error if state is :none" do
122
+ lambda do @transaction.rollback end.should raise_error(Exception, /Illegal state/)
123
+ end
124
+ it "should raise error if state is :commit" do
125
+ @transaction_primitive.stub!(:begin)
126
+ @repository_transaction_primitive.stub!(:begin)
127
+ @transaction_primitive.stub!(:prepare)
128
+ @repository_transaction_primitive.stub!(:prepare)
129
+ @transaction_primitive.stub!(:commit)
130
+ @repository_transaction_primitive.stub!(:commit)
131
+ @transaction_primitive.stub!(:close)
132
+ @repository_transaction_primitive.stub!(:close)
133
+ @transaction.begin
134
+ @transaction.commit
135
+ lambda do @transaction.rollback end.should raise_error(Exception, /Illegal state/)
136
+ end
137
+ it "should try to rollback each adapter (or rollback and close), then then close (or log fatal error)" do
138
+ @transaction.should_receive(:each_adapter).with(:connect_adapter, [:log_fatal_transaction_breakage])
139
+ @transaction.should_receive(:each_adapter).with(:begin_adapter, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
140
+ @transaction.should_receive(:each_adapter).with(:rollback_adapter_if_begin, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
141
+ @transaction.should_receive(:each_adapter).with(:close_adapter_if_open, [:log_fatal_transaction_breakage])
142
+ @transaction.should_receive(:each_adapter).with(:rollback_prepared_adapter_if_prepare, [:rollback_prepared_and_close_adapter_if_begin, :close_adapter_if_none])
143
+ @transaction.begin
144
+ @transaction.rollback
145
+ end
146
+ it "should leave with state :rollback" do
147
+ @transaction_primitive.stub!(:begin)
148
+ @repository_transaction_primitive.stub!(:begin)
149
+ @transaction_primitive.stub!(:rollback)
150
+ @repository_transaction_primitive.stub!(:rollback)
151
+ @transaction_primitive.stub!(:close)
152
+ @repository_transaction_primitive.stub!(:close)
153
+ @transaction.begin
154
+ @transaction.rollback
155
+ @transaction.state.should == :rollback
156
+ end
157
+ end
158
+ describe "#commit" do
159
+ describe "without a block" do
160
+ before :each do
161
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
162
+ end
163
+ it "should raise error if state is :none" do
164
+ lambda do @transaction.commit end.should raise_error(Exception, /Illegal state/)
165
+ end
166
+ it "should raise error if state is :commit" do
167
+ @transaction_primitive.stub!(:begin)
168
+ @repository_transaction_primitive.stub!(:begin)
169
+ @transaction_primitive.stub!(:prepare)
170
+ @repository_transaction_primitive.stub!(:prepare)
171
+ @transaction_primitive.stub!(:commit)
172
+ @repository_transaction_primitive.stub!(:commit)
173
+ @transaction_primitive.stub!(:close)
174
+ @repository_transaction_primitive.stub!(:close)
175
+ @transaction.begin
176
+ @transaction.commit
177
+ lambda do @transaction.commit end.should raise_error(Exception, /Illegal state/)
178
+ end
179
+ it "should raise error if state is :rollback" do
180
+ @transaction_primitive.stub!(:begin)
181
+ @repository_transaction_primitive.stub!(:begin)
182
+ @transaction_primitive.stub!(:rollback)
183
+ @repository_transaction_primitive.stub!(:rollback)
184
+ @transaction_primitive.stub!(:close)
185
+ @repository_transaction_primitive.stub!(:close)
186
+ @transaction.begin
187
+ @transaction.rollback
188
+ lambda do @transaction.commit end.should raise_error(Exception, /Illegal state/)
189
+ end
190
+ it "should try to prepare each adapter (or rollback and close), then commit each adapter (or log fatal error), then close (or log fatal error)" do
191
+ @transaction.should_receive(:each_adapter).with(:connect_adapter, [:log_fatal_transaction_breakage])
192
+ @transaction.should_receive(:each_adapter).with(:begin_adapter, [:rollback_and_close_adapter_if_begin, :close_adapter_if_none])
193
+ @transaction.should_receive(:each_adapter).with(:prepare_adapter, [:rollback_and_close_adapter_if_begin, :rollback_prepared_and_close_adapter_if_prepare])
194
+ @transaction.should_receive(:each_adapter).with(:commit_adapter, [:log_fatal_transaction_breakage])
195
+ @transaction.should_receive(:each_adapter).with(:close_adapter, [:log_fatal_transaction_breakage])
196
+ @transaction.begin
197
+ @transaction.commit
198
+ end
199
+ it "should leave with state :commit" do
200
+ @transaction_primitive.stub!(:begin)
201
+ @repository_transaction_primitive.stub!(:begin)
202
+ @transaction_primitive.stub!(:prepare)
203
+ @repository_transaction_primitive.stub!(:prepare)
204
+ @transaction_primitive.stub!(:commit)
205
+ @repository_transaction_primitive.stub!(:commit)
206
+ @transaction_primitive.stub!(:close)
207
+ @repository_transaction_primitive.stub!(:close)
208
+ @transaction.begin
209
+ @transaction.commit
210
+ @transaction.state.should == :commit
211
+ end
212
+ end
213
+ describe "with a block" do
214
+ before :each do
215
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
216
+ end
217
+ it "should raise if state is not :none" do
218
+ @transaction_primitive.stub!(:begin)
219
+ @repository_transaction_primitive.stub!(:begin)
220
+ @transaction.begin
221
+ lambda do @transaction.commit do end end.should raise_error(Exception, /Illegal state/)
222
+ end
223
+ it "should begin, yield and commit if the block raises no exception" do
224
+ @repository_transaction_primitive.should_receive(:begin)
225
+ @repository_transaction_primitive.should_receive(:prepare)
226
+ @repository_transaction_primitive.should_receive(:commit)
227
+ @repository_transaction_primitive.should_receive(:close)
228
+ @transaction_primitive.should_receive(:begin)
229
+ @transaction_primitive.should_receive(:prepare)
230
+ @transaction_primitive.should_receive(:commit)
231
+ @transaction_primitive.should_receive(:close)
232
+ p = Proc.new do end
233
+ @transaction.should_receive(:within).with(&p)
234
+ @transaction.commit(&p)
235
+ end
236
+ it "should rollback if the block raises an exception" do
237
+ @repository_transaction_primitive.should_receive(:begin)
238
+ @repository_transaction_primitive.should_receive(:rollback)
239
+ @repository_transaction_primitive.should_receive(:close)
240
+ @transaction_primitive.should_receive(:begin)
241
+ @transaction_primitive.should_receive(:rollback)
242
+ @transaction_primitive.should_receive(:close)
243
+ p = Proc.new do raise "test exception, never mind me" end
244
+ @transaction.should_receive(:within).with(&p)
245
+ lambda do @transaction.commit(&p) end.should raise_error(Exception, /test exception, never mind me/)
246
+ end
247
+ end
248
+ end
249
+ describe "#within" do
250
+ before :each do
251
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
252
+ end
253
+ it "should raise if no block is provided" do
254
+ lambda do @transaction.within end.should raise_error(Exception, /No block/)
255
+ end
256
+ it "should raise if state is not :begin" do
257
+ lambda do @transaction.within do end end.should raise_error(Exception, /Illegal state/)
258
+ end
259
+ it "should push itself on the per thread transaction context of each adapter and then pop itself out again" do
260
+ @repository_transaction_primitive.should_receive(:begin)
261
+ @transaction_primitive.should_receive(:begin)
262
+ @repository_adapter.should_receive(:push_transaction).with(@transaction)
263
+ @adapter.should_receive(:push_transaction).with(@transaction)
264
+ @repository_adapter.should_receive(:pop_transaction)
265
+ @adapter.should_receive(:pop_transaction)
266
+ @transaction.begin
267
+ @transaction.within do end
268
+ end
269
+ it "should push itself on the per thread transaction context of each adapter and then pop itself out again even if an exception was raised" do
270
+ @repository_transaction_primitive.should_receive(:begin)
271
+ @transaction_primitive.should_receive(:begin)
272
+ @repository_adapter.should_receive(:push_transaction).with(@transaction)
273
+ @adapter.should_receive(:push_transaction).with(@transaction)
274
+ @repository_adapter.should_receive(:pop_transaction)
275
+ @adapter.should_receive(:pop_transaction)
276
+ @transaction.begin
277
+ lambda do @transaction.within do raise "test exception, never mind me" end end.should raise_error(Exception, /test exception, never mind me/)
278
+ end
279
+ end
280
+ describe "#method_missing" do
281
+ before :each do
282
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
283
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::AnyArgsConstraint).and_return(false)
284
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::NoArgsConstraint).and_return(false)
285
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Regexp).and_return(false)
286
+ end
287
+ it "should delegate calls to [a method we have]_if_[state](adapter) to [a method we have](adapter) if state of adapter is [state]" do
288
+ @transaction.should_receive(:state_for).with(@adapter).and_return(:begin)
289
+ @transaction.should_receive(:connect_adapter).with(@adapter)
290
+ @transaction.connect_adapter_if_begin(@adapter)
291
+ end
292
+ it "should not delegate calls to [a method we have]_if_[state](adapter) to [a method we have](adapter) if state of adapter is not [state]" do
293
+ @transaction.should_receive(:state_for).with(@adapter).and_return(:commit)
294
+ @transaction.should_not_receive(:connect_adapter).with(@adapter)
295
+ @transaction.connect_adapter_if_begin(@adapter)
296
+ end
297
+ it "should delegate calls to [a method we have]_unless_[state](adapter) to [a method we have](adapter) if state of adapter is not [state]" do
298
+ @transaction.should_receive(:state_for).with(@adapter).and_return(:none)
299
+ @transaction.should_receive(:connect_adapter).with(@adapter)
300
+ @transaction.connect_adapter_unless_begin(@adapter)
301
+ end
302
+ it "should not delegate calls to [a method we have]_unless_[state](adapter) to [a method we have](adapter) if state of adapter is [state]" do
303
+ @transaction.should_receive(:state_for).with(@adapter).and_return(:begin)
304
+ @transaction.should_not_receive(:connect_adapter).with(@adapter)
305
+ @transaction.connect_adapter_unless_begin(@adapter)
306
+ end
307
+ it "should not delegate calls whose first argument is not a DataMapper::Adapters::AbstractAdapter" do
308
+ lambda do @transaction.connect_adapter_unless_begin("plur") end.should raise_error
309
+ end
310
+ it "should not delegate calls that do not look like an if or unless followed by a state" do
311
+ lambda do @transaction.connect_adapter_unless_hepp(@adapter) end.should raise_error
312
+ lambda do @transaction.connect_adapter_when_begin(@adapter) end.should raise_error
313
+ end
314
+ it "should not delegate calls that we can not respond to" do
315
+ lambda do @transaction.connect_adapters_unless_begin(@adapter) end.should raise_error
316
+ lambda do @transaction.connect_adapters_if_begin(@adapter) end.should raise_error
317
+ end
318
+ end
319
+ it "should be able to produce the connection for an adapter" do
320
+ @transaction_primitive.stub!(:begin)
321
+ @repository_transaction_primitive.stub!(:begin)
322
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
323
+ @transaction.begin
324
+ @transaction.primitive_for(@adapter).should == @transaction_primitive
325
+ end
326
+ describe "#each_adapter" do
327
+ before :each do
328
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
329
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::AnyArgsConstraint).and_return(false)
330
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::NoArgsConstraint).and_return(false)
331
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Regexp).and_return(false)
332
+ @repository_adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::AnyArgsConstraint).and_return(false)
333
+ @repository_adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::NoArgsConstraint).and_return(false)
334
+ @repository_adapter.should_receive(:is_a?).any_number_of_times.with(Regexp).and_return(false)
335
+ end
336
+ it "should send the first argument to itself once for each adapter" do
337
+ @transaction.should_receive(:plupp).with(@adapter)
338
+ @transaction.should_receive(:plupp).with(@repository_adapter)
339
+ @transaction.instance_eval do each_adapter(:plupp, [:plur]) end
340
+ end
341
+ it "should stop sending if any call raises an exception, then send each element of the second argument to itself with each adapter as argument" do
342
+ a1 = @repository_adapter
343
+ a2 = @adapter
344
+ @transaction.adapters.instance_eval do
345
+ @a1 = a1
346
+ @a2 = a2
347
+ def each(&block)
348
+ yield(@a1, :none)
349
+ yield(@a2, :none)
350
+ end
351
+ end
352
+ @transaction.should_receive(:plupp).with(@repository_adapter).and_throw(Exception.new("test error - dont mind me"))
353
+ @transaction.should_not_receive(:plupp).with(@adapter)
354
+ @transaction.should_receive(:plur).with(@adapter)
355
+ @transaction.should_receive(:plur).with(@repository_adapter)
356
+ lambda do @transaction.instance_eval do each_adapter(:plupp, [:plur]) end end.should raise_error(Exception, /test error - dont mind me/)
357
+ end
358
+ it "should send each element of the second argument to itself with each adapter as argument even if exceptions occur in the process" do
359
+ a1 = @repository_adapter
360
+ a2 = @adapter
361
+ @transaction.adapters.instance_eval do
362
+ @a1 = a1
363
+ @a2 = a2
364
+ def each(&block)
365
+ yield(@a1, :none)
366
+ yield(@a2, :none)
367
+ end
368
+ end
369
+ @transaction.should_receive(:plupp).with(@repository_adapter).and_throw(Exception.new("test error - dont mind me"))
370
+ @transaction.should_not_receive(:plupp).with(@adapter)
371
+ @transaction.should_receive(:plur).with(@adapter).and_throw(Exception.new("another test error"))
372
+ @transaction.should_receive(:plur).with(@repository_adapter).and_throw(Exception.new("yet another error"))
373
+ lambda do @transaction.instance_eval do each_adapter(:plupp, [:plur]) end end.should raise_error(Exception, /test error - dont mind me/)
374
+ end
375
+ end
376
+ it "should be able to return the state for a given adapter" do
377
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
378
+ a1 = @adapter
379
+ a2 = @repository_adapter
380
+ @transaction.instance_eval do state_for(a1) end.should == :none
381
+ @transaction.instance_eval do state_for(a2) end.should == :none
382
+ @transaction.instance_eval do @adapters[a1] = :begin end
383
+ @transaction.instance_eval do state_for(a1) end.should == :begin
384
+ @transaction.instance_eval do state_for(a2) end.should == :none
385
+ end
386
+ describe "#do_adapter" do
387
+ before :each do
388
+ @transaction = DataMapper::Transaction.new(@adapter, @repository)
389
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::AnyArgsConstraint).and_return(false)
390
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::NoArgsConstraint).and_return(false)
391
+ @adapter.should_receive(:is_a?).any_number_of_times.with(Regexp).and_return(false)
392
+ end
393
+ it "should raise if there is no connection for the adapter" do
394
+ a1 = @adapter
395
+ lambda do @transaction.instance_eval do do_adapter(a1, :ping, :pong) end end.should raise_error(Exception, /No primitive/)
396
+ end
397
+ it "should raise if the adapter has the wrong state" do
398
+ @transaction_primitive.stub!(:begin)
399
+ @repository_transaction_primitive.stub!(:begin)
400
+ @transaction.begin
401
+ a1 = @adapter
402
+ @adapter.should_not_receive(:ping_transaction).with(@transaction)
403
+ lambda do @transaction.instance_eval do do_adapter(a1, :ping, :pong) end end.should raise_error(Exception, /Illegal state/)
404
+ end
405
+ it "should delegate to the adapter if the connection exists and we have the right state" do
406
+ @transaction_primitive.stub!(:begin)
407
+ @repository_transaction_primitive.stub!(:begin)
408
+ @transaction.begin
409
+ a1 = @adapter
410
+ @transaction_primitive.should_receive(:ping)
411
+ @transaction.instance_eval do do_adapter(a1, :ping, :begin) end
412
+ end
413
+ end
414
+ describe "#connect_adapter" do
415
+ before :each do
416
+ @other_adapter = mock("adapter")
417
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
418
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(true)
419
+ @transaction = DataMapper::Transaction.new(@other_adapter)
420
+ end
421
+ it "should be able to connect an adapter" do
422
+ a1 = @other_adapter
423
+ @other_adapter.should_receive(:transaction_primitive).and_return(@transaction_primitive)
424
+ @transaction.instance_eval do connect_adapter(a1) end
425
+ @transaction.transaction_primitives[@other_adapter].should == @transaction_primitive
426
+ end
427
+ end
428
+ describe "#close adapter" do
429
+ before :each do
430
+ @other_adapter = mock("adapter")
431
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
432
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(true)
433
+ @transaction = DataMapper::Transaction.new(@other_adapter)
434
+ end
435
+ it "should be able to close the connection of an adapter" do
436
+ a1 = @other_adapter
437
+ @transaction_primitive.should_receive(:close)
438
+ @other_adapter.should_receive(:transaction_primitive).and_return(@transaction_primitive)
439
+ @transaction.instance_eval do connect_adapter(a1) end
440
+ @transaction.transaction_primitives[@other_adapter].should == @transaction_primitive
441
+ @transaction.instance_eval do close_adapter(a1) end
442
+ @transaction.transaction_primitives[@other_adapter].should == nil
443
+ end
444
+ end
445
+ describe "the transaction operation methods" do
446
+ before :each do
447
+ @other_adapter = mock("adapter")
448
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Array).and_return(false)
449
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(DataMapper::Adapters::AbstractAdapter).and_return(true)
450
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::AnyArgsConstraint).and_return(false)
451
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Spec::Mocks::NoArgsConstraint).and_return(false)
452
+ @other_adapter.should_receive(:is_a?).any_number_of_times.with(Regexp).and_return(false)
453
+ @transaction = DataMapper::Transaction.new(@other_adapter)
454
+ end
455
+ it "should only allow adapters in state :none to begin" do
456
+ a1 = @other_adapter
457
+ @transaction.should_receive(:do_adapter).with(@other_adapter, :begin, :none)
458
+ @transaction.instance_eval do begin_adapter(a1) end
459
+ end
460
+ it "should only allow adapters in state :begin to prepare" do
461
+ a1 = @other_adapter
462
+ @transaction.should_receive(:do_adapter).with(@other_adapter, :prepare, :begin)
463
+ @transaction.instance_eval do prepare_adapter(a1) end
464
+ end
465
+ it "should only allow adapters in state :prepare to commit" do
466
+ a1 = @other_adapter
467
+ @transaction.should_receive(:do_adapter).with(@other_adapter, :commit, :prepare)
468
+ @transaction.instance_eval do commit_adapter(a1) end
469
+ end
470
+ it "should only allow adapters in state :begin to rollback" do
471
+ a1 = @other_adapter
472
+ @transaction.should_receive(:do_adapter).with(@other_adapter, :rollback, :begin)
473
+ @transaction.instance_eval do rollback_adapter(a1) end
474
+ end
475
+ it "should only allow adapters in state :prepare to rollback_prepared" do
476
+ a1 = @other_adapter
477
+ @transaction.should_receive(:do_adapter).with(@other_adapter, :rollback_prepared, :prepare)
478
+ @transaction.instance_eval do rollback_prepared_adapter(a1) end
479
+ end
480
+ it "should do delegate properly for rollback_and_close" do
481
+ a1 = @other_adapter
482
+ @transaction.should_receive(:rollback_adapter).with(@other_adapter)
483
+ @transaction.should_receive(:close_adapter).with(@other_adapter)
484
+ @transaction.instance_eval do rollback_and_close_adapter(a1) end
485
+ end
486
+ it "should do delegate properly for rollback_prepared_and_close" do
487
+ a1 = @other_adapter
488
+ @transaction.should_receive(:rollback_prepared_adapter).with(@other_adapter)
489
+ @transaction.should_receive(:close_adapter).with(@other_adapter)
490
+ @transaction.instance_eval do rollback_prepared_and_close_adapter(a1) end
491
+ end
492
+ end
493
+ end