vcita-gdata4ruby 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ =CHANGELOG
2
+ ==version 0.2.2
3
+ * Forked by vCita
4
+ * Resolved endless loop bug in redirect
5
+ ==version 0.2.0
6
+ * Implemented OAuth support (using the OAuth gem)
7
+ * This requires minor changes to how the Base & Service classes are called
8
+ ==version 0.1.5
9
+ * Changed license to LGPLv3
10
+ ==version 0.1.4
11
+ * Bugfix for GeoRSS and GML namespaces
12
+ ==version 0.1.3
13
+ * Added support for Geo/GeoRSS namespaces in base gdata object
14
+ * Added support for Ruby 1.9
15
+ ==version 0.1.2
16
+ * Added better support for 'default' users in AccessRule
17
+ ==version 0.1.1
18
+ * Added additional common attributes, including content, published, updated and author info
19
+ ==version 0.1.0
20
+ * Initial Version
@@ -0,0 +1,71 @@
1
+ # vcita-GData4Ruby
2
+
3
+ ## Introduction
4
+
5
+ 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.
6
+
7
+ ## Author and Contact Information
8
+
9
+ 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.
10
+
11
+ OAuth support was added by [edave](https://github.com/edave/)
12
+
13
+ ### Website
14
+
15
+ http://cookingandcoding.com/gdata4ruby/
16
+
17
+ ## Description
18
+
19
+ 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. There are currently two types of services, Service, and OAuthService.
20
+
21
+ Service uses Google's older [AuthSub](http://code.google.com/apis/accounts/docs/AuthSub.html) authentication mechanism, which is easier to setup, but requires you always know the username/password. (called Service to maintain backwards compatibility, but don't be confused, the base "Service" class is named Base)
22
+
23
+ OAuthService uses Google's newest authentication mechanism, [OAuth](http://code.google.com/apis/accounts/docs/OAuth.html), which is an OpenID-esque login system.
24
+
25
+ See Google's [Getting Started](http://code.google.com/apis/accounts/docs/GettingStarted.html) for more info on which mechanism is best for you. In general, it will likely be easiest to experiment with GData4Ruby using AuthSub and then switch to OAuth for any actual usage in your web app, etc.
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
+ service = Service.new
39
+ service.authenticate({:username => "user@gmail.com", :password => "password", :service => "cl"})
40
+
41
+ 2. Authenticate with a specified GData version
42
+ service = Service.new({:gdata_version => '3.0'})
43
+ service.authenticate({:username => "user@gmail.com", :password => "password", :service => "cl"})
44
+
45
+ ### OAuthService
46
+
47
+ You will need an OAuth Token for authenticating for any given user. If you are using Rails, [OAuth Plugin](https://github.com/pelle/oauth-plugin) makes this fairly easy for users to grant access to your application and generate [OAuth Tokens](http://code.google.com/p/oauth-plugin/wiki/AccessToken).
48
+
49
+ Once you have a token, authenticating is simple:
50
+
51
+ service = OAuthService.new
52
+ my_oauth_token = OAuth::AccessToken.new() # you will need to generate your own OAuth::Token
53
+ service.authenticate({:access_token=>my_oauth_token})
54
+
55
+
56
+
57
+ ## Base Options
58
+
59
+ GData4Ruby supports several options which are passed to any service (Base, Service, OAuthService) upon initialization using a hash
60
+
61
+ Enable SSL:
62
+
63
+ my_generic_service = Service.new({:use_ssl => true})
64
+
65
+ Enable Debug Logging:
66
+
67
+ my_generic_service = Service.new({:debug => true})
68
+
69
+ Disable checking whether the objects are publicly accessibly. This can significantly speed up the service, but also leaves you at risk of trying write to Google services which you do not have privileges. In many cases, you can disable this option.
70
+
71
+ my_generic_service = Service.new({:check_public => false})
@@ -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,229 @@
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, redirect_count = 0)
152
+ ret = nil
153
+
154
+ add_auth_header(request)
155
+ set_protocol!(request)
156
+ http = get_http_object(request.url)
157
+ log("Sending request\nHeader: #{request.headers.inspect.to_s}\nContent: #{request.content.to_s}\n")
158
+ http.start do |ht|
159
+ ret = case request.type
160
+ when :get
161
+ ht.get(request.url.to_s, request.headers)
162
+ when :post
163
+ ht.post(request.url.to_s, request.content, request.headers)
164
+ when :put
165
+ ht.put(request.url.to_s, request.content, request.headers)
166
+ when :delete
167
+ ht.delete(request.url.to_s, request.headers)
168
+ end
169
+ end
170
+
171
+ while ret.is_a?(Net::HTTPRedirection)
172
+ if request.same_url?(ret['location']) || redirect_count > 100
173
+ raise HTTPRequestFailed, "Redirect loop, please validate user"
174
+ end
175
+
176
+ log("Redirect received, resending request")
177
+ request.parameters = nil
178
+ request.url = ret['location']
179
+ log("sending #{request.type} to url = #{request.url.to_s}")
180
+ ret = do_request(request, redirect_count + 1)
181
+ end
182
+ if not ret.is_a?(Net::HTTPSuccess)
183
+ log("invalid response received: "+ret.code)
184
+ raise HTTPRequestFailed, ret.body
185
+ end
186
+ log("20x response received\nResponse: \n"+ret.read_body)
187
+ return ret
188
+ end
189
+
190
+ def get_http_object(location)
191
+ if @proxy_info and @proxy_info.address
192
+ http = Net::HTTP.new(location.host, location.port, @proxy_info.address, @proxy_info.port, @proxy_info.username, @proxy_info.password)
193
+ else
194
+ http = Net::HTTP.new(location.host, location.port)
195
+ end
196
+ if location.scheme == 'https'
197
+ #fixed http/http misnaming via JohnMetta
198
+ log("SSL True")
199
+ http.use_ssl = true
200
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
201
+ end
202
+ return http
203
+ end
204
+
205
+ def add_auth_header(request)
206
+ if @auth_token
207
+ if request.headers
208
+ request.headers.merge!({'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version})
209
+ else
210
+ content_type = (request.type == :get or request.type == :delete) ? 'application/x-www-form-urlencoded' : 'application/atom+xml'
211
+ request.headers = {'Authorization' => "GoogleLogin auth=#{@auth_token}", "GData-Version" => @gdata_version, 'Content-Type' => content_type}
212
+ end
213
+ end
214
+ end
215
+
216
+ protected
217
+ def protocol
218
+ ssl_suffix = ""
219
+ ssl_suffix = "s" if use_ssl
220
+ return "http#{ssl_suffix}"
221
+ end
222
+ def http_protocol
223
+ ssl_suffix = ""
224
+ ssl_suffix = "s" if use_ssl
225
+ return "http#{ssl_suffix}://"
226
+ end
227
+
228
+ end
229
+ end
@@ -0,0 +1,217 @@
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
+ log("Content URI: #{@content_uri}")
117
+ when 'resourceId'
118
+ @id = ele.text
119
+ when 'title'
120
+ @title = ele.text
121
+ log("Title: #{@title}")
122
+ when 'category'
123
+ @categories << {:label => ele.attributes['label'],
124
+ :scheme => ele.attributes['scheme'],
125
+ :term => ele.attributes['term']}
126
+ if ele.attributes['scheme'] and ele.attributes['scheme'] == 'http://schemas.google.com/g/2005#kind'
127
+ @kind = if ele.attributes['label']
128
+ ele.attributes['label']
129
+ else
130
+ ele.attributes['term']
131
+ end
132
+ end
133
+ when 'link'
134
+ case ele.attributes['rel']
135
+ when 'http://schemas.google.com/docs/2007#parent'
136
+ @parent_uri = ele.attributes['href']
137
+ when 'edit'
138
+ @edit_uri = ele.attributes['href']
139
+ when 'http://schemas.google.com/acl/2007#accessControlList'
140
+ @acl_uri = ele.attributes['href'] if not @acl_uri
141
+ end
142
+ when 'feedLink'
143
+ @feed_links << {:rel => ele.attributes['rel'], :href => ele.attributes['href']}
144
+ @acl_uri = ele.attributes['href'] if ele.attributes['rel'].include? 'accessControlList' and not @acl_uri
145
+ when 'author'
146
+ ele.elements.each('name'){}.map {|e| @author_name = e.text}
147
+ ele.elements.each('email'){}.map {|e| @author_email = e.text}
148
+ when 'published'
149
+ begin
150
+ @published = Time.parse(ele.text)
151
+ rescue
152
+ @published = nil
153
+ end
154
+ when 'updated'
155
+ begin
156
+ @updated = Time.parse(ele.text)
157
+ rescue
158
+ @updated = nil
159
+ end
160
+ end
161
+ end
162
+ return xml.root
163
+ end
164
+
165
+ #Saves the object if it exsits, otherwise creates it.
166
+ def save
167
+ if @exists
168
+ ret = service.send_request(Request.new(:put, @edit_uri, to_xml))
169
+ else
170
+ ret = create
171
+ end
172
+ if not ret or not load(ret.read_body)
173
+ raise SaveFailed, 'Could not save object'
174
+ end
175
+ return true
176
+ end
177
+
178
+
179
+ def log(string)
180
+ puts string if debug
181
+ end
182
+
183
+ #Creates the object. This must be overridden in a subclass, as the feed url for creating new
184
+ #objects/entries is service dependent. In other words, each google service uses a different
185
+ #URI for saving new objects.
186
+ def create
187
+ return false
188
+ end
189
+
190
+ #Deletes the object.
191
+ def delete
192
+ if @exists
193
+ service.send_request(Request.new(:delete, @edit_uri, nil, {"If-Match" => "*"}))
194
+ end
195
+ @exists = false
196
+ return true
197
+ end
198
+
199
+ #Creates a new string containing the XML representation of the object as a GData compliant <entry>
200
+ #element.
201
+ def to_xml
202
+ xml = REXML::Document.new(@xml)
203
+ xml.root.elements.each(){}.map do |ele|
204
+ if @include_etag
205
+ xml.root.attributes['gd:etag'] = @etag if @etag and @etag != ''
206
+ else
207
+ xml.root.delete_attribute('gd:etag')
208
+ end
209
+ case ele.name
210
+ when "title"
211
+ ele.text = @title
212
+ end
213
+ end
214
+ xml.to_s
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,100 @@
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, redirect_count = 0)
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
+ if request.same_url?(ret['location']) || redirect_count > 100
71
+ raise HTTPRequestFailed, "Redirect loop, please validate user"
72
+ end
73
+
74
+ log("Redirect received, resending request")
75
+ request.parameters = nil
76
+ request.url = ret['location']
77
+
78
+ log("sending #{request.type} to url = #{request.url.to_s}")
79
+ ret = do_request(request, redirect_count + 1)
80
+ end
81
+ if not ret.is_a?(Net::HTTPSuccess)
82
+ log("invalid response received: "+ret.code)
83
+ raise HTTPRequestFailed, ret.body
84
+ end
85
+ log("20x response received\nResponse: \n"+ret.read_body)
86
+ return ret
87
+ end
88
+
89
+ # Not used in OAuth implementation...
90
+ def get_http_object(location)
91
+ return nil
92
+ end
93
+
94
+ # Not used in OAuth implementation...
95
+ def add_auth_header(request)
96
+ return nil
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,66 @@
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
+ @given_url_string = new_url if new_url.is_a?(String)
48
+ @url = new_url
49
+ end
50
+
51
+ #A hash of additional query parameters (i.e. {'param' => 'value') to append to the request url
52
+ def parameters=(query_parameters)
53
+ raise ArgumentError, 'Query parameters must be a Hash' if query_parameters != nil and not query_parameters.is_a? Hash
54
+ @parameters = query_parameters.is_a?(Hash) ? "?#{query_parameters.to_a.collect{|a| a.join("=")}.join("&")}" : nil
55
+ end
56
+
57
+ def same_url?(other_url)
58
+ return @given_url_string == other_url
59
+ end
60
+
61
+ #The HTTP url to send the request to
62
+ def url
63
+ return URI.parse("#{@url+(@parameters ? @parameters : '')}")
64
+ end
65
+ end
66
+ 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
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vcita-gdata4ruby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 2
10
+ version: 0.2.2
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-12-09 00:00:00 +02: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.md
50
+ files:
51
+ - CHANGELOG
52
+ - README.md
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
+ has_rdoc: true
62
+ homepage: https://github.com/vcita/GData4Ruby
63
+ licenses: []
64
+
65
+ post_install_message:
66
+ rdoc_options: []
67
+
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ hash: 3
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ requirements: []
89
+
90
+ rubyforge_project:
91
+ rubygems_version: 1.6.2
92
+ signing_key:
93
+ specification_version: 3
94
+ summary: A full featured wrapper for interacting with the base Google Data API
95
+ test_files: []
96
+