dm-fluiddb-adapter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +20 -0
- data/README.rdoc +56 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/dm-fluiddb-adapter.gemspec +71 -0
- data/lib/dm-fluiddb-adapter/fixes.rb +31 -0
- data/lib/dm-fluiddb-adapter/typhoeus_client.rb +126 -0
- data/lib/fluiddb_adapter.rb +611 -0
- data/lib/loggable.rb +27 -0
- data/spec/fluiddb_adapter_spec.rb +189 -0
- data/spec/integration_spec.rb +302 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/typhoeus_client_spec.rb +33 -0
- metadata +115 -0
data/lib/loggable.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Loggable
|
4
|
+
def self.logger=(global)
|
5
|
+
@logger = global
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.logger
|
9
|
+
@logger ||= Logger.new(STDERR)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.class_eval do
|
14
|
+
def logger=(l)
|
15
|
+
@logger = l
|
16
|
+
end
|
17
|
+
|
18
|
+
def logger
|
19
|
+
@logger ||= Loggable.logger
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def logger
|
25
|
+
self.class.logger
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe DataMapper::Adapters::FluidDBAdapter do
|
4
|
+
before :all do
|
5
|
+
@adapter = DataMapper.setup(:default, DATABASE_URL)
|
6
|
+
@repository = DataMapper.repository(:default)
|
7
|
+
|
8
|
+
@http = mock('http client')
|
9
|
+
@adapter.instance_variable_set("@http", @http)
|
10
|
+
|
11
|
+
@cache = mock('memcache client')
|
12
|
+
DataMapper::Adapters::FluidDBAdapter.cache = @cache
|
13
|
+
|
14
|
+
class Flinstone
|
15
|
+
include DataMapper::Resource
|
16
|
+
|
17
|
+
property :id, String, :key => true
|
18
|
+
property :name, String
|
19
|
+
property :age, Integer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def tag(name)
|
24
|
+
prefix = @adapter.send(:tag_prefix) + '/flinstones'
|
25
|
+
name.nil? ? prefix : (prefix + '/' + name)
|
26
|
+
end
|
27
|
+
|
28
|
+
after :all do
|
29
|
+
DataMapper::Adapters::FluidDBAdapter.cache = CACHE
|
30
|
+
end
|
31
|
+
|
32
|
+
describe :create do
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe :read do
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
describe :delete do
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe :check_tag do
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe :check_namespace do
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
describe :paged_query do
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe :update do
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe :conditions_statement do
|
61
|
+
def test(c)
|
62
|
+
@adapter.send(:conditions_statement, c)
|
63
|
+
end
|
64
|
+
|
65
|
+
before(:all) do
|
66
|
+
@id = Flinstone.properties[:id]
|
67
|
+
@name = Flinstone.properties[:name]
|
68
|
+
@age = Flinstone.properties[:age]
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should extract ids' do
|
72
|
+
cond = DataMapper::Query::Conditions::EqualToComparison.new(@id, 'wilma')
|
73
|
+
test(cond).should == '|id:wilma|'
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'should handle inclusion comparisons' do
|
77
|
+
andc = DataMapper::Query::Conditions::AndOperation.new
|
78
|
+
andc << DataMapper::Query::Conditions::InclusionComparison.new(@name, 1...6)
|
79
|
+
|
80
|
+
test(andc).should == "(#{tag('name')} >= 1 and #{tag('name')} < 6)"
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should handle negative operators' do
|
84
|
+
notc = DataMapper::Query::Conditions::NotOperation.new
|
85
|
+
notc << DataMapper::Query::Conditions::EqualToComparison.new(@age, 78)
|
86
|
+
andc = DataMapper::Query::Conditions::AndOperation.new
|
87
|
+
andc << "has #{tag(nil)}"
|
88
|
+
andc << notc
|
89
|
+
|
90
|
+
test(andc).should == "has #{tag(nil)} except #{tag('age')} = 78"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe :query_ids do
|
95
|
+
it 'should parse extracted ids' do
|
96
|
+
#@adapter.stub(:blocking_request)
|
97
|
+
|
98
|
+
q = DataMapper::Query.new(@repository, Flinstone, :id => 'wilma')
|
99
|
+
@adapter.send(:query_ids, q, false).should == [ 'wilma' ]
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should add the identity query' do
|
103
|
+
@adapter.should_receive(:fluiddb_query).with("has #{tag(nil)}").and_yield(['list', 'of', 'ids'])
|
104
|
+
|
105
|
+
q = DataMapper::Query.new(@repository, Flinstone)
|
106
|
+
@adapter.send(:query_ids, q).should == [ 'list', 'of', 'ids' ]
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe :fetch_fields do
|
111
|
+
def mock_multi_get(keys, values=nil)
|
112
|
+
result = keys.map_hash do |k|
|
113
|
+
value =values.nil? ? nil : values[keys.index(k)]
|
114
|
+
[ k, value ]
|
115
|
+
end
|
116
|
+
|
117
|
+
@cache.should_receive(:get) {|args|
|
118
|
+
args.sort.should == keys.sort
|
119
|
+
result
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
before(:each) do
|
124
|
+
@ids = [ 'id1', 'id2' ]
|
125
|
+
@fields = [ 'description', 'path' ]
|
126
|
+
@tags = @fields.map_hash {|f| [ f, 'fluiddb/tags/'+f] }
|
127
|
+
@cached = @ids.map_hash do |id|
|
128
|
+
[ @adapter.send(:object_cache_key, id, @tags.values),
|
129
|
+
@fields.map_hash.merge("id"=>id) ]
|
130
|
+
end
|
131
|
+
|
132
|
+
@http.should_receive(:parallel).and_yield
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'when objects are cached' do
|
136
|
+
it 'should not fetch any fields' do
|
137
|
+
mock_multi_get(@cached.keys, @cached.values)
|
138
|
+
@adapter.send(:fetch_fields, @ids, @tags)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'when objects are not cached' do
|
143
|
+
before(:each) do
|
144
|
+
mock_multi_get(@cached.keys)
|
145
|
+
|
146
|
+
@ids.each do |id|
|
147
|
+
key = @adapter.send(:object_cache_key, id, @tags.values)
|
148
|
+
@cache.should_receive(:set).with(key, @fields.map_hash.merge("id"=>id), 600)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should fetch fields when not cached' do
|
153
|
+
@ids.each do |id|
|
154
|
+
mock_multi_get @fields.map {|f| id+'/fluiddb/tags/'+f }
|
155
|
+
|
156
|
+
@fields.each do |field|
|
157
|
+
resp = mock(:code => 200, :body => field)
|
158
|
+
@http.should_receive(:get).with("/objects/#{id}/fluiddb/tags/#{field}").and_yield(resp)
|
159
|
+
@cache.should_receive(:set).with("#{id}/fluiddb/tags/#{field}", field, 600)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
@adapter.send(:fetch_fields, @ids, @tags)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should not fetch fields when cached' do
|
167
|
+
@ids.each do |id|
|
168
|
+
values = @fields.map_hash {|f| [ id+'/fluiddb/tags/'+f, f ] }
|
169
|
+
mock_multi_get(values.keys, values.values)
|
170
|
+
end
|
171
|
+
|
172
|
+
@adapter.send(:fetch_fields, @ids, @tags)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe :save do
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
describe :serialize do
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
describe :tags do
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
# This had to be copied from the db-core source because this adapter
|
2
|
+
# doesn't support serial keys which the spec previously relied upon
|
3
|
+
require 'spec/spec_helper'
|
4
|
+
|
5
|
+
describe DataMapper::Adapters::FluidDBAdapter do
|
6
|
+
before :all do
|
7
|
+
@adapter = DataMapper.setup(:default, DATABASE_URL)
|
8
|
+
@repository = DataMapper.repository(:default)
|
9
|
+
|
10
|
+
class ::Heffalump
|
11
|
+
include DataMapper::Resource
|
12
|
+
|
13
|
+
property :id, String, :key => true
|
14
|
+
property :color, String
|
15
|
+
property :num_spots, Integer
|
16
|
+
property :striped, Boolean
|
17
|
+
end
|
18
|
+
|
19
|
+
# create all tables and constraints before each spec
|
20
|
+
if @repository.respond_to?(:auto_migrate!)
|
21
|
+
Heffalump.auto_migrate!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
after :all do
|
26
|
+
Heffalump.repository.delete(Heffalump.all)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.adapter_supports?(*methods)
|
30
|
+
methods.all? do |method|
|
31
|
+
# TODO: figure out a way to see if the instance method is only inherited
|
32
|
+
# from the Abstract Adapter, and not defined in it's class. If that is
|
33
|
+
# the case return false
|
34
|
+
|
35
|
+
# CRUD methods can be inherited from parent class
|
36
|
+
described_type.instance_methods.any? { |instance_method| method.to_s == instance_method.to_s }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
if adapter_supports?(:create)
|
41
|
+
describe '#create' do
|
42
|
+
it 'should not raise any errors' do
|
43
|
+
lambda {
|
44
|
+
Heffalump.create(:color => 'peach')
|
45
|
+
}.should_not raise_error
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should set the identity field for the resource' do
|
49
|
+
heffalump = Heffalump.new(:color => 'peach')
|
50
|
+
heffalump.id.should be_nil
|
51
|
+
heffalump.save
|
52
|
+
heffalump.id.should_not be_nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
else
|
56
|
+
it 'needs to support #create'
|
57
|
+
end
|
58
|
+
|
59
|
+
if adapter_supports?(:read)
|
60
|
+
describe '#read' do
|
61
|
+
before :all do
|
62
|
+
@heffalump = Heffalump.create(:color => 'brownish hue')
|
63
|
+
#just going to borrow this, so I can check the return values
|
64
|
+
@query = Heffalump.all.query
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should not raise any errors' do
|
68
|
+
lambda {
|
69
|
+
Heffalump.all()
|
70
|
+
}.should_not raise_error
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should return stuff' do
|
74
|
+
Heffalump.all.should be_include(@heffalump)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
else
|
78
|
+
it 'needs to support #read'
|
79
|
+
end
|
80
|
+
|
81
|
+
if adapter_supports?(:update)
|
82
|
+
describe '#update' do
|
83
|
+
before do
|
84
|
+
@heffalump = Heffalump.create(:color => 'indigo')
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should not raise any errors' do
|
88
|
+
lambda {
|
89
|
+
@heffalump.color = 'violet'
|
90
|
+
@heffalump.save
|
91
|
+
}.should_not raise_error
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should not alter the identity field' do
|
95
|
+
id = @heffalump.id
|
96
|
+
@heffalump.color = 'violet'
|
97
|
+
@heffalump.save
|
98
|
+
@heffalump.id.should == id
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should update altered fields' do
|
102
|
+
@heffalump.color = 'violet'
|
103
|
+
@heffalump.save
|
104
|
+
Heffalump.get(*@heffalump.key).color.should == 'violet'
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should not alter other fields' do
|
108
|
+
color = @heffalump.color
|
109
|
+
@heffalump.num_spots = 3
|
110
|
+
@heffalump.save
|
111
|
+
Heffalump.get(*@heffalump.key).color.should == color
|
112
|
+
end
|
113
|
+
end
|
114
|
+
else
|
115
|
+
it 'needs to support #update'
|
116
|
+
end
|
117
|
+
|
118
|
+
if adapter_supports?(:delete)
|
119
|
+
describe '#delete' do
|
120
|
+
before do
|
121
|
+
@heffalump = Heffalump.create(:color => 'forest green')
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should not raise any errors' do
|
125
|
+
lambda {
|
126
|
+
@heffalump.destroy
|
127
|
+
}.should_not raise_error
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'should delete the requested resource' do
|
131
|
+
id = @heffalump.id
|
132
|
+
@heffalump.destroy
|
133
|
+
Heffalump.get(id).should be_nil
|
134
|
+
end
|
135
|
+
end
|
136
|
+
else
|
137
|
+
it 'needs to support #delete'
|
138
|
+
end
|
139
|
+
|
140
|
+
if adapter_supports?(:read, :create)
|
141
|
+
describe 'query matching' do
|
142
|
+
before :all do
|
143
|
+
@red = Heffalump.create(:color => 'red')
|
144
|
+
@two = Heffalump.create(:num_spots => 2)
|
145
|
+
@five = Heffalump.create(:num_spots => 5)
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'conditions' do
|
149
|
+
describe 'eql' do
|
150
|
+
it 'should be able to search for objects included in an inclusive range of values' do
|
151
|
+
Heffalump.all(:num_spots => 1..5).should be_include(@five)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should be able to search for objects included in an exclusive range of values' do
|
155
|
+
Heffalump.all(:num_spots => 1...6).should be_include(@five)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should not be able to search for values not included in an inclusive range of values' do
|
159
|
+
Heffalump.all(:num_spots => 1..4).should_not be_include(@five)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should not be able to search for values not included in an exclusive range of values' do
|
163
|
+
Heffalump.all(:num_spots => 1...5).should_not be_include(@five)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'not' do
|
168
|
+
it 'should be able to search for objects with not equal value' do
|
169
|
+
Heffalump.all(:color.not => 'red').should_not be_include(@red)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should include objects that are not like the value' do
|
173
|
+
Heffalump.all(:color.not => 'black').should be_include(@red)
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should be able to search for objects with not nil value' do
|
177
|
+
Heffalump.all(:color.not => nil).should be_include(@red)
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should not include objects with a nil value' do
|
181
|
+
Heffalump.all(:color.not => nil).should_not be_include(@two)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should be able to search for objects not included in an array of values' do
|
185
|
+
Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should be_include(@two)
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should be able to search for objects not included in an array of values' do
|
189
|
+
Heffalump.all(:num_spots.not => [ 1, 3, 5, 7 ]).should_not be_include(@five)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should be able to search for objects not included in an inclusive range of values' do
|
193
|
+
Heffalump.all(:num_spots.not => 1..4).should be_include(@five)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should be able to search for objects not included in an exclusive range of values' do
|
197
|
+
Heffalump.all(:num_spots.not => 1...5).should be_include(@five)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should not be able to search for values not included in an inclusive range of values' do
|
201
|
+
Heffalump.all(:num_spots.not => 1..5).should_not be_include(@five)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should not be able to search for values not included in an exclusive range of values' do
|
205
|
+
Heffalump.all(:num_spots.not => 1...6).should_not be_include(@five)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe 'like' do
|
210
|
+
it 'should be able to search for objects that match value' do
|
211
|
+
Heffalump.all(:color.like => '%ed').should be_include(@red)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'should not search for objects that do not match the value' do
|
215
|
+
Heffalump.all(:color.like => '%blak%').should_not be_include(@red)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
describe 'regexp' do
|
220
|
+
before do
|
221
|
+
if defined?(DataMapper::Adapters::Sqlite3Adapter) && @adapter.kind_of?(DataMapper::Adapters::Sqlite3Adapter)
|
222
|
+
pending 'delegate regexp matches to same system that the InMemory and YAML adapters use'
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'should be able to search for objects that match value' do
|
227
|
+
Heffalump.all(:color => /ed/).should be_include(@red)
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'should not be able to search for objects that do not match the value' do
|
231
|
+
Heffalump.all(:color => /blak/).should_not be_include(@red)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should be able to do a negated search for objects that match value' do
|
235
|
+
Heffalump.all(:color.not => /blak/).should be_include(@red)
|
236
|
+
end
|
237
|
+
|
238
|
+
it 'should not be able to do a negated search for objects that do not match value' do
|
239
|
+
Heffalump.all(:color.not => /ed/).should_not be_include(@red)
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
describe 'gt' do
|
245
|
+
it 'should be able to search for objects with value greater than' do
|
246
|
+
Heffalump.all(:num_spots.gt => 1).should be_include(@two)
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'should not find objects with a value less than' do
|
250
|
+
Heffalump.all(:num_spots.gt => 3).should_not be_include(@two)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe 'gte' do
|
255
|
+
it 'should be able to search for objects with value greater than' do
|
256
|
+
Heffalump.all(:num_spots.gte => 1).should be_include(@two)
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'should be able to search for objects with values equal to' do
|
260
|
+
Heffalump.all(:num_spots.gte => 2).should be_include(@two)
|
261
|
+
end
|
262
|
+
|
263
|
+
it 'should not find objects with a value less than' do
|
264
|
+
Heffalump.all(:num_spots.gte => 3).should_not be_include(@two)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
describe 'lt' do
|
269
|
+
it 'should be able to search for objects with value less than' do
|
270
|
+
Heffalump.all(:num_spots.lt => 3).should be_include(@two)
|
271
|
+
end
|
272
|
+
|
273
|
+
it 'should not find objects with a value less than' do
|
274
|
+
Heffalump.all(:num_spots.gt => 2).should_not be_include(@two)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
describe 'lte' do
|
279
|
+
it 'should be able to search for objects with value less than' do
|
280
|
+
Heffalump.all(:num_spots.lte => 3).should be_include(@two)
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'should be able to search for objects with values equal to' do
|
284
|
+
Heffalump.all(:num_spots.lte => 2).should be_include(@two)
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'should not find objects with a value less than' do
|
288
|
+
Heffalump.all(:num_spots.lte => 1).should_not be_include(@two)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe 'limits' do
|
294
|
+
it 'should be able to limit the objects' do
|
295
|
+
Heffalump.all(:limit => 2).length.should == 2
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
else
|
300
|
+
it 'needs to support #read and #create to test query matching'
|
301
|
+
end
|
302
|
+
end
|