mwilliams-fleakr 0.5.1
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/README.rdoc +350 -0
- data/Rakefile +41 -0
- data/lib/fleakr/api/file_parameter.rb +47 -0
- data/lib/fleakr/api/method_request.rb +66 -0
- data/lib/fleakr/api/option.rb +175 -0
- data/lib/fleakr/api/parameter.rb +35 -0
- data/lib/fleakr/api/parameter_list.rb +97 -0
- data/lib/fleakr/api/response.rb +35 -0
- data/lib/fleakr/api/upload_request.rb +75 -0
- data/lib/fleakr/api/value_parameter.rb +36 -0
- data/lib/fleakr/api.rb +8 -0
- data/lib/fleakr/core_ext/false_class.rb +7 -0
- data/lib/fleakr/core_ext/hash.rb +22 -0
- data/lib/fleakr/core_ext/true_class.rb +7 -0
- data/lib/fleakr/core_ext.rb +3 -0
- data/lib/fleakr/objects/authentication_token.rb +60 -0
- data/lib/fleakr/objects/comment.rb +49 -0
- data/lib/fleakr/objects/contact.rb +31 -0
- data/lib/fleakr/objects/error.rb +22 -0
- data/lib/fleakr/objects/group.rb +36 -0
- data/lib/fleakr/objects/image.rb +50 -0
- data/lib/fleakr/objects/photo.rb +147 -0
- data/lib/fleakr/objects/photo_context.rb +49 -0
- data/lib/fleakr/objects/search.rb +30 -0
- data/lib/fleakr/objects/set.rb +50 -0
- data/lib/fleakr/objects/tag.rb +56 -0
- data/lib/fleakr/objects/user.rb +95 -0
- data/lib/fleakr/objects.rb +12 -0
- data/lib/fleakr/support/attribute.rb +46 -0
- data/lib/fleakr/support/object.rb +110 -0
- data/lib/fleakr/support.rb +2 -0
- data/lib/fleakr/version.rb +13 -0
- data/lib/fleakr.rb +164 -0
- data/test/fixtures/auth.checkToken.xml +8 -0
- data/test/fixtures/auth.getFullToken.xml +8 -0
- data/test/fixtures/auth.getToken.xml +8 -0
- data/test/fixtures/contacts.getPublicList.xml +7 -0
- data/test/fixtures/groups.pools.getPhotos.xml +7 -0
- data/test/fixtures/people.findByEmail.xml +6 -0
- data/test/fixtures/people.findByUsername.xml +6 -0
- data/test/fixtures/people.getInfo.xml +18 -0
- data/test/fixtures/people.getPublicGroups.xml +7 -0
- data/test/fixtures/people.getPublicPhotos.xml +7 -0
- data/test/fixtures/photos.comments.getList.xml +7 -0
- data/test/fixtures/photos.getContext.xml +6 -0
- data/test/fixtures/photos.getInfo.xml +20 -0
- data/test/fixtures/photos.getSizes.xml +10 -0
- data/test/fixtures/photos.search.xml +7 -0
- data/test/fixtures/photosets.comments.getList.xml +7 -0
- data/test/fixtures/photosets.getList.xml +13 -0
- data/test/fixtures/photosets.getPhotos.xml +7 -0
- data/test/fixtures/tags.getListPhoto.xml +9 -0
- data/test/fixtures/tags.getListUser.xml +10 -0
- data/test/fixtures/tags.getRelated.xml +9 -0
- data/test/test_helper.rb +141 -0
- data/test/unit/fleakr/api/file_parameter_test.rb +63 -0
- data/test/unit/fleakr/api/method_request_test.rb +94 -0
- data/test/unit/fleakr/api/option_test.rb +179 -0
- data/test/unit/fleakr/api/parameter_list_test.rb +176 -0
- data/test/unit/fleakr/api/parameter_test.rb +34 -0
- data/test/unit/fleakr/api/response_test.rb +49 -0
- data/test/unit/fleakr/api/upload_request_test.rb +149 -0
- data/test/unit/fleakr/api/value_parameter_test.rb +41 -0
- data/test/unit/fleakr/core_ext/false_class_test.rb +13 -0
- data/test/unit/fleakr/core_ext/hash_test.rb +32 -0
- data/test/unit/fleakr/core_ext/true_class_test.rb +13 -0
- data/test/unit/fleakr/objects/authentication_token_test.rb +61 -0
- data/test/unit/fleakr/objects/comment_test.rb +66 -0
- data/test/unit/fleakr/objects/contact_test.rb +61 -0
- data/test/unit/fleakr/objects/error_test.rb +21 -0
- data/test/unit/fleakr/objects/group_test.rb +46 -0
- data/test/unit/fleakr/objects/image_test.rb +81 -0
- data/test/unit/fleakr/objects/photo_context_test.rb +80 -0
- data/test/unit/fleakr/objects/photo_test.rb +246 -0
- data/test/unit/fleakr/objects/search_test.rb +74 -0
- data/test/unit/fleakr/objects/set_test.rb +82 -0
- data/test/unit/fleakr/objects/tag_test.rb +98 -0
- data/test/unit/fleakr/objects/user_test.rb +91 -0
- data/test/unit/fleakr/support/attribute_test.rb +126 -0
- data/test/unit/fleakr/support/object_test.rb +129 -0
- data/test/unit/fleakr_test.rb +171 -0
- metadata +175 -0
data/README.rdoc
ADDED
@@ -0,0 +1,350 @@
|
|
1
|
+
= Fleakr
|
2
|
+
|
3
|
+
== Description
|
4
|
+
|
5
|
+
A small, yet powerful, gem to interface with Flickr photostreams
|
6
|
+
|
7
|
+
== Installation
|
8
|
+
|
9
|
+
=== Stable
|
10
|
+
|
11
|
+
sudo gem install fleakr
|
12
|
+
|
13
|
+
=== Bleeding Edge
|
14
|
+
|
15
|
+
sudo gem install reagent-fleakr --source=http://gems.github.com
|
16
|
+
|
17
|
+
Or ...
|
18
|
+
|
19
|
+
$ git clone git://github.com/reagent/fleakr.git
|
20
|
+
$ cd fleakr
|
21
|
+
$ rake gem && sudo gem install pkg/fleakr-<version>.gem
|
22
|
+
|
23
|
+
== Usage
|
24
|
+
|
25
|
+
To get started, you'll need to grab an API key from Flickr to at least perform any of
|
26
|
+
the non-authenticated, read-only calls. Head on over to the Flickr site to grab one, I'll
|
27
|
+
be here when you get back: http://www.flickr.com/services/api/misc.api_keys.html
|
28
|
+
|
29
|
+
Now that you have your key, you can get things rolling with irb and the fleakr gem:
|
30
|
+
|
31
|
+
$ irb -r rubygems
|
32
|
+
>> require 'fleakr'
|
33
|
+
|
34
|
+
Then, set your API key (only need to do this once per session):
|
35
|
+
|
36
|
+
>> Fleakr.api_key = '<your api key here>'
|
37
|
+
|
38
|
+
=== A Brief Tour
|
39
|
+
|
40
|
+
With just an API key, you have the ability to retrieve a substantial amount of data
|
41
|
+
about users, their photosets, photos, contacts, and groups. Let's start by finding a
|
42
|
+
user by his username:
|
43
|
+
|
44
|
+
>> user = Fleakr.user('the decapitator')
|
45
|
+
=> #<Fleakr::Objects::User:0x692648 @username="the decapitator", @id="21775151@N06">
|
46
|
+
|
47
|
+
Or by email:
|
48
|
+
|
49
|
+
>> user = Fleakr.user('user@host.com')
|
50
|
+
=> #<Fleakr::Objects::User:0x11f484c @username="bckspcr", @id="84481630@N00">
|
51
|
+
|
52
|
+
Once you have a user, you can find his associated sets:
|
53
|
+
|
54
|
+
>> user.sets
|
55
|
+
=> [#<Fleakr::Objects::Set:0x671358 @title="The Decapitator", @description="">,
|
56
|
+
#<Fleakr::Objects::Set:0x66d898 @title="londonpaper hijack", ...
|
57
|
+
|
58
|
+
His individual photos:
|
59
|
+
|
60
|
+
>> user.photos.first
|
61
|
+
=> #<Fleakr::Objects::Photo:0x161b024 @title="\"Be Fabulous\"" ... >
|
62
|
+
|
63
|
+
Or contacts:
|
64
|
+
|
65
|
+
>> user.contacts.first
|
66
|
+
=> #<Fleakr::Objects::User:0x19039bc @username=".schill",
|
67
|
+
@id="12289718@N00", @icon_farm="1", @icon_server="4">
|
68
|
+
|
69
|
+
Or his groups if you would like:
|
70
|
+
|
71
|
+
>> user.groups
|
72
|
+
=> [#<Fleakr::Objects::Group:0x11f2330 ...,
|
73
|
+
#<Fleakr::Objects::Group:0x11f2308 ...
|
74
|
+
>> user.groups.first.name
|
75
|
+
=> "Rural Decay"
|
76
|
+
>> user.groups.first.id
|
77
|
+
=> "14581414@N00"
|
78
|
+
|
79
|
+
Groups also contain photos:
|
80
|
+
|
81
|
+
>> user.groups.last.photos.first.title
|
82
|
+
=> "Welcome To The Machine"
|
83
|
+
|
84
|
+
When accessing a set, you can also grab all the photos that are in that set:
|
85
|
+
|
86
|
+
>> user.sets.first
|
87
|
+
=> #<Fleakr::Objects::Set:0x1195bbc @title="The Decapitator", @id="72157603480986566", @description="">
|
88
|
+
>> user.sets.first.photos.first
|
89
|
+
=> #<Fleakr::Objects::Photo:0x1140108 ... >
|
90
|
+
>> user.sets.first.photos.first.title
|
91
|
+
=> "Untitled1"
|
92
|
+
|
93
|
+
=== Photos
|
94
|
+
|
95
|
+
Each photo object contains metadata about a collection of images, each representing different
|
96
|
+
sizes. Once we have a single photo:
|
97
|
+
|
98
|
+
>> photo = user.photos.first
|
99
|
+
=> #<Fleakr::Objects::Photo:0x161b024 @title="\"Be Fabulous\"" ... >
|
100
|
+
|
101
|
+
We can get information about one of the sizes:
|
102
|
+
|
103
|
+
>> photo.small
|
104
|
+
=> #<Fleakr::Objects::Image:0x1768f1c @height="172", @size="Small", @width="240",
|
105
|
+
@url="http://farm4.static.flickr.com/3250/2924549350_cbc1804258_m.jpg",
|
106
|
+
@page="http://www.flickr.com/photos/the_decapitator/2924549350/sizes/s/">
|
107
|
+
|
108
|
+
Grab the URL for the image itself:
|
109
|
+
|
110
|
+
>> photo.small.url
|
111
|
+
=> "http://farm4.static.flickr.com/3250/2924549350_cbc1804258_m.jpg"
|
112
|
+
|
113
|
+
Or grab the URL for its page on the Flickr site:
|
114
|
+
|
115
|
+
>> photo.small.page
|
116
|
+
=> "http://www.flickr.com/photos/the_decapitator/2924549350/sizes/s/"
|
117
|
+
|
118
|
+
Other sizes are available (:square, :thumbnail, :small, :medium, :large, :original) and
|
119
|
+
are accessed in the same way:
|
120
|
+
|
121
|
+
>> photo.original.url
|
122
|
+
=> "http://farm4.static.flickr.com/3250/2924549350_1cf67c2d47_o.jpg"
|
123
|
+
|
124
|
+
=== Tags
|
125
|
+
|
126
|
+
Tags are available for users and photos. Retrieving them is easy:
|
127
|
+
|
128
|
+
>> user = Fleakr.user('the decapitator')
|
129
|
+
>> user.tags
|
130
|
+
=> [#<Fleakr::Objects::Tag:0x190d5fc @value="ad">,
|
131
|
+
#<Fleakr::Objects::Tag:0x1908a20 @value="ads">, ...
|
132
|
+
>> user.photos.first.tags
|
133
|
+
=> [#<Fleakr::Objects::Tag:0x17b1b18 @machine_flag="0", @author_id="21775151@N06", ...
|
134
|
+
|
135
|
+
All tags have values, but for tags associated with photos there is some additional information:
|
136
|
+
|
137
|
+
>> tag = user.photos.first.tags.first
|
138
|
+
>> tag.id
|
139
|
+
=> "21729829-3263659141-427098"
|
140
|
+
>> tag.raw
|
141
|
+
=> "decapitator"
|
142
|
+
>> tag.value
|
143
|
+
=> "decapitator"
|
144
|
+
>> tag.to_s
|
145
|
+
=> "decapitator"
|
146
|
+
>> tag.machine?
|
147
|
+
=> false
|
148
|
+
>> tag.author
|
149
|
+
=> #<Fleakr::Objects::User:0x1a149f0 @username="the decapitator", ... >
|
150
|
+
|
151
|
+
Each tag can also have related tags:
|
152
|
+
|
153
|
+
>> user.photos.first.tags[1].related.first.related.first.to_s
|
154
|
+
=> "face"
|
155
|
+
|
156
|
+
You get the idea - see Fleakr::Objects::Tag for more information.
|
157
|
+
|
158
|
+
=== Comments
|
159
|
+
|
160
|
+
Similar to tags, photosets and photos can each have comments:
|
161
|
+
|
162
|
+
>> user.sets.first.comments
|
163
|
+
=> [#<Fleakr::Objects::Comment:0x19795cc ...
|
164
|
+
>> user.photos.first.comments
|
165
|
+
=> [#<Fleakr::Objects::Comment:0x17bf0b0 @body="Dayum, that's some wishful thinking!" ...
|
166
|
+
|
167
|
+
All comments have additional information:
|
168
|
+
|
169
|
+
>> comment = user.photos.first.comments.first
|
170
|
+
>> comment.id
|
171
|
+
=> "21729829-3263659141-72157613553885978"
|
172
|
+
>> comment.body
|
173
|
+
=> "Dayum, that's some wishful thinking!"
|
174
|
+
>> comment.to_s
|
175
|
+
=> "Dayum, that's some wishful thinking!"
|
176
|
+
>> comment.url
|
177
|
+
=> "http://www.flickr.com/photos/the_decapitator/3263659141/#comment72157613553885978"
|
178
|
+
>> comment.author
|
179
|
+
=> #<Fleakr::Objects::User:0x178e3d4 @username="jaspertandy", ... >
|
180
|
+
|
181
|
+
See Fleakr::Objects::Comment for more information.
|
182
|
+
|
183
|
+
=== Saving Images
|
184
|
+
|
185
|
+
If a photo interests you, save it down to a directory of your choosing:
|
186
|
+
|
187
|
+
>> photo.original.save_to('/tmp')
|
188
|
+
=> #<File:/tmp/2924549350_1cf67c2d47_o.jpg (closed)>
|
189
|
+
|
190
|
+
Similarly, you can save down entire sets. Just specify the target directory and the size
|
191
|
+
of the images you're interested in:
|
192
|
+
|
193
|
+
>> user.sets.first.save_to('/tmp', :square)
|
194
|
+
=> [#<Fleakr::Objects::Photo:0x1187a1c @secret="715587b2cb" ...
|
195
|
+
|
196
|
+
This creates a subdirectory within the target directory based on the set's name and preserves
|
197
|
+
the original order of the photos:
|
198
|
+
|
199
|
+
>> Dir["/tmp/#{user.sets.first.title}/*.jpg"].map
|
200
|
+
=> ["/tmp/The Decapitator/01_2117922283_715587b2cb_s.jpg",
|
201
|
+
"/tmp/The Decapitator/02_2125604584_9c09348fd6_s.jpg",
|
202
|
+
"/tmp/The Decapitator/03_2118696542_8af5763bde_s.jpg", ... ]
|
203
|
+
|
204
|
+
=== Searching
|
205
|
+
|
206
|
+
If you would prefer to just search photos, you can do that with search text:
|
207
|
+
|
208
|
+
>> photos = Fleakr.search('ponies!!')
|
209
|
+
=> [#<Fleakr::Objects::Photo:0x11f4e64 @title="hiroshima atomic garden", @id="3078234390">,
|
210
|
+
#<Fleakr::Objects::Photo:0x11f4928 @title="PONYLOV", @id="3077360853">, ...
|
211
|
+
>> photos.first.title
|
212
|
+
=> "hiroshima atomic garden"
|
213
|
+
|
214
|
+
You can also search based on tags:
|
215
|
+
|
216
|
+
>> photos = Fleakr.search(:tags => 'macro')
|
217
|
+
>> photos.first.title
|
218
|
+
=> "Demure"
|
219
|
+
>> photos.first.id
|
220
|
+
=> "3076049945"
|
221
|
+
|
222
|
+
Searches can also be scoped to other entities in the system (namely Users and Groups):
|
223
|
+
|
224
|
+
>> user.groups.first.search('awesome')
|
225
|
+
=> [#<Fleakr::Objects::Photo:0x18cb4cc @server_id="2012", @id="2181921273",
|
226
|
+
@farm_id="3", @title="", @secret="634eda7521">, ...
|
227
|
+
>> user.search('serpent')
|
228
|
+
=> [#<Fleakr::Objects::Photo:0x18a6960 @server_id="41", @id="81370156",
|
229
|
+
@farm_id="1", @title="Clear and Serpent Danger", @secret="013091582a">]
|
230
|
+
|
231
|
+
=== Uploading Files
|
232
|
+
|
233
|
+
Before you can upload files, you need to be able to make authenticated calls to the Flickr
|
234
|
+
API. Skip to the next section (Authenticated Calls) for details on how to make this work.
|
235
|
+
|
236
|
+
Uploading single files is simple:
|
237
|
+
|
238
|
+
>> Fleakr.upload('/path/to/image.jpg')
|
239
|
+
=> [#<Fleakr::Objects::Photo:0x217fb54 @updated="1236133594", @server_id="3266", ...>]
|
240
|
+
|
241
|
+
Notice that the newly-uploaded image is returned. You can further inspect / modify this as
|
242
|
+
necessary. The real magic is in uploading multiple files - the upload method takes a file
|
243
|
+
glob:
|
244
|
+
|
245
|
+
>> Fleakr.upload('/path/to/images/*.jpg')
|
246
|
+
=> [#<Fleakr::Objects::Photo:0x217faa0 ...>,
|
247
|
+
#<Fleakr::Objects::Photo:0x212fb18 ...>,
|
248
|
+
#<Fleakr::Objects::Photo:0x20e09c8 ...>]
|
249
|
+
|
250
|
+
You can also set options on the file(s) that you're uploading:
|
251
|
+
|
252
|
+
>> Fleakr.upload('/path/to/party/images/*.jpg', :viewable_by => :everyone,
|
253
|
+
:title => 'Party Pics')
|
254
|
+
|
255
|
+
The full list of options can be found in the Fleakr::Objects::Photo documentation.
|
256
|
+
|
257
|
+
=== Authenticated Calls
|
258
|
+
|
259
|
+
While read-only access to the API gets you quite a bit of data, you'll need to generate an
|
260
|
+
authentication token if you want access to the more powerful features (like uploading your
|
261
|
+
own photos).
|
262
|
+
|
263
|
+
Assuming you've already applied for a key, go back and make sure you have the right settings
|
264
|
+
to get your auth token. Click on the 'Edit key details' link and ensure that:
|
265
|
+
|
266
|
+
1. Your application description and notes are up-to-date
|
267
|
+
1. The value for 'Authentication Type' is set to 'Mobile Application'
|
268
|
+
1. The value for 'Mobile Permissions' is set to either 'write' or 'delete'
|
269
|
+
|
270
|
+
Once this is set, you'll see your Authentication URL on the key details page (it will look
|
271
|
+
something like http://www.flickr.com/auth-534525246245). Paste this URL into your browser and
|
272
|
+
confirm access to get your mini-token. Now you're ready to make authenticated requests:
|
273
|
+
|
274
|
+
require 'rubygems'
|
275
|
+
require 'fleakr'
|
276
|
+
|
277
|
+
Fleakr.api_key = 'ABC123'
|
278
|
+
Fleakr.shared_secret = 'sekrit' # Available with your key details on the Flickr site
|
279
|
+
Fleakr.mini_token = '362-133-214'
|
280
|
+
|
281
|
+
Fleakr.upload('/path/to/my/photo.jpg')
|
282
|
+
Fleakr.token.value # => "34132412341235-12341234ef34"
|
283
|
+
|
284
|
+
Once you use the mini-token once, it is no longer available. To use the generated auth_token
|
285
|
+
for future requests, just set Fleakr.auth_token to the generated value. Similarly, if you have
|
286
|
+
an authenticated frob from Flickr (using authentication for desktop applications, for example)
|
287
|
+
you can also set <tt>Fleakr.frob</tt> to the frob value returned from the API.
|
288
|
+
|
289
|
+
=== What Went Wrong?
|
290
|
+
|
291
|
+
Because so much of the underlying API is hidden under the covers, it's often tough to know
|
292
|
+
what's really going on when you run into unexpected behavior. To make things easier, you can
|
293
|
+
have Fleakr log all of the API traffic. Here's how:
|
294
|
+
|
295
|
+
Fleakr.logger = Logger.new('/tmp/fleakr.log')
|
296
|
+
|
297
|
+
Now any calls to the API will log both their request and response data to that file. But be
|
298
|
+
warned, this can be pretty verbose by default (especially if you're doing file uploads). To see
|
299
|
+
just the requests you need to tune the log level:
|
300
|
+
|
301
|
+
logger = Logger.new('/tmp/fleakr.log')
|
302
|
+
logger.level = Logger::INFO
|
303
|
+
|
304
|
+
Fleakr.logger = logger
|
305
|
+
|
306
|
+
Even if something doesn't go wrong, this is a good way to get a sense for when you're making
|
307
|
+
API requests.
|
308
|
+
|
309
|
+
== Roadmap / TODO
|
310
|
+
|
311
|
+
=== 0.4.x
|
312
|
+
|
313
|
+
* Implement remaining bits of person and photo-related API calls (read-only)
|
314
|
+
|
315
|
+
=== 0.5.x
|
316
|
+
|
317
|
+
* Implement asynchronous file upload / replacement w/ ticket checking
|
318
|
+
* Provide a better searching interface with ruby-like option syntax
|
319
|
+
|
320
|
+
=== Future
|
321
|
+
|
322
|
+
* Implement save-able search results (e.g. Fleakr.search('ponies').save_to('/path', :medium))
|
323
|
+
* Implement deeper associations for core elements (e.g. tags / etc..)
|
324
|
+
* Implement write methods for photos & photosets
|
325
|
+
* Implement flickr.places.* portion of API
|
326
|
+
|
327
|
+
== License
|
328
|
+
|
329
|
+
Copyright (c) 2008 Patrick Reagan (reaganpr@gmail.com)
|
330
|
+
|
331
|
+
Permission is hereby granted, free of charge, to any person
|
332
|
+
obtaining a copy of this software and associated documentation
|
333
|
+
files (the "Software"), to deal in the Software without
|
334
|
+
restriction, including without limitation the rights to use,
|
335
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
336
|
+
copies of the Software, and to permit persons to whom the
|
337
|
+
Software is furnished to do so, subject to the following
|
338
|
+
conditions:
|
339
|
+
|
340
|
+
The above copyright notice and this permission notice shall be
|
341
|
+
included in all copies or substantial portions of the Software.
|
342
|
+
|
343
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
344
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
345
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
346
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
347
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
348
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
349
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
350
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
require 'lib/fleakr/version'
|
6
|
+
|
7
|
+
task :default => :test
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
s.name = 'fleakr'
|
11
|
+
s.version = Fleakr::Version.to_s
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.extra_rdoc_files = %w(README.rdoc)
|
14
|
+
s.rdoc_options = %w(--main README.rdoc)
|
15
|
+
s.summary = "A small, yet powerful, gem to interface with Flickr photostreams"
|
16
|
+
s.author = 'Patrick Reagan'
|
17
|
+
s.email = 'reaganpr@gmail.com'
|
18
|
+
s.homepage = 'http://sneaq.net'
|
19
|
+
s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
|
20
|
+
|
21
|
+
s.add_dependency('hpricot', '~> 0.8.1')
|
22
|
+
s.add_dependency('activesupport', '~> 2.0')
|
23
|
+
s.add_dependency('loggable', '~> 0.2.0')
|
24
|
+
end
|
25
|
+
|
26
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
27
|
+
pkg.gem_spec = spec
|
28
|
+
end
|
29
|
+
|
30
|
+
Rake::TestTask.new do |t|
|
31
|
+
t.libs << 'test'
|
32
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
33
|
+
t.verbose = true
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Generate the gemspec to serve this Gem from Github'
|
37
|
+
task :github do
|
38
|
+
file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
|
39
|
+
File.open(file, 'w') {|f| f << spec.to_ruby }
|
40
|
+
puts "Created gemspec: #{file}"
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = FileParameter
|
5
|
+
#
|
6
|
+
# Parameter class to encapsulate file data sent to the Flickr upload API
|
7
|
+
#
|
8
|
+
class FileParameter < Parameter
|
9
|
+
|
10
|
+
MIME_TYPES = {
|
11
|
+
'.jpg' => 'image/jpeg',
|
12
|
+
'.png' => 'image/png',
|
13
|
+
'.gif' => 'image/gif'
|
14
|
+
}
|
15
|
+
|
16
|
+
# Create a parameter with name and specified filename
|
17
|
+
#
|
18
|
+
def initialize(name, filename)
|
19
|
+
@filename = filename
|
20
|
+
super(name, false)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Discover MIME type by file extension using MIME_TYPES constant
|
24
|
+
#
|
25
|
+
def mime_type
|
26
|
+
MIME_TYPES[File.extname(@filename)]
|
27
|
+
end
|
28
|
+
|
29
|
+
# File data (from @filename) to pass to the Flickr API
|
30
|
+
#
|
31
|
+
def value
|
32
|
+
@value ||= File.read(@filename)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generate a form representation of this file for upload (as multipart/form-data)
|
36
|
+
#
|
37
|
+
def to_form
|
38
|
+
"Content-Disposition: form-data; name=\"#{self.name}\"; filename=\"#{@filename}\"\r\n" +
|
39
|
+
"Content-Type: #{self.mime_type}\r\n" +
|
40
|
+
"\r\n" +
|
41
|
+
"#{self.value}\r\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = MethodRequest
|
5
|
+
#
|
6
|
+
# Handles all API requests that are non-upload related. For upload requests see the
|
7
|
+
# UploadRequest class.
|
8
|
+
#
|
9
|
+
class MethodRequest
|
10
|
+
attr_reader :parameters, :method
|
11
|
+
|
12
|
+
# Makes a request to the Flickr API and returns a valid Response object. If
|
13
|
+
# there are errors on the response it will raise an ApiError exception. See
|
14
|
+
# MethodRequest#new for details about the additional parameters
|
15
|
+
#
|
16
|
+
def self.with_response!(method, additional_parameters = {})
|
17
|
+
request = self.new(method, additional_parameters)
|
18
|
+
response = request.send
|
19
|
+
|
20
|
+
raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
21
|
+
|
22
|
+
response
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create a new request for the specified API method and pass along any additional
|
26
|
+
# parameters. The Flickr API uses namespacing for its methods - this is optional
|
27
|
+
# when calling this method.
|
28
|
+
#
|
29
|
+
# This must be called after initializing the library with the required API key
|
30
|
+
# see (#Fleakr.api_key=)
|
31
|
+
#
|
32
|
+
# The <tt>additional_parameters</tt> is a list of parameters to pass directly to
|
33
|
+
# the Flickr API call. The exception to this is the <tt>:authenticate?</tt> option
|
34
|
+
# that will force the call to not be authenticated if it is set to false (The default
|
35
|
+
# behavior is to authenticate all calls when we have a token).
|
36
|
+
#
|
37
|
+
def initialize(method, additional_parameters = {})
|
38
|
+
@parameters = ParameterList.new(additional_parameters)
|
39
|
+
|
40
|
+
self.method = method
|
41
|
+
end
|
42
|
+
|
43
|
+
def method=(method) # :nodoc:
|
44
|
+
@method = method.sub(/^(flickr\.)?/, 'flickr.')
|
45
|
+
@parameters << ValueParameter.new('method', @method)
|
46
|
+
end
|
47
|
+
|
48
|
+
def send # :nodoc:
|
49
|
+
logger.info("Sending request to: #{endpoint_uri}")
|
50
|
+
response_xml = Net::HTTP.get(endpoint_uri)
|
51
|
+
logger.debug("Response data:\n#{response_xml}")
|
52
|
+
|
53
|
+
Response.new(response_xml)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def endpoint_uri
|
58
|
+
uri = URI.parse('http://api.flickr.com/services/rest/')
|
59
|
+
uri.query = self.parameters.to_query
|
60
|
+
uri
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = Option
|
5
|
+
#
|
6
|
+
# Top-level class for creating specific option instances based on type
|
7
|
+
#
|
8
|
+
class Option
|
9
|
+
|
10
|
+
MAPPING = {
|
11
|
+
:tags => 'TagOption',
|
12
|
+
:viewable_by => 'ViewOption',
|
13
|
+
:level => 'LevelOption',
|
14
|
+
:type => 'TypeOption',
|
15
|
+
:hide? => 'HiddenOption'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Initialize a new option for the specified type and value
|
19
|
+
#
|
20
|
+
def self.for(type, value)
|
21
|
+
class_for(type).new(type, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.class_for(type) # :nodoc:
|
25
|
+
class_name = MAPPING[type] || 'SimpleOption'
|
26
|
+
"Fleakr::Api::#{class_name}".constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# = SimpleOption
|
32
|
+
#
|
33
|
+
# Simple name / value option pair
|
34
|
+
#
|
35
|
+
class SimpleOption
|
36
|
+
|
37
|
+
attr_reader :type, :value
|
38
|
+
|
39
|
+
# Create an option of the specified type and value
|
40
|
+
#
|
41
|
+
def initialize(type, value)
|
42
|
+
@type = type
|
43
|
+
@value = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate hash representation of this option
|
47
|
+
#
|
48
|
+
def to_hash
|
49
|
+
{type => value}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# = TagOption
|
55
|
+
#
|
56
|
+
# Represents values for tags
|
57
|
+
#
|
58
|
+
class TagOption < SimpleOption
|
59
|
+
|
60
|
+
# Tag with specified values. Value passed will be converted to an array if it isn't
|
61
|
+
# already
|
62
|
+
#
|
63
|
+
def initialize(type, value)
|
64
|
+
super type, value
|
65
|
+
@value = Array(self.value)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Hash representation of tag values (separated by spaces). Handles multi-word tags
|
69
|
+
# by enclosing each tag in double quotes.
|
70
|
+
#
|
71
|
+
def to_hash
|
72
|
+
tags = value.map {|tag| "\"#{tag}\"" }
|
73
|
+
{type => tags.join(' ')}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# = ViewOption
|
79
|
+
#
|
80
|
+
# Specify who is able to view the photo.
|
81
|
+
#
|
82
|
+
class ViewOption < SimpleOption
|
83
|
+
|
84
|
+
# Specify who this is viewable by (e.g. everyone / friends / family).
|
85
|
+
#
|
86
|
+
def initialize(type, value)
|
87
|
+
super type, Array(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Is this publicly viewable? (i.e. :everyone)
|
91
|
+
#
|
92
|
+
def public?
|
93
|
+
value == [:everyone]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is this viewable by friends? (i.e. :friends)
|
97
|
+
#
|
98
|
+
def friends?
|
99
|
+
value.include?(:friends)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Is this viewable by family? (i.e. :family)
|
103
|
+
#
|
104
|
+
def family?
|
105
|
+
value.include?(:family)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Hash representation of photo permissions
|
109
|
+
#
|
110
|
+
def to_hash
|
111
|
+
{:is_public => public?.to_i, :is_friend => friends?.to_i, :is_family => family?.to_i}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# = LevelOption
|
117
|
+
#
|
118
|
+
# Specify the "safety level" of this photo (e.g. safe / moderate / restricted)
|
119
|
+
#
|
120
|
+
class LevelOption < SimpleOption
|
121
|
+
|
122
|
+
def value # :nodoc:
|
123
|
+
case @value
|
124
|
+
when :safe then 1
|
125
|
+
when :moderate then 2
|
126
|
+
when :restricted then 3
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Hash representation of the safety_level for this photo
|
131
|
+
#
|
132
|
+
def to_hash
|
133
|
+
{:safety_level => value}
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
# = TypeOption
|
139
|
+
#
|
140
|
+
# Specify the type of this photo (e.g. photo / screenshot / other)
|
141
|
+
#
|
142
|
+
class TypeOption < SimpleOption
|
143
|
+
|
144
|
+
def value # :nodoc:
|
145
|
+
case @value
|
146
|
+
when :photo then 1
|
147
|
+
when :screenshot then 2
|
148
|
+
when :other then 3
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Hash representation of this type
|
153
|
+
#
|
154
|
+
def to_hash
|
155
|
+
{:content_type => value}
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
# = HiddenOption
|
161
|
+
#
|
162
|
+
# Specify whether this photo should be hidden from search
|
163
|
+
#
|
164
|
+
class HiddenOption < SimpleOption
|
165
|
+
|
166
|
+
# Hash representation of whether to hide this photo
|
167
|
+
#
|
168
|
+
def to_hash
|
169
|
+
{:hidden => (value ? 2 : 1)}
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|