intouch-gdata4ruby 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +16 -0
- data/README.md +45 -0
- data/lib/gdata4ruby.rb +1 -0
- data/lib/gdata4ruby/acl/access_rule.rb +127 -0
- data/lib/gdata4ruby/base.rb +213 -0
- data/lib/gdata4ruby/gdata_object.rb +197 -0
- data/lib/gdata4ruby/request.rb +61 -0
- data/lib/gdata4ruby/service.rb +58 -0
- data/lib/gdata4ruby/utils/utils.rb +35 -0
- data/test/unit.rb +55 -0
- metadata +73 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#=CHANGELOG
|
2
|
+
#==version 0.1.6
|
3
|
+
#* Save the 'S'-cookie and send it with future requests to avoid '302 Redirect' responses
|
4
|
+
#==version 0.1.5
|
5
|
+
#* Changed license to LGPLv3
|
6
|
+
#==version 0.1.4
|
7
|
+
#* Bugfix for GeoRSS and GML namespaces
|
8
|
+
#==version 0.1.3
|
9
|
+
#* Added support for Geo/GeoRSS namespaces in base gdata object
|
10
|
+
#* Added support for Ruby 1.9
|
11
|
+
#==version 0.1.2
|
12
|
+
#* Added better support for 'default' users in AccessRule
|
13
|
+
#==version 0.1.1
|
14
|
+
#* Added additional common attributes, including content, published, updated and author info
|
15
|
+
#==version 0.1.0
|
16
|
+
#* Initial Version
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#GData4Ruby
|
2
|
+
|
3
|
+
##Introduction
|
4
|
+
|
5
|
+
GData4Ruby is a full featured wrapper for the Google Data base API. GData4Ruby provides the ability
|
6
|
+
to authenticate with GData using the ClientLogin method. The package also includes a base gdata object
|
7
|
+
that can be subclassed to provide basic CRUD functions for all Google API service objects. Additionally,
|
8
|
+
a basic ACL object is included for interacting with ACL feeds and setting access rules.
|
9
|
+
|
10
|
+
##Author and Contact Information
|
11
|
+
|
12
|
+
GData4Ruby was created and is maintained by [Mike Reich](mailto:mike@seabourneconsulting.com])
|
13
|
+
and is licenses under the LGPL v3. You can find the text of the LGPL
|
14
|
+
here: http://www.gnu.org/licenses/lgpl.html. Feel free to use and update, but be sure to contribute your
|
15
|
+
code back to the project and attribute as required by the license.
|
16
|
+
|
17
|
+
##Website
|
18
|
+
|
19
|
+
[http://cookingandcoding.com/gdata4ruby/](http://cookingandcoding.com/gdata4ruby/)
|
20
|
+
|
21
|
+
##Description
|
22
|
+
|
23
|
+
GData4Ruby has three major components: the service, the GData object and the AccessRule object. Each service
|
24
|
+
represents a google account, and includes a username (email) and a password. You can use the GData service
|
25
|
+
to authenticate either a google account or a google apps account.
|
26
|
+
|
27
|
+
The GData object provides a base class for interacting with Google API objects, i.e. Documents, Events, etc. The GData object contains common attributes present in all Google API objects, and provides interfaces for basic CRUD functions. This class is meant to be subclassed.
|
28
|
+
|
29
|
+
The AccessRule object provides a base class for interacting with Google Access Control Lists. ACLs provide the main permissions mechanism for most Google API services.
|
30
|
+
|
31
|
+
##Examples
|
32
|
+
|
33
|
+
Below are some common usage examples. For more examples, check the documentation.
|
34
|
+
|
35
|
+
###Service
|
36
|
+
|
37
|
+
1. Authenticate
|
38
|
+
|
39
|
+
service = Service.new
|
40
|
+
service.authenticate("user@gmail.com", "password", "cl")
|
41
|
+
|
42
|
+
2. Authenticate with a specified GData version
|
43
|
+
|
44
|
+
service = Service.new({:gdata_version => '3.0'})
|
45
|
+
service.authenticate("user@gmail.com", "password", "cl")
|
data/lib/gdata4ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "gdata4ruby/service"
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
require 'gdata4ruby/gdata_object'
|
19
|
+
|
20
|
+
module GData4Ruby
|
21
|
+
|
22
|
+
#Contains classes for interacting with Google ACL feeds
|
23
|
+
module ACL
|
24
|
+
|
25
|
+
#Represents an individual access rule entry in a Google ACL feed.
|
26
|
+
class AccessRule < GDataObject
|
27
|
+
XML = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gAcl='http://schemas.google.com/acl/2007'>
|
28
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
29
|
+
term='http://schemas.google.com/acl/2007#accessRule'/>
|
30
|
+
<gAcl:role value=''/>
|
31
|
+
<gAcl:scope type='user' value=''/>
|
32
|
+
</entry>"
|
33
|
+
|
34
|
+
#The Rule's user
|
35
|
+
attr_accessor :user
|
36
|
+
|
37
|
+
#The user's role
|
38
|
+
attr_accessor :role
|
39
|
+
|
40
|
+
#The parent GDataObject the rule applies to
|
41
|
+
attr_accessor :parent
|
42
|
+
|
43
|
+
#Creates a new AccessRule object. You must pass a valid Service and GDataObject, and can pass an optional hash
|
44
|
+
#of attributes to initialize the object with.
|
45
|
+
def initialize(service, parent, attributes = {})
|
46
|
+
super(service, attributes)
|
47
|
+
@xml = XML
|
48
|
+
raise ArgumentError, 'parent must be a GData4Ruby::GDataObject' if not parent.is_a? GData4Ruby::GDataObject
|
49
|
+
@parent = parent
|
50
|
+
@role = @user = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
#Creates the AccessRule using the parent's acl_uri attribute.
|
54
|
+
def create
|
55
|
+
ret = service.send_request(Request.new(:post, @parent.acl_uri, to_xml))
|
56
|
+
if not ret or not load(ret.body)
|
57
|
+
raise SaveFailed, 'Could not create access rule'
|
58
|
+
end
|
59
|
+
return ret
|
60
|
+
end
|
61
|
+
|
62
|
+
#Loads data into the object. Accepts a string containing an XML <entry> object.
|
63
|
+
def load(string)
|
64
|
+
super(string)
|
65
|
+
@folders = []
|
66
|
+
xml = REXML::Document.new(string)
|
67
|
+
xml.root.elements.each(){}.map do |ele|
|
68
|
+
case ele.name
|
69
|
+
when 'role'
|
70
|
+
@role = ele.attributes['value']
|
71
|
+
when 'scope'
|
72
|
+
@user = ele.attributes['value'] ? ele.attributes['value'] : ele.attributes['type']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#Deletes the AccessRule
|
78
|
+
def delete
|
79
|
+
if @exists
|
80
|
+
@role = 'none'
|
81
|
+
service.send_request(Request.new(:put, @edit_uri, self.to_xml, {"If-Match" => "*", 'Content-Type' => 'application/atom+xml'}))
|
82
|
+
end
|
83
|
+
@exists = false
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
#Finds an AccessRule based on the args passed.
|
88
|
+
#
|
89
|
+
#Args can be a hash containing either:
|
90
|
+
#*user*:: an email address/user id to search for. If found, returns the matching AccessRule object.
|
91
|
+
#*role*:: the role to search for. Returns an array of matching AccessRules, or an empty array if no matches are found.
|
92
|
+
def self.find(service, parent, args = {})
|
93
|
+
raise ArgumentError, 'Must supply a username or role to find by' if not args[:user] and not args[:role]
|
94
|
+
rules = []
|
95
|
+
ret = service.send_request(GData4Ruby::Request.new(:get, parent.acl_uri))
|
96
|
+
xml = REXML::Document.new(ret.body).root
|
97
|
+
xml.elements.each("entry") do |e|
|
98
|
+
e = GData4Ruby::Utils::add_namespaces(e)
|
99
|
+
rule = AccessRule.new(service, parent)
|
100
|
+
rule.load(e.to_s)
|
101
|
+
return rule if args[:user] and rule.user == args[:user]
|
102
|
+
rules << rule if args[:role] and rule.role == args[:role]
|
103
|
+
end
|
104
|
+
return args[:user] ? false : rules
|
105
|
+
end
|
106
|
+
|
107
|
+
#Returns a string containing the XML representation of the AccessRule
|
108
|
+
def to_xml
|
109
|
+
xml = REXML::Document.new(super)
|
110
|
+
xml.root.elements.each(){}.map do |ele|
|
111
|
+
case ele.name
|
112
|
+
when "role"
|
113
|
+
ele.attributes['value'] = @role
|
114
|
+
when 'scope'
|
115
|
+
if @user and @user != 'default'
|
116
|
+
ele.attributes['value'] = @user
|
117
|
+
else
|
118
|
+
ele.attributes['type'] = 'default'
|
119
|
+
ele.delete_attribute("value")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
xml.to_s
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
|
19
|
+
require "net/http"
|
20
|
+
require "net/https"
|
21
|
+
require 'time'
|
22
|
+
require 'cgi'
|
23
|
+
require 'gdata4ruby/request'
|
24
|
+
require 'gdata4ruby/utils/utils'
|
25
|
+
require 'rexml/document'
|
26
|
+
|
27
|
+
Net::HTTP.version_1_2
|
28
|
+
|
29
|
+
# GData4Ruby is a full featured wrapper for the base google data API
|
30
|
+
|
31
|
+
module GData4Ruby
|
32
|
+
|
33
|
+
class AuthenticationFailed < StandardError; end #:nodoc: all
|
34
|
+
|
35
|
+
class NotAuthenticated < StandardError; end
|
36
|
+
|
37
|
+
class InvalidService < StandardError; end
|
38
|
+
|
39
|
+
class HTTPRequestFailed < StandardError; end
|
40
|
+
|
41
|
+
class QueryParameterError < StandardError; end
|
42
|
+
|
43
|
+
#The ProxyInfo class contains information for configuring a proxy connection
|
44
|
+
|
45
|
+
class ProxyInfo
|
46
|
+
attr_accessor :address, :port, :username, :password
|
47
|
+
@address = nil
|
48
|
+
@port = nil
|
49
|
+
@username = nil
|
50
|
+
@password = nil
|
51
|
+
|
52
|
+
#The initialize function accepts four variables for configuring the ProxyInfo object.
|
53
|
+
#The proxy connection is initiated using the builtin Net::HTTP proxy support.
|
54
|
+
|
55
|
+
def initialize(address, port, username=nil, password=nil)
|
56
|
+
@address = address
|
57
|
+
@port = port
|
58
|
+
@username = username
|
59
|
+
@password = password
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
#The Base class includes the basic HTTP methods for communicating with the Google Data API.
|
64
|
+
#You shouldn't use this class directly, rather access the functionality through
|
65
|
+
#the Service subclass.
|
66
|
+
|
67
|
+
class Base
|
68
|
+
AUTH_URL = "https://www.google.com/accounts/ClientLogin"
|
69
|
+
@proxy_info = nil
|
70
|
+
@auth_token = nil
|
71
|
+
@debug = false
|
72
|
+
@gdata_version = '2.1'
|
73
|
+
@session_cookie = nil
|
74
|
+
|
75
|
+
#Contains the ProxyInfo object for using a proxy server
|
76
|
+
attr_accessor :proxy_info
|
77
|
+
|
78
|
+
#If set to true, debug will dump all raw HTTP requests and responses
|
79
|
+
attr_accessor :debug
|
80
|
+
|
81
|
+
#The GData version used by the service
|
82
|
+
attr_accessor :gdata_version
|
83
|
+
|
84
|
+
# Will have the service use https instead of http
|
85
|
+
attr_accessor :use_ssl
|
86
|
+
|
87
|
+
#Optionally, pass a hash of attributes to populate the class. If you want to use a GData version
|
88
|
+
#other than the default (2.1), pass a key/value pair, i.e. {:gdata_version => '1.0'}
|
89
|
+
def initialize(attributes = {})
|
90
|
+
attributes.each do |key, value|
|
91
|
+
if self.respond_to?("#{key}=")
|
92
|
+
self.send("#{key}=", value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
@gdata_version = attributes[:gdata_version] ? attributes[:gdata_version] : '2.1'
|
96
|
+
@use_ssl ||= false
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_url(path)
|
100
|
+
return http_protocol + path
|
101
|
+
end
|
102
|
+
|
103
|
+
#Sends a request to the Google Data System. Accepts a valid Request object, and returns a
|
104
|
+
#HTTPResult class.
|
105
|
+
def send_request(request)
|
106
|
+
raise ArgumentError 'Request must be a GData4Ruby::Request object' if not request.is_a?Request
|
107
|
+
puts "sending #{request.type} to url = #{request.url.to_s}" if @debug
|
108
|
+
do_request(request)
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
def set_protocol!(request)
|
114
|
+
uri = request.url
|
115
|
+
if uri.scheme != protocol
|
116
|
+
request.url = URI.parse(uri.to_s.sub(uri.scheme, protocol))
|
117
|
+
# values = uri.select(*uri.component)
|
118
|
+
# keys = uri.component
|
119
|
+
# components_hash = {}
|
120
|
+
# # Build a hash where the keys are from keys[] and values are from values[]
|
121
|
+
# keys.zip(values) {|a,b| components_hash[a] = b }
|
122
|
+
# components_hash[:scheme] = protocol
|
123
|
+
# request.url = case protocol
|
124
|
+
# when 'https'
|
125
|
+
# URI::HTTPS.build(components_hash)
|
126
|
+
# when 'http'
|
127
|
+
# URI::HTTP.build(components_hash)
|
128
|
+
# end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def do_request(request)
|
133
|
+
ret = nil
|
134
|
+
add_auth_header(request)
|
135
|
+
set_protocol!(request)
|
136
|
+
# Add the session cookie if available
|
137
|
+
request.headers.merge!({'Cookie' => @session_cookie}) if @session_cookie
|
138
|
+
http = get_http_object(request.url)
|
139
|
+
puts "Sending request\nHeader: #{request.headers.inspect.to_s}\nContent: #{request.content.to_s}\n" if @debug
|
140
|
+
http.start do |ht|
|
141
|
+
ret = case request.type
|
142
|
+
when :get
|
143
|
+
ht.get(request.url.to_s, request.headers)
|
144
|
+
when :post
|
145
|
+
ht.post(request.url.to_s, request.content, request.headers)
|
146
|
+
when :put
|
147
|
+
ht.put(request.url.to_s, request.content, request.headers)
|
148
|
+
when :delete
|
149
|
+
ht.delete(request.url.to_s, request.headers)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if @debug
|
154
|
+
puts "Response code: #{ret.status}"
|
155
|
+
puts "Headers: \n"
|
156
|
+
ret.headers.each { |h, v| puts "#{h}:#{v}" }
|
157
|
+
puts "Body: \n" + ret.body
|
158
|
+
end
|
159
|
+
|
160
|
+
# Save the session cookie if set
|
161
|
+
ret.get_fields('set-cookie').to_a.each do |header|
|
162
|
+
cookie = header.split(';').first
|
163
|
+
@session_cookie = cookie if cookie =~ /^S=.+/
|
164
|
+
end
|
165
|
+
|
166
|
+
if not ret.success?
|
167
|
+
puts "invalid response received: "+ret.status if @debug
|
168
|
+
raise HTTPRequestFailed, ret.body
|
169
|
+
end
|
170
|
+
return ret
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_http_object(location)
|
174
|
+
if @proxy_info and @proxy_info.address
|
175
|
+
http = Net::HTTP.new(location.host, location.port, @proxy_info.address, @proxy_info.port, @proxy_info.username, @proxy_info.password)
|
176
|
+
else
|
177
|
+
http = Net::HTTP.new(location.host, location.port)
|
178
|
+
end
|
179
|
+
if location.scheme == 'https'
|
180
|
+
#fixed http/http misnaming via JohnMetta
|
181
|
+
puts "SSL True" if @debug
|
182
|
+
http.use_ssl = true
|
183
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
184
|
+
end
|
185
|
+
return http
|
186
|
+
end
|
187
|
+
|
188
|
+
def add_auth_header(request)
|
189
|
+
if @auth_token
|
190
|
+
if request.headers
|
191
|
+
request.headers.merge!({'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version})
|
192
|
+
else
|
193
|
+
content_type = (request.type == :get or request.type == :delete) ? 'application/x-www-form-urlencoded' : 'application/atom+xml'
|
194
|
+
request.headers = {'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version, 'Content-Type' => content_type}
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
protected
|
200
|
+
|
201
|
+
def protocol
|
202
|
+
ssl_suffix = ""
|
203
|
+
ssl_suffix = "s" if use_ssl
|
204
|
+
return "http#{ssl_suffix}"
|
205
|
+
end
|
206
|
+
|
207
|
+
def http_protocol
|
208
|
+
ssl_suffix = ""
|
209
|
+
ssl_suffix = "s" if use_ssl
|
210
|
+
return "http#{ssl_suffix}://"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
|
19
|
+
require 'gdata4ruby/service'
|
20
|
+
require 'time'
|
21
|
+
|
22
|
+
module GData4Ruby
|
23
|
+
#The GDataObject class represents any <entry> object returned by a Google Service. Includes
|
24
|
+
#attributes for accessing the common elements/parameters of the object, and methods for CRUD
|
25
|
+
#operations.
|
26
|
+
class GDataObject
|
27
|
+
#A Service object
|
28
|
+
attr_accessor :service
|
29
|
+
|
30
|
+
#The entry title.
|
31
|
+
attr_accessor :title
|
32
|
+
|
33
|
+
#The raw date the document was published
|
34
|
+
attr_reader :published
|
35
|
+
|
36
|
+
#The raw date the document was last updated
|
37
|
+
attr_reader :updated
|
38
|
+
|
39
|
+
#The author/owner name
|
40
|
+
attr_reader :author_name
|
41
|
+
|
42
|
+
#The author/owner email
|
43
|
+
attr_reader :author_email
|
44
|
+
|
45
|
+
#The current instance etag for the entry
|
46
|
+
attr_reader :etag
|
47
|
+
|
48
|
+
#The parent URI, if any
|
49
|
+
attr_reader :parent_uri
|
50
|
+
|
51
|
+
#The edit URI, for making changes to the entry
|
52
|
+
attr_reader :edit_uri
|
53
|
+
|
54
|
+
#A hash of additional feedLinks
|
55
|
+
attr_reader :feed_links
|
56
|
+
|
57
|
+
#The unique entry id, as represented by the <gd:resourceId> tag. Not to be confused
|
58
|
+
#with the Atom <id> tag, which is accessible as the feed_uri attribute.
|
59
|
+
attr_reader :id
|
60
|
+
|
61
|
+
#The entry's feed uri, otherwise known as the Atom <id> element value.
|
62
|
+
attr_reader :feed_uri
|
63
|
+
|
64
|
+
#A hash of categories
|
65
|
+
attr_reader :categories
|
66
|
+
|
67
|
+
#The feedLink that represents the entry's ACL feed.
|
68
|
+
attr_reader :acl_uri
|
69
|
+
|
70
|
+
#The content uri for exporting the object content
|
71
|
+
attr_reader :content_uri
|
72
|
+
|
73
|
+
#The kind (type) of the object
|
74
|
+
attr_reader :kind
|
75
|
+
|
76
|
+
#Indicates whether the object exists on the Google servers, i.e. has been created/saved.
|
77
|
+
def exists?
|
78
|
+
return @exists
|
79
|
+
end
|
80
|
+
|
81
|
+
#Initializes a new GDataObject. You must pass a valid Service object, and can pass
|
82
|
+
#an optional array of attributes to initialize values. To load data into an object,
|
83
|
+
#use the load method.
|
84
|
+
def initialize(service, attributes = {})
|
85
|
+
@xml ||= ''
|
86
|
+
@service ||= service
|
87
|
+
@exists = false
|
88
|
+
@title = @content_uri = @etag = @acl_uri = @edit_uri = @parent_uri = @feed_uri = @kind = nil
|
89
|
+
@categories = @feed_links = []
|
90
|
+
@include_etag = true
|
91
|
+
attributes.each do |key, value|
|
92
|
+
self.send("#{key}=", value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
public
|
97
|
+
#Loads data into the object. Accepts a string containing an XML <entry> from a GData
|
98
|
+
#compliant feed.
|
99
|
+
def load(string)
|
100
|
+
@exists = @include_etag = true
|
101
|
+
@xml = string
|
102
|
+
xml = REXML::Document.new(string)
|
103
|
+
xml.root.elements.each(){}.map do |ele|
|
104
|
+
@etag = xml.root.attributes['etag'] if xml.root.attributes['etag']
|
105
|
+
case ele.name
|
106
|
+
when "id"
|
107
|
+
puts 'setting id' if service.debug
|
108
|
+
@feed_uri = ele.text
|
109
|
+
when 'content'
|
110
|
+
@content_uri = ele.attributes['src'] if ele.attributes['src']
|
111
|
+
when 'resourceId'
|
112
|
+
@id = ele.text
|
113
|
+
when 'title'
|
114
|
+
@title = ele.text
|
115
|
+
when 'category'
|
116
|
+
@categories << {:label => ele.attributes['label'],
|
117
|
+
:scheme => ele.attributes['scheme'],
|
118
|
+
:term => ele.attributes['term']}
|
119
|
+
if ele.attributes['scheme'] and ele.attributes['scheme'] == 'http://schemas.google.com/g/2005#kind'
|
120
|
+
@kind = if ele.attributes['label']
|
121
|
+
ele.attributes['label']
|
122
|
+
else
|
123
|
+
ele.attributes['term']
|
124
|
+
end
|
125
|
+
end
|
126
|
+
when 'link'
|
127
|
+
case ele.attributes['rel']
|
128
|
+
when 'http://schemas.google.com/docs/2007#parent'
|
129
|
+
@parent_uri = ele.attributes['href']
|
130
|
+
when 'edit'
|
131
|
+
@edit_uri = ele.attributes['href']
|
132
|
+
when 'http://schemas.google.com/acl/2007#accessControlList'
|
133
|
+
@acl_uri = ele.attributes['href'] if not @acl_uri
|
134
|
+
end
|
135
|
+
when 'feedLink'
|
136
|
+
@feed_links << {:rel => ele.attributes['rel'], :href => ele.attributes['href']}
|
137
|
+
@acl_uri = ele.attributes['href'] if ele.attributes['rel'].include? 'accessControlList' and not @acl_uri
|
138
|
+
when 'author'
|
139
|
+
ele.elements.each('name'){}.map {|e| @author_name = e.text}
|
140
|
+
ele.elements.each('email'){}.map {|e| @author_email = e.text}
|
141
|
+
when 'published'
|
142
|
+
@published = Time.parse(ele.text) rescue nil
|
143
|
+
when 'updated'
|
144
|
+
@updated = Time.parse(ele.text) rescue nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
return xml.root
|
148
|
+
end
|
149
|
+
|
150
|
+
#Saves the object if it exsits, otherwise creates it.
|
151
|
+
def save
|
152
|
+
if @exists
|
153
|
+
ret = service.send_request(Request.new(:put, @edit_uri, to_xml))
|
154
|
+
else
|
155
|
+
ret = create
|
156
|
+
end
|
157
|
+
if not ret or not load(ret.body)
|
158
|
+
raise SaveFailed, 'Could not save object'
|
159
|
+
end
|
160
|
+
return true
|
161
|
+
end
|
162
|
+
|
163
|
+
#Creates the object. This must be overridden in a subclass, as the feed url for creating new
|
164
|
+
#objects/entries is service dependent. In other words, each google service uses a different
|
165
|
+
#URI for saving new objects.
|
166
|
+
def create
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
|
170
|
+
#Deletes the object.
|
171
|
+
def delete
|
172
|
+
if @exists
|
173
|
+
service.send_request(Request.new(:delete, @edit_uri, nil, {"If-Match" => "*"}))
|
174
|
+
end
|
175
|
+
@exists = false
|
176
|
+
return true
|
177
|
+
end
|
178
|
+
|
179
|
+
#Creates a new string containing the XML representation of the object as a GData compliant <entry>
|
180
|
+
#element.
|
181
|
+
def to_xml
|
182
|
+
xml = REXML::Document.new(@xml)
|
183
|
+
xml.root.elements.each(){}.map do |ele|
|
184
|
+
if @include_etag
|
185
|
+
xml.root.attributes['gd:etag'] = @etag if @etag and @etag != ''
|
186
|
+
else
|
187
|
+
xml.root.delete_attribute('gd:etag')
|
188
|
+
end
|
189
|
+
case ele.name
|
190
|
+
when "title"
|
191
|
+
ele.text = @title
|
192
|
+
end
|
193
|
+
end
|
194
|
+
xml.to_s
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
require "uri"
|
19
|
+
|
20
|
+
module GData4Ruby
|
21
|
+
#The Request class holds all information needed to make a Request to a Google service.
|
22
|
+
class Request
|
23
|
+
#The HTTP request type, must be one of :get, :post, :put, :delete
|
24
|
+
attr_accessor :type
|
25
|
+
|
26
|
+
#The HTTP request content, only valid for :put and :post requests
|
27
|
+
attr_accessor :content
|
28
|
+
|
29
|
+
#Optional. Additional headers to pass with the request.
|
30
|
+
attr_accessor :headers
|
31
|
+
|
32
|
+
#Optional. Additional query parameters (i.e. "?param=value") to append to the request url
|
33
|
+
attr_reader :parameters
|
34
|
+
|
35
|
+
#Creates a new request object.
|
36
|
+
def initialize(type, url, content = nil, headers = nil, query_parameters = nil)
|
37
|
+
@parameters = nil
|
38
|
+
@headers = headers
|
39
|
+
@content = content
|
40
|
+
@type = type
|
41
|
+
@url = URI.parse(url)
|
42
|
+
self.parameters = query_parameters
|
43
|
+
end
|
44
|
+
|
45
|
+
#The HTTP url to send the request to
|
46
|
+
def url=(new_url)
|
47
|
+
@url = new_url
|
48
|
+
end
|
49
|
+
|
50
|
+
#A hash of additional query parameters (i.e. {'param' => 'value') to append to the request url
|
51
|
+
def parameters=(query_parameters)
|
52
|
+
raise ArgumentError, 'Query parameters must be a Hash' if query_parameters != nil and not query_parameters.is_a? Hash
|
53
|
+
@parameters = query_parameters.is_a?(Hash) ? "?#{query_parameters.to_a.collect{|a| a.join("=")}.join("&")}" : nil
|
54
|
+
end
|
55
|
+
|
56
|
+
#The HTTP url to send the request to
|
57
|
+
def url
|
58
|
+
return URI.parse("#{@url+(@parameters ? @parameters : '')}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
require 'gdata4ruby/base'
|
19
|
+
|
20
|
+
module GData4Ruby
|
21
|
+
#The service class is the main handler for all direct interactions with the
|
22
|
+
#Google Data API.
|
23
|
+
|
24
|
+
class Service < Base
|
25
|
+
#Convenience attribute contains the currently authenticated account name
|
26
|
+
attr_reader :account
|
27
|
+
|
28
|
+
# The token returned by the Google servers, used to authorize all subsequent messages
|
29
|
+
attr_reader :auth_token
|
30
|
+
|
31
|
+
#Accepts an optional attributes hash for initialization values, most likely :gdata_version
|
32
|
+
def initialize(attributes = {})
|
33
|
+
super(attributes)
|
34
|
+
attributes.each do |key, value|
|
35
|
+
self.send("#{key}=", value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# The authenticate method passes the username and password to google servers.
|
40
|
+
# If authentication succeeds, returns true, otherwise raises the AuthenticationFailed error.
|
41
|
+
# Thanks to David King and Scott Taylor for Ruby 1.9 fix.
|
42
|
+
def authenticate(username, password, service)
|
43
|
+
@auth_token = nil
|
44
|
+
ret = nil
|
45
|
+
ret = send_request(Request.new(:post, AUTH_URL, "Email=#{username}&Passwd=#{password}&source=GCal4Ruby&service=#{service}&accountType=HOSTED_OR_GOOGLE"))
|
46
|
+
if ret.class == Net::HTTPOK
|
47
|
+
body = ret.read_body
|
48
|
+
lines = body.send(body.respond_to?(:lines) ? :lines : :to_s).to_a
|
49
|
+
@auth_token = lines.to_a[2].gsub("Auth=", "").strip
|
50
|
+
@account = username
|
51
|
+
@password = password
|
52
|
+
return true
|
53
|
+
else
|
54
|
+
raise AuthenticationFailed
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Author:: Mike Reich (mike@seabourneconsulting.com)
|
2
|
+
# Copyright:: Copyright (C) 2010 Mike Reich
|
3
|
+
# License:: GPL v2
|
4
|
+
#--
|
5
|
+
# Licensed under the General Public License (GPL), Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
#
|
15
|
+
# Feel free to use and update, but be sure to contribute your
|
16
|
+
# code back to the project and attribute as required by the license.
|
17
|
+
#++
|
18
|
+
module GData4Ruby
|
19
|
+
#A helper class that includes commonly used utility methods.
|
20
|
+
class Utils
|
21
|
+
#Adds common Google namespaces to an element. Useful for processing individual events returned from in a feed.
|
22
|
+
def self.add_namespaces(entry)
|
23
|
+
entry.attributes["xmlns:openSearch"] = "http://a9.com/-/spec/opensearch/1.1/"
|
24
|
+
entry.attributes["xmlns:gAcl"] = "http://schemas.google.com/acl/2007"
|
25
|
+
entry.attributes["xmlns:gCal"] = "http://schemas.google.com/gCal/2005"
|
26
|
+
entry.attributes["xmlns:gd"] = "http://schemas.google.com/g/2005"
|
27
|
+
entry.attributes["xmlns:app"] = "http://www.w3.org/2007/app"
|
28
|
+
entry.attributes["xmlns:docs"] = "http://schemas.google.com/docs/2007"
|
29
|
+
entry.attributes["xmlns"] = "http://www.w3.org/2005/Atom"
|
30
|
+
entry.attributes["xmlns:georss"] = "http://www.georss.org/georss"
|
31
|
+
entry.attributes["xmlns:gml"] = "http://www.opengis.net/gml"
|
32
|
+
entry
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/test/unit.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'gdata4ruby'
|
5
|
+
include GData4Ruby
|
6
|
+
|
7
|
+
@service = Service.new
|
8
|
+
@username = nil
|
9
|
+
@password = nil
|
10
|
+
|
11
|
+
def tester
|
12
|
+
if ARGV.include?("-d")
|
13
|
+
@service.debug = true
|
14
|
+
end
|
15
|
+
ARGV.each do |ar|
|
16
|
+
if ar.match("username=")
|
17
|
+
@username = ar.gsub("username=", "")
|
18
|
+
end
|
19
|
+
if ar.match("password=")
|
20
|
+
@password = ar.gsub("password=", "")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
service_test
|
24
|
+
end
|
25
|
+
|
26
|
+
def service_test
|
27
|
+
puts "---Starting Service Test---"
|
28
|
+
puts "1. Authenticate"
|
29
|
+
if @service.authenticate(@username, @password, 'cl')
|
30
|
+
successful
|
31
|
+
else
|
32
|
+
failed
|
33
|
+
end
|
34
|
+
|
35
|
+
puts "2. Authenticate with GData version 3.0"
|
36
|
+
@service = Service.new({:gdata_version => '3.0'})
|
37
|
+
if @service.authenticate(@username, @password, 'cl') and @service.gdata_version == '3.0'
|
38
|
+
successful
|
39
|
+
else
|
40
|
+
failed
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def failed(m = nil)
|
45
|
+
puts "Test Failed"
|
46
|
+
puts m if m
|
47
|
+
exit()
|
48
|
+
end
|
49
|
+
|
50
|
+
def successful(m = nil)
|
51
|
+
puts "Test Successful"
|
52
|
+
puts m if m
|
53
|
+
end
|
54
|
+
|
55
|
+
tester
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: intouch-gdata4ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.5
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Mike Reich
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-04-29 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: oauth2
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.4.1
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: *id001
|
26
|
+
description: A full featured wrapper for interacting with the base Google Data API, including authentication and basic object handling
|
27
|
+
email: mike@seabourneconsulting.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files: []
|
33
|
+
|
34
|
+
files:
|
35
|
+
- README.md
|
36
|
+
- CHANGELOG
|
37
|
+
- lib/gdata4ruby.rb
|
38
|
+
- lib/gdata4ruby/base.rb
|
39
|
+
- lib/gdata4ruby/service.rb
|
40
|
+
- lib/gdata4ruby/request.rb
|
41
|
+
- lib/gdata4ruby/gdata_object.rb
|
42
|
+
- lib/gdata4ruby/utils/utils.rb
|
43
|
+
- lib/gdata4ruby/acl/access_rule.rb
|
44
|
+
- test/unit.rb
|
45
|
+
homepage: http://cookingandcoding.com/gdata4ruby/
|
46
|
+
licenses: []
|
47
|
+
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: "0"
|
65
|
+
requirements: []
|
66
|
+
|
67
|
+
rubyforge_project: gdata4ruby
|
68
|
+
rubygems_version: 1.7.2
|
69
|
+
signing_key:
|
70
|
+
specification_version: 3
|
71
|
+
summary: A full featured wrapper for interacting with the base Google Data API
|
72
|
+
test_files:
|
73
|
+
- test/unit.rb
|