appengine-apis 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|