machu-appengine-pstore 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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