rpbertp13-dm-core 0.9.11.1

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