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
@@ -0,0 +1,50 @@
|
|
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 "net/http"
|
16
|
+
require "uri"
|
17
|
+
|
18
|
+
# Use json/pure if you opt-out of including the validation code in
|
19
|
+
# opensocial/auth. This gem adds standard to_json behavior for the
|
20
|
+
# request classes, instead of using ActiveSupport.
|
21
|
+
require "rubygems"
|
22
|
+
|
23
|
+
module OpenSocial #:nodoc:
|
24
|
+
|
25
|
+
# Provides base functionality for the OpenSocial child classes.
|
26
|
+
#
|
27
|
+
|
28
|
+
class Base
|
29
|
+
|
30
|
+
# Creates an attr_accessor for the specified variable name.
|
31
|
+
def add_attr(name)
|
32
|
+
self.class.class_eval "attr_accessor :#{name}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Wraps the functionality of an OpenSocial collection as defined by the
|
37
|
+
# specification. In practical uses, a Collection serves as a Hash (usually
|
38
|
+
# index by ID) with the added benefit of being convertable to an Array, when
|
39
|
+
# it's necessary to iterate over all of the values.
|
40
|
+
#
|
41
|
+
|
42
|
+
class Collection < Hash
|
43
|
+
|
44
|
+
# Converts the Collection to an Array by returning each of the values from
|
45
|
+
# key/value pairs.
|
46
|
+
def to_array
|
47
|
+
values
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,128 @@
|
|
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
|
+
# Describes a connection to an OpenSocial container, including the ability to
|
18
|
+
# declare an authorization mechanism and appropriate credentials.
|
19
|
+
#
|
20
|
+
|
21
|
+
class Connection
|
22
|
+
ORKUT = { :endpoint => "http://sandbox.orkut.com/social",
|
23
|
+
:rest => "rest/",
|
24
|
+
:rpc => "rpc/" }
|
25
|
+
IGOOGLE = { :endpoint => "http://gmodules.com/api",
|
26
|
+
:rest => "",
|
27
|
+
:rpc => "rpc" }
|
28
|
+
MYSPACE = { :endpoint => "http://api.myspace.com/v2",
|
29
|
+
:rest => "",
|
30
|
+
:rpc => "",
|
31
|
+
:base_uri => "http://api.myspace.com",
|
32
|
+
:request_token_path => "/request_token",
|
33
|
+
:authorize_path => "/authorize",
|
34
|
+
:access_token_path => "/access_token",
|
35
|
+
:http_method => :get }
|
36
|
+
|
37
|
+
AUTH_HMAC = 0
|
38
|
+
AUTH_ST = 1
|
39
|
+
|
40
|
+
DEFAULT_OPTIONS = { :container => ORKUT,
|
41
|
+
:st => "",
|
42
|
+
:consumer_key => "",
|
43
|
+
:consumer_secret => "",
|
44
|
+
:consumer_token => OAuth::Token.new("", ""),
|
45
|
+
:xoauth_requestor_id => "",
|
46
|
+
:auth => AUTH_HMAC }
|
47
|
+
|
48
|
+
# Defines the container that will be used in requests.
|
49
|
+
attr_accessor :container
|
50
|
+
|
51
|
+
# Defines the security token, for when OAuth is not in use.
|
52
|
+
attr_accessor :st
|
53
|
+
|
54
|
+
# Defines the consumer key for OAuth.
|
55
|
+
attr_accessor :consumer_key
|
56
|
+
|
57
|
+
# Defines the consumer secret for OAuth.
|
58
|
+
attr_accessor :consumer_secret
|
59
|
+
|
60
|
+
# Defines the consumer token for OAuth.
|
61
|
+
attr_accessor :consumer_token
|
62
|
+
|
63
|
+
# Defines the ID of the requestor (required by some implementations when
|
64
|
+
# using OAuth).
|
65
|
+
attr_accessor :xoauth_requestor_id
|
66
|
+
|
67
|
+
# Defines the authentication scheme: HMAC or security token.
|
68
|
+
attr_accessor :auth
|
69
|
+
|
70
|
+
# Initializes the Connection using the supplied options hash, or the
|
71
|
+
# defaults. Verifies that the supplied authentication type has proper
|
72
|
+
# (ie. non-blank) credentials, and that the authentication type is known.
|
73
|
+
def initialize(options = {})
|
74
|
+
options = DEFAULT_OPTIONS.merge(options)
|
75
|
+
options.each do |key, value|
|
76
|
+
self.send("#{key}=", value)
|
77
|
+
end
|
78
|
+
|
79
|
+
if @auth == AUTH_HMAC && !has_valid_hmac_double?
|
80
|
+
raise ArgumentError.new("Connection authentication is set to " +
|
81
|
+
"HMAC-SHA1, but a valid consumer_key and" +
|
82
|
+
"secret pair was not supplied.")
|
83
|
+
elsif @auth == AUTH_ST && @st.empty?
|
84
|
+
raise ArgumentError.new("Connection authentication is set to " +
|
85
|
+
"security token, but a security token was " +
|
86
|
+
"not supplied.")
|
87
|
+
elsif ![AUTH_HMAC, AUTH_ST].include?(@auth)
|
88
|
+
raise ArgumentError.new("Connection authentication is set to an " +
|
89
|
+
"unknown value.")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Constructs a URI to the OpenSocial endpoint given a service, guid,
|
94
|
+
# selector, and pid.
|
95
|
+
def service_uri(service, guid, selector, pid, extra_fields = {})
|
96
|
+
uri = [@container[:endpoint], service, guid, selector, pid].compact.
|
97
|
+
join("/")
|
98
|
+
|
99
|
+
if @auth == AUTH_HMAC && !xoauth_requestor_id.empty?
|
100
|
+
uri << "?xoauth_requestor_id=" + @xoauth_requestor_id
|
101
|
+
elsif @auth == AUTH_ST
|
102
|
+
uri << "?st=" + self.st
|
103
|
+
end
|
104
|
+
|
105
|
+
extra_fields.each do |name, value|
|
106
|
+
uri << "&#{name}=#{value}"
|
107
|
+
end
|
108
|
+
|
109
|
+
URI.parse(uri)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Signs a request using OAuth.
|
113
|
+
def sign!(http, req)
|
114
|
+
if @auth == AUTH_HMAC
|
115
|
+
consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret)
|
116
|
+
req.oauth!(http, consumer, @consumer_token, :scheme => "query_string")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Verifies that the consumer key, consumer secret and requestor id are all
|
123
|
+
# non-blank.
|
124
|
+
def has_valid_hmac_double?
|
125
|
+
return (!@consumer_key.empty? && !@consumer_secret.empty?)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,80 @@
|
|
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 group.
|
18
|
+
#
|
19
|
+
# The Group 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 Group < Base
|
27
|
+
|
28
|
+
# Initializes the Group 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 groups for a given
|
46
|
+
# user.
|
47
|
+
#
|
48
|
+
# The FetchGroupsRequest wraps a simple request to an OpenSocial
|
49
|
+
# endpoint for a collection of groups. As parameters, it accepts
|
50
|
+
# a user ID. This request may be used, standalone, by calling send, or
|
51
|
+
# bundled into an RpcRequest.
|
52
|
+
#
|
53
|
+
|
54
|
+
class FetchGroupsRequest < Request
|
55
|
+
# Defines the service fragment for use in constructing the request URL or
|
56
|
+
# JSON
|
57
|
+
SERVICE = "groups"
|
58
|
+
|
59
|
+
# Initializes a request to fetch groups for the specified user, or the
|
60
|
+
# default (@me). A valid Connection is not necessary if the request is to
|
61
|
+
# be used as part of an RpcRequest.
|
62
|
+
def initialize(connection = nil, guid = "@me")
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
# Sends the request, passing in the appropriate SERVICE and specified
|
67
|
+
# instance variables.
|
68
|
+
def send
|
69
|
+
json = send_request(SERVICE, @guid)
|
70
|
+
|
71
|
+
groups = Collection.new
|
72
|
+
for entry in json["entry"]
|
73
|
+
group = Group.new(entry)
|
74
|
+
groups[group.id] = group
|
75
|
+
end
|
76
|
+
|
77
|
+
return groups
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,197 @@
|
|
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
|
+
# Extends the OpenSocial module, providing a wrapper for OpenSocial people.
|
16
|
+
#
|
17
|
+
# Person: Acts as a wrapper for an OpenSocial person.
|
18
|
+
# FetchPersonRequest: Provides the ability to request a single person.
|
19
|
+
# FetchPeopleRequest: Provides the ability to request a collection of people
|
20
|
+
# by describing their relationship to a single person.
|
21
|
+
#
|
22
|
+
|
23
|
+
module OpenSocial #:nodoc:
|
24
|
+
|
25
|
+
# Acts as a wrapper for an OpenSocial person.
|
26
|
+
#
|
27
|
+
# The Person class takes input JSON as an initialization parameter, and
|
28
|
+
# iterates through each of the key/value pairs of that JSON. For each key
|
29
|
+
# that is found, an attr_accessor is constructed, allowing direct access
|
30
|
+
# to the value. Each value is stored in the attr_accessor, either as a
|
31
|
+
# String, Fixnum, Hash, or Array.
|
32
|
+
#
|
33
|
+
|
34
|
+
class Person < Base
|
35
|
+
|
36
|
+
# Initializes the Person based on the provided json fragment. If no JSON
|
37
|
+
# is provided, an empty object (with no attributes) is created.
|
38
|
+
def initialize(json)
|
39
|
+
if json
|
40
|
+
json.each do |key, value|
|
41
|
+
proper_key = key.snake_case
|
42
|
+
begin
|
43
|
+
self.send("#{proper_key}=", value)
|
44
|
+
rescue NoMethodError
|
45
|
+
add_attr(proper_key)
|
46
|
+
self.send("#{proper_key}=", value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the complete name of the Person, to the best ability, given
|
53
|
+
# available fields.
|
54
|
+
def long_name
|
55
|
+
if @name && @name["givenName"] && @name["familyName"]
|
56
|
+
return @name["givenName"] + " " + @name["familyName"]
|
57
|
+
elsif @nickname
|
58
|
+
return @nickname
|
59
|
+
else
|
60
|
+
return ""
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the first name or nickname of the Person, to the best ability,
|
65
|
+
# given available fields.
|
66
|
+
def short_name
|
67
|
+
if @name && @name["givenName"]
|
68
|
+
return @name["givenName"]
|
69
|
+
elsif @nickname
|
70
|
+
return @nickname
|
71
|
+
else
|
72
|
+
return ""
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Provides the ability to request a single Person.
|
78
|
+
#
|
79
|
+
# The FetchPeopleRequests wraps a simple request to an OpenSocial
|
80
|
+
# endpoint for a single person. As parameters, it accepts a user ID and
|
81
|
+
# selector and optionally an ID of a particular person, in order to display
|
82
|
+
# the user ID"s view of that person. This request may be used, standalone,
|
83
|
+
# by calling send, or bundled into an RpcRequest.
|
84
|
+
#
|
85
|
+
|
86
|
+
class FetchPersonRequest < Request
|
87
|
+
# Defines the service fragment for use in constructing the request URL or
|
88
|
+
# JSON
|
89
|
+
SERVICE = "people"
|
90
|
+
DEFAULT_FIELDS = [:id, :dateOfBirth, :name, :emails, :gender, :state, :postalCode, :ethnicity, :relationshipStatus, :thumbnailUrl, :displayName]
|
91
|
+
|
92
|
+
# Initializes a request to the specified user, or the default (@me, @self).
|
93
|
+
# A valid Connection is not necessary if the request is to be used as part
|
94
|
+
# of an RpcRequest.
|
95
|
+
def initialize(connection = nil, guid = "@me", selector = "@self")
|
96
|
+
super
|
97
|
+
end
|
98
|
+
|
99
|
+
# Sends the request, passing in the appropriate SERVICE and specified
|
100
|
+
# instance variables.
|
101
|
+
def send
|
102
|
+
json = send_request(SERVICE, @guid, @selector, nil, false, :fields => DEFAULT_FIELDS.join(","))
|
103
|
+
|
104
|
+
return parse_response(json["entry"])
|
105
|
+
end
|
106
|
+
|
107
|
+
# Selects the appropriate fragment from the JSON response in order to
|
108
|
+
# create a native object.
|
109
|
+
def parse_rpc_response(response)
|
110
|
+
return parse_response(response["data"])
|
111
|
+
end
|
112
|
+
|
113
|
+
# Converts the request into a JSON fragment that can be used as part of a
|
114
|
+
# larger RpcRequest.
|
115
|
+
def to_json(*a)
|
116
|
+
value = {
|
117
|
+
"method" => SERVICE + GET,
|
118
|
+
"params" => {
|
119
|
+
"userId" => ["#{@guid}"],
|
120
|
+
"groupId" => "#{@selector}"
|
121
|
+
},
|
122
|
+
"id" => @key
|
123
|
+
}.to_json(*a)
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
|
128
|
+
# Converts the JSON response into a person.
|
129
|
+
def parse_response(response)
|
130
|
+
return Person.new(response)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# Provides the ability to request a Collection of people by describing their
|
135
|
+
# relationship to a single person.
|
136
|
+
#
|
137
|
+
# The FetchPeopleRequests wraps a simple request to an OpenSocial
|
138
|
+
# endpoint for a Collection of people. As parameters, it accepts
|
139
|
+
# a user ID and selector. This request may be used, standalone, by calling
|
140
|
+
# send, or bundled into an RpcRequest.
|
141
|
+
#
|
142
|
+
|
143
|
+
class FetchPeopleRequest < Request
|
144
|
+
# Defines the service fragment for use in constructing the request URL or
|
145
|
+
# JSON
|
146
|
+
SERVICE = "people"
|
147
|
+
|
148
|
+
# Initializes a request to the specified user's group, or the default (@me,
|
149
|
+
# @friends). A valid Connection is not necessary if the request is to be
|
150
|
+
# used as part of an RpcRequest.
|
151
|
+
def initialize(connection = nil, guid = "@me", selector = "@friends", extra_fields = {})
|
152
|
+
super
|
153
|
+
@extra_fields = extra_fields
|
154
|
+
end
|
155
|
+
|
156
|
+
# Sends the request, passing in the appropriate SERVICE and specified
|
157
|
+
# instance variables.
|
158
|
+
def send
|
159
|
+
@extra_fields[:fields] ||= FetchPersonRequest::DEFAULT_FIELDS.join(",")
|
160
|
+
json = send_request(SERVICE, @guid, @selector, nil, false, @extra_fields)
|
161
|
+
|
162
|
+
return parse_response(json["entry"])
|
163
|
+
end
|
164
|
+
|
165
|
+
# Selects the appropriate fragment from the JSON response in order to
|
166
|
+
# create a native object.
|
167
|
+
def parse_rpc_response(response)
|
168
|
+
return parse_response(response["data"]["list"])
|
169
|
+
end
|
170
|
+
|
171
|
+
# Converts the request into a JSON fragment that can be used as part of a
|
172
|
+
# larger RPC request.
|
173
|
+
def to_json(*a)
|
174
|
+
value = {
|
175
|
+
"method" => SERVICE + GET,
|
176
|
+
"params" => {
|
177
|
+
"userId" => ["#{@guid}"],
|
178
|
+
"groupId" => "#{@selector}"
|
179
|
+
},
|
180
|
+
"id" => @key
|
181
|
+
}.to_json(*a)
|
182
|
+
end
|
183
|
+
|
184
|
+
private
|
185
|
+
|
186
|
+
# Converts the JSON response into a Collection of people, indexed by id.
|
187
|
+
def parse_response(response)
|
188
|
+
people = Collection.new
|
189
|
+
for entry in response
|
190
|
+
person = Person.new(entry)
|
191
|
+
people[person.id] = person
|
192
|
+
end
|
193
|
+
|
194
|
+
return people
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|