machu-appengine-pstore 0.1.0 → 0.1.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/README.rdoc +4 -11
- data/VERSION +1 -1
- data/appengine-pstore.gemspec +2 -2
- data/lib/appengine-pstore.rb +81 -20
- data/spec/appengine-pstore_spec.rb +196 -3
- data/spec/spec_helper.rb +5 -3
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -1,17 +1,10 @@
|
|
1
1
|
= appengine-pstore
|
2
2
|
|
3
|
-
|
3
|
+
* http://github.com/machu/appengine-pstore
|
4
4
|
|
5
|
-
==
|
6
|
-
|
7
|
-
|
8
|
-
* Make your feature addition or bug fix.
|
9
|
-
* Add tests for it. This is important so I don't break it in a
|
10
|
-
future version unintentionally.
|
11
|
-
* Commit, do not mess with rakefile, version, or history.
|
12
|
-
(if you want to have your own version, that is fine but
|
13
|
-
bump version in a commit by itself I can ignore when I pull)
|
14
|
-
* Send me a pull request. Bonus points for topic branches.
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
The PStore interfaces for the Google App Engine Datastore.
|
15
8
|
|
16
9
|
== Copyright
|
17
10
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.1
|
data/appengine-pstore.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{appengine-pstore}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["MATSUOKA Kohei"]
|
12
|
-
s.date = %q{2009-09-
|
12
|
+
s.date = %q{2009-09-27}
|
13
13
|
s.description = %q{PStore compatible interface for Google Apps Engine.}
|
14
14
|
s.email = %q{kohei@machu.jp}
|
15
15
|
s.extra_rdoc_files = [
|
data/lib/appengine-pstore.rb
CHANGED
@@ -1,6 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Copyright:: Copyright 2009 MATSUOKA Kohei
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
1
16
|
require 'rubygems'
|
2
17
|
require 'appengine-apis/datastore'
|
3
18
|
|
19
|
+
# from pstore.rb
|
20
|
+
class PStore
|
21
|
+
# The error type thrown by all PStore methods.
|
22
|
+
class Error < StandardError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
4
26
|
module AppEngine
|
5
27
|
module Datastore
|
6
28
|
class Query
|
@@ -23,51 +45,88 @@ module AppEngine
|
|
23
45
|
end
|
24
46
|
|
25
47
|
def in_transaction
|
26
|
-
|
27
|
-
|
48
|
+
if @transaction == nil || @transaction.active? == false
|
49
|
+
#unless transaction && transaction.active
|
50
|
+
#unless AppEngine::Datastore.current_transaction(nil)
|
51
|
+
raise ::PStore::Error, "not in transaction"
|
28
52
|
end
|
29
53
|
end
|
30
54
|
|
31
55
|
def in_transaction_wr
|
32
56
|
in_transaction
|
33
|
-
raise PStore::Error, "in read-only transaction" if @rdonly
|
57
|
+
raise ::PStore::Error, "in read-only transaction" if @rdonly
|
34
58
|
end
|
35
59
|
private :in_transaction, :in_transaction_wr
|
36
60
|
|
37
61
|
def transaction(readonly = false)
|
62
|
+
raise ::PStore::Error, "nested transaction" if @transaction
|
38
63
|
@rdonly = readonly
|
39
64
|
@transaction = AppEngine::Datastore.begin_transaction
|
40
|
-
|
41
|
-
|
65
|
+
# uncommited entities
|
66
|
+
@uncommited = {
|
67
|
+
:added => {},
|
68
|
+
:deleted => {}
|
69
|
+
}
|
70
|
+
begin
|
71
|
+
catch(:pstore_abort_transaction) do
|
72
|
+
yield self
|
73
|
+
end
|
74
|
+
rescue Exception
|
75
|
+
@transaction.rollback if @transaction.active?
|
76
|
+
raise
|
77
|
+
ensure
|
78
|
+
@transaction.commit if @transaction.active?
|
79
|
+
@transaction = nil
|
80
|
+
@uncommited = nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def commit
|
85
|
+
in_transaction
|
42
86
|
@transaction.commit
|
43
|
-
|
87
|
+
throw :pstore_abort_transaction
|
88
|
+
end
|
89
|
+
|
90
|
+
def abort
|
91
|
+
in_transaction
|
92
|
+
@transaction.rollback
|
93
|
+
throw :pstore_abort_transaction
|
44
94
|
end
|
45
95
|
|
46
96
|
def [](name)
|
47
97
|
in_transaction
|
48
|
-
|
49
|
-
if @
|
50
|
-
|
51
|
-
|
52
|
-
|
98
|
+
# return uncommited data if exist
|
99
|
+
return @uncommited[:added][name] if @uncommited[:added].key?(name)
|
100
|
+
return nil if @uncommited[:deleted].key?(name)
|
101
|
+
|
102
|
+
key = AppEngine::Datastore::Key.from_path(@parent_key, @kind, dump(name))
|
103
|
+
begin
|
53
104
|
entity = AppEngine::Datastore.get(@transaction, key)
|
54
105
|
load(entity[:value])
|
106
|
+
rescue AppEngine::Datastore::EntityNotFound
|
107
|
+
nil
|
55
108
|
end
|
56
109
|
end
|
57
110
|
|
58
111
|
def []=(name, value)
|
59
112
|
in_transaction_wr
|
60
|
-
entity = AppEngine::Datastore::Entity.new(@kind, name, @parent_key)
|
113
|
+
entity = AppEngine::Datastore::Entity.new(@kind, dump(name), @parent_key)
|
61
114
|
entity[:value] = dump(value)
|
62
115
|
AppEngine::Datastore.put(@transaction, entity)
|
63
|
-
@
|
116
|
+
@uncommited[:added][name] = value
|
117
|
+
@uncommited[:deleted].delete(name)
|
64
118
|
value
|
65
119
|
end
|
66
120
|
|
67
121
|
def delete(name)
|
68
|
-
|
69
|
-
AppEngine::Datastore.
|
70
|
-
|
122
|
+
in_transaction_wr
|
123
|
+
key = AppEngine::Datastore::Key.from_path(@parent_key, @kind, dump(name))
|
124
|
+
value = self[name]
|
125
|
+
# Datastore.delete requires array keys
|
126
|
+
AppEngine::Datastore.delete(@transaction, [key])
|
127
|
+
@uncommited[:added].delete(name)
|
128
|
+
@uncommited[:deleted][name] = value
|
129
|
+
value
|
71
130
|
end
|
72
131
|
|
73
132
|
def load(content)
|
@@ -81,17 +140,19 @@ module AppEngine
|
|
81
140
|
def roots
|
82
141
|
in_transaction
|
83
142
|
query = AppEngine::Datastore::Query.new(@kind, @parent_key)
|
84
|
-
query.keysonly.fetch.map {|
|
85
|
-
|
86
|
-
}
|
143
|
+
db_keys = query.keysonly.fetch.map {|entity|
|
144
|
+
load(entity.key.name)
|
145
|
+
}
|
146
|
+
(db_keys + @uncommited[:added].keys - @uncommited[:deleted].keys).uniq
|
87
147
|
end
|
88
148
|
|
89
149
|
def root?(key)
|
90
150
|
in_transaction
|
151
|
+
self.roots.include?(key)
|
91
152
|
end
|
92
153
|
|
93
154
|
def path
|
94
|
-
@parent_key
|
155
|
+
@parent_key.name
|
95
156
|
end
|
96
157
|
end
|
97
158
|
end
|
@@ -1,7 +1,200 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'appengine-pstore'
|
3
|
+
require 'pp'
|
2
4
|
|
3
|
-
describe
|
4
|
-
|
5
|
-
|
5
|
+
describe AppEngine::PStore do
|
6
|
+
# initializing datastore in memory each time
|
7
|
+
before :each do
|
8
|
+
AppEngine::Testing.install_test_datastore
|
9
|
+
@db = AppEngine::PStore.new('test.pstore')
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
#it should close transactions
|
14
|
+
AppEngine::Datastore.active_transactions.to_a.should == []
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should support get/put' do
|
18
|
+
@db.transaction do |db|
|
19
|
+
db['var1'] = "hello"
|
20
|
+
db['var2'] = "world"
|
21
|
+
db['var1'].should == "hello"
|
22
|
+
db['var2'].should == "world"
|
23
|
+
end
|
24
|
+
@db.transaction do |db|
|
25
|
+
db['var1'].should == "hello"
|
26
|
+
db['var2'].should == "world"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should support symbol key' do
|
31
|
+
@db.transaction do |db|
|
32
|
+
db[:key] = "hello"
|
33
|
+
db['key'] = "world"
|
34
|
+
end
|
35
|
+
@db.transaction do |db|
|
36
|
+
db[:key].should == "hello"
|
37
|
+
db['key'].should == "world"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should return nil' do
|
42
|
+
@db.transaction do |db|
|
43
|
+
db['undefined'].should == nil
|
44
|
+
db.delete('undefined').should == nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'should support multiple database' do
|
49
|
+
db1 = AppEngine::PStore.new('test1.pstore')
|
50
|
+
db2 = AppEngine::PStore.new('test2.pstore')
|
51
|
+
db1.transaction do |db1|
|
52
|
+
db2.transaction do |db2|
|
53
|
+
db1[:key] = "hello"
|
54
|
+
db2[:key] = "world"
|
55
|
+
db1[:key].should == "hello"
|
56
|
+
db2[:key].should == "world"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
db1.transaction do |db1|
|
61
|
+
db2.transaction do |db2|
|
62
|
+
db1[:key].should == "hello"
|
63
|
+
db2[:key].should == "world"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should support delete' do
|
69
|
+
@db.transaction do |db|
|
70
|
+
db[:key] = 'hello'
|
71
|
+
end
|
72
|
+
@db.transaction do |db|
|
73
|
+
value = db.delete(:key)
|
74
|
+
value.should == 'hello'
|
75
|
+
db[:key].should == nil
|
76
|
+
end
|
77
|
+
@db.transaction do |db|
|
78
|
+
db[:key].should == nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should suppert roots' do
|
83
|
+
@db.transaction do |db|
|
84
|
+
db[:key1] = "hello"
|
85
|
+
db[:key2] = "world"
|
86
|
+
db.roots.should == [:key1, :key2]
|
87
|
+
end
|
88
|
+
@db.transaction do |db|
|
89
|
+
db.roots.should == [:key1, :key2]
|
90
|
+
db.delete(:key1)
|
91
|
+
db[:key3] = "!"
|
92
|
+
db.roots.should == [:key2, :key3]
|
93
|
+
end
|
94
|
+
@db.transaction do |db|
|
95
|
+
db.roots.should == [:key2, :key3]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should suppert root?' do
|
100
|
+
@db.transaction do |db|
|
101
|
+
db.root?(:key).should == false
|
102
|
+
db[:key] = "hello"
|
103
|
+
db.root?(:key).should == true
|
104
|
+
end
|
105
|
+
@db.transaction do |db|
|
106
|
+
db.root?(:key).should == true
|
107
|
+
db.delete(:key)
|
108
|
+
db.root?(:key).should == false
|
109
|
+
end
|
110
|
+
@db.transaction do |db|
|
111
|
+
db.root?(:key).should == false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should support path' do
|
116
|
+
db = AppEngine::PStore.new('somewhere')
|
117
|
+
db.path.should == 'somewhere'
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should support readonly database' do
|
121
|
+
p = lambda {
|
122
|
+
@db.transaction(true) do |db|
|
123
|
+
db[:key] = "hello"
|
124
|
+
end
|
125
|
+
}
|
126
|
+
p.should raise_error PStore::Error
|
127
|
+
p = lambda {
|
128
|
+
@db.transaction(true) do |db|
|
129
|
+
db.delete(:key)
|
130
|
+
end
|
131
|
+
}
|
132
|
+
p.should raise_error PStore::Error
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'should return block value' do
|
136
|
+
@db.transaction{ 'hello' }.should == 'hello'
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'transaction' do
|
140
|
+
it 'should support commit' do
|
141
|
+
@db.transaction do |db|
|
142
|
+
db[:key] = "hello"
|
143
|
+
db.commit
|
144
|
+
# never reached
|
145
|
+
raise StandardError
|
146
|
+
end
|
147
|
+
@db.transaction do |db|
|
148
|
+
db[:key].should == "hello"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should support abort' do
|
153
|
+
@db.transaction do |db|
|
154
|
+
db[:key] = "hello"
|
155
|
+
db.abort
|
156
|
+
# never reached
|
157
|
+
raise StandardError
|
158
|
+
end
|
159
|
+
@db.transaction do |db|
|
160
|
+
db[:key].should == nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'should abort transaction if raised any Exception' do
|
165
|
+
p = lambda {
|
166
|
+
@db.transaction do |db|
|
167
|
+
db[:key] = "hello"
|
168
|
+
# abort transaction
|
169
|
+
raise StandardError
|
170
|
+
end
|
171
|
+
}
|
172
|
+
p.should raise_error StandardError
|
173
|
+
@db.transaction do |db|
|
174
|
+
db[:key].should == nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'should not support nested transaction' do
|
179
|
+
p = lambda {
|
180
|
+
@db.transaction do |db1|
|
181
|
+
@db.transaction do |db2|
|
182
|
+
end
|
183
|
+
end
|
184
|
+
}
|
185
|
+
p.should raise_error PStore::Error
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'should raise PStore::Error outside transaction' do
|
189
|
+
p = lambda { @db[:key1] = "hello" }
|
190
|
+
p.should raise_error PStore::Error
|
191
|
+
p = lambda { @db[:key1] }
|
192
|
+
p.should raise_error PStore::Error
|
193
|
+
p = lambda { @db.delete(:key1) }
|
194
|
+
p.should raise_error PStore::Error
|
195
|
+
p = lambda { @db.roots }
|
196
|
+
p.should raise_error PStore::Error
|
197
|
+
end
|
6
198
|
end
|
7
199
|
end
|
200
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
2
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
-
require 'appengine-pstore'
|
4
3
|
require 'spec'
|
5
|
-
require '
|
4
|
+
require 'appengine-sdk'
|
5
|
+
AppEngine::SDK.load_apiproxy
|
6
|
+
require 'appengine-apis/testing'
|
7
|
+
AppEngine::Testing.install_test_env
|
6
8
|
|
7
9
|
Spec::Runner.configure do |config|
|
8
|
-
|
10
|
+
# config.include(ProtoMethods)
|
9
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: machu-appengine-pstore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MATSUOKA Kohei
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-09-
|
12
|
+
date: 2009-09-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|