appengine-apis 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.
@@ -0,0 +1,340 @@
1
+ #!/usr/bin/ruby1.8 -w
2
+ #
3
+ # Copyright:: Copyright 2009 Google Inc.
4
+ # Original Author:: Ryan Brown (mailto:ribrdb@google.com)
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ #
19
+ # Ruby wrappers for the Java semantic data types for the datastore. These types
20
+ # are expected to be set as attributes of Entities.
21
+
22
+ require 'java'
23
+
24
+ class Time
25
+ def to_java
26
+ java.util.Date.new(to_i * 1000)
27
+ end
28
+
29
+ def self.new_from_java(date)
30
+ at(date.time / 1000)
31
+ end
32
+ end
33
+
34
+ module AppEngine
35
+ module Datastore
36
+ JavaDatastore = Java.ComGoogleAppengineApiDatastore
37
+
38
+ class Error < StandardError; end
39
+ class NeedIndex < Error; end
40
+ class Timeout < Error; end
41
+ class InternalError < Error; end
42
+ class Rollback < Error; end
43
+ class TransactionFailed < Error; end
44
+ class EntityNotFound < Error; end
45
+ class TooManyResults < Error; end
46
+
47
+ #A long string type.
48
+ #
49
+ # Strings of any length can be stored in the datastore using this
50
+ # type.
51
+ #
52
+ class Text < String
53
+ def to_java
54
+ JavaDatastore::Text.new(self.to_java_string)
55
+ end
56
+
57
+ def self.new_from_java(text)
58
+ self.new(text.getValue)
59
+ end
60
+ end
61
+
62
+ # A blob type, appropriate for storing binary data of any length.
63
+ class Blob < String
64
+ def to_java
65
+ JavaDatastore::Blob.new(self.to_java_bytes)
66
+ end
67
+
68
+ def self.new_from_java(blob)
69
+ self.new(self.from_java_bytes(blob.getBytes))
70
+ end
71
+ end
72
+
73
+ # A byte-string type, appropriate for storing short amounts of indexed data.
74
+ #
75
+ # This behaves identically to Blob, except it's used only for short, indexed
76
+ # byte strings.
77
+ #
78
+ class ByteString < Blob
79
+ def to_java
80
+ JavaDatastore::ShortBlob.new(self.to_java_bytes)
81
+ end
82
+ end
83
+
84
+ # A fully qualified URL. Usually http: scheme, but may also be file:, ftp:,
85
+ # news:, among others.
86
+ #
87
+ class Link < String
88
+ def to_java
89
+ JavaDatastore::Link.new(self.to_java_string)
90
+ end
91
+
92
+ def self.new_from_java(link)
93
+ self.new(link.getValue)
94
+ end
95
+ end
96
+
97
+ Key = JavaDatastore::Key
98
+
99
+ # The primary key for a datastore entity.
100
+ #
101
+ # A datastore GUID. A Key instance uniquely identifies an entity
102
+ # across all apps, and includes all information necessary to fetch
103
+ # the entity from the datastore with #Datastore.get(Key).
104
+ #
105
+ # See http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/Key.html
106
+ #
107
+ class Key
108
+
109
+ # Converts a Key into a websafe string. For example, this string
110
+ # can safely be used as an URL parameter embedded in a HTML document.
111
+ #
112
+ def to_s
113
+ JavaDatastore::KeyFactory.keyToString(self)
114
+ end
115
+
116
+ alias :inspect :to_string
117
+ alias :id :get_id
118
+ alias :== :equals?
119
+
120
+ def id_or_name
121
+ name || id
122
+ end
123
+
124
+ class << self
125
+
126
+ # Creates a new Key from an encoded String.
127
+ def new(encoded)
128
+ JavaDatastore::KeyFactory.stringToKey(encoded)
129
+ end
130
+
131
+ # call-seq:
132
+ # Key.from_path(parent=nil, kind, id, [kind, id]...) -> Key
133
+ # Constructs a Key out of a path.
134
+ #
135
+ # This is useful when an application wants to use just the 'id'
136
+ # portion of a key in e.g. a URL, where the rest of the URL
137
+ # provides enough context to fill in the rest, i.e. the app id
138
+ # (always implicit), the entity kind, and possibly an ancestor
139
+ # key. Since the 'id' is a relatively small int, it is more
140
+ # attractive for use in end-user-visible URLs than the full
141
+ # string representation of a key.
142
+ #
143
+ # Args:
144
+ # - parent: Optional parent key
145
+ # - kind: the entity kind (a string)
146
+ # - id: the id (an integer)
147
+ # - Additional, optional 'kind' and 'id' arguments are allowed in
148
+ # an alternating order (kind1, 1, kind2, 2, ...)
149
+ # - options: a Hash. If specified, options[:parent] is used as
150
+ # the parent Key.
151
+ #
152
+ # Returns:
153
+ # - A new Key instance whose #kind and #id methods return the *last*
154
+ # kind and id arugments passed
155
+ #
156
+ def from_path(parent_or_kind, kind_or_id, *args)
157
+ # Extract parent
158
+ parent = nil
159
+ if parent_or_kind.is_a? Key
160
+ parent = parent_or_kind
161
+ args[0,0] = [kind_or_id]
162
+ else
163
+ args[0,0] = [parent_or_kind, kind_or_id]
164
+ end
165
+
166
+ if args.size % 2 != 0
167
+ raise ArgumentError, 'Expected an even number of arguments ' \
168
+ '(kind1, id1, kind2, id2, ...); received ' \
169
+ "#{args.inspect}"
170
+ end
171
+
172
+ # Type-check parent
173
+ if parent
174
+ unless parent.is_a? Key
175
+ raise ArgumentError, 'Expected nil or a Key as a parent; ' \
176
+ "received #{parent} (a #{parent.class})."
177
+ end
178
+ unless parent.is_complete?
179
+ raise KeyError, 'The parent key has not yet been Put.'
180
+ end
181
+ end
182
+
183
+ current = parent
184
+ (0...args.size).step(2) do |i|
185
+ kind, id = args[i,2]
186
+ kind = kind.to_s if kind.is_a? Symbol
187
+ if current
188
+ current = current.getChild(kind, id)
189
+ else
190
+ current = JavaDatastore::KeyFactory.createKey(kind, id)
191
+ end
192
+ end
193
+
194
+ return current
195
+ end
196
+ end
197
+ end
198
+
199
+ Entity = JavaDatastore::Entity
200
+
201
+ # Entity is the fundamental unit of data storage. It has an
202
+ # immutable identifier (contained in the Key) object, a
203
+ # reference to an optional parent Entity, a kind (represented
204
+ # as an arbitrary string), and a set of zero or more typed
205
+ # properties.
206
+ #
207
+ # See http://code.google.com/appengine/docs/java/javadoc/com/google/appengine/api/datastore/Entity.html
208
+ #
209
+ class Entity
210
+ include Enumerable
211
+
212
+ SPECIAL_JAVA_TYPES = {
213
+ JavaDatastore::Text => Text,
214
+ JavaDatastore::Blob => Blob,
215
+ JavaDatastore::ShortBlob => ByteString,
216
+ JavaDatastore::Link => Link,
217
+ java.util.Date => Time,
218
+ }.freeze
219
+
220
+ alias :inspect :to_string
221
+ alias :== :equals?
222
+
223
+ # Returns the property with the specified name.
224
+ def get_property(name)
225
+ name = name.to_s if name.kind_of? Symbol
226
+ prop = Datastore.convert_exceptions { getProperty(name) }
227
+ ruby_type = SPECIAL_JAVA_TYPES[prop.class]
228
+ if ruby_type
229
+ ruby_type.new_from_java(prop)
230
+ else
231
+ prop
232
+ end
233
+ end
234
+ alias :[] :get_property
235
+
236
+ # Sets the property named, +name+, to +value+.
237
+ #
238
+ # As the value is stored in the datastore, it is converted to the
239
+ # datastore's native type.
240
+ #
241
+ # All Enumerables are prone to losing their sort order and their
242
+ # original types as they are stored in the datastore. For example, a
243
+ # Set may be returned as an Array from #getProperty, with an
244
+ # arbitrary re-ordering of elements.
245
+ #
246
+ # +value+ may be one of the supported datatypes, or a heterogenous
247
+ # Enumerable of one of the supported datatypes.
248
+ #
249
+ # Throws ArgumentError if the value is not of a type that
250
+ # the data store supports.
251
+ #
252
+ def set_property(name, value)
253
+ name = name.to_s if name.kind_of? Symbol
254
+ value = Datastore.ruby_to_java(value)
255
+ Datastore.convert_exceptions do
256
+ setProperty(name, value)
257
+ end
258
+ end
259
+ alias :[]= :set_property
260
+
261
+ # Removes any property with the specified name. If there is no
262
+ # property with this name set, simply does nothing.
263
+ #
264
+ def delete(name)
265
+ name = name.to_s if name.kind_of? Symbol
266
+ Datastore.convert_exceptions do
267
+ removeProperty(name)
268
+ end
269
+ end
270
+
271
+ # Returns true if a property has been set. This function can
272
+ # be used to test if a property has been specifically set
273
+ # to nil.
274
+ #
275
+ def has_property?(name)
276
+ name = name.to_s if name.kind_of? Symbol
277
+ Datastore.convert_exceptions do
278
+ hasProperty(name)
279
+ end
280
+ end
281
+ alias :has_property :has_property?
282
+
283
+ # Add the properties from +other+ to this Entity.
284
+ # Other may be an Entity or Hash
285
+ def update(other)
286
+ hash.each do |name, value|
287
+ self[name] = value
288
+ end
289
+ self
290
+ end
291
+ alias merge! update
292
+
293
+ # Iterates over all the properties in this Entity.
294
+ def each(&proc) # :yields: name, value
295
+ getProperties.each(&proc)
296
+ end
297
+ end
298
+
299
+ SPECIAL_RUBY_TYPES = [Time, Text, Blob, ByteString, Link].freeze
300
+
301
+ def Datastore.ruby_to_java(value) # :nodoc:
302
+ if SPECIAL_RUBY_TYPES.include? value.class
303
+ value.to_java
304
+ else
305
+ case value
306
+ when Fixnum
307
+ java.lang.Long.new(value)
308
+ when Float
309
+ java.lang.Double.new(value)
310
+ when String
311
+ value.to_java_string
312
+ else
313
+ value
314
+ end
315
+ end
316
+ end
317
+
318
+ def Datastore.convert_exceptions # :nodoc:
319
+ begin
320
+ yield
321
+ rescue java.lang.IllegalArgumentException => ex
322
+ raise ArgumentError, ex.message
323
+ rescue java.util.ConcurrentModificationException => ex
324
+ raise TransactionFailed, ex.message
325
+ rescue java.util.NoSuchElementException => ex
326
+ raise IndexError, ex.message
327
+ rescue JavaDatastore::DatastoreNeedIndexException => ex
328
+ raise NeedIndex, ex.message
329
+ rescue JavaDatastore::DatastoreTimeoutException => ex
330
+ raise Timeout, ex.message
331
+ rescue JavaDatastore::DatastoreFailureException => ex
332
+ raise InternalError, ex.message
333
+ rescue JavaDatastore::EntityNotFoundException => ex
334
+ raise EnityNotFound, ex.message
335
+ rescue JavaDatastore::PreparedQuery::TooManyResultsException => ex
336
+ raise TooManyResults, ex.message
337
+ end
338
+ end
339
+ end
340
+ end
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/ruby1.8 -w
2
+ #
3
+ # Copyright:: Copyright 2009 Google Inc.
4
+ # Original Author:: Ryan Brown (mailto:ribrdb@google.com)
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ #
19
+ # Replacement for the standard logger.rb which uses the Google App Engine
20
+ # logging API.
21
+
22
+ require 'appengine-apis/apiproxy'
23
+ require 'logger'
24
+
25
+ module AppEngine
26
+ class Logger < ::Logger
27
+ SEVERITIES = {
28
+ DEBUG => ApiProxy::LogRecord::Level::debug,
29
+ INFO => ApiProxy::LogRecord::Level::info,
30
+ WARN => ApiProxy::LogRecord::Level::warn,
31
+ ERROR => ApiProxy::LogRecord::Level::error,
32
+ FATAL => ApiProxy::LogRecord::Level::fatal,
33
+ }
34
+ SEVERITIES.default = ApiProxy::LogRecord::Level::info
35
+
36
+ def initialize(*args)
37
+ super(STDERR)
38
+ end
39
+
40
+ def <<(msg)
41
+ write_log(INFO, msg.to_s, "")
42
+ end
43
+
44
+ def add(severity, msg=nil, progname=nil, &block)
45
+ severity ||= UNKNOWN
46
+ return if severity < @level
47
+ progname ||= @progname
48
+ if msg.nil?
49
+ if block_given?
50
+ msg = yield
51
+ else
52
+ msg = progname
53
+ progname = @progname
54
+ end
55
+ end
56
+ write_log(severity, msg, progname)
57
+ end
58
+ alias log add
59
+
60
+ private
61
+ def write_log(severity, msg, progname)
62
+ level = SEVERITIES[severity]
63
+ if progname && !progname.empty? && progname != msg
64
+ msg = "#{progname}: #{msg}"
65
+ end
66
+ ApiProxy.log(level, msg)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/ruby1.8 -w
2
+ #
3
+ # Copyright:: Copyright 2009 Google Inc.
4
+ # Original Author:: Ryan Brown (mailto:ribrdb@google.com)
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ #
19
+ # Switches the Merb Logger class to use the Google App Engine logging API.
20
+
21
+ require 'merb-core/logger'
22
+
23
+ module Merb
24
+ class Logger
25
+ def <<(string = nil)
26
+ AppEngine::ApiProxy.log(
27
+ AppEngine::ApiProxy::LogRecord::Level::info, string)
28
+ end
29
+ alias :push :<<
30
+
31
+ # Re-generate the logging methods for Merb.logger for each log level.
32
+ Levels.each_pair do |name, number|
33
+ class_eval <<-LEVELMETHODS, __FILE__, __LINE__
34
+
35
+ # Appends a message to the log if the log level is at least as high as
36
+ # the log level of the logger.
37
+ #
38
+ # ==== Parameters
39
+ # string<String>:: The message to be logged. Defaults to nil.
40
+ #
41
+ # ==== Returns
42
+ # self:: The logger object for chaining.
43
+ def #{name}(message = nil)
44
+ if #{number} >= level
45
+ message = block_given? ? yield : message
46
+ AppEngine::ApiProxy.log(
47
+ AppEngine::ApiProxy::LogRecord::Level::#{name}, message)
48
+ end
49
+ self
50
+ end
51
+ alias :#{name}! :#{name}
52
+ LEVELMETHODS
53
+ end
54
+ end
55
+ end