neves-ruby_picasa 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +22 -0
- data/README.txt +79 -0
- data/lib/ruby_picasa/types.rb +315 -0
- data/lib/ruby_picasa.rb +397 -0
- data/spec/ruby_picasa/types_spec.rb +303 -0
- data/spec/ruby_picasa_spec.rb +382 -0
- data/spec/sample/album.atom +141 -0
- data/spec/sample/recent.atom +111 -0
- data/spec/sample/search.atom +99 -0
- data/spec/sample/user.atom +107 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +13 -0
- metadata +76 -0
data/History.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
=== 0.2.1 / 2009-03-05
|
2
|
+
|
3
|
+
* Now fully documented.
|
4
|
+
* Full spec suite with 100% coverage.
|
5
|
+
* Even easier to use the api.
|
6
|
+
* Move thumbnail and url to Base so it can be used for Photos and Albums.
|
7
|
+
* Enable url to optionally return width and height for image_tag.
|
8
|
+
* Use the numeric gphoto:id as id and rename tho standard atom feed id to feed_id
|
9
|
+
* Fixed handling of cropped thumbnails and automatically query for valid thumbnail sizes.
|
10
|
+
* Stopped using the wrong url to #load albums. Renamed method to #feed.
|
11
|
+
|
12
|
+
=== 0.2.0 / 2009-02-25
|
13
|
+
|
14
|
+
* First public release.
|
15
|
+
|
16
|
+
=== 0.1.0 / 2009-02-21
|
17
|
+
|
18
|
+
* First working version.
|
19
|
+
|
20
|
+
=== 0.0.0 / 2009-02-21
|
21
|
+
|
22
|
+
* Needed to access Picasa.
|
data/README.txt
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
= ruby_picasa
|
2
|
+
|
3
|
+
* http://github.com/pangloss/ruby_picasa
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Provides a super easy to use object layer for authenticating and accessing
|
8
|
+
Picasa through their API.
|
9
|
+
|
10
|
+
== FEATURES:
|
11
|
+
|
12
|
+
* Simplifies the process of obtaining both a temporary and a permanent AuthSub
|
13
|
+
token.
|
14
|
+
* Very easy to use API.
|
15
|
+
* Allows access to both public and private User, Album and Photo data.
|
16
|
+
* Uses Objectify::Xml to define the XML object-relational layer with a very
|
17
|
+
easy to understand DSL. See www.github.com/pangloss/objectify_xml
|
18
|
+
|
19
|
+
== PROBLEMS:
|
20
|
+
|
21
|
+
* None known.
|
22
|
+
|
23
|
+
== SYNOPSIS:
|
24
|
+
|
25
|
+
# 1. Authorize application for access (in a rails controller)
|
26
|
+
#
|
27
|
+
redirect_to RubyPicasa.authorization_url(auth_result_url)
|
28
|
+
|
29
|
+
# 2. Extract the Picasa token from the request Picasa sends back to your app
|
30
|
+
# and create a permanent AuthSub token. Returns an initialized Picasa
|
31
|
+
# session. (Called from the Rails action for auth_result_url above)
|
32
|
+
picasa = RubyPicasa.authorize_request(self.request)
|
33
|
+
|
34
|
+
# 3. Access the data you are interested in
|
35
|
+
@album = picasa.user.albums.first
|
36
|
+
@photo = @album.photos.first
|
37
|
+
|
38
|
+
# 4. Display your photos
|
39
|
+
image_tag @photo.url
|
40
|
+
image_tag @photo.url('160c') # Picasa thumbnail names are predefined
|
41
|
+
|
42
|
+
== PUBLIC ALBUMS:
|
43
|
+
# 1. Create an public user by its id:
|
44
|
+
#
|
45
|
+
user = Picasa.public_user("user_id")
|
46
|
+
|
47
|
+
== REQUIREMENTS:
|
48
|
+
|
49
|
+
* objectify_xml
|
50
|
+
|
51
|
+
== INSTALL:
|
52
|
+
|
53
|
+
* gem install ruby-picasa
|
54
|
+
* gem install pangloss-ruby-picasa --source http://gems.github.com
|
55
|
+
|
56
|
+
== LICENSE:
|
57
|
+
|
58
|
+
(The MIT License)
|
59
|
+
|
60
|
+
Copyright (c) 2009 Darrick Wiebe
|
61
|
+
|
62
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
63
|
+
a copy of this software and associated documentation files (the
|
64
|
+
'Software'), to deal in the Software without restriction, including
|
65
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
66
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
67
|
+
permit persons to whom the Software is furnished to do so, subject to
|
68
|
+
the following conditions:
|
69
|
+
|
70
|
+
The above copyright notice and this permission notice shall be
|
71
|
+
included in all copies or substantial portions of the Software.
|
72
|
+
|
73
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
74
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
75
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
76
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
77
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
78
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
79
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,315 @@
|
|
1
|
+
module RubyPicasa
|
2
|
+
# attributes :url, :height, :width
|
3
|
+
class PhotoUrl < Objectify::ElementParser
|
4
|
+
attributes :url, :height, :width
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
class ThumbnailUrl < PhotoUrl
|
9
|
+
|
10
|
+
# The name of the current thumbnail. For possible names, see Photo#url
|
11
|
+
def thumb_name
|
12
|
+
name = url.scan(%r{/s([^/]+)/[^/]+$}).flatten.compact.first
|
13
|
+
if name
|
14
|
+
name.sub(/-/, '')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Note that in all defined classes I'm ignoring values I don't happen to need
|
21
|
+
# or know about. Please do add support for the ones I've missed. Be sure to
|
22
|
+
# declare which namespaces are supported with the namespaces method. Any
|
23
|
+
# elements defined in other namespaces are automatically ignored.
|
24
|
+
#
|
25
|
+
# Base class for User, Photo and Album types, not used independently.
|
26
|
+
#
|
27
|
+
# attribute :id, 'gphoto:id'
|
28
|
+
# attribute :feed_id, 'id'
|
29
|
+
# attributes :updated, :title
|
30
|
+
#
|
31
|
+
# has_many :links, Objectify::Atom::Link, 'link'
|
32
|
+
# has_one :content, PhotoUrl, 'media:content'
|
33
|
+
# has_many :thumbnails, ThumbnailUrl, 'media:thumbnail'
|
34
|
+
# has_one :author, Objectify::Atom::Author, 'author'
|
35
|
+
class Base < Objectify::DocumentParser
|
36
|
+
namespaces :openSearch, :gphoto, :media
|
37
|
+
flatten 'media:group'
|
38
|
+
|
39
|
+
attribute :id, 'gphoto:id'
|
40
|
+
attribute :feed_id, 'id'
|
41
|
+
attributes :updated, :title
|
42
|
+
|
43
|
+
has_many :links, Objectify::Atom::Link, 'link'
|
44
|
+
has_one :content, PhotoUrl, 'media:content'
|
45
|
+
has_many :thumbnails, ThumbnailUrl, 'media:thumbnail'
|
46
|
+
has_one :author, Objectify::Atom::Author, 'author'
|
47
|
+
|
48
|
+
# Return the link object with a matching rel attribute value. +rel+ can be
|
49
|
+
# either a fully matching string or a regular expression.
|
50
|
+
def link(rel)
|
51
|
+
links.find { |l| rel === l.rel }
|
52
|
+
end
|
53
|
+
|
54
|
+
def session=(session)
|
55
|
+
@session = session
|
56
|
+
end
|
57
|
+
|
58
|
+
# Should return the Picasa instance that retrieved this data.
|
59
|
+
def session
|
60
|
+
if @session
|
61
|
+
@session
|
62
|
+
else
|
63
|
+
@session = parent.session if parent
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Retrieves the data feed at the url of the current record.
|
68
|
+
def feed(options = {})
|
69
|
+
session.get_url(link('http://schemas.google.com/g/2005#feed').href, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
# If the results are paginated, retrieve the next page.
|
73
|
+
def next
|
74
|
+
if link = link('next')
|
75
|
+
session.get_url(link.href)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# If the results are paginated, retrieve the previous page.
|
80
|
+
def previous
|
81
|
+
if link = link('previous')
|
82
|
+
session.get_url(link.href)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Thumbnail names are by image width in pixels. Sizes up to 160 may be
|
87
|
+
# either cropped (square) or uncropped:
|
88
|
+
#
|
89
|
+
# cropped: 32c, 48c, 64c, 72c, 144c, 160c
|
90
|
+
# uncropped: 32u, 48u, 64u, 72u, 144u, 160u
|
91
|
+
#
|
92
|
+
# The rest of the image sizes should be specified by the desired width
|
93
|
+
# alone. Widths up to 800px may be embedded on a webpage:
|
94
|
+
#
|
95
|
+
# embeddable: 200, 288, 320, 400, 512, 576, 640, 720, 800
|
96
|
+
# not embeddable: 912, 1024, 1152, 1280, 1440, 1600
|
97
|
+
#
|
98
|
+
# if a options is set to true or a hash is given, the width and height of
|
99
|
+
# the image will be added to the hash and returned. Useful for passing to
|
100
|
+
# the rails image_tag helper as follows:
|
101
|
+
#
|
102
|
+
# image_tag(*image.url('72c', { :class => 'thumb' }))
|
103
|
+
#
|
104
|
+
# which results in:
|
105
|
+
#
|
106
|
+
# <img href="..." class="thumb" width="72" height="72">
|
107
|
+
#
|
108
|
+
def url(thumb_name = nil, options = nil)
|
109
|
+
url = nil
|
110
|
+
if thumb_name.is_a? Hash
|
111
|
+
options = thumb_name
|
112
|
+
thumb_name = nil
|
113
|
+
end
|
114
|
+
options = {} if options and not options.is_a? Hash
|
115
|
+
if thumb_name
|
116
|
+
if thumb = thumbnail(thumb_name)
|
117
|
+
url = thumb.url
|
118
|
+
options = { :width => thumb.width, :height => thumb.height }.merge(options) if options
|
119
|
+
end
|
120
|
+
else
|
121
|
+
url = content.url
|
122
|
+
options = { :width => content.width, :height => content.height }.merge(options) if options
|
123
|
+
end
|
124
|
+
if options
|
125
|
+
[url, options]
|
126
|
+
else
|
127
|
+
url
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# See +url+ for possible image sizes
|
132
|
+
def thumbnail(thumb_name)
|
133
|
+
raise PicasaError, 'Invalid thumbnail size' unless Photo::VALID.include?(thumb_name.to_s)
|
134
|
+
thumb = thumbnails.find { |t| t.thumb_name == thumb_name }
|
135
|
+
if thumb
|
136
|
+
thumb
|
137
|
+
elsif session
|
138
|
+
f = feed(:thumbsize => thumb_name)
|
139
|
+
if f
|
140
|
+
f.thumbnails.first
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# convert a thumbnail from a size to another, example:
|
146
|
+
# tb(160) converts this thumbnail url:
|
147
|
+
# http://lh5.ggpht.com/_8t0hxCMjpcM/SZ7OKoCAAsI/AAAAAAAABYA/_lUCR6-iV2g/s144-c/Calibration_Control_Image.jpg
|
148
|
+
# into this one with new size:
|
149
|
+
# http://lh5.ggpht.com/_8t0hxCMjpcM/SZ7OKoCAAsI/AAAAAAAABYA/_lUCR6-iV2g/s160/Calibration_Control_Image.jpg
|
150
|
+
# the thumb does not need to exist on the xml
|
151
|
+
def tb(new_size, crop = false)
|
152
|
+
crop = crop ? "-c" : ""
|
153
|
+
parts = thumbnails.first.url.split "/"
|
154
|
+
parts[-2] = "s" + new_size.to_s + crop
|
155
|
+
parts.join "/"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
# Includes attributes and associations defined on Base, plus:
|
161
|
+
#
|
162
|
+
# attributes :total_results, # represents total number of albums
|
163
|
+
# :start_index,
|
164
|
+
# :items_per_page,
|
165
|
+
# :thumbnail
|
166
|
+
# has_many :entries, :Album, 'entry'
|
167
|
+
class User < Base
|
168
|
+
attributes :total_results, # represents total number of albums
|
169
|
+
:start_index,
|
170
|
+
:items_per_page,
|
171
|
+
:thumbnail
|
172
|
+
has_many :entries, :Album, 'entry'
|
173
|
+
|
174
|
+
# The current page of albums associated to the user.
|
175
|
+
def albums
|
176
|
+
entries
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
# Includes attributes and associations defined on Base and User, plus:
|
182
|
+
#
|
183
|
+
# has_many :entries, :Photo, 'entry'
|
184
|
+
class RecentPhotos < User
|
185
|
+
has_many :entries, :Photo, 'entry'
|
186
|
+
|
187
|
+
# The current page of recently updated photos associated to the user.
|
188
|
+
def photos
|
189
|
+
entries
|
190
|
+
end
|
191
|
+
|
192
|
+
undef albums
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
# Includes attributes and associations defined on Base, plus:
|
197
|
+
#
|
198
|
+
# attributes :published,
|
199
|
+
# :summary,
|
200
|
+
# :rights,
|
201
|
+
# :name,
|
202
|
+
# :access,
|
203
|
+
# :numphotos, # number of pictures in this album
|
204
|
+
# :total_results, # number of pictures matching this 'search'
|
205
|
+
# :start_index,
|
206
|
+
# :items_per_page,
|
207
|
+
# :allow_downloads
|
208
|
+
# has_many :entries, :Photo, 'entry'
|
209
|
+
class Album < Base
|
210
|
+
attributes :published,
|
211
|
+
:summary,
|
212
|
+
:rights,
|
213
|
+
:name,
|
214
|
+
:access,
|
215
|
+
:numphotos, # number of pictures in this album
|
216
|
+
:total_results, # number of pictures matching this 'search'
|
217
|
+
:start_index,
|
218
|
+
:items_per_page,
|
219
|
+
:allow_downloads
|
220
|
+
has_many :entries, :Photo, 'entry'
|
221
|
+
|
222
|
+
# True if this album's rights are set to public
|
223
|
+
def public?
|
224
|
+
rights == 'public'
|
225
|
+
end
|
226
|
+
|
227
|
+
# True if this album's rights are set to private
|
228
|
+
def private?
|
229
|
+
rights == 'private'
|
230
|
+
end
|
231
|
+
|
232
|
+
# The current page of photos in the album.
|
233
|
+
def photos(options = {})
|
234
|
+
if entries.blank? and !@photos_requested
|
235
|
+
@photos_requested = true
|
236
|
+
if session and data = feed
|
237
|
+
self.entries = data.entries
|
238
|
+
else
|
239
|
+
[]
|
240
|
+
end
|
241
|
+
else
|
242
|
+
entries
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
class Search < Album
|
249
|
+
# The current page of photos matching the search.
|
250
|
+
def photos(options = {})
|
251
|
+
super
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
# Includes attributes and associations defined on Base, plus:
|
257
|
+
#
|
258
|
+
# attributes :published,
|
259
|
+
# :summary,
|
260
|
+
# :version, # can use to determine if need to update...
|
261
|
+
# :position,
|
262
|
+
# :albumid, # useful from the recently updated feed for instance.
|
263
|
+
# :width,
|
264
|
+
# :height,
|
265
|
+
# :description,
|
266
|
+
# :keywords,
|
267
|
+
# :credit
|
268
|
+
# attribute :unique_id, 'exif:imageUniqueID'
|
269
|
+
# attribute :exif_distance, 'exif:distance'
|
270
|
+
# attribute :exif_exposure, 'exif:exposure'
|
271
|
+
# attribute :exif_flash, 'exif:flash'
|
272
|
+
# attribute :exif_focallength, 'exif:focallength'
|
273
|
+
# attribute :exif_fstop, 'exif:fstop'
|
274
|
+
# attribute :exif_iso, 'exif:iso'
|
275
|
+
# attribute :exif_make, 'exif:make'
|
276
|
+
# attribute :exif_model, 'exif:model'
|
277
|
+
# attribute :exif_time, 'exif:time'
|
278
|
+
# has_one :author, Objectify::Atom::Author, 'author'
|
279
|
+
class Photo < Base
|
280
|
+
CROPPED = %w[ 32c 48c 64c 72c 144c 160c ]
|
281
|
+
UNCROPPED = %w[ 32u 48u 64u 72u 144u 160u 32 48 64 72 144 160 ]
|
282
|
+
MEDIUM = %w[ 200 288 320 400 512 576 640 720 800 ]
|
283
|
+
LARGE = %w[ 912 1024 1152 1280 1440 1600 ]
|
284
|
+
VALID = CROPPED + UNCROPPED + MEDIUM + LARGE
|
285
|
+
|
286
|
+
namespace :exif
|
287
|
+
|
288
|
+
attributes :published,
|
289
|
+
:summary,
|
290
|
+
:version, # can use to determine if need to update...
|
291
|
+
:position,
|
292
|
+
:albumid, # useful from the recently updated feed for instance.
|
293
|
+
:width,
|
294
|
+
:height,
|
295
|
+
:description,
|
296
|
+
:keywords,
|
297
|
+
:credit
|
298
|
+
|
299
|
+
flatten 'exif:tags'
|
300
|
+
attribute :unique_id, 'exif:imageUniqueID'
|
301
|
+
attribute :exif_distance, 'exif:distance'
|
302
|
+
attribute :exif_exposure, 'exif:exposure'
|
303
|
+
attribute :exif_flash, 'exif:flash'
|
304
|
+
attribute :exif_focallength, 'exif:focallength'
|
305
|
+
attribute :exif_fstop, 'exif:fstop'
|
306
|
+
attribute :exif_iso, 'exif:iso'
|
307
|
+
attribute :exif_make, 'exif:make'
|
308
|
+
attribute :exif_model, 'exif:model'
|
309
|
+
attribute :exif_time, 'exif:time'
|
310
|
+
|
311
|
+
has_one :author, Objectify::Atom::Author, 'author'
|
312
|
+
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|