tiny_ds 0.0.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/LICENSE +20 -0
- data/README.rdoc +25 -0
- data/Rakefile +45 -0
- data/lib/tiny_ds/base.rb +290 -0
- data/lib/tiny_ds/base.rb~ +115 -0
- data/lib/tiny_ds/base_tx.rb +161 -0
- data/lib/tiny_ds/base_tx.rb~ +180 -0
- data/lib/tiny_ds/low_ds.rb +77 -0
- data/lib/tiny_ds/low_ds.rb~ +80 -0
- data/lib/tiny_ds/property_definition.rb +68 -0
- data/lib/tiny_ds/property_definition.rb~ +54 -0
- data/lib/tiny_ds/query.rb +76 -0
- data/lib/tiny_ds/query.rb~ +68 -0
- data/lib/tiny_ds/validations.rb +26 -0
- data/lib/tiny_ds/validations.rb~ +9 -0
- data/lib/tiny_ds/version.rb +3 -0
- data/lib/tiny_ds.rb +16 -0
- data/lib/tiny_ds.rb~ +2 -0
- data/lib/tinyds.rb~ +2 -0
- data/spec/basic_spec.rb +648 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/spec_helper.rb~ +0 -0
- metadata +86 -0
@@ -0,0 +1,180 @@
|
|
1
|
+
=begin
|
2
|
+
使い方
|
3
|
+
class TxMoveUserPoint < BaseTx
|
4
|
+
def src_phase(src, args)
|
5
|
+
src[:point] -= args[:amount]
|
6
|
+
end
|
7
|
+
def dest_phase(dest, args)
|
8
|
+
dest[:point] += args[:amount]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
u1 = User.create(:point=>1000)
|
13
|
+
=> #<User @sid=21 @name=nil @message=nil @point=1000 @created_at=Thu, 17 Dec 2009 21:14:29 +0900 @updated_at=Thu, 17 Dec 2009 21:14:29 +0900>
|
14
|
+
|
15
|
+
u2 = User.create(:point=>1000)
|
16
|
+
=> #<User @sid=22 @name=nil @message=nil @point=1000 @created_at=Thu, 17 Dec 2009 21:14:33 +0900 @updated_at=Thu, 17 Dec 2009 21:14:33 +0900>
|
17
|
+
|
18
|
+
TxMoveUserPoint.exec(u1.__entity__, u2.__entity__, :amount=>200)
|
19
|
+
=> true
|
20
|
+
|
21
|
+
u1.reload
|
22
|
+
=> #<User @sid=21 @name=nil @message=nil @point=800 @created_at=Thu, 17 Dec 2009 21:14:29 +0900 @updated_at=Thu, 17 Dec 2009 21:14:29 +0900>
|
23
|
+
|
24
|
+
u2.reload
|
25
|
+
=> #<User @sid=22 @name=nil @message=nil @point=1200 @created_at=Thu, 17 Dec 2009 21:14:33 +0900 @updated_at=Thu, 17 Dec 2009 21:14:33 +0900>
|
26
|
+
|
27
|
+
TxMoveUserPoint.roll_forward_all
|
28
|
+
=end
|
29
|
+
# memo デプロイ時に未完了のトランザクションがあってトランザクション定義がかわったら?version???
|
30
|
+
|
31
|
+
class BaseTx
|
32
|
+
LowDS = TinyDS::LowDS
|
33
|
+
|
34
|
+
def src_phase(src, args)
|
35
|
+
raise "no impl."
|
36
|
+
end
|
37
|
+
def dest_phase(dest, args)
|
38
|
+
raise "no impl."
|
39
|
+
end
|
40
|
+
def self.tx_kind
|
41
|
+
name
|
42
|
+
end
|
43
|
+
def roll_forward_retries_limit
|
44
|
+
100
|
45
|
+
end
|
46
|
+
attr_reader :tx_key
|
47
|
+
|
48
|
+
# トランザクション実行
|
49
|
+
# : src_phase dest_phase
|
50
|
+
# raised : failed ----
|
51
|
+
# false : OK failed
|
52
|
+
# true : OK OK
|
53
|
+
def self.exec(src, dest, args={})
|
54
|
+
tx = new
|
55
|
+
tx.create_tx(src, dest, args)
|
56
|
+
begin
|
57
|
+
tx.roll_forward
|
58
|
+
rescue AppEngine::Datastore::TransactionFailed => e
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.roll_forward_all(limit=50)
|
65
|
+
pending_tx_query.each(:limit=>limit) do |tx_ent|
|
66
|
+
tx = new
|
67
|
+
tx.restore_tx(tx_ent)
|
68
|
+
begin
|
69
|
+
tx.roll_forward
|
70
|
+
rescue => e
|
71
|
+
# logger.warn("roll_forward failed. tx=[#{tx.tx_key}] e=[#{e.inspect}]")
|
72
|
+
end
|
73
|
+
end
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.pending_tx_query(status="pending", dire=:asc) # pending/done/failed
|
78
|
+
direction = dire==:desc ? AppEngine::Datastore::Query::DESCENDING : AppEngine::Datastore::Query::ASCENDING
|
79
|
+
AppEngine::Datastore::Query.new("TxSrc").
|
80
|
+
filter(:tx_kind, AppEngine::Datastore::Query::EQUAL, tx_kind).
|
81
|
+
filter(:status, AppEngine::Datastore::Query::EQUAL, status).
|
82
|
+
sort(:created_at, direction)
|
83
|
+
end
|
84
|
+
|
85
|
+
# doneなTxSrcと対応するTxDoneを削除する
|
86
|
+
# def self.delete_done_tx
|
87
|
+
# end
|
88
|
+
|
89
|
+
require "benchmark"
|
90
|
+
def create_tx(src, dest, args)
|
91
|
+
#RAILS_DEFAULT_LOGGER.info ["create_tx", Benchmark.measure{
|
92
|
+
_create_tx(src, dest, args)
|
93
|
+
#}].inspect
|
94
|
+
end
|
95
|
+
def roll_forward
|
96
|
+
#RAILS_DEFAULT_LOGGER.info ["roll_forward", Benchmark.measure{
|
97
|
+
_roll_forward
|
98
|
+
#}].inspect
|
99
|
+
end
|
100
|
+
|
101
|
+
# トランザクション前半
|
102
|
+
# TODO TxIDを指定できるようにする。TxSrc#key.nameにTxIDを指定して重複実行防止
|
103
|
+
def _create_tx(src, dest, args)
|
104
|
+
tx_ent = nil
|
105
|
+
ds_transaction{
|
106
|
+
src = src.class.get(src.key)
|
107
|
+
src_phase(src, args)
|
108
|
+
src.save
|
109
|
+
|
110
|
+
attrs = {
|
111
|
+
:tx_kind => self.class.tx_kind,
|
112
|
+
:dest_key => dest.key.to_s,
|
113
|
+
:status => "pending",
|
114
|
+
:roll_forward_failed_count => 0,
|
115
|
+
:args => args.to_yaml,
|
116
|
+
:created_at => Time.now
|
117
|
+
}
|
118
|
+
tx_ent = LowDS.create("TxSrc", attrs, :parent=>src.entity) # srcがparent, tx_entがchild
|
119
|
+
# COMMIT:「srcの処理(=src_phase)、TxSrc作成」
|
120
|
+
}
|
121
|
+
@tx_key = tx_ent.key.to_s
|
122
|
+
nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def restore_tx(tx_ent)
|
126
|
+
@tx_key = tx_ent.key.to_s
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# トランザクション後半
|
131
|
+
def _roll_forward
|
132
|
+
tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
|
133
|
+
|
134
|
+
ds_transaction{
|
135
|
+
dest_key = LowDS::KeyFactory.stringToKey(tx_ent[:dest_key])
|
136
|
+
dest = dest_key.kind.constantize.get(dest_key)
|
137
|
+
done_name = "TxDone_#{@tx_key}"
|
138
|
+
done_key = LowDS::KeyFactory.createKey(dest.key, "TxDone", done_name)
|
139
|
+
begin
|
140
|
+
LowDS.get(done_key, "TxDone")
|
141
|
+
# なにもしない : TxDoneが存在しているということはdest_phaseは処理済み
|
142
|
+
rescue AppEngine::Datastore::EntityNotFound => e
|
143
|
+
# TxDoneが無い→dest_phaseが未実行
|
144
|
+
attrs = {:done_at=>Time.now}
|
145
|
+
done_ent = LowDS.create("TxDone", attrs, :parent=>dest.entity, :name=>done_name)
|
146
|
+
dest_phase(dest, YAML.load(tx_ent[:args])) # destの処理を実行
|
147
|
+
dest.save
|
148
|
+
end
|
149
|
+
# memo: done_keyが同じTxDoneをcommitしようとするとTransactionFailedになるはず→dest_phaseもキャンセル
|
150
|
+
# COMMIT:「destの処理(=dest_phase)、TxDone作成」
|
151
|
+
}
|
152
|
+
|
153
|
+
# TxSrc#statusをdoneに
|
154
|
+
ds_transaction{
|
155
|
+
tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
|
156
|
+
if tx_ent[:status]=="pending"
|
157
|
+
tx_ent[:status] = "done"
|
158
|
+
tx_ent[:done_at] = Time.now
|
159
|
+
LowDS.save(tx_ent)
|
160
|
+
end
|
161
|
+
}
|
162
|
+
return true
|
163
|
+
rescue
|
164
|
+
ds_transaction{
|
165
|
+
tx_ent = LowDS.get(@tx_key, :kind=>"TxSrc")
|
166
|
+
tx_ent[:roll_forward_failed_count] += 1
|
167
|
+
if roll_forward_retries_limit < tx_ent[:roll_forward_failed_count]
|
168
|
+
tx_ent[:status] = "failed"
|
169
|
+
end
|
170
|
+
LowDS.save(tx_ent)
|
171
|
+
}
|
172
|
+
return false
|
173
|
+
end
|
174
|
+
|
175
|
+
# EntityGroup単位のtransaction
|
176
|
+
def ds_transaction(&block)
|
177
|
+
retries = 3
|
178
|
+
AppEngine::Datastore.transaction(retries, &block)
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'appengine-apis/datastore'
|
2
|
+
|
3
|
+
module TinyDS
|
4
|
+
module LowDS
|
5
|
+
KeyFactory = com.google.appengine.api.datastore.KeyFactory
|
6
|
+
|
7
|
+
# build
|
8
|
+
def self.build(kind, attrs, opts={})
|
9
|
+
raise "invalid kind=#{kind}" unless kind.kind_of?(String)
|
10
|
+
raise "invalid opts=#{opts.inspect}" if [:id, :name, :key].collect{|k| opts[k] }.compact.size >= 2
|
11
|
+
raise ":id must be Integer" if opts[:id] && !opts[:id].kind_of?(Integer)
|
12
|
+
raise ":name must be String" if opts[:name] && !opts[:name].kind_of?(String)
|
13
|
+
raise ":key must be Key or String" if opts[:key] && !(opts[:key].kind_of?(String) || opts[:key].kind_of?(AppEngine::Datastore::Key))
|
14
|
+
name_or_id = opts[:name] || opts[:id] # name(String) or id(Integer)
|
15
|
+
key = opts[:key].kind_of?(String) ? KeyFactory.stringToKey(opts[:key]) : opts[:key]
|
16
|
+
parent = opts[:parent]
|
17
|
+
ent = if key
|
18
|
+
AppEngine::Datastore::Entity.new(key)
|
19
|
+
elsif parent
|
20
|
+
parent_key = case parent
|
21
|
+
when AppEngine::Datastore::Entity; parent.key
|
22
|
+
when AppEngine::Datastore::Key; parent
|
23
|
+
when String; KeyFactory.stringToKey(parent)
|
24
|
+
else raise "invalid parent type parent=[#{parent.inspect}]"
|
25
|
+
end
|
26
|
+
if name_or_id
|
27
|
+
new_key = KeyFactory.createKey(parent_key, kind, name_or_id)
|
28
|
+
AppEngine::Datastore::Entity.new(new_key)
|
29
|
+
else
|
30
|
+
AppEngine::Datastore::Entity.new(kind, parent_key)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
if name_or_id
|
34
|
+
new_key = KeyFactory.createKey(kind, name_or_id)
|
35
|
+
AppEngine::Datastore::Entity.new(new_key)
|
36
|
+
else
|
37
|
+
AppEngine::Datastore::Entity.new(kind)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
attrs.each do |k,v|
|
41
|
+
ent[k] = v
|
42
|
+
end
|
43
|
+
ent
|
44
|
+
end
|
45
|
+
|
46
|
+
# create
|
47
|
+
def self.create(kind, attrs, opts={})
|
48
|
+
ent = build(kind, attrs, opts)
|
49
|
+
AppEngine::Datastore.put(ent)
|
50
|
+
ent
|
51
|
+
end
|
52
|
+
|
53
|
+
# get by key
|
54
|
+
def self.get(key, opts={})
|
55
|
+
key = case key
|
56
|
+
when AppEngine::Datastore::Key; key
|
57
|
+
when String; KeyFactory.stringToKey(key)
|
58
|
+
else raise "invalid key type key.class=[#{key.class}] key.inspect=[#{key.inspect}]"
|
59
|
+
end
|
60
|
+
ent = AppEngine::Datastore.get(key)
|
61
|
+
if opts[:kind]
|
62
|
+
raise "kind missmatch. #{ent.kind}!=#{opts[:kind]}" if ent.kind!=opts[:kind]
|
63
|
+
end
|
64
|
+
ent
|
65
|
+
end
|
66
|
+
|
67
|
+
# update
|
68
|
+
def self.save(ent)
|
69
|
+
AppEngine::Datastore.put(ent)
|
70
|
+
end
|
71
|
+
|
72
|
+
# delete
|
73
|
+
def self.delete(ent)
|
74
|
+
AppEngine::Datastore.delete([ent.key])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module LowDS
|
2
|
+
KeyFactory = com.google.appengine.api.datastore.KeyFactory
|
3
|
+
|
4
|
+
# create
|
5
|
+
def self.build(kind, attrs, opts={})
|
6
|
+
raise "invalid opts=#{opts.inspect}" if [:id, :name, :key].collect{|k| opts[k] }.compact.size >= 2
|
7
|
+
raise ":id must be Integer" if opts[:id] && !opts[:id].kind_of?(Integer)
|
8
|
+
raise ":name must be String" if opts[:name] && !opts[:name].kind_of?(String)
|
9
|
+
raise ":key must be Key or String" if opts[:key] && !(opts[:key].kind_of?(String) || opts[:key].kind_of?(AppEngine::Datastore::Key))
|
10
|
+
name_or_id = opts[:name] || opts[:id] # name(String) or id(Integer)
|
11
|
+
key = opts[:key].kind_of?(String) ? KeyFactory.stringToKey(parent) : opts[:key]
|
12
|
+
parent = opts[:parent]
|
13
|
+
ent = if key
|
14
|
+
AppEngine::Datastore::Entity.new(key)
|
15
|
+
elsif parent
|
16
|
+
parent_key = case parent
|
17
|
+
when AppEngine::Datastore::Entity; parent.key
|
18
|
+
when AppEngine::Datastore::Key; parent
|
19
|
+
when String; KeyFactory.stringToKey(parent)
|
20
|
+
else raise "invalid parent type parent=[#{parent.inspect}]"
|
21
|
+
end
|
22
|
+
if name_or_id
|
23
|
+
new_key = KeyFactory.createKey(parent_key, kind, name_or_id)
|
24
|
+
AppEngine::Datastore::Entity.new(new_key)
|
25
|
+
else
|
26
|
+
AppEngine::Datastore::Entity.new(kind, parent_key)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
if name_or_id
|
30
|
+
new_key = KeyFactory.createKey(kind, name_or_id)
|
31
|
+
AppEngine::Datastore::Entity.new(new_key)
|
32
|
+
else
|
33
|
+
AppEngine::Datastore::Entity.new(kind)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
attrs.each do |k,v|
|
37
|
+
ent[k] = v
|
38
|
+
end
|
39
|
+
ent
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.create(kind, attrs, opts={})
|
43
|
+
ent = build(kind, attrs, opts)
|
44
|
+
AppEngine::Datastore.put(ent)
|
45
|
+
ent
|
46
|
+
end
|
47
|
+
|
48
|
+
# get by key
|
49
|
+
def self.get(key, opts={})
|
50
|
+
key = case key
|
51
|
+
when AppEngine::Datastore::Key; key
|
52
|
+
when String; KeyFactory.stringToKey(key)
|
53
|
+
else raise "invalid key type key.class=[#{key.class}] key.inspect=[#{key.inspect}]"
|
54
|
+
end
|
55
|
+
ent = AppEngine::Datastore.get(key)
|
56
|
+
if opts[:kind]
|
57
|
+
raise "kind missmatch. #{ent.kind}!=#{opts[:kind]}" if ent.kind!=opts[:kind]
|
58
|
+
end
|
59
|
+
ent
|
60
|
+
end
|
61
|
+
|
62
|
+
# search
|
63
|
+
def self.search
|
64
|
+
# TODO
|
65
|
+
end
|
66
|
+
|
67
|
+
# update
|
68
|
+
def self.save(ent)
|
69
|
+
AppEngine::Datastore.put(ent)
|
70
|
+
end
|
71
|
+
|
72
|
+
# delete
|
73
|
+
def self.delete(ent)
|
74
|
+
AppEngine::Datastore.delete(ent.key)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.tx(retries=0, &block)
|
78
|
+
AppEngine::Datastore.transaction(retries, &block)
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module TinyDS
|
2
|
+
class PropertyDefinition
|
3
|
+
def initialize(pname, ptype, opts)
|
4
|
+
@pname = pname
|
5
|
+
@ptype = ptype
|
6
|
+
@opts = opts
|
7
|
+
end
|
8
|
+
def default_value
|
9
|
+
raise "no default." unless has_default?
|
10
|
+
default = @opts[:default]
|
11
|
+
case default
|
12
|
+
when Proc
|
13
|
+
default.call
|
14
|
+
else
|
15
|
+
default
|
16
|
+
end
|
17
|
+
end
|
18
|
+
def has_default?
|
19
|
+
@opts.has_key?(:default)
|
20
|
+
end
|
21
|
+
def to_ds_value(v)
|
22
|
+
case @ptype
|
23
|
+
when :string
|
24
|
+
v.nil? ? nil : v.to_s
|
25
|
+
when :integer
|
26
|
+
v.nil? ? nil : v.to_i
|
27
|
+
when :text
|
28
|
+
v.nil? ? nil : com.google.appengine.api.datastore::Text.new(v.to_s)
|
29
|
+
when :time
|
30
|
+
case v
|
31
|
+
when Time
|
32
|
+
v
|
33
|
+
when NilClass
|
34
|
+
nil
|
35
|
+
else
|
36
|
+
raise "not Time value."
|
37
|
+
end
|
38
|
+
when :list
|
39
|
+
case v
|
40
|
+
when Array
|
41
|
+
v.empty? ? nil : v
|
42
|
+
when NilClass
|
43
|
+
nil
|
44
|
+
else
|
45
|
+
raise "not Array value."
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise "unknown property type '#{@ptype}'"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
def to_ruby_value(ds_v)
|
52
|
+
case @ptype
|
53
|
+
when :string
|
54
|
+
ds_v.nil? ? nil : ds_v.to_s
|
55
|
+
when :integer
|
56
|
+
ds_v.nil? ? nil : ds_v.to_i
|
57
|
+
when :text
|
58
|
+
ds_v.nil? ? nil : ds_v.to_s
|
59
|
+
when :time
|
60
|
+
ds_v.nil? ? nil : ds_v
|
61
|
+
when :list
|
62
|
+
ds_v.nil? ? [] : ds_v.to_a
|
63
|
+
else
|
64
|
+
raise "unknown property type '#{@ptype}'"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module TinyDS
|
2
|
+
class PropertyDefinition
|
3
|
+
def initialize(pname, ptype, opts)
|
4
|
+
@pname = pname
|
5
|
+
@ptype = ptype
|
6
|
+
@opts = opts
|
7
|
+
end
|
8
|
+
def default_value
|
9
|
+
if @opts.has_key?(:default)
|
10
|
+
default = @opts[:default]
|
11
|
+
case default
|
12
|
+
when Proc
|
13
|
+
default.call
|
14
|
+
else
|
15
|
+
default
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def to_ds_value(v)
|
20
|
+
return nil if v.nil?
|
21
|
+
case @ptype
|
22
|
+
when :string
|
23
|
+
v.to_s
|
24
|
+
when :integer
|
25
|
+
v.to_i
|
26
|
+
when :text
|
27
|
+
com.google.appengine.api.datastore::Text.new(java.lang.String.new(v))
|
28
|
+
when :time
|
29
|
+
Time.parse(v.to_s)
|
30
|
+
#when :list
|
31
|
+
# raise "todo"
|
32
|
+
else
|
33
|
+
raise "unknown type @ptype=#{@ptype}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def to_ruby_value(ds_v)
|
37
|
+
return nil if ds_v.nil?
|
38
|
+
case @ptype
|
39
|
+
when :string
|
40
|
+
ds_v.to_s
|
41
|
+
when :integer
|
42
|
+
ds_v.to_i
|
43
|
+
when :text
|
44
|
+
ds_v.to_s
|
45
|
+
when :time
|
46
|
+
Time.parse(ds_v.to_s)
|
47
|
+
#when :list
|
48
|
+
# raise "todo"
|
49
|
+
else
|
50
|
+
raise "unknown type @ptype=#{@ptype}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module TinyDS
|
2
|
+
class Query
|
3
|
+
def initialize(model_class)
|
4
|
+
@model_class = model_class
|
5
|
+
@q = AppEngine::Datastore::Query.new(@model_class.kind)
|
6
|
+
end
|
7
|
+
def ancestor(anc)
|
8
|
+
anc = case anc
|
9
|
+
when Base; anc.key
|
10
|
+
when String; LowDS::KeyFactory.stringToKey(anc)
|
11
|
+
when AppEngine::Datastore::Key; anc
|
12
|
+
else raise "unknown type. anc=#{anc.inspect}"
|
13
|
+
end
|
14
|
+
@q.set_ancestor(anc)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
def filter(*args)
|
18
|
+
if args.size==1 && args.first.kind_of?(Hash)
|
19
|
+
args.first.each do |k,v|
|
20
|
+
filter(k,"==",v)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
name, operator, value = *args
|
24
|
+
@model_class.property_definition(name) # check exist?
|
25
|
+
@q.filter(name, operator, value)
|
26
|
+
end
|
27
|
+
self
|
28
|
+
end
|
29
|
+
def sort(name, dir=:asc)
|
30
|
+
@model_class.property_definition(name) # check exist?
|
31
|
+
direction = {
|
32
|
+
:asc => AppEngine::Datastore::Query::ASCENDING,
|
33
|
+
:desc => AppEngine::Datastore::Query::DESCENDING
|
34
|
+
}[dir]
|
35
|
+
@q.sort(name, direction)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
def keys_only
|
39
|
+
@q.java_query.setKeysOnly
|
40
|
+
self
|
41
|
+
end
|
42
|
+
def count #todo(tx=nil)
|
43
|
+
@q.count
|
44
|
+
end
|
45
|
+
def one #todo(tx=nil)
|
46
|
+
@model_class.new_from_entity(@q.entity)
|
47
|
+
end
|
48
|
+
def all(opts={}) #todo(tx=nil)
|
49
|
+
models = []
|
50
|
+
@q.each(opts) do |entity|
|
51
|
+
models << @model_class.new_from_entity(entity)
|
52
|
+
end
|
53
|
+
models
|
54
|
+
end
|
55
|
+
def each(opts={}) #todo(tx=nil)
|
56
|
+
@q.each(opts) do |entity|
|
57
|
+
yield(@model_class.new_from_entity(entity))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
def collect(opts={}) #todo(tx=nil)
|
61
|
+
collected = []
|
62
|
+
@q.each(opts) do |entity|
|
63
|
+
collected << yield(@model_class.new_from_entity(entity))
|
64
|
+
end
|
65
|
+
collected
|
66
|
+
end
|
67
|
+
def keys(opts={}) #todo(tx=nil)
|
68
|
+
keys = []
|
69
|
+
self.keys_only
|
70
|
+
@q.each(opts) do |entity|
|
71
|
+
keys << entity.key
|
72
|
+
end
|
73
|
+
keys
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module TinyDS
|
2
|
+
class Query
|
3
|
+
def initialize(model_class)
|
4
|
+
@model_class = model_class
|
5
|
+
@q = AppEngine::Datastore::Query.new(@model_class.kind)
|
6
|
+
end
|
7
|
+
def ancestor(anc)
|
8
|
+
anc = case anc
|
9
|
+
when Base; anc.key
|
10
|
+
when String; LowDS::KeyFactory.stringToKey(anc)
|
11
|
+
when AppEngine::Datastore::Key; anc
|
12
|
+
else raise "unknown type. anc=#{anc.inspect}"
|
13
|
+
end
|
14
|
+
@q.set_ancestor(anc)
|
15
|
+
self
|
16
|
+
end
|
17
|
+
def filter(name, operator, value)
|
18
|
+
@q.filter(name, operator, value)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
def sort(name, dir=:asc)
|
22
|
+
direction = {
|
23
|
+
:asc => AppEngine::Datastore::Query::ASCENDING,
|
24
|
+
:desc => AppEngine::Datastore::Query::DESCENDING
|
25
|
+
}[dir]
|
26
|
+
@q.sort(name, direction)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
def keys_only
|
30
|
+
@q.java_query.setKeysOnly
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def count #todo(tx=nil)
|
35
|
+
@q.count
|
36
|
+
end
|
37
|
+
def one #todo(tx=nil)
|
38
|
+
@model_class.new_from_entity(@q.entity)
|
39
|
+
end
|
40
|
+
def all(opts={}) #todo(tx=nil)
|
41
|
+
models = []
|
42
|
+
@q.each(opts) do |entity|
|
43
|
+
models << @model_class.new_from_entity(entity)
|
44
|
+
end
|
45
|
+
models
|
46
|
+
end
|
47
|
+
def each(opts={}) #todo(tx=nil)
|
48
|
+
@q.each(opts) do |entity|
|
49
|
+
yield(@model_class.new_from_entity(entity))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
def collect(opts={}) #todo(tx=nil)
|
53
|
+
collected = []
|
54
|
+
@q.each(opts) do |entity|
|
55
|
+
collected << yield(@model_class.new_from_entity(entity))
|
56
|
+
end
|
57
|
+
collected
|
58
|
+
end
|
59
|
+
def keys(opts={}) #todo(tx=nil)
|
60
|
+
keys = []
|
61
|
+
self.keys_only
|
62
|
+
@q.each(opts) do |entity|
|
63
|
+
keys << entity.key
|
64
|
+
end
|
65
|
+
keys
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module TinyDS
|
2
|
+
class Base
|
3
|
+
def valid?
|
4
|
+
true # todo
|
5
|
+
end
|
6
|
+
def self.create!(attrs={}, opts={})
|
7
|
+
m = new(attrs, opts)
|
8
|
+
m.save!
|
9
|
+
m
|
10
|
+
end
|
11
|
+
def save
|
12
|
+
save!
|
13
|
+
true
|
14
|
+
rescue RecordInvalid => e
|
15
|
+
false
|
16
|
+
end
|
17
|
+
def save!
|
18
|
+
unless valid?
|
19
|
+
raise RecordInvalid
|
20
|
+
end
|
21
|
+
do_save
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class RecordInvalid < StandardError
|
25
|
+
end
|
26
|
+
end
|
data/lib/tiny_ds.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'appengine-apis'
|
2
|
+
require 'appengine-apis/datastore'
|
3
|
+
require File.dirname(__FILE__)+"/tiny_ds/low_ds.rb"
|
4
|
+
require File.dirname(__FILE__)+"/tiny_ds/property_definition.rb"
|
5
|
+
require File.dirname(__FILE__)+"/tiny_ds/base.rb"
|
6
|
+
require File.dirname(__FILE__)+"/tiny_ds/query.rb"
|
7
|
+
require File.dirname(__FILE__)+"/tiny_ds/validations.rb"
|
8
|
+
require File.dirname(__FILE__)+"/tiny_ds/base_tx.rb"
|
9
|
+
|
10
|
+
module TinyDS
|
11
|
+
def self.tx(retries=0, &block)
|
12
|
+
AppEngine::Datastore.transaction(retries){
|
13
|
+
yield(block)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
data/lib/tiny_ds.rb~
ADDED
data/lib/tinyds.rb~
ADDED