appengine-apis 0.0.1 → 0.0.2
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/History.txt +4 -0
- data/Manifest.txt +4 -0
- data/README.rdoc +10 -0
- data/lib/appengine-apis.rb +1 -1
- data/lib/appengine-apis/datastore.rb +27 -9
- data/lib/appengine-apis/datastore_types.rb +24 -1
- data/lib/appengine-apis/logger.rb +13 -0
- data/lib/appengine-apis/merb-logger.rb +4 -1
- data/lib/appengine-apis/testing.rb +38 -6
- data/lib/appengine-apis/urlfetch.rb +226 -0
- data/lib/appengine-apis/users.rb +105 -0
- data/spec/spec.opts +1 -1
- data/spec/urlfetch_spec.rb +152 -0
- data/spec/users_spec.rb +58 -0
- metadata +12 -4
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -10,6 +10,8 @@ lib/appengine-apis/datastore_types.rb
|
|
10
10
|
lib/appengine-apis/logger.rb
|
11
11
|
lib/appengine-apis/merb-logger.rb
|
12
12
|
lib/appengine-apis/testing.rb
|
13
|
+
lib/appengine-apis/urlfetch.rb
|
14
|
+
lib/appengine-apis/users.rb
|
13
15
|
script/console
|
14
16
|
script/destroy
|
15
17
|
script/generate
|
@@ -18,4 +20,6 @@ spec/datastore_types_spec.rb
|
|
18
20
|
spec/logger_spec.rb
|
19
21
|
spec/spec.opts
|
20
22
|
spec/spec_helper.rb
|
23
|
+
spec/urlfetch_spec.rb
|
24
|
+
spec/users_spec.rb
|
21
25
|
tasks/rspec.rake
|
data/README.rdoc
CHANGED
@@ -6,6 +6,16 @@
|
|
6
6
|
|
7
7
|
APIs and utilities for using JRuby on Google App Engine.
|
8
8
|
|
9
|
+
See these classes for an overview of each API:
|
10
|
+
- AppEngine::Logger
|
11
|
+
- AppEngine::Testing
|
12
|
+
- AppEngine::Users
|
13
|
+
- AppEngine::URLFetch
|
14
|
+
- AppEngine::Datastore
|
15
|
+
|
16
|
+
Unless you're implementing your own ORM, you probably want to use the
|
17
|
+
DataMapper API instead of the lower level AppEngine::Datastore API.
|
18
|
+
|
9
19
|
== REQUIREMENTS:
|
10
20
|
|
11
21
|
* Google App Engine SDK for Java (http://code.google.com/appengine)
|
data/lib/appengine-apis.rb
CHANGED
@@ -29,12 +29,12 @@ require 'appengine-apis/datastore_types'
|
|
29
29
|
|
30
30
|
module AppEngine
|
31
31
|
|
32
|
-
# The
|
32
|
+
# The Datastore provides access to a schema-less data
|
33
33
|
# storage system. The fundamental unit of data in this system is the
|
34
|
-
#
|
35
|
-
#
|
34
|
+
# Datastore::Entity, which has an immutable identity (represented by a
|
35
|
+
# Datastore::Key) and zero of more mutable properties. Entity
|
36
36
|
# objects can be created, updated, deleted, retrieved by identifier,
|
37
|
-
# and queried via a combination of properties.
|
37
|
+
# and queried via a combination of properties using Datastore::Query.
|
38
38
|
#
|
39
39
|
# The +Datastore+ can be used transactionally and supports the
|
40
40
|
# notion of a "current" transaction. A current transaction is established by
|
@@ -47,6 +47,21 @@ module AppEngine
|
|
47
47
|
# Users of this class have the choice of explicitly passing a (potentially
|
48
48
|
# null) +Transaction+ to these methods or relying on the current transaction.
|
49
49
|
#
|
50
|
+
# Supported property types:
|
51
|
+
# - String (max 500 chars)
|
52
|
+
# - Integer ((-2**63)..(2**63 - 1))
|
53
|
+
# - Float
|
54
|
+
# - Time
|
55
|
+
# - TrueClass
|
56
|
+
# - FalseClass
|
57
|
+
# - NilClass
|
58
|
+
# - Datastore::Key
|
59
|
+
# - Datastore::Link
|
60
|
+
# - Datastore::Text
|
61
|
+
# - Datastore::Blob
|
62
|
+
# - Datastore::ByteString
|
63
|
+
# - com.google.appengine.api.users.User
|
64
|
+
|
50
65
|
module Datastore
|
51
66
|
module_function
|
52
67
|
|
@@ -254,11 +269,14 @@ module Datastore
|
|
254
269
|
FetchOptions = JavaDatastore::FetchOptions
|
255
270
|
|
256
271
|
module Constants
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
272
|
+
EQUAL = JQuery::FilterOperator::EQUAL
|
273
|
+
GREATER_THAN = JQuery::FilterOperator::GREATER_THAN
|
274
|
+
GREATER_THAN_OR_EQUAL = JQuery::FilterOperator::GREATER_THAN_OR_EQUAL
|
275
|
+
LESS_THAN = JQuery::FilterOperator::LESS_THAN
|
276
|
+
LESS_THAN_OR_EQUAL = JQuery::FilterOperator::LESS_THAN_OR_EQUAL
|
277
|
+
|
278
|
+
ASCENDING = JQuery::SortDirection::ASCENDING
|
279
|
+
DESCENDING = JQuery::SortDirection::DESCENDING
|
262
280
|
end
|
263
281
|
include Constants
|
264
282
|
|
@@ -35,20 +35,42 @@ module AppEngine
|
|
35
35
|
module Datastore
|
36
36
|
JavaDatastore = Java.ComGoogleAppengineApiDatastore
|
37
37
|
|
38
|
+
# Base class of Datastore Errors
|
38
39
|
class Error < StandardError; end
|
40
|
+
|
41
|
+
# Raised when a query requires a Composite index that does not exist
|
39
42
|
class NeedIndex < Error; end
|
43
|
+
|
44
|
+
# The datastore operation timed out. This can happen when you attempt to
|
45
|
+
# put, get, or delete too many entities or an entity with too many
|
46
|
+
# properties, or if the datastore is overloaded or having trouble.
|
40
47
|
class Timeout < Error; end
|
48
|
+
|
49
|
+
# An internal datastore error. Please report this to Google.
|
41
50
|
class InternalError < Error; end
|
51
|
+
|
52
|
+
# May be raised during a call to #transaction to abort and rollback the
|
53
|
+
# transaction. Note that *any* exception raised by a transaction
|
54
|
+
# block will cause a rollback. This is purely for convenience.
|
42
55
|
class Rollback < Error; end
|
56
|
+
|
57
|
+
# Raised when a transaction could not be committed, usually due to
|
58
|
+
# contention.
|
43
59
|
class TransactionFailed < Error; end
|
60
|
+
|
61
|
+
# Raised by #get when the requested entity is not found.
|
44
62
|
class EntityNotFound < Error; end
|
63
|
+
|
64
|
+
# Raised by Datastore::Query.entity if the query returns more
|
65
|
+
# than one entity
|
45
66
|
class TooManyResults < Error; end
|
46
67
|
|
47
|
-
#A long string type.
|
68
|
+
# A long string type.
|
48
69
|
#
|
49
70
|
# Strings of any length can be stored in the datastore using this
|
50
71
|
# type.
|
51
72
|
#
|
73
|
+
# Not indexed.
|
52
74
|
class Text < String
|
53
75
|
def to_java
|
54
76
|
JavaDatastore::Text.new(self.to_java_string)
|
@@ -60,6 +82,7 @@ module AppEngine
|
|
60
82
|
end
|
61
83
|
|
62
84
|
# A blob type, appropriate for storing binary data of any length.
|
85
|
+
# Not indexed.
|
63
86
|
class Blob < String
|
64
87
|
def to_java
|
65
88
|
JavaDatastore::Blob.new(self.to_java_bytes)
|
@@ -19,10 +19,23 @@
|
|
19
19
|
# Replacement for the standard logger.rb which uses the Google App Engine
|
20
20
|
# logging API.
|
21
21
|
|
22
|
+
|
22
23
|
require 'appengine-apis/apiproxy'
|
23
24
|
require 'logger'
|
24
25
|
|
25
26
|
module AppEngine
|
27
|
+
|
28
|
+
# Replacement for the standard logger.rb. Saves logs to the App Engine
|
29
|
+
# Dashboard (or to the java logging api when running locally).
|
30
|
+
#
|
31
|
+
# logger = AppEngine::Logger.new
|
32
|
+
# logger.warn "foobar"
|
33
|
+
#
|
34
|
+
# or (for compatibility with code already using logger.rb)
|
35
|
+
#
|
36
|
+
# Logger = AppEngine::Logger
|
37
|
+
# logger = Logger.new(stream) # stream is ignored
|
38
|
+
# logger.info "Hello, dashboard"
|
26
39
|
class Logger < ::Logger
|
27
40
|
SEVERITIES = {
|
28
41
|
DEBUG => ApiProxy::LogRecord::Level::debug,
|
@@ -20,7 +20,10 @@
|
|
20
20
|
|
21
21
|
require 'merb-core/logger'
|
22
22
|
|
23
|
-
module Merb
|
23
|
+
module Merb # :nodoc:
|
24
|
+
|
25
|
+
# Modifies the Merb Logger class to save logs using the Logging API
|
26
|
+
# instead of writing directly to a stream.
|
24
27
|
class Logger
|
25
28
|
def <<(string = nil)
|
26
29
|
AppEngine::ApiProxy.log(
|
@@ -18,9 +18,18 @@
|
|
18
18
|
#
|
19
19
|
# Helpers for installing stub apis in unit tests.
|
20
20
|
|
21
|
+
|
21
22
|
require 'appengine-apis/apiproxy'
|
22
23
|
|
23
24
|
module AppEngine
|
25
|
+
|
26
|
+
# Local testing support for Google App Engine
|
27
|
+
#
|
28
|
+
# If you run your code on Google's servers or under dev_appserver,
|
29
|
+
# the api's are already configured.
|
30
|
+
#
|
31
|
+
# To run outside this environment, you need to install a test environment and
|
32
|
+
# api stubs.
|
24
33
|
module Testing
|
25
34
|
import com.google.appengine.tools.development.ApiProxyLocalFactory
|
26
35
|
import com.google.appengine.api.datastore.dev.LocalDatastoreService
|
@@ -28,7 +37,7 @@ module AppEngine
|
|
28
37
|
class TestEnv # :nodoc:
|
29
38
|
include AppEngine::ApiProxy::Environment
|
30
39
|
|
31
|
-
attr_writer :appid, :version, :email, :admin
|
40
|
+
attr_writer :appid, :version, :email, :admin
|
32
41
|
attr_writer :auth_domain, :request_namespace, :default_namespace
|
33
42
|
|
34
43
|
def initialize
|
@@ -50,11 +59,11 @@ module AppEngine
|
|
50
59
|
end
|
51
60
|
|
52
61
|
def isLoggedIn
|
53
|
-
@
|
62
|
+
!(@email.nil? || @auth_domain.nil?)
|
54
63
|
end
|
55
64
|
|
56
65
|
def isAdmin
|
57
|
-
|
66
|
+
!!@admin
|
58
67
|
end
|
59
68
|
|
60
69
|
def getAuthDomain
|
@@ -75,26 +84,43 @@ module AppEngine
|
|
75
84
|
end
|
76
85
|
|
77
86
|
class << self
|
87
|
+
|
88
|
+
# Install a test environment for the current thread.
|
89
|
+
#
|
90
|
+
# You must call this before making any api calls.
|
91
|
+
#
|
92
|
+
# Note that Google's production and local environments are
|
93
|
+
# single threaded. You may run into problems if you use multiple
|
94
|
+
# threads.
|
78
95
|
def install_test_env
|
79
96
|
env = TestEnv.new
|
80
97
|
ApiProxy::setEnvironmentForCurrentThread(env)
|
81
98
|
env
|
82
99
|
end
|
83
100
|
|
84
|
-
def factory
|
101
|
+
def factory # :nodoc:
|
85
102
|
@factory ||= ApiProxyLocalFactory.new
|
86
103
|
end
|
87
104
|
|
105
|
+
# The application directory, or '.' if not set.
|
106
|
+
# Composite index definitions are written to "#{app_dir}/WEB-INF/".
|
88
107
|
def app_dir
|
89
108
|
file = factory.getApplicationDirectory
|
90
109
|
file && file.path
|
91
110
|
end
|
92
|
-
|
111
|
+
|
112
|
+
# Sets the application directory. Should be called before
|
113
|
+
# creating stubs.
|
114
|
+
#
|
115
|
+
# Composite index definitions are written to "#{app_dir}/WEB-INF/".
|
93
116
|
def app_dir=(dir)
|
94
117
|
factory.setApplicationDirectory(java.io.File.new(dir))
|
95
118
|
end
|
96
119
|
|
97
|
-
#
|
120
|
+
# Install stub apis and force all datastore operations to use
|
121
|
+
# an in-memory datastore.
|
122
|
+
#
|
123
|
+
# You may call this multiple times to reset to a new in-memory datastore.
|
98
124
|
def install_test_datastore
|
99
125
|
self.app_dir = '.' if app_dir.nil?
|
100
126
|
delegate = factory.create
|
@@ -110,6 +136,12 @@ module AppEngine
|
|
110
136
|
delegate
|
111
137
|
end
|
112
138
|
|
139
|
+
# Install stub apis. The datastore will be written to the disk
|
140
|
+
# inside #app_dir.
|
141
|
+
#
|
142
|
+
# You could potentially use this to run under a ruby web server
|
143
|
+
# instead of dev_appserver. In that case you will need to install
|
144
|
+
# and configure a test environment for each request.
|
113
145
|
def install_api_stubs
|
114
146
|
self.app_dir = '.' if app_dir.nil?
|
115
147
|
delegate = factory.create
|
@@ -0,0 +1,226 @@
|
|
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
|
+
require 'net/https'
|
20
|
+
|
21
|
+
|
22
|
+
module AppEngine
|
23
|
+
# The URLFetch Service provides a way for user code to execute HTTP requests
|
24
|
+
# to external URLs.
|
25
|
+
#
|
26
|
+
# Chunked and hanging requests are not supported, and all content will be
|
27
|
+
# returned in a single block.
|
28
|
+
module URLFetch
|
29
|
+
import com.google.appengine.api.urlfetch.FetchOptions
|
30
|
+
import com.google.appengine.api.urlfetch.HTTPHeader
|
31
|
+
import com.google.appengine.api.urlfetch.HTTPMethod
|
32
|
+
import com.google.appengine.api.urlfetch.HTTPRequest
|
33
|
+
import com.google.appengine.api.urlfetch.ResponseTooLargeException
|
34
|
+
import com.google.appengine.api.urlfetch.URLFetchServiceFactory
|
35
|
+
|
36
|
+
# Raised if the remote service could not be contacted
|
37
|
+
class DownloadError < StandardError; end
|
38
|
+
|
39
|
+
# Raised if the url cannot be parsed.
|
40
|
+
class InvalidURLError < StandardError; end
|
41
|
+
|
42
|
+
# Raised if the response is too large.
|
43
|
+
class ResponseTooLargeError < StandardError; end
|
44
|
+
|
45
|
+
module_function
|
46
|
+
|
47
|
+
#Fetches the given HTTP URL, blocking until the result is returned.
|
48
|
+
#
|
49
|
+
# Supported options:
|
50
|
+
# [:method] GET, POST, HEAD, PUT, or DELETE
|
51
|
+
# [:payload] POST or PUT payload (implies method is not GET, HEAD,
|
52
|
+
# or DELETE)
|
53
|
+
# [:headers]
|
54
|
+
# HTTP headers to send with the request. May be a Hash or
|
55
|
+
# Net::HTTPHeaders.
|
56
|
+
# [:allow_truncated]
|
57
|
+
# if true, truncate large responses and return them
|
58
|
+
# without error. otherwise, ResponseTooLargeError will be thrown when a
|
59
|
+
# response is truncated.
|
60
|
+
# [:follow_redirects]
|
61
|
+
# if true (the default), redirects are transparently followed and the
|
62
|
+
# response (if less than 5 redirects) contains the final destination's
|
63
|
+
# payload and the response status is 200. You lose, however, the
|
64
|
+
# redirect chaininformation. If false, you see the HTTP response
|
65
|
+
# yourself, including the 'Location' header, and redirects are not
|
66
|
+
# followed.
|
67
|
+
#
|
68
|
+
# Returns a Net::HTTPResponse.
|
69
|
+
#
|
70
|
+
# Throws:
|
71
|
+
# - InvalidURLError if the provided url is malformed.
|
72
|
+
# - DownloadError if the remote service could not be contacted or the URL
|
73
|
+
# could not be fetched.
|
74
|
+
# - ResponseTooLargeError if response truncation has been disabled and the
|
75
|
+
# response is too large. Some responses are too large to even retrieve
|
76
|
+
# from the remote server, and in these cases the exception is thrown even
|
77
|
+
# if response truncation is enabled.
|
78
|
+
#
|
79
|
+
def fetch(url, options={})
|
80
|
+
request = build_urlfetch_request(url, options)
|
81
|
+
begin
|
82
|
+
java_response = urlfetch_service.fetch(request)
|
83
|
+
return convert_urlfetch_body(java_response)
|
84
|
+
rescue java.lang.IllegalArgumentException => ex
|
85
|
+
raise ArgumentError, ex.message
|
86
|
+
rescue java.net.MalformedURLException => ex
|
87
|
+
raise InvalidURLError, ex.message
|
88
|
+
rescue java.io.IOException => ex
|
89
|
+
raise DownloadError, ex.message
|
90
|
+
rescue ResponseTooLargeException => ex
|
91
|
+
raise ResponseTooLargeError, ex.message
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def build_urlfetch_request(url, options) # :nodoc:
|
96
|
+
method = options.delete(:method) || 'GET'
|
97
|
+
payload = options.delete(:payload)
|
98
|
+
headers = options.delete(:headers) || {}
|
99
|
+
truncate = options.delete(:allow_truncated)
|
100
|
+
follow_redirects = options.delete(:follow_redirects) || true
|
101
|
+
|
102
|
+
unless options.empty?
|
103
|
+
raise ArgumentError, "Unsupported options #{options.inspect}."
|
104
|
+
end
|
105
|
+
|
106
|
+
begin
|
107
|
+
method = HTTPMethod.value_of(method.to_s.upcase)
|
108
|
+
rescue java.lang.IllegalArgumentException
|
109
|
+
raise ArgumentError, "Invalid method #{method.inspect}."
|
110
|
+
end
|
111
|
+
|
112
|
+
if truncate
|
113
|
+
options = FetchOptions::Builder.allow_truncate
|
114
|
+
else
|
115
|
+
options = FetchOptions::Builder.disallow_truncate
|
116
|
+
end
|
117
|
+
if follow_redirects
|
118
|
+
options.follow_redirects
|
119
|
+
else
|
120
|
+
options.do_not_follow_redirects
|
121
|
+
end
|
122
|
+
|
123
|
+
url = java.net.URL.new(url) unless url.java_kind_of? java.net.URL
|
124
|
+
request = HTTPRequest.new(url, method, options)
|
125
|
+
|
126
|
+
iterator = if headers.respond_to?(:canonical_each)
|
127
|
+
:canonical_each
|
128
|
+
else
|
129
|
+
:each
|
130
|
+
end
|
131
|
+
|
132
|
+
headers.send(iterator) do |name, value|
|
133
|
+
request.set_header(HTTPHeader.new(name, value))
|
134
|
+
end
|
135
|
+
|
136
|
+
if payload
|
137
|
+
request.set_payload(payload.as_java_bytes)
|
138
|
+
end
|
139
|
+
|
140
|
+
return request
|
141
|
+
end
|
142
|
+
|
143
|
+
def convert_urlfetch_body(java_response) # :nodoc:
|
144
|
+
status = java_response.response_code.to_s
|
145
|
+
klass = Net::HTTPResponse.send(:response_class, status)
|
146
|
+
mesg = klass.name
|
147
|
+
mesg = mesg[4, mesg.size]
|
148
|
+
response = klass.new(nil, status, mesg)
|
149
|
+
java_response.headers.each do |header|
|
150
|
+
response.add_field(header.name, header.value)
|
151
|
+
end
|
152
|
+
body = if java_response.content
|
153
|
+
String.from_java_bytes(java_response.content)
|
154
|
+
else
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
response.urlfetch_body = body
|
158
|
+
return response
|
159
|
+
end
|
160
|
+
|
161
|
+
def urlfetch_service # :nodoc:
|
162
|
+
@service ||= URLFetchServiceFactory.getURLFetchService
|
163
|
+
end
|
164
|
+
|
165
|
+
def urlfetch_service=(service) # :nodoc:
|
166
|
+
@service = service
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# A subclass of Net::HTTP that makes requests using Google App Engine's
|
171
|
+
# URLFetch Service.
|
172
|
+
#
|
173
|
+
# To replace the standard implementation throughout your app you can do:
|
174
|
+
# require 'appengine-apis/urlfetch'
|
175
|
+
# Net::HTTP = AppEngine::URLFetch::HTTP
|
176
|
+
class HTTP < Net::HTTP
|
177
|
+
alias connect on_connect
|
178
|
+
|
179
|
+
def request(req, body=nil, &block)
|
180
|
+
begin
|
181
|
+
proto = use_ssl? ? 'https' : 'http'
|
182
|
+
url = "#{proto}://#{addr_port}#{req.path}"
|
183
|
+
options = {
|
184
|
+
:payload => body,
|
185
|
+
:follow_redirects => false,
|
186
|
+
:allow_truncated => true,
|
187
|
+
:method => req.method,
|
188
|
+
:headers => req
|
189
|
+
}
|
190
|
+
res = URLFetch.fetch(url, options)
|
191
|
+
end while res.kind_of?(Net::HTTPContinue)
|
192
|
+
res.reading_body(nil, req.response_body_permitted?) {
|
193
|
+
yield res if block_given?
|
194
|
+
}
|
195
|
+
return res
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
module Net # :nodoc:
|
202
|
+
class HTTPResponse # :nodoc:
|
203
|
+
alias stream_check_without_urlfetch stream_check
|
204
|
+
|
205
|
+
def stream_check
|
206
|
+
return if @urlfetch_body
|
207
|
+
stream_check_without_urlfetch
|
208
|
+
end
|
209
|
+
|
210
|
+
alias read_body_0_without_urlfetch read_body_0
|
211
|
+
|
212
|
+
def read_body_0(dest)
|
213
|
+
if @urlfetch_body
|
214
|
+
dest << @urlfetch_body
|
215
|
+
return
|
216
|
+
else
|
217
|
+
read_body_0_without_urlfetch(dest)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def urlfetch_body=(body)
|
222
|
+
@body_exist = body && self.class.body_permitted?
|
223
|
+
@urlfetch_body = body || ''
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,105 @@
|
|
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
|
+
module AppEngine
|
20
|
+
# Users provides information useful for forcing a user to log in or out, and
|
21
|
+
# retrieving information about the user who is currently logged-in.
|
22
|
+
module Users
|
23
|
+
import com.google.appengine.api.users.User
|
24
|
+
import com.google.appengine.api.users.UserServiceFactory
|
25
|
+
|
26
|
+
Service = UserServiceFactory.getUserService
|
27
|
+
|
28
|
+
class << self
|
29
|
+
|
30
|
+
# If the user is logged in, this method will return a User that contains
|
31
|
+
# information about them. Note that repeated calls may not necessarily
|
32
|
+
# return the same User object.
|
33
|
+
def current_user
|
34
|
+
Service.current_user
|
35
|
+
end
|
36
|
+
|
37
|
+
# Computes the login URL for this request and specified destination URL.
|
38
|
+
#
|
39
|
+
# Args:
|
40
|
+
# - dest_url: The desired final destination URL for the user
|
41
|
+
# once login is complete. If +dest_url+ does not have a host
|
42
|
+
# specified, we will use the host from the current request.
|
43
|
+
def create_login_url(url)
|
44
|
+
Service.create_login_url(url)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Computes the logout URL for this request and specified destination URL.
|
48
|
+
#
|
49
|
+
# Args:
|
50
|
+
# - dest_url: String that is the desired final destination URL for the
|
51
|
+
# user once logout is complete. If +dest_url+ does not have
|
52
|
+
# a host specified, uses the host from the current request.
|
53
|
+
def create_logout_url(url)
|
54
|
+
Service.create_logout_url(url)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns true if there is a user logged in, false otherwise.
|
58
|
+
def logged_in?
|
59
|
+
Service.is_user_logged_in?
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if the user making this request is an admin for this
|
63
|
+
# application, false otherwise.
|
64
|
+
#
|
65
|
+
# This is a separate function, and not a member function of the User
|
66
|
+
# class, because admin status is not persisted in the datastore. It
|
67
|
+
# only exists for the user making this request right now.
|
68
|
+
def admin?
|
69
|
+
Service.is_user_admin?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# User represents a specific user, represented by the combination of an
|
74
|
+
# email address and a specific Google Apps domain (which we call an
|
75
|
+
# auth_domain). For normal Google login, authDomain will be set to
|
76
|
+
# "gmail.com".
|
77
|
+
class User
|
78
|
+
alias == equals?
|
79
|
+
alias to_s toString
|
80
|
+
|
81
|
+
if nil # rdoc only
|
82
|
+
# Creates a new User.
|
83
|
+
#
|
84
|
+
# Args:
|
85
|
+
# - email: a non-nil email address.
|
86
|
+
# - auth_domain: a non-nil domain name into which this user has
|
87
|
+
# authenticated, or "gmail.com" for normal Google authentication.
|
88
|
+
def initialize(email, auth_domain)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Return this user's nickname. The nickname will be a unique, human
|
92
|
+
# readable identifier for this user with respect to this application.
|
93
|
+
# It will be an email address for some users, but not all.
|
94
|
+
def nickname
|
95
|
+
end
|
96
|
+
|
97
|
+
def auth_domain
|
98
|
+
end
|
99
|
+
|
100
|
+
def email
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/spec/spec.opts
CHANGED
@@ -1 +1 @@
|
|
1
|
-
--colour
|
1
|
+
--colour --backtrace
|
@@ -0,0 +1,152 @@
|
|
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
|
+
|
20
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
21
|
+
require 'appengine-apis/urlfetch'
|
22
|
+
require 'uri'
|
23
|
+
|
24
|
+
module AppEngine::URLFetch
|
25
|
+
class FakeResponse
|
26
|
+
def initialize(status, body, headers)
|
27
|
+
@headers = java.util.ArrayList.new
|
28
|
+
headers.each do |k, v|
|
29
|
+
@headers << HTTPHeader.new(k, v)
|
30
|
+
end
|
31
|
+
@content = body.to_java_bytes if body
|
32
|
+
@status = status
|
33
|
+
end
|
34
|
+
|
35
|
+
def getResponseCode
|
36
|
+
@status
|
37
|
+
end
|
38
|
+
alias get_response_code getResponseCode
|
39
|
+
alias response_code getResponseCode
|
40
|
+
|
41
|
+
def getContent
|
42
|
+
@content
|
43
|
+
end
|
44
|
+
alias get_content getContent
|
45
|
+
alias content getContent
|
46
|
+
|
47
|
+
def getHeaders
|
48
|
+
@headers
|
49
|
+
end
|
50
|
+
alias get_headers getHeaders
|
51
|
+
alias headers getHeaders
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe AppEngine::URLFetch do
|
56
|
+
HTTPRequest = com.google.appengine.api.urlfetch.HTTPRequest
|
57
|
+
|
58
|
+
before :each do
|
59
|
+
AppEngine::URLFetch.urlfetch_service = mock("URLFetchService")
|
60
|
+
@url = 'http://foo.example.com/'
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch_and_return(status, body='body', headers={}, &block)
|
64
|
+
response = AppEngine::URLFetch::FakeResponse.new(status, body, headers)
|
65
|
+
if block
|
66
|
+
AppEngine::URLFetch.urlfetch_service.should_receive(:fetch) do |req|
|
67
|
+
yield req
|
68
|
+
response
|
69
|
+
end
|
70
|
+
else
|
71
|
+
matcher = AppEngine::URLFetch.urlfetch_service.should_receive(:fetch)
|
72
|
+
matcher.with(kind_of(HTTPRequest)).and_return(response)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should fetch correct url" do
|
77
|
+
fetch_and_return(200) do |req|
|
78
|
+
req.url.to_string.should == @url
|
79
|
+
end
|
80
|
+
AppEngine::URLFetch.fetch(@url)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should read ok status" do
|
84
|
+
fetch_and_return(200)
|
85
|
+
|
86
|
+
AppEngine::URLFetch.fetch(@url).should be_a(Net::HTTPOK)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should read error status" do
|
90
|
+
fetch_and_return(500)
|
91
|
+
AppEngine::URLFetch.fetch(@url).should be_a(Net::HTTPInternalServerError)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should read body" do
|
95
|
+
fetch_and_return(200, 'foo')
|
96
|
+
AppEngine::URLFetch.fetch(@url).body.should == 'foo'
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should read binary body" do
|
100
|
+
fetch_and_return(200, 'bar\0')
|
101
|
+
AppEngine::URLFetch.fetch(@url).body.should == 'bar\0'
|
102
|
+
end
|
103
|
+
|
104
|
+
# TODO can't read fetch options, so there's no way to tell if they're set
|
105
|
+
end
|
106
|
+
|
107
|
+
describe AppEngine::URLFetch::HTTP do
|
108
|
+
HTTPRequest = com.google.appengine.api.urlfetch.HTTPRequest
|
109
|
+
|
110
|
+
before :each do
|
111
|
+
AppEngine::URLFetch.urlfetch_service = mock("URLFetchService")
|
112
|
+
@url = 'http://foo.example.com/'
|
113
|
+
end
|
114
|
+
|
115
|
+
def fetch_and_return(status, body='body', headers={}, &block)
|
116
|
+
response = AppEngine::URLFetch::FakeResponse.new(status, body, headers)
|
117
|
+
if block
|
118
|
+
AppEngine::URLFetch.urlfetch_service.should_receive(:fetch) do |req|
|
119
|
+
yield req
|
120
|
+
response
|
121
|
+
end
|
122
|
+
else
|
123
|
+
matcher = AppEngine::URLFetch.urlfetch_service.should_receive(:fetch)
|
124
|
+
matcher.with(kind_of(HTTPRequest)).and_return(response)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should fetch correct url" do
|
129
|
+
fetch_and_return(200) do |req|
|
130
|
+
req.url.to_string.should == @url
|
131
|
+
end
|
132
|
+
AppEngine::URLFetch::HTTP.get URI.parse(@url)
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
it "should read body" do
|
137
|
+
fetch_and_return(200, 'foo')
|
138
|
+
AppEngine::URLFetch::HTTP.get(URI.parse(@url)).should == 'foo'
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should support https" do
|
142
|
+
@url = 'https://foo.example.com/'
|
143
|
+
fetch_and_return(200, 'secure') do |req|
|
144
|
+
req.url.to_string.should == @url
|
145
|
+
end
|
146
|
+
|
147
|
+
uri = URI.parse(@url)
|
148
|
+
http = AppEngine::URLFetch::HTTP.new(uri.host, uri.port)
|
149
|
+
http.use_ssl = true
|
150
|
+
http.get(uri.path).body.should == 'secure'
|
151
|
+
end
|
152
|
+
end
|
data/spec/users_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Copyright:: Copyright 2009 Google Inc.
|
2
|
+
# Original Author:: Ryan Brown (mailto:ribrdb@google.com)
|
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
|
+
|
16
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
17
|
+
require 'appengine-apis/users'
|
18
|
+
|
19
|
+
describe AppEngine::Users do
|
20
|
+
before :all do
|
21
|
+
AppEngine::Testing::install_api_stubs
|
22
|
+
end
|
23
|
+
|
24
|
+
before :each do
|
25
|
+
@env = AppEngine::Testing::install_test_env
|
26
|
+
@env.email = 'foo@example.com'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should have default auth_domain" do
|
30
|
+
user = AppEngine::Users.current_user
|
31
|
+
user.email.should == 'foo@example.com'
|
32
|
+
user.auth_domain.should == 'gmail.com'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should read auth_domain' do
|
36
|
+
@env.auth_domain = 'example.com'
|
37
|
+
AppEngine::Users.current_user.auth_domain.should == 'example.com'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should read admin' do
|
41
|
+
AppEngine::Users.admin?.should == false
|
42
|
+
@env.admin = true
|
43
|
+
AppEngine::Users.admin?.should == true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should set logged_in?' do
|
47
|
+
AppEngine::Users.logged_in?.should == true
|
48
|
+
@env.email = nil
|
49
|
+
AppEngine::Users.logged_in?.should == false
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should create urls' do
|
53
|
+
login = AppEngine::Users.create_login_url('/foobar')
|
54
|
+
logout = AppEngine::Users.create_logout_url('/foobaz')
|
55
|
+
login.should =~ /foobar/
|
56
|
+
logout.should =~ /foobaz/
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -9,7 +9,7 @@ email:
|
|
9
9
|
- ribrdb@gmail.com
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
summary: APIs and utilities for using JRuby on Google App Engine
|
12
|
+
summary: APIs and utilities for using JRuby on Google App Engine
|
13
13
|
post_install_message: PostInstall.txt
|
14
14
|
extra_rdoc_files:
|
15
15
|
- History.txt
|
@@ -26,7 +26,11 @@ autorequire:
|
|
26
26
|
rubyforge_project: appengine-jruby
|
27
27
|
executables: []
|
28
28
|
|
29
|
-
description: APIs and utilities for using JRuby on Google App Engine.
|
29
|
+
description: 'APIs and utilities for using JRuby on Google App Engine. See these
|
30
|
+
classes for an overview of each API: - AppEngine::Logger - AppEngine::Testing -
|
31
|
+
AppEngine::Users - AppEngine::URLFetch - AppEngine::Datastore Unless you''re implementing
|
32
|
+
your own ORM, you probably want to use the DataMapper API instead of the lower level
|
33
|
+
AppEngine::Datastore API.'
|
30
34
|
specification_version: 2
|
31
35
|
default_executable:
|
32
36
|
files:
|
@@ -42,6 +46,8 @@ files:
|
|
42
46
|
- lib/appengine-apis/logger.rb
|
43
47
|
- lib/appengine-apis/merb-logger.rb
|
44
48
|
- lib/appengine-apis/testing.rb
|
49
|
+
- lib/appengine-apis/urlfetch.rb
|
50
|
+
- lib/appengine-apis/users.rb
|
45
51
|
- script/console
|
46
52
|
- script/destroy
|
47
53
|
- script/generate
|
@@ -50,6 +56,8 @@ files:
|
|
50
56
|
- spec/logger_spec.rb
|
51
57
|
- spec/spec.opts
|
52
58
|
- spec/spec_helper.rb
|
59
|
+
- spec/urlfetch_spec.rb
|
60
|
+
- spec/users_spec.rb
|
53
61
|
- tasks/rspec.rake
|
54
62
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
63
|
requirements:
|
@@ -64,12 +72,12 @@ requirements: []
|
|
64
72
|
|
65
73
|
authors:
|
66
74
|
- Ryan Brown
|
67
|
-
date: 2009-04-
|
75
|
+
date: 2009-04-15 07:00:00 +00:00
|
68
76
|
platform: ruby
|
69
77
|
test_files: []
|
70
78
|
|
71
79
|
version: !ruby/object:Gem::Version
|
72
|
-
version: 0.0.
|
80
|
+
version: 0.0.2
|
73
81
|
require_paths:
|
74
82
|
- lib
|
75
83
|
dependencies:
|