sam-dm-core 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
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