dm-datastore-adapter 0.1.0 → 0.2.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.
- 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
|