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 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