qoobaa-opensocial 0.1.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +202 -0
- data/NOTICE +60 -0
- data/README.rdoc +99 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/opensocial.rb +31 -0
- data/lib/opensocial/activity.rb +113 -0
- data/lib/opensocial/appdata.rb +114 -0
- data/lib/opensocial/auth/action_controller_request.rb +88 -0
- data/lib/opensocial/auth/base.rb +109 -0
- data/lib/opensocial/base.rb +50 -0
- data/lib/opensocial/connection.rb +128 -0
- data/lib/opensocial/group.rb +80 -0
- data/lib/opensocial/person.rb +197 -0
- data/lib/opensocial/request.rb +263 -0
- data/lib/opensocial/string/merb_string.rb +32 -0
- data/lib/opensocial/string/os_string.rb +23 -0
- data/qoobaa-opensocial.gemspec +99 -0
- data/test/fixtures/activities.json +28 -0
- data/test/fixtures/activity.json +13 -0
- data/test/fixtures/appdata.json +6 -0
- data/test/fixtures/appdatum.json +5 -0
- data/test/fixtures/group.json +7 -0
- data/test/fixtures/groups.json +16 -0
- data/test/fixtures/people.json +20 -0
- data/test/fixtures/person.json +9 -0
- data/test/fixtures/person_appdata_rpc.json +1 -0
- data/test/fixtures/person_rpc.json +1 -0
- data/test/helper.rb +41 -0
- data/test/test_activity.rb +105 -0
- data/test/test_appdata.rb +57 -0
- data/test/test_connection.rb +52 -0
- data/test/test_group.rb +70 -0
- data/test/test_online.rb +67 -0
- data/test/test_person.rb +140 -0
- data/test/test_request.rb +49 -0
- data/test/test_rpcrequest.rb +93 -0
- metadata +157 -0
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/opensocial.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright (c) 2008 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "active_support"
|
16
|
+
require "oauth"
|
17
|
+
require "oauth/consumer"
|
18
|
+
require "oauth/request_proxy/action_controller_request"
|
19
|
+
require "uri"
|
20
|
+
|
21
|
+
require "opensocial/base"
|
22
|
+
require "opensocial/request"
|
23
|
+
require "opensocial/activity"
|
24
|
+
require "opensocial/appdata"
|
25
|
+
require "opensocial/auth/action_controller_request"
|
26
|
+
require "opensocial/auth/base"
|
27
|
+
require "opensocial/connection"
|
28
|
+
require "opensocial/group"
|
29
|
+
require "opensocial/person"
|
30
|
+
require "opensocial/string/merb_string"
|
31
|
+
require "opensocial/string/os_string"
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Copyright (c) 2008 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module OpenSocial #:nodoc:
|
16
|
+
|
17
|
+
# Acts as a wrapper for an OpenSocial activity.
|
18
|
+
#
|
19
|
+
# The Activity class takes input JSON as an initialization parameter, and
|
20
|
+
# iterates through each of the key/value pairs of that JSON. For each key
|
21
|
+
# that is found, an attr_accessor is constructed, allowing direct access
|
22
|
+
# to the value. Each value is stored in the attr_accessor, either as a
|
23
|
+
# String, Fixnum, Hash, or Array.
|
24
|
+
#
|
25
|
+
|
26
|
+
class Activity < Base
|
27
|
+
|
28
|
+
# Initializes the Activity based on the provided json fragment. If no JSON
|
29
|
+
# is provided, an empty object (with no attributes) is created.
|
30
|
+
def initialize(json)
|
31
|
+
if json
|
32
|
+
json.each do |key, value|
|
33
|
+
proper_key = key.snake_case
|
34
|
+
begin
|
35
|
+
self.send("#{proper_key}=", value)
|
36
|
+
rescue NoMethodError
|
37
|
+
add_attr(proper_key)
|
38
|
+
self.send("#{proper_key}=", value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Provides the ability to request a Collection of activities for a given
|
46
|
+
# user or set of users.
|
47
|
+
#
|
48
|
+
# The FetchActivitiesRequest wraps a simple request to an OpenSocial
|
49
|
+
# endpoint for a Collection of activities. As parameters, it accepts
|
50
|
+
# a user ID and selector (and optionally an ID of a particular activity).
|
51
|
+
# This request may be used, standalone, by calling send, or bundled into
|
52
|
+
# an RpcRequest.
|
53
|
+
#
|
54
|
+
|
55
|
+
class FetchActivitiesRequest < Request
|
56
|
+
# Defines the service fragment for use in constructing the request URL or
|
57
|
+
# JSON
|
58
|
+
SERVICE = "activities"
|
59
|
+
|
60
|
+
# This is only necessary because of a spec inconsistency
|
61
|
+
RPC_SERVICE = "activity"
|
62
|
+
|
63
|
+
# Initializes a request to fetch activities for the specified user and
|
64
|
+
# group, or the default (@me, @self). A valid Connection is not necessary
|
65
|
+
# if the request is to be used as part of an RpcRequest.
|
66
|
+
def initialize(connection = nil, guid = "@me", selector = "@self",
|
67
|
+
pid = nil)
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
# Sends the request, passing in the appropriate SERVICE and specified
|
72
|
+
# instance variables.
|
73
|
+
def send
|
74
|
+
json = send_request(SERVICE, @guid, @selector, @pid)
|
75
|
+
|
76
|
+
return parse_response(json["entry"])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Selects the appropriate fragment from the JSON response in order to
|
80
|
+
# create a native object.
|
81
|
+
def parse_rpc_response(response)
|
82
|
+
return parse_response(response["data"]["list"])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Converts the request into a JSON fragment that can be used as part of a
|
86
|
+
# larger RpcRequest.
|
87
|
+
def to_json(*a)
|
88
|
+
value = {
|
89
|
+
"method" => RPC_SERVICE + GET,
|
90
|
+
"params" => {
|
91
|
+
"userId" => ["#{@guid}"],
|
92
|
+
"groupId" => "#{@selector}",
|
93
|
+
"appId" => "#{@pid}"
|
94
|
+
},
|
95
|
+
"id" => @key
|
96
|
+
}.to_json(*a)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
# Converts the JSON response into a Collection of activities, indexed by
|
102
|
+
# id.
|
103
|
+
def parse_response(response)
|
104
|
+
activities = Collection.new
|
105
|
+
for entry in response
|
106
|
+
activity = Activity.new(entry)
|
107
|
+
activities[activity.id] = activity
|
108
|
+
end
|
109
|
+
|
110
|
+
return activities
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# Copyright (c) 2008 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module OpenSocial #:nodoc:
|
16
|
+
|
17
|
+
# Acts as a wrapper for an OpenSocial appdata entry.
|
18
|
+
#
|
19
|
+
# The AppData class takes a person's ID and input JSON as initialization
|
20
|
+
# parameters, and iterates through each of the key/value pairs of that JSON.
|
21
|
+
# For each key that is found, an attr_accessor is constructed (except for
|
22
|
+
# :id which is preconstructed, and required), allowing direct access to the
|
23
|
+
# value. Each value is stored in the attr_accessor, either as a String,
|
24
|
+
# Fixnum, Hash, or Array.
|
25
|
+
#
|
26
|
+
|
27
|
+
class AppData < Base
|
28
|
+
attr_accessor :id
|
29
|
+
|
30
|
+
# Initializes the AppData entry based on the provided id and json fragment.
|
31
|
+
# If no JSON is provided, an empty object (with only an ID) is created.
|
32
|
+
def initialize(id, json)
|
33
|
+
@id = id
|
34
|
+
|
35
|
+
if json
|
36
|
+
json.each do |key, value|
|
37
|
+
begin
|
38
|
+
self.send("#{key}=", value)
|
39
|
+
rescue NoMethodError
|
40
|
+
add_attr(key)
|
41
|
+
self.send("#{key}=", value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Provides the ability to request a Collection of AppData for a given
|
49
|
+
# user or set of users.
|
50
|
+
#
|
51
|
+
# The FetchAppData wraps a simple request to an OpenSocial
|
52
|
+
# endpoint for a Collection of AppData. As parameters, it accepts
|
53
|
+
# a user ID and selector. This request may be used, standalone, by calling
|
54
|
+
# send, or bundled into an RpcRequest.
|
55
|
+
#
|
56
|
+
|
57
|
+
class FetchAppDataRequest < Request
|
58
|
+
# Defines the service fragment for use in constructing the request URL or
|
59
|
+
# JSON
|
60
|
+
SERVICE = "appdata"
|
61
|
+
|
62
|
+
# Initializes a request to fetch appdata for the specified user and
|
63
|
+
# group, or the default (@me, @self). A valid Connection is not necessary
|
64
|
+
# if the request is to be used as part of an RpcRequest.
|
65
|
+
def initialize(connection = nil, guid = "@me", selector = "@self",
|
66
|
+
aid = "@app")
|
67
|
+
super(connection, guid, selector, aid)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Sends the request, passing in the appropriate SERVICE and specified
|
71
|
+
# instance variables. Accepts an unescape parameter, defaulting to true,
|
72
|
+
# if the returned data should be unescaped.
|
73
|
+
def send(unescape = true)
|
74
|
+
json = send_request(SERVICE, @guid, @selector, @pid, unescape)
|
75
|
+
|
76
|
+
return parse_response(json["entry"])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Selects the appropriate fragment from the JSON response in order to
|
80
|
+
# create a native object.
|
81
|
+
def parse_rpc_response(response)
|
82
|
+
return parse_response(response["data"])
|
83
|
+
end
|
84
|
+
|
85
|
+
# Converts the request into a JSON fragment that can be used as part of a
|
86
|
+
# larger RpcRequest.
|
87
|
+
def to_json(*a)
|
88
|
+
value = {
|
89
|
+
"method" => SERVICE + GET,
|
90
|
+
"params" => {
|
91
|
+
"userId" => ["#{@guid}"],
|
92
|
+
"groupId" => "#{@selector}",
|
93
|
+
"appId" => "#{@pid}",
|
94
|
+
"fields" => []
|
95
|
+
},
|
96
|
+
"id" => @key
|
97
|
+
}.to_json(*a)
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Converts the JSON response into a Collection of AppData entries, indexed
|
103
|
+
# by id.
|
104
|
+
def parse_response(response)
|
105
|
+
appdata = Collection.new
|
106
|
+
response.each do |key, value|
|
107
|
+
data = AppData.new(key, value)
|
108
|
+
appdata[key] = data
|
109
|
+
end
|
110
|
+
|
111
|
+
return appdata
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Copyright (c) 2007 Blaine Cook, Larry Halff, Pelle Braendgaard
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Includes modifications by Robin Luckey from:
|
23
|
+
# http://github.com/robinluckey/oauth/tree/master/lib%2Foauth%2Frequest_proxy%2Faction_controller_request.rb
|
24
|
+
|
25
|
+
module OAuth::RequestProxy #:nodoc: all
|
26
|
+
class ActionControllerRequest < OAuth::RequestProxy::Base
|
27
|
+
if defined?(ActionDispatch::Request)
|
28
|
+
proxies(ActionDispatch::Request)
|
29
|
+
elsif defined?(ActionController::AbstractRequest)
|
30
|
+
proxies(ActionController::AbstractRequest)
|
31
|
+
else
|
32
|
+
proxies(ActionController::Request)
|
33
|
+
end
|
34
|
+
|
35
|
+
def method
|
36
|
+
request.method.to_s.upcase
|
37
|
+
end
|
38
|
+
|
39
|
+
def uri
|
40
|
+
uri = URI.parse(request.protocol + request.host + request.port_string + request.path)
|
41
|
+
uri.query = nil
|
42
|
+
uri.to_s
|
43
|
+
end
|
44
|
+
|
45
|
+
def parameters
|
46
|
+
if options[:clobber_request]
|
47
|
+
options[:parameters] || {}
|
48
|
+
else
|
49
|
+
params = request_params.merge(query_params).merge(header_params)
|
50
|
+
params.stringify_keys! if params.respond_to?(:stringify_keys!)
|
51
|
+
params.merge(options[:parameters] || {})
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Override from OAuth::RequestProxy::Base to avoid roundtrip
|
56
|
+
# conversion to Hash or Array and thus preserve the original
|
57
|
+
# parameter names
|
58
|
+
def parameters_for_signature
|
59
|
+
params = []
|
60
|
+
params << options[:parameters].to_query if options[:parameters]
|
61
|
+
|
62
|
+
unless options[:clobber_request]
|
63
|
+
params << header_params.to_query
|
64
|
+
params << CGI.unescape(request.query_string) unless request.query_string.blank?
|
65
|
+
if request.content_type == Mime::Type.lookup("application/x-www-form-urlencoded")
|
66
|
+
params << CGI.unescape(request.raw_post)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
params.
|
71
|
+
join("&").split("&").
|
72
|
+
reject { |kv| kv =~ /^oauth_signature=.*/}.
|
73
|
+
reject(&:blank?).
|
74
|
+
map { |p| p.split("=") }
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def query_params
|
80
|
+
request.query_parameters
|
81
|
+
end
|
82
|
+
|
83
|
+
def request_params
|
84
|
+
request.request_parameters
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# Copyright (c) 2008 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module OpenSocial #:nodoc:
|
16
|
+
|
17
|
+
# Provides helper classes to be used in verifying and validating the user.
|
18
|
+
# In particular, support is provided for:
|
19
|
+
#
|
20
|
+
# * Verification of signed makeRequest using OAuth/HMAC-SHA1
|
21
|
+
# class ExampleController < ApplicationController
|
22
|
+
# OpenSocial::Auth::CONSUMER_KEY = "623061448914"
|
23
|
+
# OpenSocial::Auth::CONSUMER_SECRET = "uynAeXiWTisflWX99KU1D2q5"
|
24
|
+
#
|
25
|
+
# include OpenSocial::Auth
|
26
|
+
#
|
27
|
+
# before_filter :validate
|
28
|
+
#
|
29
|
+
# def return_private_data
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# * Request for an OAuth request token
|
34
|
+
#
|
35
|
+
# * Request for an OAuth access token, when supplied with a request token
|
36
|
+
#
|
37
|
+
|
38
|
+
|
39
|
+
module Auth
|
40
|
+
|
41
|
+
# Validates an incoming request by using the OAuth library and the supplied
|
42
|
+
# key and secret.
|
43
|
+
def validate(key = CONSUMER_KEY, secret = CONSUMER_SECRET)
|
44
|
+
consumer = OAuth::Consumer.new(key, secret)
|
45
|
+
begin
|
46
|
+
signature = OAuth::Signature.build(request) do
|
47
|
+
[nil, consumer.secret]
|
48
|
+
end
|
49
|
+
pass = signature.verify
|
50
|
+
rescue OAuth::Signature::UnknownSignatureMethod => e
|
51
|
+
logger.error "An unknown signature method was supplied: " + e.to_s
|
52
|
+
end
|
53
|
+
return pass
|
54
|
+
end
|
55
|
+
|
56
|
+
# Gets an OAuth request token, and redirects the user to authorize the app
|
57
|
+
# to access data on their behalf.
|
58
|
+
def get_oauth_token(key, secret, container, callback)
|
59
|
+
consumer = OAuth::Consumer.new(key, secret, {
|
60
|
+
:site => container[:base_uri],
|
61
|
+
:request_token_path => container[:request_token_path],
|
62
|
+
:authorize_path => container[:authorize_path],
|
63
|
+
:access_token_path => container[:access_token_path],
|
64
|
+
:http_method => container[:http_method]
|
65
|
+
})
|
66
|
+
request_token = consumer.get_request_token
|
67
|
+
|
68
|
+
session[:token] = request_token.token
|
69
|
+
session[:secret] = request_token.secret
|
70
|
+
|
71
|
+
redirect_to request_token.authorize_url + "&oauth_callback=" + CGI.escape(callback)
|
72
|
+
end
|
73
|
+
|
74
|
+
# If neccesary, swaps an existing request token and secret for an access
|
75
|
+
# token, storing it in the Connection class, and returning the access token
|
76
|
+
# and secret for later use.
|
77
|
+
def get_access_token(connection, token, secret)
|
78
|
+
if (token && secret)
|
79
|
+
consumer = OAuth::Consumer.new(connection.consumer_key,
|
80
|
+
connection.consumer_secret,
|
81
|
+
connection.container)
|
82
|
+
|
83
|
+
if connection.consumer_token.token.empty? &&
|
84
|
+
connection.consumer_token.secret.empty?
|
85
|
+
connection.consumer_token = OAuth::Token.new(token, secret)
|
86
|
+
|
87
|
+
uri = URI.parse(connection.container[:base_uri] +
|
88
|
+
connection.container[:access_token_path])
|
89
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
90
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
91
|
+
connection.sign!(http, req)
|
92
|
+
|
93
|
+
resp = http.get(req.path)
|
94
|
+
|
95
|
+
matches = resp.body.match(/oauth_token=(.*?)&oauth_token_secret=(.*)/)
|
96
|
+
access_token = matches[1]
|
97
|
+
access_secret = matches[2]
|
98
|
+
end
|
99
|
+
|
100
|
+
reusable_token = OAuth::AccessToken.new(consumer, access_token, access_secret)
|
101
|
+
connection.consumer_token = reusable_token
|
102
|
+
|
103
|
+
return access_token, access_secret
|
104
|
+
end
|
105
|
+
|
106
|
+
return nil, nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|