edave-gdata4ruby 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,17 @@
1
+ =CHANGELOG
2
+ ==version 0.2.0
3
+ * Implemented OAuth support (using the OAuth gem)
4
+ * This requires minor changes to how the Base & Service classes are called
5
+ ==version 0.1.5
6
+ * Changed license to LGPLv3
7
+ ==version 0.1.4
8
+ * Bugfix for GeoRSS and GML namespaces
9
+ ==version 0.1.3
10
+ * Added support for Geo/GeoRSS namespaces in base gdata object
11
+ * Added support for Ruby 1.9
12
+ ==version 0.1.2
13
+ * Added better support for 'default' users in AccessRule
14
+ ==version 0.1.1
15
+ * Added additional common attributes, including content, published, updated and author info
16
+ ==version 0.1.0
17
+ * Initial Version
data/README ADDED
@@ -0,0 +1,29 @@
1
+ =GData4Ruby
2
+
3
+ ==Introduction
4
+ GData4Ruby is a full featured wrapper for the Google Data base API. GData4Ruby provides the ability to authenticate with GData using the ClientLogin method. The package also includes a base gdata object that can be subclassed to provide basic CRUD functions for all Google API service objects. Additionally, a basic ACL object is included for interacting with ACL feeds and setting access rules.
5
+
6
+ ==Author and Contact Information
7
+ GData4Ruby was created and is maintained by {Mike Reich}[mailto:mike@seabourneconsulting.com] and is licenses under the LGPL v3. You can find the text of the LGPL here: http://www.gnu.org/licenses/lgpl.html. Feel free to use and update, but be sure to contribute your code back to the project and attribute as required by the license.
8
+
9
+ ===Website
10
+ http://cookingandcoding.com/gdata4ruby/
11
+
12
+ ==Description
13
+ GData4Ruby has three major components: the service, the GData object and the AccessRule object. Each service represents a google account, and includes a username (email) and a password. You can use the GData service to authenticate either a google account or a google apps account.
14
+
15
+ 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.
16
+
17
+ 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.
18
+
19
+ ==Examples
20
+ Below are some common usage examples. For more examples, check the documentation.
21
+ ===Service
22
+ 1. Authenticate
23
+ service = Service.new
24
+ service.authenticate({:username => "user@gmail.com", :password => "password", :service => "cl"})
25
+
26
+ 2. Authenticate with a specified GData version
27
+ service = Service.new({:gdata_version => '3.0'})
28
+ service.authenticate({:username => "user@gmail.com", :password => "password", :service => "cl"})
29
+
data/lib/gdata4ruby.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "gdata4ruby/service"
2
+ require "gdata4ruby/oauth_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.read_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.read_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,224 @@
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
+
74
+ #Contains the ProxyInfo object for using a proxy server
75
+ attr_accessor :proxy_info
76
+
77
+ #If set to true, debug will dump all raw HTTP requests and responses
78
+ attr_accessor :debug
79
+
80
+ #The GData version used by the service
81
+ attr_accessor :gdata_version
82
+
83
+ # Will have the service use https instead of http
84
+ attr_accessor :use_ssl
85
+
86
+ #Optionally, pass a hash of attributes to populate the class. If you want to use a GData version
87
+ #other than the default (2.1), pass a key/value pair, i.e. {:gdata_version => '1.0'}
88
+ def initialize(attributes = {})
89
+ attributes.each do |key, value|
90
+ if self.respond_to?("#{key}=")
91
+ self.send("#{key}=", value)
92
+ end
93
+ end
94
+ @gdata_version ||= '2.1'
95
+ @use_ssl ||= false
96
+ @debug ||= false
97
+ end
98
+
99
+ # Provides a mechanism for your service to receive credentials and authenticate
100
+ def authenticate(options = {})
101
+
102
+ end
103
+
104
+ # Allows a user to revive the authentication (connection)
105
+ def reauthenticate(options = {})
106
+
107
+ end
108
+
109
+ # Is the service successfully authenticated and connected to google?
110
+ def authenticated?
111
+ return false
112
+ end
113
+
114
+ def log(string)
115
+ puts string if self.debug
116
+ end
117
+
118
+ def create_url(path)
119
+ return http_protocol + path
120
+ end
121
+
122
+ #Sends a request to the Google Data System. Accepts a valid Request object, and returns a
123
+ #HTTPResult class.
124
+ def send_request(request)
125
+ raise ArgumentError 'Request must be a GData4Ruby::Request object' if not request.is_a?Request
126
+ log("sending #{request.type} to url = #{request.url.to_s}")
127
+ do_request(request)
128
+ end
129
+
130
+ private
131
+
132
+ def set_protocol!(request)
133
+ uri = request.url
134
+ if uri.scheme != protocol
135
+ request.url = URI.parse(uri.to_s.sub(uri.scheme, protocol))
136
+ # values = uri.select(*uri.component)
137
+ # keys = uri.component
138
+ # components_hash = {}
139
+ # # Build a hash where the keys are from keys[] and values are from values[]
140
+ # keys.zip(values) {|a,b| components_hash[a] = b }
141
+ # components_hash[:scheme] = protocol
142
+ # request.url = case protocol
143
+ # when 'https'
144
+ # URI::HTTPS.build(components_hash)
145
+ # when 'http'
146
+ # URI::HTTP.build(components_hash)
147
+ # end
148
+ end
149
+ end
150
+
151
+ def do_request(request)
152
+ ret = nil
153
+ add_auth_header(request)
154
+ set_protocol!(request)
155
+ http = get_http_object(request.url)
156
+ log("Sending request\nHeader: #{request.headers.inspect.to_s}\nContent: #{request.content.to_s}\n")
157
+ http.start do |ht|
158
+ ret = case request.type
159
+ when :get
160
+ ht.get(request.url.to_s, request.headers)
161
+ when :post
162
+ ht.post(request.url.to_s, request.content, request.headers)
163
+ when :put
164
+ ht.put(request.url.to_s, request.content, request.headers)
165
+ when :delete
166
+ ht.delete(request.url.to_s, request.headers)
167
+ end
168
+ end
169
+
170
+ while ret.is_a?(Net::HTTPRedirection)
171
+ log("Redirect received, resending request")
172
+ request.parameters = nil
173
+ request.url = ret['location']
174
+ log("sending #{request.type} to url = #{request.url.to_s}")
175
+ ret = do_request(request)
176
+ end
177
+ if not ret.is_a?(Net::HTTPSuccess)
178
+ log("invalid response received: "+ret.code)
179
+ raise HTTPRequestFailed, ret.body
180
+ end
181
+ log("20x response received\nResponse: \n"+ret.read_body)
182
+ return ret
183
+ end
184
+
185
+ def get_http_object(location)
186
+ if @proxy_info and @proxy_info.address
187
+ http = Net::HTTP.new(location.host, location.port, @proxy_info.address, @proxy_info.port, @proxy_info.username, @proxy_info.password)
188
+ else
189
+ http = Net::HTTP.new(location.host, location.port)
190
+ end
191
+ if location.scheme == 'https'
192
+ #fixed http/http misnaming via JohnMetta
193
+ log("SSL True")
194
+ http.use_ssl = true
195
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
196
+ end
197
+ return http
198
+ end
199
+
200
+ def add_auth_header(request)
201
+ if @auth_token
202
+ if request.headers
203
+ request.headers.merge!({'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version})
204
+ else
205
+ content_type = (request.type == :get or request.type == :delete) ? 'application/x-www-form-urlencoded' : 'application/atom+xml'
206
+ request.headers = {'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version, 'Content-Type' => content_type}
207
+ end
208
+ end
209
+ end
210
+
211
+ protected
212
+ def protocol
213
+ ssl_suffix = ""
214
+ ssl_suffix = "s" if use_ssl
215
+ return "http#{ssl_suffix}"
216
+ end
217
+ def http_protocol
218
+ ssl_suffix = ""
219
+ ssl_suffix = "s" if use_ssl
220
+ return "http#{ssl_suffix}://"
221
+ end
222
+
223
+ end
224
+ end
@@ -0,0 +1,216 @@
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
+ #Used for debugging
77
+ attr_accessor :debug
78
+
79
+ #Indicates whether the object exists on the Google servers, i.e. has been created/saved.
80
+ def exists?
81
+ return @exists
82
+ end
83
+
84
+ #Initializes a new GDataObject. You must pass a valid Service object, and can pass
85
+ #an optional array of attributes to initialize values. To load data into an object,
86
+ #use the load method.
87
+ def initialize(service, attributes = {})
88
+ @xml ||= ''
89
+ @service ||= service
90
+ @exists = false
91
+ @title = @content_uri = @etag = @acl_uri = @edit_uri = @parent_uri = @feed_uri = @kind = nil
92
+ @categories = @feed_links = []
93
+ @include_etag = true
94
+ attributes.each do |key, value|
95
+ if self.respond_to?("#{key}=")
96
+ self.send("#{key}=", value)
97
+ end
98
+ end
99
+ end
100
+
101
+ public
102
+ #Loads data into the object. Accepts a string containing an XML <entry> from a GData
103
+ #compliant feed.
104
+ def load(string)
105
+ @exists = @include_etag = true
106
+ @xml = string
107
+ xml = REXML::Document.new(string)
108
+ xml.root.elements.each(){}.map do |ele|
109
+ @etag = xml.root.attributes['etag'] if xml.root.attributes['etag']
110
+ case ele.name
111
+ when "id"
112
+ @feed_uri = ele.text
113
+ log("ID: #{@feed_uri}")
114
+ when 'content'
115
+ @content_uri = ele.attributes['src'] if ele.attributes['src']
116
+ when 'resourceId'
117
+ @id = ele.text
118
+ when 'title'
119
+ @title = ele.text
120
+ log("Title: #{@title}")
121
+ when 'category'
122
+ @categories << {:label => ele.attributes['label'],
123
+ :scheme => ele.attributes['scheme'],
124
+ :term => ele.attributes['term']}
125
+ if ele.attributes['scheme'] and ele.attributes['scheme'] == 'http://schemas.google.com/g/2005#kind'
126
+ @kind = if ele.attributes['label']
127
+ ele.attributes['label']
128
+ else
129
+ ele.attributes['term']
130
+ end
131
+ end
132
+ when 'link'
133
+ case ele.attributes['rel']
134
+ when 'http://schemas.google.com/docs/2007#parent'
135
+ @parent_uri = ele.attributes['href']
136
+ when 'edit'
137
+ @edit_uri = ele.attributes['href']
138
+ when 'http://schemas.google.com/acl/2007#accessControlList'
139
+ @acl_uri = ele.attributes['href'] if not @acl_uri
140
+ end
141
+ when 'feedLink'
142
+ @feed_links << {:rel => ele.attributes['rel'], :href => ele.attributes['href']}
143
+ @acl_uri = ele.attributes['href'] if ele.attributes['rel'].include? 'accessControlList' and not @acl_uri
144
+ when 'author'
145
+ ele.elements.each('name'){}.map {|e| @author_name = e.text}
146
+ ele.elements.each('email'){}.map {|e| @author_email = e.text}
147
+ when 'published'
148
+ begin
149
+ @published = Time.parse(ele.text)
150
+ rescue
151
+ @published = nil
152
+ end
153
+ when 'updated'
154
+ begin
155
+ @updated = Time.parse(ele.text)
156
+ rescue
157
+ @updated = nil
158
+ end
159
+ end
160
+ end
161
+ return xml.root
162
+ end
163
+
164
+ #Saves the object if it exsits, otherwise creates it.
165
+ def save
166
+ if @exists
167
+ ret = service.send_request(Request.new(:put, @edit_uri, to_xml))
168
+ else
169
+ ret = create
170
+ end
171
+ if not ret or not load(ret.read_body)
172
+ raise SaveFailed, 'Could not save object'
173
+ end
174
+ return true
175
+ end
176
+
177
+
178
+ def log(string)
179
+ puts string if debug
180
+ end
181
+
182
+ #Creates the object. This must be overridden in a subclass, as the feed url for creating new
183
+ #objects/entries is service dependent. In other words, each google service uses a different
184
+ #URI for saving new objects.
185
+ def create
186
+ return false
187
+ end
188
+
189
+ #Deletes the object.
190
+ def delete
191
+ if @exists
192
+ service.send_request(Request.new(:delete, @edit_uri, nil, {"If-Match" => "*"}))
193
+ end
194
+ @exists = false
195
+ return true
196
+ end
197
+
198
+ #Creates a new string containing the XML representation of the object as a GData compliant <entry>
199
+ #element.
200
+ def to_xml
201
+ xml = REXML::Document.new(@xml)
202
+ xml.root.elements.each(){}.map do |ele|
203
+ if @include_etag
204
+ xml.root.attributes['gd:etag'] = @etag if @etag and @etag != ''
205
+ else
206
+ xml.root.delete_attribute('gd:etag')
207
+ end
208
+ case ele.name
209
+ when "title"
210
+ ele.text = @title
211
+ end
212
+ end
213
+ xml.to_s
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,95 @@
1
+ # Author:: David Pitman (ui-design@vestaldesign.com )
2
+ # Copyright:: Copyright (C) 2010 David Pitman
3
+ # License:: LGPL v2.1
4
+ #--
5
+ # Licensed under the Lesser General Public License (GPL), Version 2.1 (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/lgpl-2.1.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 OAuthService < Base
25
+ #Convenience attribute contains the currently OAuth access token
26
+ attr_reader :access_token
27
+
28
+ #Accepts an optional attributes hash for initialization values, most likely :gdata_version
29
+ def initialize(attributes = {})
30
+ super(attributes)
31
+ attributes.each do |key, value|
32
+ if self.respond_to?("#{key}=")
33
+ self.send("#{key}=", value)
34
+ end
35
+ end
36
+ end
37
+
38
+ # The method is a generic hash to allow subclasses to easily receive args for their own type of authentication
39
+ # In this case, we're using an OAuth AccessToken - http://oauth.rubyforge.org/rdoc/classes/OAuth/AccessToken.html
40
+ def authenticate(options = {})
41
+ @access_token = options[:access_token]
42
+ end
43
+
44
+ def reauthenticate(options = {})
45
+ @access_token ||= options[:access_token]
46
+ end
47
+
48
+
49
+ def authenticated?
50
+ return (@access_token != nil)
51
+ end
52
+
53
+ private
54
+
55
+ def do_request(request)
56
+ log("Sending request\nHeader: #{request.headers.inspect.to_s}\nContent: #{request.content.to_s}\n")
57
+ set_protocol!(request)
58
+ ret = case request.type
59
+ when :get
60
+ @access_token.get(request.url.to_s, request.headers)
61
+ when :post
62
+ @access_token.post(request.url.to_s, request.content, request.headers)
63
+ when :put
64
+ @access_token.put(request.url.to_s, request.content, request.headers)
65
+ when :delete
66
+ @access_token.delete(request.url.to_s, request.headers)
67
+ end
68
+
69
+ while ret.is_a?(Net::HTTPRedirection)
70
+ log("Redirect received, resending request")
71
+ request.parameters = nil
72
+ request.url = ret['location']
73
+ log("sending #{request.type} to url = #{request.url.to_s}")
74
+ ret = do_request(request)
75
+ end
76
+ if not ret.is_a?(Net::HTTPSuccess)
77
+ log("invalid response received: "+ret.code)
78
+ raise HTTPRequestFailed, ret.body
79
+ end
80
+ log("20x response received\nResponse: \n"+ret.read_body)
81
+ return ret
82
+ end
83
+
84
+ # Not used in OAuth implementation...
85
+ def get_http_object(location)
86
+ return nil
87
+ end
88
+
89
+ # Not used in OAuth implementation...
90
+ def add_auth_header(request)
91
+ return nil
92
+ end
93
+
94
+ end
95
+ 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,77 @@
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
+ if self.respond_to?("#{key}=")
36
+ self.send("#{key}=", value)
37
+ end
38
+ end
39
+ end
40
+
41
+ # The authenticate method passes the username and password to google servers.
42
+ # If authentication succeeds, returns true, otherwise raises the AuthenticationFailed error.
43
+ # Thanks to David King and Scott Taylor for Ruby 1.9 fix.
44
+ def authenticate(options = {})
45
+ username = options[:username]
46
+ password = options[:password]
47
+ service = options[:service]
48
+ @auth_token = nil
49
+ ret = nil
50
+ auth_args = "Email=#{username}&Passwd=#{password}&source=GCal4Ruby&service=#{service}&accountType=HOSTED_OR_GOOGLE"
51
+ log(auth_args)
52
+ ret = send_request(Request.new(:post, @@auth_url, auth_args))
53
+ if ret.class == Net::HTTPOK
54
+ body = ret.read_body
55
+ lines = body.send(body.respond_to?(:lines) ? :lines : :to_s).to_a
56
+ @auth_token = lines.to_a[2].gsub("Auth=", "").strip
57
+ @account = username
58
+ @password = password
59
+ return true
60
+ else
61
+ @auth_token = nil
62
+ raise AuthenticationFailed
63
+ end
64
+ end
65
+
66
+ def reauthenticate(options = {})
67
+ options[:username] ||= @account
68
+ options[:password] ||= @password
69
+ authenticate(options)
70
+ end
71
+
72
+ def authenticated?
73
+ log("Authenticated: #{@auth_token}")
74
+ return (@auth_token != nil)
75
+ end
76
+ end
77
+ 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,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: edave-gdata4ruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Mike Reich
14
+ - Anthony Underwood
15
+ - David Pitman
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+
20
+ date: 2011-01-11 00:00:00 -05:00
21
+ default_executable:
22
+ dependencies:
23
+ - !ruby/object:Gem::Dependency
24
+ name: oauth
25
+ prerelease: false
26
+ requirement: &id001 !ruby/object:Gem::Requirement
27
+ none: false
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ hash: 7
32
+ segments:
33
+ - 0
34
+ - 4
35
+ - 4
36
+ version: 0.4.4
37
+ type: :runtime
38
+ version_requirements: *id001
39
+ description:
40
+ email:
41
+ - mike@seabourneconsulting.com
42
+ - email2ants@gmail.com
43
+ - ui-design@vestaldesign.com
44
+ executables: []
45
+
46
+ extensions: []
47
+
48
+ extra_rdoc_files:
49
+ - README
50
+ files:
51
+ - CHANGELOG
52
+ - README
53
+ - lib/gdata4ruby.rb
54
+ - lib/gdata4ruby/acl/access_rule.rb
55
+ - lib/gdata4ruby/base.rb
56
+ - lib/gdata4ruby/gdata_object.rb
57
+ - lib/gdata4ruby/oauth_service.rb
58
+ - lib/gdata4ruby/request.rb
59
+ - lib/gdata4ruby/service.rb
60
+ - lib/gdata4ruby/utils/utils.rb
61
+ - test/unit.rb
62
+ has_rdoc: true
63
+ homepage: https://github.com/edave/GData4Ruby
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options: []
68
+
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 3
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ requirements: []
90
+
91
+ rubyforge_project:
92
+ rubygems_version: 1.4.1
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: A full featured wrapper for interacting with the base Google Data API
96
+ test_files:
97
+ - test/unit.rb