neves-ruby_picasa 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
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
+