dm-datastore-adapter 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/lib/dm-datastore-adapter/datastore-adapter.rb +142 -35
- data/lib/dm-datastore-adapter/transaction.rb +67 -0
- data/lib/dm-datastore-adapter.rb +1 -0
- metadata +3 -2
data/Rakefile
CHANGED
@@ -5,6 +5,8 @@ require 'appengine-api-1.0-sdk-1.2.0.jar'
|
|
5
5
|
module DataMapper
|
6
6
|
module Adapters
|
7
7
|
class DataStoreAdapter < AbstractAdapter
|
8
|
+
class InvalidConditionError < StandardError; end
|
9
|
+
|
8
10
|
module DS
|
9
11
|
unless const_defined?("Service")
|
10
12
|
import com.google.appengine.api.datastore.DatastoreServiceFactory
|
@@ -24,18 +26,18 @@ module DataMapper
|
|
24
26
|
resources.each do |resource|
|
25
27
|
entity = DS::Entity.new(resource.class.name)
|
26
28
|
resource.attributes.each do |key, value|
|
27
|
-
ds_set(entity, key, value)
|
29
|
+
ds_set(entity, resource.model.properties[key], value)
|
28
30
|
end
|
29
31
|
begin
|
30
|
-
ds_key =
|
32
|
+
ds_key = ds_service_put(entity)
|
31
33
|
rescue Exception
|
32
34
|
else
|
33
35
|
ds_id = ds_key.get_id
|
34
36
|
resource.model.key.each do |property|
|
35
37
|
resource.attribute_set property.field, ds_id
|
36
|
-
ds_set(entity, property
|
38
|
+
ds_set(entity, property, ds_id)
|
37
39
|
end
|
38
|
-
|
40
|
+
ds_service_put(entity)
|
39
41
|
created += 1
|
40
42
|
end
|
41
43
|
end
|
@@ -46,12 +48,12 @@ module DataMapper
|
|
46
48
|
updated = 0
|
47
49
|
resources = read_many(query)
|
48
50
|
resources.each do |resource|
|
49
|
-
entity =
|
50
|
-
|
51
|
-
ds_set(entity,
|
51
|
+
entity = ds_service_get(ds_key_from_resource(resource))
|
52
|
+
attributes.each do |property, value|
|
53
|
+
ds_set(entity, property, value)
|
52
54
|
end
|
53
55
|
begin
|
54
|
-
ds_key =
|
56
|
+
ds_key = ds_service_put(entity)
|
55
57
|
rescue Exception
|
56
58
|
else
|
57
59
|
resource.model.key.each do |property|
|
@@ -64,39 +66,56 @@ module DataMapper
|
|
64
66
|
end
|
65
67
|
|
66
68
|
def delete(query)
|
67
|
-
deleted = 0
|
68
69
|
resources = read_many(query)
|
69
|
-
resources.
|
70
|
+
ds_keys = resources.map do |resource|
|
70
71
|
begin
|
71
|
-
|
72
|
-
DS::Service.delete([ds_key].to_java(DS::Key))
|
72
|
+
ds_key_from_resource(resource)
|
73
73
|
rescue Exception
|
74
|
-
|
75
|
-
deleted += 1
|
74
|
+
nil
|
76
75
|
end
|
77
|
-
end
|
78
|
-
|
76
|
+
end.compact
|
77
|
+
ds_service_delete(ds_keys.to_java(DS::Key))
|
78
|
+
ds_keys.size
|
79
|
+
rescue Exception
|
80
|
+
0
|
79
81
|
end
|
80
82
|
|
81
83
|
def read_many(query)
|
82
|
-
q = build_query(query)
|
83
|
-
fo = build_fetch_option(query)
|
84
|
-
iter = if fo
|
85
|
-
DS::Service.prepare(q).as_iterable(fo)
|
86
|
-
else
|
87
|
-
DS::Service.prepare(q).as_iterable
|
88
|
-
end
|
89
84
|
Collection.new(query) do |collection|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
85
|
+
loop do
|
86
|
+
negas, posis = {}, {}
|
87
|
+
q = build_query(query) do |op, property, value|
|
88
|
+
negas[property] = value if op == :not
|
89
|
+
posis[property] = value if op == :eql
|
90
|
+
end
|
91
|
+
fo = build_fetch_option(query)
|
92
|
+
iter = if fo
|
93
|
+
DS::Service.prepare(q).as_iterable(fo)
|
94
|
+
else
|
95
|
+
DS::Service.prepare(q).as_iterable
|
96
|
+
end
|
97
|
+
iter.each do |entity|
|
98
|
+
next if negative?(entity, negas)
|
99
|
+
collection.load(query.fields.map do |property|
|
100
|
+
property.key? ?
|
101
|
+
entity.key.get_id : ds_get(entity, property.field)
|
102
|
+
end)
|
103
|
+
end
|
104
|
+
break if posis.empty?
|
105
|
+
query = assert_query(query, posis)
|
94
106
|
end
|
95
107
|
end
|
96
108
|
end
|
97
109
|
|
98
110
|
def read_one(query)
|
99
|
-
|
111
|
+
negas = {}
|
112
|
+
q = build_query(query) do |op, property, value|
|
113
|
+
negas[property] = value if op == :not
|
114
|
+
if op == :eql
|
115
|
+
raise InvalidConditionError,
|
116
|
+
"OR condition is not afflowed for read_one"
|
117
|
+
end
|
118
|
+
end
|
100
119
|
fo = build_fetch_option(query)
|
101
120
|
entity = if fo
|
102
121
|
DS::Service.prepare(q).as_iterable(fo).map{|i| break i}
|
@@ -104,6 +123,7 @@ module DataMapper
|
|
104
123
|
DS::Service.prepare(q).asSingleEntity
|
105
124
|
end
|
106
125
|
return nil if entity.blank?
|
126
|
+
return nil if negative?(entity, negas)
|
107
127
|
query.model.load(query.fields.map do |property|
|
108
128
|
property.key? ? entity.key.get_id : ds_get(entity, property.field)
|
109
129
|
end, query)
|
@@ -122,9 +142,22 @@ module DataMapper
|
|
122
142
|
end
|
123
143
|
|
124
144
|
def count(query)
|
125
|
-
|
126
|
-
|
127
|
-
|
145
|
+
result, limit = 0, query.limit
|
146
|
+
loop do
|
147
|
+
negas, posis = {}, {}
|
148
|
+
q = build_query(query) do |op, property, value|
|
149
|
+
negas[property] = value if op == :not
|
150
|
+
posis[property] = value if op == :eql
|
151
|
+
end
|
152
|
+
result += DS::Service.prepare(q).countEntities
|
153
|
+
unless negas.empty?
|
154
|
+
result -= count(negate_query(query, negas)).first
|
155
|
+
end
|
156
|
+
break if posis.empty?
|
157
|
+
break if limit && result >= limit
|
158
|
+
query = assert_query(query, posis)
|
159
|
+
end
|
160
|
+
[limit ? [result, limit].min : result]
|
128
161
|
end
|
129
162
|
|
130
163
|
protected
|
@@ -156,9 +189,18 @@ module DataMapper
|
|
156
189
|
when :gte; DS::Query::FilterOperator::GREATER_THAN_OR_EQUAL
|
157
190
|
when :lt; DS::Query::FilterOperator::LESS_THAN
|
158
191
|
when :lte; DS::Query::FilterOperator::LESS_THAN_OR_EQUAL
|
192
|
+
when :not; yield(:not, property, value); next
|
159
193
|
else next
|
160
194
|
end
|
161
|
-
|
195
|
+
if value.is_a?(Array)
|
196
|
+
if op != :eql
|
197
|
+
raise InvalidConditionError,
|
198
|
+
"OR condition is allowed only for :eql operator"
|
199
|
+
end
|
200
|
+
value[1..-1].each{|v| yield(:eql, property, v)}
|
201
|
+
value = value.first
|
202
|
+
end
|
203
|
+
q = q.add_filter(property.field, ds_op, value)
|
162
204
|
end
|
163
205
|
query.order.each do |o|
|
164
206
|
key = o.property.name.to_s
|
@@ -196,12 +238,77 @@ module DataMapper
|
|
196
238
|
end
|
197
239
|
end
|
198
240
|
|
199
|
-
def ds_set(entity,
|
241
|
+
def ds_set(entity, property, value)
|
242
|
+
name = property.field.to_s
|
243
|
+
case value
|
244
|
+
when DateTime
|
245
|
+
value = value.to_time
|
246
|
+
end
|
200
247
|
if value.is_a?(String) && value.length >= 500
|
201
|
-
entity.set_property(name
|
248
|
+
entity.set_property(name, DS::Text.new(value))
|
249
|
+
else
|
250
|
+
entity.set_property(name, value)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def ds_service_get(ds_key)
|
255
|
+
if tx = ds_transaction
|
256
|
+
DS::Service.get(tx, ds_key)
|
202
257
|
else
|
203
|
-
|
258
|
+
DS::Service.get(ds_key)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def ds_service_put(entity)
|
263
|
+
if tx = ds_transaction
|
264
|
+
DS::Service.put(tx, entity)
|
265
|
+
else
|
266
|
+
DS::Service.put(entity)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def ds_service_delete(ds_key)
|
271
|
+
if tx = ds_transaction
|
272
|
+
DS::Service.delete(tx, ds_key)
|
273
|
+
else
|
274
|
+
DS::Service.delete(ds_key)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def ds_transaction
|
279
|
+
if tx = current_transaction
|
280
|
+
primitive = tx.primitive_for(self)
|
281
|
+
primitive.transaction
|
282
|
+
else
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def negative?(entity, negas)
|
288
|
+
negas.any? do |property, value|
|
289
|
+
property.typecast(ds_get(entity, property.field)) == value
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def negate_query(query, negas)
|
294
|
+
query = query.dup
|
295
|
+
query.conditions.delete_if do |tuple|
|
296
|
+
tuple.size != 2 && tuple[0] == :not
|
297
|
+
end
|
298
|
+
negas.each do |property, value|
|
299
|
+
query.conditions.push([:eql, property, value])
|
300
|
+
end
|
301
|
+
query
|
302
|
+
end
|
303
|
+
|
304
|
+
def assert_query(query, posis)
|
305
|
+
query = query.dup
|
306
|
+
property = posis.keys.first
|
307
|
+
query.conditions.delete_if do |tuple|
|
308
|
+
tuple.size != 2 && tuple[0] == :eql && tuple[1] == property
|
204
309
|
end
|
310
|
+
query.conditions.push([:eql, property, posis[property]])
|
311
|
+
query
|
205
312
|
end
|
206
313
|
end
|
207
314
|
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Adapters
|
3
|
+
class DataStoreAdapter < AbstractAdapter
|
4
|
+
class Transaction
|
5
|
+
class AlreadyBeginError < StandardError; end
|
6
|
+
|
7
|
+
attr_reader :transaction
|
8
|
+
|
9
|
+
def begin
|
10
|
+
raise AlreadyBeginError if @transaction
|
11
|
+
@transaction = DS::Service.beginTransaction
|
12
|
+
end
|
13
|
+
|
14
|
+
def commit
|
15
|
+
@transaction.commit
|
16
|
+
end
|
17
|
+
|
18
|
+
def rollback
|
19
|
+
@transaction.rollback
|
20
|
+
end
|
21
|
+
|
22
|
+
def rollback_prepared
|
23
|
+
@transaction.rollback
|
24
|
+
end
|
25
|
+
|
26
|
+
def prepare
|
27
|
+
# TODO
|
28
|
+
end
|
29
|
+
|
30
|
+
def close
|
31
|
+
@transaction = nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def transaction_primitive
|
36
|
+
DataStoreAdapter::Transaction.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def push_transaction(transaction)
|
40
|
+
transactions(Thread::current) << transaction
|
41
|
+
end
|
42
|
+
|
43
|
+
def pop_transaction
|
44
|
+
transactions(Thread::current).pop
|
45
|
+
end
|
46
|
+
|
47
|
+
def current_transaction
|
48
|
+
transactions(Thread::current).last
|
49
|
+
end
|
50
|
+
|
51
|
+
def within_transaction?
|
52
|
+
!current_transaction.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def transactions(thread)
|
57
|
+
unless @transactions[thread]
|
58
|
+
@transactions.delete_if do |key, value|
|
59
|
+
!key.respond_to?(:alive?) || !key.alive?
|
60
|
+
end
|
61
|
+
@transactions[thread] = []
|
62
|
+
end
|
63
|
+
@transactions[thread]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/dm-datastore-adapter.rb
CHANGED
@@ -8,6 +8,7 @@ if defined?(Merb::Plugins)
|
|
8
8
|
Merb::BootLoader.before_app_loads do
|
9
9
|
# require code that must be loaded before the application
|
10
10
|
require File.join(%w(dm-datastore-adapter datastore-adapter))
|
11
|
+
require File.join(%w(dm-datastore-adapter transaction))
|
11
12
|
end
|
12
13
|
|
13
14
|
Merb::BootLoader.after_app_loads do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dm-datastore-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Genki Takiuchi
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-17 00:00:00 +09:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -51,6 +51,7 @@ files:
|
|
51
51
|
- lib/dm-datastore-adapter
|
52
52
|
- lib/dm-datastore-adapter/datastore-adapter.rb
|
53
53
|
- lib/dm-datastore-adapter/merbtasks.rb
|
54
|
+
- lib/dm-datastore-adapter/transaction.rb
|
54
55
|
- lib/dm-datastore-adapter.rb
|
55
56
|
- spec/dm-datastore-adapter_spec.rb
|
56
57
|
- spec/spec_helper.rb
|