dm-appengine 0.0.2 → 0.0.3
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.
- data/Rakefile +3 -2
- data/lib/appengine_adapter.rb +2 -4
- data/spec/spec_helper.rb +3 -1
- metadata +2 -5
- data/lib/dm-appengine.rb-custom_resource +0 -365
- data/lib/dm-appengine/appengine_resource.rb +0 -34
- data/lib/dm-appengine/transactions.rb +0 -26
data/Rakefile
CHANGED
@@ -5,7 +5,7 @@ require 'date'
|
|
5
5
|
require 'spec/rake/spectask'
|
6
6
|
|
7
7
|
GEM = "dm-appengine"
|
8
|
-
GEM_VERSION = "0.0.
|
8
|
+
GEM_VERSION = "0.0.3"
|
9
9
|
AUTHOR = "Ryan Brown"
|
10
10
|
EMAIL = "ribrdb@gmail.com"
|
11
11
|
HOMEPAGE = "http://code.google.com/p/appengine-jruby"
|
@@ -28,7 +28,8 @@ spec = Gem::Specification.new do |s|
|
|
28
28
|
|
29
29
|
s.require_path = 'lib'
|
30
30
|
s.autorequire = GEM
|
31
|
-
s.files = %w(LICENSE README.rdoc Rakefile) +
|
31
|
+
s.files = %w(LICENSE README.rdoc Rakefile lib/appengine_adapter.rb) +
|
32
|
+
Dir.glob("spec/**/*")
|
32
33
|
end
|
33
34
|
|
34
35
|
task :default => :spec
|
data/lib/appengine_adapter.rb
CHANGED
@@ -21,8 +21,6 @@ require 'date'
|
|
21
21
|
require 'rubygems'
|
22
22
|
require 'time'
|
23
23
|
|
24
|
-
gem 'appengine-apis', '~> 0.0.6'
|
25
|
-
|
26
24
|
require 'appengine-apis/datastore'
|
27
25
|
require 'dm-core'
|
28
26
|
|
@@ -359,13 +357,13 @@ module DataMapper
|
|
359
357
|
# TODO: This is broken. We should be setting all properties
|
360
358
|
return if entity.nil?
|
361
359
|
key = entity.get_key
|
362
|
-
hash =
|
360
|
+
hash = {}
|
363
361
|
@dm_query.fields.each do |property|
|
364
362
|
name = property.field
|
365
363
|
if property.key?
|
366
364
|
hash[name] = key.get_name || key.get_id
|
367
365
|
else
|
368
|
-
hash[name] = property.typecast(
|
366
|
+
hash[name] = property.typecast(entity.get_property(name))
|
369
367
|
end
|
370
368
|
end
|
371
369
|
hash
|
data/spec/spec_helper.rb
CHANGED
@@ -19,7 +19,9 @@ $TESTING=true
|
|
19
19
|
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
20
20
|
|
21
21
|
require 'rubygems'
|
22
|
-
require '
|
22
|
+
require 'appengine-sdk'
|
23
|
+
AppEngine::SDK.load_apiproxy
|
24
|
+
require 'dm-core'
|
23
25
|
|
24
26
|
ADAPTERS = ['default']
|
25
27
|
PRIMARY = {'default' => "appengine://memory"}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-appengine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Brown
|
@@ -9,7 +9,7 @@ autorequire: dm-appengine
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-14 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -46,9 +46,6 @@ files:
|
|
46
46
|
- README.rdoc
|
47
47
|
- Rakefile
|
48
48
|
- lib/appengine_adapter.rb
|
49
|
-
- lib/dm-appengine/appengine_resource.rb
|
50
|
-
- lib/dm-appengine/transactions.rb
|
51
|
-
- lib/dm-appengine.rb-custom_resource
|
52
49
|
- spec/dm-appengine_spec.rb
|
53
50
|
- spec/spec_helper.rb
|
54
51
|
has_rdoc: true
|
@@ -1,365 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby1.8 -w
|
2
|
-
#
|
3
|
-
# Copyright:: Copyright 2009 Google Inc.
|
4
|
-
# Original Author:: Ryan Brown (mailto:ribrdb@google.com)
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
# Datamapper adapter for Google App Engine
|
19
|
-
|
20
|
-
$:.unshift(File.dirname(__FILE__)) unless
|
21
|
-
$:.include?(File.dirname(__FILE__)) ||
|
22
|
-
$:.include?(File.expand_path(File.dirname(__FILE__)))
|
23
|
-
|
24
|
-
require 'rubygems'
|
25
|
-
gem 'appengine-apis', '~> 0.0.3'
|
26
|
-
|
27
|
-
require 'appengine-apis/datastore'
|
28
|
-
require 'dm-core'
|
29
|
-
require 'dm-appengine/appengine_resource'
|
30
|
-
|
31
|
-
module DataMapper
|
32
|
-
module Adapters
|
33
|
-
class AppEngineAdapter < AbstractAdapter
|
34
|
-
Datastore = AppEngine::Datastore
|
35
|
-
|
36
|
-
def initialize(name, uri_or_options)
|
37
|
-
super
|
38
|
-
if uri_or_options.kind_of? Hash
|
39
|
-
options = uri_or_options
|
40
|
-
if options['host'] == 'memory'
|
41
|
-
require 'appengine-apis/testing'
|
42
|
-
begin
|
43
|
-
AppEngine::ApiProxy.get_app_id
|
44
|
-
rescue NoMethodError
|
45
|
-
AppEngine::Testing::install_test_env
|
46
|
-
end
|
47
|
-
AppEngine::Testing::install_test_datastore
|
48
|
-
end
|
49
|
-
end
|
50
|
-
@resource_naming_convention = lambda do |value|
|
51
|
-
Extlib::Inflection.pluralize(Extlib::Inflection.camelize(value))
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def kind(model)
|
56
|
-
model.storage_name(name)
|
57
|
-
end
|
58
|
-
|
59
|
-
def create(resources)
|
60
|
-
created = 0
|
61
|
-
entities = []
|
62
|
-
resources.each do |resource|
|
63
|
-
attributes = resource.attributes
|
64
|
-
|
65
|
-
entity = create_entity(resource)
|
66
|
-
|
67
|
-
attributes.each do |name, value|
|
68
|
-
unless name =~ /^__.+__$/
|
69
|
-
entity.set_property(name.to_s, value)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
entities << entity
|
73
|
-
created += 1
|
74
|
-
end
|
75
|
-
Datastore.put(entities)
|
76
|
-
resources.zip(entities) do |resource, entity|
|
77
|
-
key = entity.key
|
78
|
-
if identity_field = resource.model.identity_field(name)
|
79
|
-
identity_field.set!(resource, key.get_name || key.get_id)
|
80
|
-
end
|
81
|
-
resource.instance_variable_set :@__entity__, entity
|
82
|
-
end
|
83
|
-
return created
|
84
|
-
end
|
85
|
-
|
86
|
-
def read(query)
|
87
|
-
query = QueryBuilder.new(query, kind(query.model), self)
|
88
|
-
query.run
|
89
|
-
end
|
90
|
-
|
91
|
-
def update(attributes, collection)
|
92
|
-
attributes = attributes_as_fields(attributes)
|
93
|
-
entities = collection.collect do |resource|
|
94
|
-
entity = resource.instance_variable_get :@__entity__
|
95
|
-
entity.update(attributes)
|
96
|
-
end
|
97
|
-
|
98
|
-
Datastore.put(entities)
|
99
|
-
entities.size
|
100
|
-
end
|
101
|
-
|
102
|
-
def delete(collection)
|
103
|
-
keys = collection.collect do |resource|
|
104
|
-
entity = resource.instance_variable_get :@__entity__
|
105
|
-
entity.key
|
106
|
-
end
|
107
|
-
Datastore.delete(keys)
|
108
|
-
end
|
109
|
-
|
110
|
-
private
|
111
|
-
|
112
|
-
def create_entity(resource)
|
113
|
-
kind = self.kind(resource.model)
|
114
|
-
parent, name = resource.appengine_key_components(resource.repository)
|
115
|
-
if name.kind_of? Integer
|
116
|
-
if name == 0
|
117
|
-
name = nil
|
118
|
-
else
|
119
|
-
raise ArgumentError, 'Numeric ids must be assigned by the Datastore'
|
120
|
-
end
|
121
|
-
end
|
122
|
-
if name
|
123
|
-
Datastore::Entity.new(kind, name, parent)
|
124
|
-
elsif parent
|
125
|
-
Datastore::Entity.new(kind, parent)
|
126
|
-
else
|
127
|
-
Datastore::Entity.new(kind)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
class QueryBuilder
|
132
|
-
import Datastore::JavaDatastore::FetchOptions
|
133
|
-
include Datastore::Query::Constants
|
134
|
-
@@OPERATORS = {
|
135
|
-
Conditions::EqualToComparison => EQUAL,
|
136
|
-
Conditions::GreaterThanComparison => GREATER_THAN,
|
137
|
-
Conditions::GreaterThanOrEqualToComparison => GREATER_THAN_OR_EQUAL,
|
138
|
-
Conditions::LessThanComparison => LESS_THAN,
|
139
|
-
Conditions::LessThanOrEqualToComparison => LESS_THAN_OR_EQUAL,
|
140
|
-
}.freeze
|
141
|
-
|
142
|
-
def initialize(query, kind, adapter)
|
143
|
-
@model = query.model
|
144
|
-
@kind = kind
|
145
|
-
@limit = query.limit
|
146
|
-
@offset = query.offset
|
147
|
-
@maybe_get = true
|
148
|
-
@must_be_get = false
|
149
|
-
@keys = []
|
150
|
-
@dm_query = query
|
151
|
-
@adapter_name = adapter.name
|
152
|
-
|
153
|
-
@query = Datastore::Query.new(kind)
|
154
|
-
parse_order(query.order)
|
155
|
-
parse_conditions(query.conditions)
|
156
|
-
raise NotImplementedError if @must_be_get && !@maybe_get
|
157
|
-
end
|
158
|
-
|
159
|
-
def property_name(property, qualify=false)
|
160
|
-
if qualify
|
161
|
-
table_name = property.model.storage_name(name)
|
162
|
-
"#{table_name}.#{property.field}"
|
163
|
-
else
|
164
|
-
property.field
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def parse_order(order)
|
169
|
-
if order.size == 1 && order[0].direction != :desc
|
170
|
-
if order[0].property.key?
|
171
|
-
# omit the default key ordering.
|
172
|
-
# This lets inequality filters work
|
173
|
-
return
|
174
|
-
end
|
175
|
-
end
|
176
|
-
order.map do |order|
|
177
|
-
if order.direction == :desc
|
178
|
-
direction = DESCENDING
|
179
|
-
else
|
180
|
-
direction = ASCENDING
|
181
|
-
end
|
182
|
-
name = if order.property.key?
|
183
|
-
'__key__'
|
184
|
-
else
|
185
|
-
property_name(order.property)
|
186
|
-
end
|
187
|
-
@query.sort(name, direction)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
def parse_conditions(conditions)
|
192
|
-
case conditions
|
193
|
-
when Conditions::NotOperation then
|
194
|
-
raise NotImplementedError, "NOT operator is not supported"
|
195
|
-
when Conditions::AbstractComparison then
|
196
|
-
parse_comparison(conditions)
|
197
|
-
when Conditions::OrOperation then
|
198
|
-
parse_or(conditions)
|
199
|
-
when Conditions::AndOperation then
|
200
|
-
parse_and(conditions)
|
201
|
-
else
|
202
|
-
raise ArgumentError, "invalid conditions #{conditions.class}: #{conditions.inspect}"
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def parse_key(property, value)
|
207
|
-
unless property.key?
|
208
|
-
raise ArgumentError, "#{property_name(property, true)} is not the key"
|
209
|
-
end
|
210
|
-
case value
|
211
|
-
when Integer, String
|
212
|
-
Datastore::Key.from_path(@kind, value)
|
213
|
-
when Symbol
|
214
|
-
Datastore::Key.from_path(@kind, value.to_s)
|
215
|
-
else
|
216
|
-
raise ArgumentError "Unsupported key value #{value.inspect} (a #{value.class})"
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def parse_or(or_op)
|
221
|
-
if !@maybe_get
|
222
|
-
raise NotImplementedError, "OR only supported with key equality comparisons"
|
223
|
-
end
|
224
|
-
@must_be_get = true
|
225
|
-
or_op.operands.each do |op|
|
226
|
-
case op
|
227
|
-
when Conditions::OrOperation then
|
228
|
-
parse_or(op)
|
229
|
-
when Conditions::EqualToComparison then
|
230
|
-
key = parse_key(op.property, op.value)
|
231
|
-
@keys << key
|
232
|
-
when Conditions::InclusionComparison then
|
233
|
-
parse_key_inclusion(op)
|
234
|
-
else
|
235
|
-
raise NotImplementedError, "Unsupported condition #{op.class} inside OR"
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def parse_key_inclusion(op)
|
241
|
-
raise NotImplementedError unless op.value.kind_of? Array
|
242
|
-
op.value.each do |value|
|
243
|
-
@keys << parse_key(op.property, value)
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
def parse_and(op)
|
248
|
-
if @maybe_get && (@found_and || op.operands.size > 1)
|
249
|
-
@maybe_get = false
|
250
|
-
end
|
251
|
-
@found_and = true
|
252
|
-
op.operands.each do |conditions|
|
253
|
-
parse_conditions(conditions)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
def parse_comparison(op)
|
258
|
-
property = op.property
|
259
|
-
value = op.value
|
260
|
-
if @maybe_get
|
261
|
-
if property.key?
|
262
|
-
case op
|
263
|
-
when Conditions::EqualToComparison
|
264
|
-
@keys << parse_key(property, value)
|
265
|
-
when Conditions::InclusionComparison
|
266
|
-
parse_key_inclusion(op)
|
267
|
-
@must_be_get = true
|
268
|
-
return
|
269
|
-
else
|
270
|
-
@maybe_get = false
|
271
|
-
end
|
272
|
-
elsif ['__ancestor__', '__parent__'].include? property.field
|
273
|
-
if value.java_kind_of? AppEngine::Datastore::Key
|
274
|
-
query.ancestor = value
|
275
|
-
else
|
276
|
-
query.ancestor = value.entity.key
|
277
|
-
end
|
278
|
-
return
|
279
|
-
else
|
280
|
-
@maybe_get = false
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
if op.kind_of? Conditions::InclusionComparison
|
285
|
-
parse_range(op)
|
286
|
-
else
|
287
|
-
filter_op = @@OPERATORS[op.class]
|
288
|
-
if filter_op.nil?
|
289
|
-
raise ArgumentError, "#{op.class} is not a supported comparison"
|
290
|
-
end
|
291
|
-
@query.filter(property_name(op.property), filter_op, op.value)
|
292
|
-
end
|
293
|
-
end
|
294
|
-
|
295
|
-
def parse_range(op)
|
296
|
-
range = op.value
|
297
|
-
raise NotImplementedError unless range.is_a? Range
|
298
|
-
name = property_name(op.property)
|
299
|
-
begin_op = GREATER_THAN_OR_EQUAL
|
300
|
-
end_op = if range.exclude_end?
|
301
|
-
LESS_THAN
|
302
|
-
else
|
303
|
-
LESS_THAN_OR_EQUAL
|
304
|
-
end
|
305
|
-
@query.filter(name, begin_op, range.begin)
|
306
|
-
@query.filter(name, end_op, range.end)
|
307
|
-
end
|
308
|
-
|
309
|
-
def is_get?
|
310
|
-
@maybe_get && @keys.size > 0
|
311
|
-
end
|
312
|
-
|
313
|
-
def get_entities
|
314
|
-
if is_get?
|
315
|
-
Datastore.get(@keys)
|
316
|
-
else
|
317
|
-
begin
|
318
|
-
chunk_size = FetchOptions::DEFAULT_CHUNK_SIZE
|
319
|
-
options = FetchOptions::Builder.with_chunk_size(
|
320
|
-
chunk_size)
|
321
|
-
options.limit(@limit) if @limit
|
322
|
-
options.offset(@offset) if @offset
|
323
|
-
@query.iterator(options).collect {|e| e}
|
324
|
-
rescue java.lang.IllegalArgumentException => ex
|
325
|
-
raise ArgumentError, ex.message
|
326
|
-
end
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
def run
|
331
|
-
key_prop = @model.key(@adapter_name)[0].field
|
332
|
-
get_entities.map do |entity|
|
333
|
-
entity_to_model(key_prop, entity)
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
|
-
def entity_to_model(key_prop, entity)
|
338
|
-
# TODO: This is broken. We should be setting all properties
|
339
|
-
return if entity.nil?
|
340
|
-
props = entity.to_hash
|
341
|
-
props['__entity__'] = entity
|
342
|
-
props['__parent__'] = entity.key.parent
|
343
|
-
|
344
|
-
key = entity.get_key
|
345
|
-
id_or_name = key.get_name || key.get_id
|
346
|
-
|
347
|
-
@dm_query.fields.map do |property|
|
348
|
-
if property.key? && property.type == id_or_name.class
|
349
|
-
props[property.field] = id_or_name
|
350
|
-
break
|
351
|
-
end
|
352
|
-
end
|
353
|
-
props
|
354
|
-
end
|
355
|
-
|
356
|
-
def keys
|
357
|
-
@keys
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
# required naming scheme
|
363
|
-
AppengineAdapter = AppEngineAdapter
|
364
|
-
end
|
365
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby1.8 -w
|
2
|
-
#
|
3
|
-
# Copyright:: Copyright 2009 Google Inc.
|
4
|
-
# Original Author:: Ryan Brown (mailto:ribrdb@google.com)
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
# Custom Resource class for App Engine. Adds optimistic transaction support.
|
19
|
-
|
20
|
-
module DataMapper
|
21
|
-
module AppEngineResource
|
22
|
-
|
23
|
-
def self.included(mod)
|
24
|
-
mod.class_eval do
|
25
|
-
include DataMapper::Resource
|
26
|
-
|
27
|
-
def transaction(retries=3, &block)
|
28
|
-
AppEngine::Datastore.transaction(retries, &block)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
#!/usr/bin/ruby1.8 -w
|
2
|
-
#
|
3
|
-
# Copyright:: Copyright 2009 Google Inc.
|
4
|
-
# Original Author:: Ryan Brown (mailto:ribrdb@google.com)
|
5
|
-
#
|
6
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
-
# you may not use this file except in compliance with the License.
|
8
|
-
# You may obtain a copy of the License at
|
9
|
-
#
|
10
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
-
#
|
12
|
-
# Unless required by applicable law or agreed to in writing, software
|
13
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
-
# See the License for the specific language governing permissions and
|
16
|
-
# limitations under the License.
|
17
|
-
#
|
18
|
-
# Datamapper adapter for Google App Engine
|
19
|
-
|
20
|
-
require 'appengine-apis/datastore'
|
21
|
-
|
22
|
-
module AppEngine::Datastore
|
23
|
-
class DMTransaction
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|