ardm-do-adapter 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +35 -0
- data/Gemfile +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +6 -0
- data/ardm-do-adapter.gemspec +25 -0
- data/lib/ardm-do-adapter.rb +1 -0
- data/lib/dm-do-adapter.rb +1 -0
- data/lib/dm-do-adapter/adapter.rb +761 -0
- data/lib/dm-do-adapter/spec/shared_spec.rb +415 -0
- data/lib/dm-do-adapter/version.rb +5 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +118 -0
@@ -0,0 +1,415 @@
|
|
1
|
+
share_examples_for 'A DataObjects Adapter' do
|
2
|
+
before :all do
|
3
|
+
raise '+@adapter+ should be defined in before block' unless instance_variable_get('@adapter')
|
4
|
+
|
5
|
+
@log = StringIO.new
|
6
|
+
|
7
|
+
@original_logger = DataMapper.logger
|
8
|
+
DataMapper.logger = DataMapper::Logger.new(@log, :debug)
|
9
|
+
|
10
|
+
# set up the adapter after switching the logger so queries can be captured
|
11
|
+
@adapter = DataMapper.setup(@adapter.name, @adapter.options)
|
12
|
+
|
13
|
+
@jruby = !!(RUBY_PLATFORM =~ /java/)
|
14
|
+
|
15
|
+
@postgres = defined?(DataMapper::Adapters::PostgresAdapter) && @adapter.kind_of?(DataMapper::Adapters::PostgresAdapter)
|
16
|
+
@mysql = defined?(DataMapper::Adapters::MysqlAdapter) && @adapter.kind_of?(DataMapper::Adapters::MysqlAdapter)
|
17
|
+
@sql_server = defined?(DataMapper::Adapters::SqlserverAdapter) && @adapter.kind_of?(DataMapper::Adapters::SqlserverAdapter)
|
18
|
+
@oracle = defined?(DataMapper::Adapters::OracleAdapter) && @adapter.kind_of?(DataMapper::Adapters::OracleAdapter)
|
19
|
+
end
|
20
|
+
|
21
|
+
after :all do
|
22
|
+
DataMapper.logger = @original_logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def reset_log
|
26
|
+
@log.truncate(0)
|
27
|
+
@log.rewind
|
28
|
+
end
|
29
|
+
|
30
|
+
def log_output
|
31
|
+
@log.rewind
|
32
|
+
output = @log.read
|
33
|
+
output.chomp!
|
34
|
+
output.gsub!(/\A\s+~ \(\d+[\.,]?\d*\)\s+/, '')
|
35
|
+
output.gsub!(/\Acom\.\w+\.jdbc\.JDBC4PreparedStatement@[^:]+:\s+/, '') if @jruby
|
36
|
+
output.split($/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def supports_default_values?
|
40
|
+
@adapter.send(:supports_default_values?)
|
41
|
+
end
|
42
|
+
|
43
|
+
def supports_returning?
|
44
|
+
@adapter.send(:supports_returning?)
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#create' do
|
48
|
+
describe 'serial properties' do
|
49
|
+
before :all do
|
50
|
+
class ::Article
|
51
|
+
include DataMapper::Resource
|
52
|
+
|
53
|
+
property :id, Serial
|
54
|
+
|
55
|
+
auto_migrate!
|
56
|
+
end
|
57
|
+
|
58
|
+
reset_log
|
59
|
+
|
60
|
+
Article.create
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should not send NULL values' do
|
64
|
+
statement = if @mysql
|
65
|
+
/\AINSERT INTO `articles` \(\) VALUES \(\)\z/
|
66
|
+
elsif @oracle
|
67
|
+
/\AINSERT INTO "ARTICLES" \("ID"\) VALUES \(DEFAULT\) RETURNING "ID"/
|
68
|
+
elsif supports_default_values? && supports_returning?
|
69
|
+
/\AINSERT INTO "articles" DEFAULT VALUES RETURNING \"id\"\z/
|
70
|
+
elsif supports_default_values?
|
71
|
+
/\AINSERT INTO "articles" DEFAULT VALUES\z/
|
72
|
+
else
|
73
|
+
/\AINSERT INTO "articles" \(\) VALUES \(\)\z/
|
74
|
+
end
|
75
|
+
|
76
|
+
log_output.first.should =~ statement
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe 'properties without a default' do
|
81
|
+
before :all do
|
82
|
+
class ::Article
|
83
|
+
include DataMapper::Resource
|
84
|
+
|
85
|
+
property :id, Serial
|
86
|
+
property :title, String
|
87
|
+
|
88
|
+
auto_migrate!
|
89
|
+
end
|
90
|
+
|
91
|
+
reset_log
|
92
|
+
|
93
|
+
Article.create(:id => 1)
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should not send NULL values' do
|
97
|
+
regexp = if @mysql
|
98
|
+
/^INSERT INTO `articles` \(`id`\) VALUES \(.{1,2}\)$/i
|
99
|
+
elsif @sql_server
|
100
|
+
/^SET IDENTITY_INSERT \"articles\" ON INSERT INTO "articles" \("id"\) VALUES \(.{1,2}\) SET IDENTITY_INSERT \"articles\" OFF $/i
|
101
|
+
else
|
102
|
+
/^INSERT INTO "articles" \("id"\) VALUES \(('.{1,2}'|.{1,2})\)$/i
|
103
|
+
end
|
104
|
+
|
105
|
+
log_output.first.should =~ regexp
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#select' do
|
111
|
+
before :all do
|
112
|
+
class ::Article
|
113
|
+
include DataMapper::Resource
|
114
|
+
|
115
|
+
property :name, String, :key => true
|
116
|
+
property :author, String, :required => true
|
117
|
+
|
118
|
+
auto_migrate!
|
119
|
+
end
|
120
|
+
|
121
|
+
@article_model = Article
|
122
|
+
|
123
|
+
@article_model.create(:name => 'Learning DataMapper', :author => 'Dan Kubb')
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'when one field specified in SELECT statement' do
|
127
|
+
before :all do
|
128
|
+
@return = @adapter.select('SELECT name FROM articles')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should return an Array' do
|
132
|
+
@return.should be_kind_of(Array)
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should have a single result' do
|
136
|
+
@return.size.should == 1
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should return an Array of values' do
|
140
|
+
@return.should == [ 'Learning DataMapper' ]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'when more than one field specified in SELECT statement' do
|
145
|
+
before :all do
|
146
|
+
@return = @adapter.select('SELECT name, author FROM articles')
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should return an Array' do
|
150
|
+
@return.should be_kind_of(Array)
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should have a single result' do
|
154
|
+
@return.size.should == 1
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should return an Array of Struct objects' do
|
158
|
+
@return.first.should be_kind_of(Struct)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should return expected values' do
|
162
|
+
@return.first.values.should == [ 'Learning DataMapper', 'Dan Kubb' ]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe '#execute' do
|
168
|
+
before :all do
|
169
|
+
class ::Article
|
170
|
+
include DataMapper::Resource
|
171
|
+
|
172
|
+
property :name, String, :key => true
|
173
|
+
property :author, String, :required => true
|
174
|
+
|
175
|
+
auto_migrate!
|
176
|
+
end
|
177
|
+
|
178
|
+
@article_model = Article
|
179
|
+
end
|
180
|
+
|
181
|
+
before :all do
|
182
|
+
@result = @adapter.execute('INSERT INTO articles (name, author) VALUES(?, ?)', 'Learning DataMapper', 'Dan Kubb')
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'should return a DataObjects::Result' do
|
186
|
+
@result.should be_kind_of(DataObjects::Result)
|
187
|
+
end
|
188
|
+
|
189
|
+
it 'should affect 1 row' do
|
190
|
+
@result.affected_rows.should == 1
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should not have an insert_id' do
|
194
|
+
pending_if 'Inconsistent insert_id results', !(@postgres || @mysql || @oracle) do
|
195
|
+
@result.insert_id.should be_nil
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe '#read' do
|
201
|
+
before :all do
|
202
|
+
class ::Article
|
203
|
+
include DataMapper::Resource
|
204
|
+
|
205
|
+
property :name, String, :key => true
|
206
|
+
property :description, String, :required => false
|
207
|
+
|
208
|
+
belongs_to :parent, self, :required => false
|
209
|
+
has n, :children, self, :inverse => :parent
|
210
|
+
|
211
|
+
auto_migrate!
|
212
|
+
end
|
213
|
+
|
214
|
+
class ::Publisher
|
215
|
+
include DataMapper::Resource
|
216
|
+
|
217
|
+
property :name, String, :key => true
|
218
|
+
|
219
|
+
auto_migrate!
|
220
|
+
end
|
221
|
+
|
222
|
+
class ::Author
|
223
|
+
include DataMapper::Resource
|
224
|
+
|
225
|
+
property :name, String, :key => true
|
226
|
+
|
227
|
+
belongs_to :article
|
228
|
+
belongs_to :publisher
|
229
|
+
|
230
|
+
auto_migrate!
|
231
|
+
end
|
232
|
+
|
233
|
+
@article_model = Article
|
234
|
+
@publisher_model = Publisher
|
235
|
+
@author_model = Author
|
236
|
+
end
|
237
|
+
|
238
|
+
describe 'with a raw query' do
|
239
|
+
before :all do
|
240
|
+
@article_model.create(:name => 'Test', :description => 'Description').should be_saved
|
241
|
+
@article_model.create(:name => 'NoDescription').should be_saved
|
242
|
+
|
243
|
+
@query = DataMapper::Query.new(@repository, @article_model, :conditions => [ 'description IS NOT NULL' ])
|
244
|
+
|
245
|
+
@return = @adapter.read(@query)
|
246
|
+
end
|
247
|
+
|
248
|
+
it 'should return an Array of Hashes' do
|
249
|
+
@return.should be_kind_of(Array)
|
250
|
+
@return.all? { |entry| entry.should be_kind_of(Hash) }
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'should return expected values' do
|
254
|
+
@return.should == [ { @article_model.properties[:name] => 'Test',
|
255
|
+
@article_model.properties[:description] => 'Description',
|
256
|
+
@article_model.properties[:parent_name] => nil } ]
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'with a raw query with a bind value mismatch' do
|
261
|
+
before :all do
|
262
|
+
@article_model.create(:name => 'Test').should be_saved
|
263
|
+
|
264
|
+
@query = DataMapper::Query.new(@repository, @article_model, :conditions => [ 'name IS NOT NULL', nil ])
|
265
|
+
end
|
266
|
+
|
267
|
+
it 'should raise an error' do
|
268
|
+
lambda {
|
269
|
+
@adapter.read(@query)
|
270
|
+
}.should raise_error(ArgumentError, 'Binding mismatch: 1 for 0')
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe 'with a Collection bind value' do
|
275
|
+
describe 'with an inclusion comparison' do
|
276
|
+
before :all do
|
277
|
+
5.times do |index|
|
278
|
+
@article_model.create(:name => "Test #{index}", :parent => @article_model.last).should be_saved
|
279
|
+
end
|
280
|
+
|
281
|
+
@parents = @article_model.all
|
282
|
+
@query = DataMapper::Query.new(@repository, @article_model, :parent => @parents)
|
283
|
+
|
284
|
+
@expected = @article_model.all[1, 4].map { |article| article.attributes(:property) }
|
285
|
+
end
|
286
|
+
|
287
|
+
describe 'that is not loaded' do
|
288
|
+
before :all do
|
289
|
+
reset_log
|
290
|
+
@return = @adapter.read(@query)
|
291
|
+
end
|
292
|
+
|
293
|
+
it 'should return an Array of Hashes' do
|
294
|
+
@return.should be_kind_of(Array)
|
295
|
+
@return.all? { |entry| entry.should be_kind_of(Hash) }
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should return expected values' do
|
299
|
+
@return.should == @expected
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'should execute one subquery' do
|
303
|
+
pending_if @mysql do
|
304
|
+
log_output.size.should == 1
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe 'that is loaded' do
|
310
|
+
before :all do
|
311
|
+
@parents.to_a # lazy load the collection
|
312
|
+
end
|
313
|
+
|
314
|
+
before :all do
|
315
|
+
reset_log
|
316
|
+
@return = @adapter.read(@query)
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'should return an Array of Hashes' do
|
320
|
+
@return.should be_kind_of(Array)
|
321
|
+
@return.all? { |entry| entry.should be_kind_of(Hash) }
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'should return expected values' do
|
325
|
+
@return.should == @expected
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'should execute one query' do
|
329
|
+
log_output.size.should == 1
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
describe 'with an negated inclusion comparison' do
|
335
|
+
before :all do
|
336
|
+
5.times do |index|
|
337
|
+
@article_model.create(:name => "Test #{index}", :parent => @article_model.last).should be_saved
|
338
|
+
end
|
339
|
+
|
340
|
+
@parents = @article_model.all
|
341
|
+
@query = DataMapper::Query.new(@repository, @article_model, :parent.not => @parents)
|
342
|
+
|
343
|
+
@expected = []
|
344
|
+
end
|
345
|
+
|
346
|
+
describe 'that is not loaded' do
|
347
|
+
before :all do
|
348
|
+
reset_log
|
349
|
+
@return = @adapter.read(@query)
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'should return an Array of Hashes' do
|
353
|
+
@return.should be_kind_of(Array)
|
354
|
+
@return.all? { |entry| entry.should be_kind_of(Hash) }
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'should return expected values' do
|
358
|
+
@return.should == @expected
|
359
|
+
end
|
360
|
+
|
361
|
+
it 'should execute one subquery' do
|
362
|
+
pending_if @mysql do
|
363
|
+
log_output.size.should == 1
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
describe 'that is loaded' do
|
369
|
+
before :all do
|
370
|
+
@parents.to_a # lazy load the collection
|
371
|
+
end
|
372
|
+
|
373
|
+
before :all do
|
374
|
+
reset_log
|
375
|
+
@return = @adapter.read(@query)
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'should return an Array of Hashes' do
|
379
|
+
@return.should be_kind_of(Array)
|
380
|
+
@return.all? { |entry| entry.should be_kind_of(Hash) }
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'should return expected values' do
|
384
|
+
@return.should == @expected
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'should execute one query' do
|
388
|
+
log_output.size.should == 1
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
end
|
394
|
+
|
395
|
+
describe 'with a Query Path' do
|
396
|
+
subject { @author_model.all(query).to_a }
|
397
|
+
|
398
|
+
let(:article_name) { 'DataMapper Rocks!' }
|
399
|
+
let(:publisher_name) { 'Unbiased Press' }
|
400
|
+
let(:query) { { 'article.name' => article_name, 'publisher.name' => publisher_name } }
|
401
|
+
|
402
|
+
before do
|
403
|
+
@author = @author_model.first_or_create(
|
404
|
+
:name => 'Dan Kubb',
|
405
|
+
:article => { :name => article_name },
|
406
|
+
:publisher => { :name => publisher_name }
|
407
|
+
)
|
408
|
+
end
|
409
|
+
|
410
|
+
specify { expect { subject }.to_not raise_error }
|
411
|
+
|
412
|
+
it { should == [ @author ] }
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
data/tasks/yard.rake
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
begin
|
2
|
+
require 'pathname'
|
3
|
+
require 'yardstick/rake/measurement'
|
4
|
+
require 'yardstick/rake/verify'
|
5
|
+
|
6
|
+
# yardstick_measure task
|
7
|
+
Yardstick::Rake::Measurement.new
|
8
|
+
|
9
|
+
# verify_measurements task
|
10
|
+
Yardstick::Rake::Verify.new do |verify|
|
11
|
+
verify.threshold = 100
|
12
|
+
end
|
13
|
+
rescue LoadError
|
14
|
+
%w[ yardstick_measure verify_measurements ].each do |name|
|
15
|
+
task name.to_s do
|
16
|
+
abort "Yardstick is not available. In order to run #{name}, you must: gem install yardstick"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|