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 CHANGED
@@ -5,7 +5,7 @@ require 'merb-core'
5
5
  require 'merb-core/tasks/merb'
6
6
 
7
7
  GEM_NAME = "dm-datastore-adapter"
8
- GEM_VERSION = "0.1.0"
8
+ GEM_VERSION = "0.2.1"
9
9
  AUTHOR = "Genki Takiuchi"
10
10
  EMAIL = "genki@s21g.com"
11
11
  HOMEPAGE = "http://jmerbist.appspot.com/"
@@ -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 = DS::Service.put(entity)
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.field, ds_id)
38
+ ds_set(entity, property, ds_id)
37
39
  end
38
- DS::Service.put(entity)
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 = DS::Service.get(ds_key_from_resource(resource))
50
- resource.attributes.each do |key, value|
51
- ds_set(entity, key, value)
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 = DS::Service.put(entity)
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.each do |resource|
70
+ ds_keys = resources.map do |resource|
70
71
  begin
71
- ds_key = ds_key_from_resource(resource)
72
- DS::Service.delete([ds_key].to_java(DS::Key))
72
+ ds_key_from_resource(resource)
73
73
  rescue Exception
74
- else
75
- deleted += 1
74
+ nil
76
75
  end
77
- end
78
- deleted
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
- iter.each do |entity|
91
- collection.load(query.fields.map do |property|
92
- property.key? ? entity.key.get_id : ds_get(entity, property.field)
93
- end)
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
- q = build_query(query)
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
- q = build_query(query)
126
- count = DS::Service.prepare(q).countEntities
127
- [query.limit ? [count, query.limit].min : count]
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
- q = q.add_filter(property.name.to_s, ds_op, [value].flatten.first)
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, name, value)
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.to_s, DS::Text.new(value))
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
- entity.set_property(name.to_s, value)
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
@@ -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.0
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-15 00:00:00 +09:00
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