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 CHANGED
@@ -1,17 +1,10 @@
1
1
  = appengine-pstore
2
2
 
3
- Description goes here.
3
+ * http://github.com/machu/appengine-pstore
4
4
 
5
- == Note on Patches/Pull Requests
6
-
7
- * Fork the project.
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.0
1
+ 0.1.1
@@ -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.0"
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-26}
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 = [
@@ -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
- unless AppEngine::Datastore.current_transaction(nil)
27
- raise PStore::Error, "not in transaction"
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
- @cache = {}
41
- yield self
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
- @transaction = nil
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
- raise PStore::Error, "in read-only transaction" if @rdonly
49
- if @cache.key?(name)
50
- @cache[name] # return latest put data
51
- else
52
- key = AppEngine::Datastore::Key.from_path(@parent_key, @kind, name)
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
- @cache[name] = value
116
+ @uncommited[:added][name] = value
117
+ @uncommited[:deleted].delete(name)
64
118
  value
65
119
  end
66
120
 
67
121
  def delete(name)
68
- key = AppEngine::Datastore::Key.from_path(@parent_key, @kind, name)
69
- AppEngine::Datastore.delete(@transaction, key)
70
- @cache.delete(name)
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 {|value|
85
- value.key.name
86
- }.concat(@cache.keys).uniq
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 "AppenginePstore" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
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 'spec/autorun'
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.0
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-26 00:00:00 -07:00
12
+ date: 2009-09-27 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency