go-picasa-go 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. data/README.rdoc +135 -0
  2. data/Rakefile +16 -0
  3. data/TODO +10 -0
  4. data/VERSION +1 -0
  5. data/bin/go-picasa-go +64 -0
  6. data/examples/rails-example/README +243 -0
  7. data/examples/rails-example/Rakefile +10 -0
  8. data/examples/rails-example/app/controllers/albums_controller.rb +62 -0
  9. data/examples/rails-example/app/controllers/application_controller.rb +10 -0
  10. data/examples/rails-example/app/controllers/photos_controller.rb +77 -0
  11. data/examples/rails-example/app/helpers/albums_helper.rb +9 -0
  12. data/examples/rails-example/app/helpers/application_helper.rb +3 -0
  13. data/examples/rails-example/app/models/my_user.rb +9 -0
  14. data/examples/rails-example/app/views/albums/_form.html.erb +15 -0
  15. data/examples/rails-example/app/views/albums/edit.html.erb +10 -0
  16. data/examples/rails-example/app/views/albums/index.html.erb +50 -0
  17. data/examples/rails-example/app/views/albums/new.html.erb +10 -0
  18. data/examples/rails-example/app/views/photos/index.html.erb +30 -0
  19. data/examples/rails-example/app/views/photos/new.html.erb +8 -0
  20. data/examples/rails-example/app/views/photos/show.html.erb +19 -0
  21. data/examples/rails-example/config/boot.rb +110 -0
  22. data/examples/rails-example/config/database.yml +22 -0
  23. data/examples/rails-example/config/environment.rb +42 -0
  24. data/examples/rails-example/config/environments/development.rb +17 -0
  25. data/examples/rails-example/config/environments/production.rb +28 -0
  26. data/examples/rails-example/config/environments/test.rb +28 -0
  27. data/examples/rails-example/config/initializers/backtrace_silencers.rb +7 -0
  28. data/examples/rails-example/config/initializers/inflections.rb +10 -0
  29. data/examples/rails-example/config/initializers/mime_types.rb +5 -0
  30. data/examples/rails-example/config/initializers/new_rails_defaults.rb +21 -0
  31. data/examples/rails-example/config/initializers/session_store.rb +15 -0
  32. data/examples/rails-example/config/locales/en.yml +5 -0
  33. data/examples/rails-example/config/routes.rb +49 -0
  34. data/examples/rails-example/db/seeds.rb +7 -0
  35. data/examples/rails-example/doc/README_FOR_APP +2 -0
  36. data/examples/rails-example/log/development.log +6923 -0
  37. data/examples/rails-example/log/production.log +0 -0
  38. data/examples/rails-example/log/server.log +0 -0
  39. data/examples/rails-example/log/test.log +0 -0
  40. data/examples/rails-example/public/404.html +30 -0
  41. data/examples/rails-example/public/422.html +30 -0
  42. data/examples/rails-example/public/500.html +30 -0
  43. data/examples/rails-example/public/favicon.ico +0 -0
  44. data/examples/rails-example/public/images/rails.png +0 -0
  45. data/examples/rails-example/public/index.html +275 -0
  46. data/examples/rails-example/public/javascripts/application.js +2 -0
  47. data/examples/rails-example/public/javascripts/controls.js +963 -0
  48. data/examples/rails-example/public/javascripts/dragdrop.js +973 -0
  49. data/examples/rails-example/public/javascripts/effects.js +1128 -0
  50. data/examples/rails-example/public/javascripts/prototype.js +4320 -0
  51. data/examples/rails-example/public/robots.txt +5 -0
  52. data/examples/rails-example/script/about +4 -0
  53. data/examples/rails-example/script/console +3 -0
  54. data/examples/rails-example/script/dbconsole +3 -0
  55. data/examples/rails-example/script/destroy +3 -0
  56. data/examples/rails-example/script/generate +3 -0
  57. data/examples/rails-example/script/performance/benchmarker +3 -0
  58. data/examples/rails-example/script/performance/profiler +3 -0
  59. data/examples/rails-example/script/plugin +3 -0
  60. data/examples/rails-example/script/runner +3 -0
  61. data/examples/rails-example/script/server +3 -0
  62. data/examples/rails-example/test/functional/albums_controller_test.rb +8 -0
  63. data/examples/rails-example/test/performance/browsing_test.rb +9 -0
  64. data/examples/rails-example/test/test_helper.rb +38 -0
  65. data/examples/rails-example/test/unit/helpers/albums_helper_test.rb +4 -0
  66. data/go_picasa_go.gemspec +184 -0
  67. data/init.rb +1 -0
  68. data/lib/generators/authentication_token_generator.rb +12 -0
  69. data/lib/generators/template/user_class.erb +9 -0
  70. data/lib/generators/user_class_generator.rb +50 -0
  71. data/lib/go_picasa_go.rb +26 -0
  72. data/lib/patchs/object.rb +21 -0
  73. data/lib/patchs/ssl.rb +9 -0
  74. data/lib/picasa/album.rb +267 -0
  75. data/lib/picasa/authentication.rb +19 -0
  76. data/lib/picasa/default_album.rb +9 -0
  77. data/lib/picasa/default_photo.rb +6 -0
  78. data/lib/picasa/default_user.rb +6 -0
  79. data/lib/picasa/http/album.rb +113 -0
  80. data/lib/picasa/http/authentication.rb +40 -0
  81. data/lib/picasa/http/photo.rb +117 -0
  82. data/lib/picasa/missing.rb +22 -0
  83. data/lib/picasa/photo.rb +190 -0
  84. data/lib/picasa/template/album.xml.erb +20 -0
  85. data/lib/picasa/template/photo.erb +15 -0
  86. data/lib/picasa/user.rb +57 -0
  87. data/lib/picasa/util.rb +33 -0
  88. data/script.rb +46 -0
  89. data/spec/album_spec.rb +245 -0
  90. data/spec/authentication_spec.rb +19 -0
  91. data/spec/default_classes_spec.rb +50 -0
  92. data/spec/fixture/photo.jpg +0 -0
  93. data/spec/fixture/photo2.jpg +0 -0
  94. data/spec/generators/user_class_generator_spec.rb +40 -0
  95. data/spec/http/album_spec.rb +129 -0
  96. data/spec/http/authentication_spec.rb +15 -0
  97. data/spec/http/photo_spec.rb +78 -0
  98. data/spec/http_response_helper.rb +23 -0
  99. data/spec/mock_helper.rb +113 -0
  100. data/spec/photo_spec.rb +201 -0
  101. data/spec/spec_helper.rb +151 -0
  102. data/spec/suites/all.rb +14 -0
  103. data/spec/suites/all_mocked.rb +5 -0
  104. data/spec/user_spec.rb +74 -0
  105. metadata +222 -0
@@ -0,0 +1,267 @@
1
+ # Module that offers methods to do high level operations concerning
2
+ # a album within Picasa.
3
+
4
+ module Picasa::Album
5
+
6
+ include Picasa::Missing
7
+
8
+ # Class methods to be added to the class that will include this module.
9
+
10
+ module ClassMethods
11
+ include Picasa::Util
12
+
13
+ # Sets the user class configured so it can be used later.
14
+
15
+ def belongs_to_picasa_user params
16
+ unless params[:class_name] and params[:class_name].class == String
17
+ raise Exception, 'You should pass the string of the class name that includes Picasa::User.'
18
+ end
19
+
20
+ define_dependent_class_methods :user_class, params[:class_name]
21
+ end
22
+
23
+ # Method used to tell the gem what is the class that implements the
24
+ # Picasa::Photo module.
25
+
26
+ def has_many_picasa_photos params
27
+ unless params[:class_name] and params[:class_name].class == String
28
+ raise Exception, 'You should pass the string of the class name that includes Picasa::Album.'
29
+ end
30
+
31
+ define_dependent_class_methods :photo_class, params[:class_name]
32
+ end
33
+
34
+ # Find an album by user_id and album_id. It's mandatory to inform the
35
+ # authentication token. If no album is found, then an exception is raised.
36
+
37
+ def picasa_find user_id, album_id, auth_token
38
+ resp, data = Picasa::HTTP::Album.get_album user_id, album_id, auth_token
39
+
40
+ if resp.code != "200" or resp.message != "OK"
41
+ return nil
42
+ end
43
+
44
+ album = new
45
+ album.send(:populate_attributes_from_xml, data)
46
+ album.user = create_user user_id, auth_token
47
+ album
48
+ end
49
+
50
+ # Find an album by user_id and album_id. It's mandatory to inform the
51
+ # authentication token. If no album is found, then an exception is raised.
52
+
53
+ def picasa_find_all user_id, auth_token
54
+ albums = []
55
+ resp, data = Picasa::HTTP::Album.get_albums user_id, auth_token
56
+
57
+ if resp.code != "200" or resp.message != "OK"
58
+ return nil
59
+ end
60
+
61
+ doc = Nokogiri::XML(data)
62
+ doc.css('entry').each do |entry|
63
+ album = new
64
+ album.send(:populate_attributes, entry)
65
+ album.user = create_user user_id, auth_token
66
+ albums << album
67
+ end
68
+ albums
69
+ end
70
+
71
+ private
72
+
73
+ def create_user picasa_id, auth_token
74
+ user = user_class.new
75
+ user.picasa_id = picasa_id
76
+ user.auth_token = auth_token
77
+ user
78
+ end
79
+ end
80
+
81
+ def self.included(base)
82
+ base.extend(ClassMethods)
83
+ end
84
+
85
+ attr_reader :picasa_id, :author_name, :author_uri, :timestamp, :num_photos,
86
+ :nickname, :commenting_enable, :comment_count,
87
+ :media_content_url, :media_thumbnail_url, :user, :link_edit
88
+
89
+ attr_accessor :title, :summary, :location, :keywords, :user, :access
90
+
91
+ alias_method :cover_url, :media_content_url
92
+ alias_method :thumbnail_url, :media_thumbnail_url
93
+
94
+ # Post an album into Picasa.
95
+ # If cannot create the album an exception is raised.
96
+
97
+ def picasa_save!
98
+ if self.picasa_id and picasa_id.length > 0
99
+ return self.picasa_update!
100
+ end
101
+
102
+ params = {
103
+ :title => title,
104
+ :summary => summary,
105
+ :location => location,
106
+ :keywords => keywords,
107
+ :access => access
108
+ }
109
+
110
+ resp, data = Picasa::HTTP::Album.post_album(user_id, auth_token, params)
111
+
112
+ if resp.code != "201" or resp.message != "Created"
113
+ raise Exception, "Error creating album: #{resp.message}."
114
+ end
115
+
116
+ populate_attributes_from_xml data
117
+ self
118
+ end
119
+
120
+ # Post an album into Picasa.
121
+ # If cannot create the album returns false.
122
+
123
+ def picasa_save
124
+ raise_exception? do
125
+ picasa_save!
126
+ end
127
+ end
128
+
129
+ # Update the attributes of the current album.
130
+ # If cannot update, an exception is raised.
131
+
132
+ def picasa_update_attributes! params
133
+ resp, data = Picasa::HTTP::Album.update_album user_id, self.picasa_id, auth_token, params
134
+
135
+ if resp.code != "200" or resp.message != "OK"
136
+ raise Exception, "Error updating album."
137
+ end
138
+
139
+ populate_attributes_from_xml data
140
+ end
141
+
142
+ # Update the attributes of the current album.
143
+ # If cannot update, return false.
144
+
145
+ def picasa_update_attributes params
146
+ raise_exception? do
147
+ picasa_update_attributes! params
148
+ end
149
+ end
150
+
151
+ # Update all attributes of the current album.
152
+ # If cannot update, an exception is raised.
153
+
154
+ def picasa_update!
155
+ params = {
156
+ :title => title,
157
+ :summary => summary,
158
+ :location => location,
159
+ :keywords => keywords
160
+ }
161
+ picasa_update_attributes! params
162
+ end
163
+
164
+ # Update all attributes of the current album.
165
+ # If cannot update, returns false.
166
+
167
+ def picasa_update
168
+ raise_exception? do
169
+ picasa_update!
170
+ end
171
+ end
172
+
173
+
174
+ # Destroy the current album.
175
+ # If cannot destroy it, an exception is raised.
176
+
177
+ def picasa_destroy!
178
+ resp, data = Picasa::HTTP::Album.delete_album user_id, self.picasa_id, auth_token
179
+
180
+ if resp.code != "200" or resp.message != "OK"
181
+ raise Exception, "Error destroying album."
182
+ end
183
+ end
184
+
185
+ # Destroy the current album and returns true.
186
+ # If cannot destroy it, returns false.
187
+
188
+ def picasa_destroy
189
+ raise_exception? do
190
+ picasa_destroy!
191
+ end
192
+ end
193
+
194
+ def find_photo photo_id
195
+ photo_class.picasa_find user_id, picasa_id, photo_id, auth_token
196
+ end
197
+
198
+ # Find all photos from the current album. The operation is done only one time
199
+ # for an instance, if it it's needed to do it to refresh the data you can
200
+ # pass the parameter true so it can be reloaded.
201
+
202
+ def photos(reload = false)
203
+ @photos = photo_class.picasa_find_all user_id, picasa_id, auth_token if reload
204
+ @photos ||= photo_class.picasa_find_all user_id, picasa_id, auth_token
205
+ end
206
+
207
+ ##############################################################################
208
+ # Private Methods #
209
+ ##############################################################################
210
+
211
+ private
212
+
213
+ # Returns the user email based on the user_id methods
214
+
215
+ def user_email
216
+ "#{user_id}@gmail.com"
217
+ end
218
+
219
+ # Populates the attributes of the object based on the xml
220
+
221
+ def populate_attributes doc
222
+ hash = doc_to_hash doc
223
+
224
+ hash.keys.each do |k|
225
+ self.instance_variable_set("@#{k}", hash[k])
226
+ end
227
+ end
228
+
229
+ def populate_attributes_from_xml xml
230
+ doc = Nokogiri::XML xml
231
+ entry = doc.css('entry')
232
+ populate_attributes entry
233
+ end
234
+
235
+ # Generate a hash representing the attributes of the album from a Nokogiri
236
+ # document
237
+
238
+ def doc_to_hash(doc)
239
+ hash = {
240
+ :picasa_id => doc.at_xpath('//gphoto:id').content,
241
+ :title => doc.at_css('title').content,
242
+ :summary => doc.at_css('summary').content,
243
+ :location => doc.at_xpath('gphoto:location').content,
244
+ :keywords => doc.at_xpath('//media:keywords').content,
245
+ :access => doc.at_xpath('gphoto:access').content,
246
+ :author_name => doc.at_css('author name').content,
247
+ :author_uri => doc.at_css('author uri').content,
248
+ :timestamp => Time.at(doc.at_xpath('gphoto:timestamp').content.slice(0..9).to_i),
249
+ :num_photos => doc.at_xpath('gphoto:numphotos').content.to_i,
250
+ :nickname => doc.at_xpath('gphoto:nickname').content,
251
+ :commenting_enable => doc.at_xpath('gphoto:commentingEnabled').content == "true" ? true : false,
252
+ :comment_count => doc.at_xpath('gphoto:commentCount').content.to_i,
253
+ :media_content_url => doc.at_xpath('//media:content').attr('url'),
254
+ :media_thumbnail_url => doc.at_xpath('//media:thumbnail').attr('url'),
255
+ :link_edit => doc.at_css('link[@rel="edit"]').attr('href')
256
+ }
257
+ end
258
+
259
+ def user_id
260
+ @user.picasa_id
261
+ end
262
+
263
+ def auth_token
264
+ @user.auth_token
265
+ end
266
+
267
+ end
@@ -0,0 +1,19 @@
1
+ # Module that offers methods to do the basic HTTP requests to authenticate
2
+ # an user with a google account.
3
+
4
+ module Picasa::Authentication
5
+ extend Picasa::Util
6
+
7
+ # Authenticate user and returns the authorization token
8
+ # if the operation succeeds other whise an exception is raised.
9
+
10
+ def self.authenticate user_id, password
11
+ resp, body = Picasa::HTTP::Authentication.authenticate user_id, password
12
+
13
+ if resp.code != '200' or resp.message != 'OK'
14
+ raise Exception, "Could not authenticate user. Code: #{resp.code}. Message: #{resp.message}"
15
+ end
16
+
17
+ extract_auth_token body
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # Default class already configured to be used with Picasa api.
2
+
3
+ module Picasa
4
+ class DefaultAlbum
5
+ acts_as_picasa_album
6
+ belongs_to_picasa_user :class_name => "Picasa::DefaultUser"
7
+ has_many_picasa_photos :class_name => 'Picasa::DefaultPhoto'
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ module Picasa
2
+ class DefaultPhoto
3
+ acts_as_picasa_photo
4
+ belongs_to_picasa_album :class_name => 'Picasa::DefaultAlbum'
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Picasa
2
+ class DefaultUser
3
+ acts_as_picasa_user
4
+ has_many_picasa_albums :class_name => "Picasa::DefaultAlbum"
5
+ end
6
+ end
@@ -0,0 +1,113 @@
1
+ # Module that offers methods to do the basic HTTP requests to services provided
2
+ # by Picasa to manipulate albums.
3
+ module Picasa
4
+ module HTTP
5
+ module Album
6
+ # Do a post request to create an album into the user account.
7
+ # The attributes used to create the album are:
8
+ #
9
+ # title, summary, location, keywords
10
+
11
+ def self.post_album user_id, auth_token, params
12
+
13
+ puts params.inspect
14
+
15
+ headers = albums_headers auth_token
16
+
17
+ uri = albums_uri user_id
18
+ http = Net::HTTP.new(uri.host, uri.port)
19
+
20
+ # Render the album template
21
+ template_path = File.dirname(__FILE__) + '/../template/'
22
+ template = ERB.new File.open(template_path+"album.xml.erb").read
23
+
24
+ data = template.result(binding)
25
+
26
+ puts data
27
+
28
+ return http.post(uri.path, data, headers)
29
+ end
30
+
31
+ # Do a put request to update album data
32
+
33
+ def self.update_album user_id, album_id, auth_token, params
34
+ resp, data = get_album user_id, album_id, auth_token
35
+
36
+ raise Exception, "Album not found." unless resp.code == "200"
37
+
38
+ data = update_album_xml data, params
39
+
40
+ headers = albums_headers auth_token, "If-Match" => "*"
41
+
42
+ uri = album_uri user_id, album_id
43
+ http = Net::HTTP.new(uri.host, uri.port)
44
+ http.send_request('PUT',uri.path, data, headers)
45
+ end
46
+
47
+ # Do a delete request to delete an album from a user
48
+
49
+ def self.delete_album user_id, album_id, auth_token
50
+ headers = albums_headers auth_token, "If-Match" => "*"
51
+
52
+ uri = album_uri user_id, album_id
53
+ http = Net::HTTP.new(uri.host, uri.port)
54
+ http.send_request('DELETE',uri.path, nil, headers)
55
+ end
56
+
57
+
58
+ # Do a get request to retrieve all the albums from a user using the user_id.
59
+
60
+ def self.get_albums user_id, auth_token
61
+ headers = albums_headers auth_token
62
+
63
+ uri = albums_uri user_id
64
+
65
+ http = Net::HTTP.new(uri.host, 80)
66
+ http.get uri.path, headers
67
+ end
68
+
69
+ # Do a get request to retrieve one specific album from a user
70
+
71
+ def self.get_album user_id, album_id, auth_token = nil
72
+ headers = albums_headers auth_token
73
+
74
+ uri = album_uri user_id, album_id
75
+
76
+ http = Net::HTTP.new(uri.host, 80)
77
+ http.get uri.path, headers
78
+ end
79
+
80
+ private
81
+
82
+ def self.update_album_xml xml, params
83
+ doc = Nokogiri::XML xml
84
+ doc.at_css('title').content = params[:title] if params[:title]
85
+ doc.at_css('summary').content = params[:summary] if params[:summary]
86
+ doc.at_xpath('//gphoto:access').content = params[:access] if params[:access]
87
+ doc.at_xpath('//gphoto:location').content = params[:location] if params[:location]
88
+ doc.at_xpath('//media:keywords').set_attribute("value", params[:keywords]) if params[:keywords]
89
+ doc.to_xml
90
+ end
91
+
92
+ def self.albums_headers auth_token, opts = {}
93
+ headers = {}
94
+ headers["Content-Type"] = "application/atom+xml"
95
+
96
+ if auth_token and auth_token.length > 0
97
+ headers["Authorization"] = "GoogleLogin auth=#{auth_token}"
98
+ end
99
+
100
+ headers.merge opts
101
+ end
102
+
103
+ def self.albums_uri user_id
104
+ URI.parse "http://picasaweb.google.com/data/feed/api/user/#{user_id}"
105
+ end
106
+
107
+ def self.album_uri user_id, album_id
108
+ URI.parse "http://picasaweb.google.com/data/entry/api/user/#{user_id}/albumid/#{album_id}"
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,40 @@
1
+ # Module that offers methods to do the basic HTTP requests to authenticate user
2
+ # for Picasa.
3
+
4
+ module Picasa
5
+ module HTTP
6
+ module Authentication
7
+
8
+ HTTPS_PORT = 443
9
+
10
+ PICASA_SERVICE = 'lh2'
11
+ APP_NAME = 'BandManager'
12
+
13
+ # Do a post request to authenticate user with the e-mail and password provided
14
+ # and return the response and data of the request
15
+
16
+ def self.authenticate email, password
17
+ uri = uri_login
18
+
19
+ http = Net::HTTP.new(uri.host, HTTPS_PORT)
20
+ http.use_ssl=true
21
+
22
+ data = login_data email, password
23
+
24
+ headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
25
+ resp, data = http.post(uri.path, data, headers)
26
+ end
27
+
28
+ private
29
+
30
+ def self.login_data email, password
31
+ "accountType=HOSTED_OR_GOOGLE&Email=#{email}&Passwd=#{password}&service=#{PICASA_SERVICE}&source=#{APP_NAME}"
32
+ end
33
+
34
+ def self.uri_login
35
+ URI.parse "http://www.google.com/accounts/ClientLogin"
36
+ end
37
+ end
38
+ end
39
+ end
40
+