fleakr 0.3.0 → 0.4.0
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 +114 -19
- data/Rakefile +1 -1
- data/lib/fleakr/api/file_parameter.rb +47 -0
- data/lib/fleakr/api/method_request.rb +57 -0
- data/lib/fleakr/api/parameter.rb +35 -0
- data/lib/fleakr/api/parameter_list.rb +96 -0
- data/lib/fleakr/api/response.rb +2 -2
- data/lib/fleakr/api/upload_request.rb +64 -0
- data/lib/fleakr/api/value_parameter.rb +36 -0
- data/lib/fleakr/api.rb +7 -0
- data/lib/fleakr/core_ext/hash.rb +22 -0
- data/lib/fleakr/core_ext.rb +1 -0
- data/lib/fleakr/objects/authentication_token.rb +43 -0
- data/lib/fleakr/objects/contact.rb +5 -5
- data/lib/fleakr/objects/error.rb +2 -2
- data/lib/fleakr/objects/group.rb +2 -2
- data/lib/fleakr/objects/image.rb +7 -7
- data/lib/fleakr/objects/photo.rb +69 -5
- data/lib/fleakr/objects/search.rb +3 -6
- data/lib/fleakr/objects/set.rb +11 -5
- data/lib/fleakr/objects/user.rb +14 -26
- data/lib/fleakr/objects.rb +9 -0
- data/lib/fleakr/support/attribute.rb +30 -12
- data/lib/fleakr/support/object.rb +20 -4
- data/lib/fleakr/support.rb +2 -0
- data/lib/fleakr/version.rb +1 -1
- data/lib/fleakr.rb +66 -7
- data/test/fixtures/auth.checkToken.xml +8 -0
- data/test/fixtures/auth.getFullToken.xml +8 -0
- data/test/fixtures/people.getInfo.xml +1 -1
- data/test/fixtures/photos.getInfo.xml +20 -0
- data/test/test_helper.rb +18 -3
- data/test/unit/fleakr/api/file_parameter_test.rb +63 -0
- data/test/unit/fleakr/api/method_request_test.rb +103 -0
- data/test/unit/fleakr/api/parameter_list_test.rb +161 -0
- data/test/unit/fleakr/api/parameter_test.rb +34 -0
- data/test/unit/fleakr/api/upload_request_test.rb +133 -0
- data/test/unit/fleakr/api/value_parameter_test.rb +41 -0
- data/test/unit/fleakr/core_ext/hash_test.rb +32 -0
- data/test/unit/fleakr/objects/authentication_token_test.rb +47 -0
- data/test/unit/fleakr/objects/image_test.rb +10 -5
- data/test/unit/fleakr/objects/photo_test.rb +96 -36
- data/test/unit/fleakr/objects/search_test.rb +1 -1
- data/test/unit/fleakr/objects/set_test.rb +12 -1
- data/test/unit/fleakr/objects/user_test.rb +2 -16
- data/test/unit/fleakr/support/attribute_test.rb +82 -24
- data/test/unit/fleakr/support/object_test.rb +26 -3
- data/test/unit/fleakr_test.rb +65 -6
- metadata +28 -5
- data/lib/fleakr/api/request.rb +0 -58
- data/test/unit/fleakr/api/request_test.rb +0 -93
data/README.rdoc
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
== Description
|
4
4
|
|
5
|
-
A
|
5
|
+
A small, yet powerful, gem to interface with Flickr photostreams
|
6
6
|
|
7
7
|
== Installation
|
8
8
|
|
@@ -22,16 +22,24 @@ Or ...
|
|
22
22
|
|
23
23
|
== Usage
|
24
24
|
|
25
|
-
|
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
|
26
28
|
|
27
|
-
|
28
|
-
>> require 'fleakr'
|
29
|
+
Now that you have your key, you can get things rolling with irb and the fleakr gem:
|
29
30
|
|
31
|
+
$ irb -r rubygems
|
32
|
+
>> require 'fleakr'
|
33
|
+
|
30
34
|
Then, set your API key (only need to do this once per session):
|
31
35
|
|
32
36
|
>> Fleakr.api_key = '<your api key here>'
|
33
|
-
|
34
|
-
|
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:
|
35
43
|
|
36
44
|
>> user = Fleakr.user('the decapitator')
|
37
45
|
=> #<Fleakr::Objects::User:0x692648 @username="the decapitator", @id="21775151@N06">
|
@@ -47,13 +55,18 @@ Once you have a user, you can find his associated sets:
|
|
47
55
|
=> [#<Fleakr::Objects::Set:0x671358 @title="The Decapitator", @description="">,
|
48
56
|
#<Fleakr::Objects::Set:0x66d898 @title="londonpaper hijack", ...
|
49
57
|
|
58
|
+
His individual photos:
|
59
|
+
|
60
|
+
>> user.photos.first
|
61
|
+
=> #<Fleakr::Objects::Photo:0x161b024 @title="\"Be Fabulous\"" ... >
|
62
|
+
|
50
63
|
Or contacts:
|
51
64
|
|
52
65
|
>> user.contacts.first
|
53
66
|
=> #<Fleakr::Objects::User:0x19039bc @username=".schill",
|
54
67
|
@id="12289718@N00", @icon_farm="1", @icon_server="4">
|
55
68
|
|
56
|
-
Or groups if you would like:
|
69
|
+
Or his groups if you would like:
|
57
70
|
|
58
71
|
>> user.groups
|
59
72
|
=> [#<Fleakr::Objects::Group:0x11f2330 ...,
|
@@ -77,20 +90,59 @@ When accessing a set, you can also grab all the photos that are in that set:
|
|
77
90
|
>> user.sets.first.photos.first.title
|
78
91
|
=> "Untitled1"
|
79
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
|
+
=== Saving Images
|
125
|
+
|
80
126
|
If a photo interests you, save it down to a directory of your choosing:
|
81
127
|
|
82
|
-
>>
|
83
|
-
=> #<File:/tmp/
|
128
|
+
>> photo.original.save_to('/tmp')
|
129
|
+
=> #<File:/tmp/2924549350_1cf67c2d47_o.jpg (closed)>
|
84
130
|
|
85
|
-
|
86
|
-
|
131
|
+
Similarly, you can save down entire sets. Just specify the target directory and the size
|
132
|
+
of the images you're interested in:
|
87
133
|
|
88
134
|
>> user.sets.first.save_to('/tmp', :square)
|
89
135
|
=> [#<Fleakr::Objects::Photo:0x1187a1c @secret="715587b2cb" ...
|
136
|
+
|
137
|
+
This creates a subdirectory within the target directory based on the set's name and preserves
|
138
|
+
the original order of the photos:
|
139
|
+
|
90
140
|
>> Dir["/tmp/#{user.sets.first.title}/*.jpg"].map
|
91
|
-
=> ["/tmp/The Decapitator/
|
92
|
-
"/tmp/The Decapitator/
|
93
|
-
"/tmp/The Decapitator/
|
141
|
+
=> ["/tmp/The Decapitator/01_2117922283_715587b2cb_s.jpg",
|
142
|
+
"/tmp/The Decapitator/02_2125604584_9c09348fd6_s.jpg",
|
143
|
+
"/tmp/The Decapitator/03_2118696542_8af5763bde_s.jpg", ... ]
|
144
|
+
|
145
|
+
=== Searching
|
94
146
|
|
95
147
|
If you would prefer to just search photos, you can do that with search text:
|
96
148
|
|
@@ -116,13 +168,56 @@ Searches can also be scoped to other entities in the system (namely Users and Gr
|
|
116
168
|
>> user.search('serpent')
|
117
169
|
=> [#<Fleakr::Objects::Photo:0x18a6960 @server_id="41", @id="81370156",
|
118
170
|
@farm_id="1", @title="Clear and Serpent Danger", @secret="013091582a">]
|
119
|
-
|
120
|
-
== TODO
|
121
171
|
|
122
|
-
|
172
|
+
=== Authenticated Calls & Uploads
|
173
|
+
|
174
|
+
While read-only access to the API gets you quite a bit of data, you'll need to generate an
|
175
|
+
authentication token if you want access to the more powerful features (like uploading your
|
176
|
+
own photos).
|
177
|
+
|
178
|
+
Assuming you've already applied for a key, go back and make sure you have the right settings
|
179
|
+
to get your auth token. Click on the 'Edit key details' link and ensure that:
|
180
|
+
|
181
|
+
1. Your application description and notes are up-to-date
|
182
|
+
1. The value for 'Authentication Type' is set to 'Mobile Application'
|
183
|
+
1. The value for 'Mobile Permissions' is set to either 'write' or 'delete'
|
184
|
+
|
185
|
+
Once this is set, you'll see your Authentication URL on the key details page (it will look
|
186
|
+
something like http://www.flickr.com/auth-534525246245). Paste this URL into your browser and
|
187
|
+
confirm access to get your mini-token. Now you're ready to make authenticated requests:
|
188
|
+
|
189
|
+
require 'rubygems'
|
190
|
+
require 'fleakr'
|
191
|
+
|
192
|
+
Fleakr.api_key = 'ABC123'
|
193
|
+
Fleakr.shared_secret = 'sekrit' # Available with your key details on the Flickr site
|
194
|
+
Fleakr.mini_token = '362-133-214'
|
195
|
+
|
196
|
+
Fleakr.upload('/path/to/my/photo.jpg')
|
197
|
+
Fleakr.token.value # => "34132412341235-12341234ef34"
|
198
|
+
|
199
|
+
Once you use the mini-token once, it is no longer available. To use the generated auth_token
|
200
|
+
for future requests, just set Fleakr.auth_token to the generated value.
|
201
|
+
|
202
|
+
== Roadmap / TODO
|
203
|
+
|
204
|
+
=== 0.4.x
|
205
|
+
|
206
|
+
* Allow passing of parameters to file uploads to allow for access control / naming
|
207
|
+
* Implement remaining bits of person and photo-related API calls (read-only)
|
208
|
+
* Automatically sign all calls (if we have a secret), authenticate all calls (if we have a token)
|
209
|
+
|
210
|
+
=== 0.5.x
|
211
|
+
|
212
|
+
* Implement asynchronous file upload / replacement w/ ticket checking
|
123
213
|
* Provide a better searching interface
|
124
|
-
|
125
|
-
|
214
|
+
|
215
|
+
=== Future
|
216
|
+
|
217
|
+
* Implement save-able search results (e.g. Fleakr.search('ponies').save_to('/path', :medium))
|
218
|
+
* Implement deeper associations for core elements (e.g. tags / etc..)
|
219
|
+
* Implement write methods for photos & photosets
|
220
|
+
|
126
221
|
== License
|
127
222
|
|
128
223
|
Copyright (c) 2008 Patrick Reagan (reaganpr@gmail.com)
|
data/Rakefile
CHANGED
@@ -12,7 +12,7 @@ spec = Gem::Specification.new do |s|
|
|
12
12
|
s.has_rdoc = true
|
13
13
|
s.extra_rdoc_files = %w(README.rdoc)
|
14
14
|
s.rdoc_options = %w(--main README.rdoc)
|
15
|
-
s.summary = "A
|
15
|
+
s.summary = "A small, yet powerful, gem to interface with Flickr photostreams"
|
16
16
|
s.author = 'Patrick Reagan'
|
17
17
|
s.email = 'reaganpr@gmail.com'
|
18
18
|
s.homepage = 'http://sneaq.net'
|
@@ -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,57 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
class MethodRequest
|
5
|
+
attr_reader :parameters, :method
|
6
|
+
|
7
|
+
# Makes a request to the Flickr API and returns a valid Response object. If
|
8
|
+
# there are errors on the response it will raise an ApiError exception. See
|
9
|
+
# #Fleakr::Api::MethodRequest.new for details about the additional parameters
|
10
|
+
#
|
11
|
+
def self.with_response!(method, additional_parameters = {})
|
12
|
+
request = self.new(method, additional_parameters)
|
13
|
+
response = request.send
|
14
|
+
|
15
|
+
raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
16
|
+
|
17
|
+
response
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a new request for the specified API method and pass along any additional
|
21
|
+
# parameters. The Flickr API uses namespacing for its methods - this is optional
|
22
|
+
# when calling this method.
|
23
|
+
#
|
24
|
+
# This must be called after initializing the library with the required API key
|
25
|
+
# see (#Fleakr.api_key=)
|
26
|
+
#
|
27
|
+
# The <tt>additional_parameters</tt> is a list of parameters to pass directly to
|
28
|
+
# the Flickr API call. Exceptions to this are the <tt>:sign?</tt> and
|
29
|
+
# <tt>:authenticate?</tt> options that determine if the call should be signed or
|
30
|
+
# authenticated.
|
31
|
+
#
|
32
|
+
def initialize(method, additional_parameters = {})
|
33
|
+
@parameters = ParameterList.new(additional_parameters)
|
34
|
+
|
35
|
+
self.method = method
|
36
|
+
end
|
37
|
+
|
38
|
+
def method=(method) # :nodoc:
|
39
|
+
@method = method.sub(/^(flickr\.)?/, 'flickr.')
|
40
|
+
@parameters << ValueParameter.new('method', @method)
|
41
|
+
end
|
42
|
+
|
43
|
+
def send # :nodoc:
|
44
|
+
Response.new(Net::HTTP.get(endpoint_uri))
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def endpoint_uri
|
49
|
+
uri = URI.parse('http://api.flickr.com/services/rest/')
|
50
|
+
uri.query = self.parameters.to_query
|
51
|
+
uri
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = Parameter
|
5
|
+
#
|
6
|
+
# Base class for other parameters that get passed to the Flickr API - see
|
7
|
+
# #FileParameter and #ValueParameter for examples
|
8
|
+
#
|
9
|
+
class Parameter
|
10
|
+
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# A new named parameter (never used directly)
|
14
|
+
#
|
15
|
+
def initialize(name, include_in_signature = true)
|
16
|
+
@name = name
|
17
|
+
@include_in_signature = include_in_signature
|
18
|
+
end
|
19
|
+
|
20
|
+
# Should this parameter be used when generating the signature?
|
21
|
+
#
|
22
|
+
def include_in_signature?
|
23
|
+
(@include_in_signature == true) ? true : false
|
24
|
+
end
|
25
|
+
|
26
|
+
# Used for sorting when generating a signature
|
27
|
+
#
|
28
|
+
def <=>(other)
|
29
|
+
self.name <=> other.name
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = ParameterList
|
5
|
+
#
|
6
|
+
# Represents a list of parameters that get passed as part of a
|
7
|
+
# MethodRequest or UploadRequest. These can be transformed as necessary
|
8
|
+
# into query strings (using #to_query) or form data (using #to_form)
|
9
|
+
#
|
10
|
+
class ParameterList
|
11
|
+
|
12
|
+
# Create a new parameter list with optional parameters:
|
13
|
+
# [:sign?] Will these parameters be used to sign the request?
|
14
|
+
# [:authenticate?] Will the request need to be authenticated?
|
15
|
+
#
|
16
|
+
# Any additional name / value pairs will be created as individual
|
17
|
+
# ValueParameters as part of the list. Example:
|
18
|
+
#
|
19
|
+
# >> list = Fleakr::Api::ParameterList.new(:foo => 'bar')
|
20
|
+
# => #<Fleakr::Api::ParameterList:0x1656e6c @list=... >
|
21
|
+
# >> list[:foo]
|
22
|
+
# => #<Fleakr::Api::ValueParameter:0x1656da4 @include_in_signature=true, @name="foo", @value="bar">
|
23
|
+
#
|
24
|
+
def initialize(options = {})
|
25
|
+
@api_options = options.extract!(:sign?, :authenticate?)
|
26
|
+
|
27
|
+
@list = Hash.new
|
28
|
+
|
29
|
+
options.each {|k,v| self << ValueParameter.new(k.to_s, v) }
|
30
|
+
|
31
|
+
self << ValueParameter.new('api_key', Fleakr.api_key)
|
32
|
+
self << ValueParameter.new('auth_token', Fleakr.token.value) if authenticate?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Add a new parameter (ValueParameter / FileParameter) to the list
|
36
|
+
#
|
37
|
+
def <<(parameter)
|
38
|
+
@list.merge!(parameter.name => parameter)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Should this parameter list be signed?
|
42
|
+
#
|
43
|
+
def sign?
|
44
|
+
(@api_options[:sign?] == true || authenticate?) ? true : false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Should we send the auth_token with the request?
|
48
|
+
#
|
49
|
+
def authenticate?
|
50
|
+
(@api_options[:authenticate?] == true) ? true : false
|
51
|
+
end
|
52
|
+
|
53
|
+
# Access an individual parameter by key (symbol or string)
|
54
|
+
#
|
55
|
+
def [](key)
|
56
|
+
list[key.to_s]
|
57
|
+
end
|
58
|
+
|
59
|
+
def boundary # :nodoc:
|
60
|
+
@boundary ||= Digest::MD5.hexdigest(rand.to_s)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Generate the query string representation of this parameter
|
64
|
+
# list - e.g. <tt>foo=bar&blee=baz</tt>
|
65
|
+
#
|
66
|
+
def to_query
|
67
|
+
list.values.map(&:to_query).join('&')
|
68
|
+
end
|
69
|
+
|
70
|
+
# Generate the form representation of this parameter list including the
|
71
|
+
# boundary
|
72
|
+
#
|
73
|
+
def to_form
|
74
|
+
form = list.values.map {|p| "--#{self.boundary}\r\n#{p.to_form}" }.join
|
75
|
+
form << "--#{self.boundary}--"
|
76
|
+
|
77
|
+
form
|
78
|
+
end
|
79
|
+
|
80
|
+
def signature # :nodoc:
|
81
|
+
parameters_to_sign = @list.values.reject {|p| !p.include_in_signature? }
|
82
|
+
signature_text = parameters_to_sign.sort.map {|p| "#{p.name}#{p.value}" }.join
|
83
|
+
|
84
|
+
Digest::MD5.hexdigest("#{Fleakr.shared_secret}#{signature_text}")
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def list
|
89
|
+
list = @list
|
90
|
+
list.merge!('api_sig' => ValueParameter.new('api_sig', signature, false)) if self.sign?
|
91
|
+
|
92
|
+
list
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/fleakr/api/response.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Fleakr
|
2
|
-
module Api
|
2
|
+
module Api # :nodoc:
|
3
3
|
|
4
4
|
# = Response
|
5
5
|
#
|
6
6
|
# Response objects contain Hpricot documents that are traversed and parsed by
|
7
7
|
# the model objects. This class is never called directly but is instantiated
|
8
|
-
# during the request cycle (see: Fleakr::Api::
|
8
|
+
# during the request cycle (see: Fleakr::Api::MethodRequest.with_response!)
|
9
9
|
#
|
10
10
|
class Response
|
11
11
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = UploadRequest
|
5
|
+
#
|
6
|
+
# This implements the upload functionality of the Flickr API which is needed
|
7
|
+
# to create new photos and replace the photo content of existing photos
|
8
|
+
#
|
9
|
+
class UploadRequest
|
10
|
+
|
11
|
+
ENDPOINT_URIS = {
|
12
|
+
:create => 'http://api.flickr.com/services/upload/',
|
13
|
+
:update => 'http://api.flickr.com/services/replace/'
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_reader :parameters, :type
|
17
|
+
|
18
|
+
# Send a request and return a Response object. If an API error occurs, this raises
|
19
|
+
# a Fleakr::ApiError with the reason for the error. See UploadRequest#new for more
|
20
|
+
# details.
|
21
|
+
#
|
22
|
+
def self.with_response!(filename, options = {})
|
23
|
+
request = self.new(filename, options)
|
24
|
+
response = request.send
|
25
|
+
|
26
|
+
raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
27
|
+
|
28
|
+
response
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create a new UploadRequest with the specified filename and options:
|
32
|
+
#
|
33
|
+
# [:type] Valid values are :create and :update and are used when uploading new
|
34
|
+
# photos or replacing existing ones
|
35
|
+
#
|
36
|
+
def initialize(filename, options = {})
|
37
|
+
type_options = options.extract!(:type)
|
38
|
+
options.merge!(:authenticate? => true)
|
39
|
+
|
40
|
+
@type = type_options[:type] || :create
|
41
|
+
|
42
|
+
@parameters = ParameterList.new(options)
|
43
|
+
@parameters << FileParameter.new('photo', filename)
|
44
|
+
end
|
45
|
+
|
46
|
+
def headers # :nodoc:
|
47
|
+
{'Content-Type' => "multipart/form-data; boundary=#{self.parameters.boundary}"}
|
48
|
+
end
|
49
|
+
|
50
|
+
def send # :nodoc:
|
51
|
+
response = Net::HTTP.start(endpoint_uri.host, endpoint_uri.port) do |http|
|
52
|
+
http.post(endpoint_uri.path, self.parameters.to_form, self.headers)
|
53
|
+
end
|
54
|
+
Response.new(response.body)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def endpoint_uri
|
59
|
+
@endpoint_uri ||= URI.parse(ENDPOINT_URIS[self.type])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = ValueParameter
|
5
|
+
#
|
6
|
+
# A simple name / value parameter for use in API calls
|
7
|
+
#
|
8
|
+
class ValueParameter < Parameter
|
9
|
+
|
10
|
+
attr_reader :value
|
11
|
+
|
12
|
+
# Create a new parameter with the specified name / value pair.
|
13
|
+
#
|
14
|
+
def initialize(name, value, include_in_signature = true)
|
15
|
+
@value = value
|
16
|
+
super(name, include_in_signature)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Generate the query string representation of this parameter.
|
20
|
+
#
|
21
|
+
def to_query
|
22
|
+
"#{self.name}=#{CGI.escape(self.value.to_s)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generate the form representation of this parameter.
|
26
|
+
#
|
27
|
+
def to_form
|
28
|
+
"Content-Disposition: form-data; name=\"#{self.name}\"\r\n" +
|
29
|
+
"\r\n" +
|
30
|
+
"#{self.value}\r\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/fleakr/api.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Extract the matching keys from the source hash and return
|
4
|
+
# a new hash with those keys:
|
5
|
+
#
|
6
|
+
# >> h = {:a => 'b', :c => 'd'}
|
7
|
+
# => {:a=>"b", :c=>"d"}
|
8
|
+
# >> h.extract!(:a)
|
9
|
+
# => {:a=>"b"}
|
10
|
+
# >> h
|
11
|
+
# => {:c=>"d"}
|
12
|
+
#
|
13
|
+
def extract!(*keys)
|
14
|
+
value = {}
|
15
|
+
|
16
|
+
keys.each {|k| value.merge!({k => self[k]}) if self.has_key?(k) }
|
17
|
+
keys.each {|k| delete(k) }
|
18
|
+
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'fleakr/core_ext/hash'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Objects # :nodoc:
|
3
|
+
|
4
|
+
# = AuthenticationToken
|
5
|
+
#
|
6
|
+
# This class represents an authentication token used for API calls that
|
7
|
+
# require authentication before they can be used
|
8
|
+
#
|
9
|
+
# == Attributes
|
10
|
+
#
|
11
|
+
# [value] The token value that is used in subsequent API calls
|
12
|
+
# [permissions] The permissions granted to this application (read / write / delete)
|
13
|
+
#
|
14
|
+
class AuthenticationToken
|
15
|
+
|
16
|
+
include Fleakr::Support::Object
|
17
|
+
|
18
|
+
flickr_attribute :value, :from => 'auth/token'
|
19
|
+
flickr_attribute :permissions, :from => 'auth/perms'
|
20
|
+
|
21
|
+
# Retrieve a full authentication token from the supplied mini-token (e.g. 123-456-789)
|
22
|
+
#
|
23
|
+
def self.from_mini_token(token)
|
24
|
+
parameters = {:mini_token => token, :sign? => true}
|
25
|
+
response = Fleakr::Api::MethodRequest.with_response!('auth.getFullToken', parameters)
|
26
|
+
|
27
|
+
self.new(response.body)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Retrieve a full authentication token from the supplied auth_token string
|
31
|
+
# (e.g. 45-76598454353455)
|
32
|
+
#
|
33
|
+
def self.from_auth_token(token)
|
34
|
+
parameters = {:auth_token => token, :sign? => true}
|
35
|
+
response = Fleakr::Api::MethodRequest.with_response!('auth.checkToken', parameters)
|
36
|
+
|
37
|
+
self.new(response.body)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -4,15 +4,15 @@ module Fleakr
|
|
4
4
|
|
5
5
|
include Fleakr::Support::Object
|
6
6
|
|
7
|
-
flickr_attribute :id, :
|
8
|
-
flickr_attribute :username
|
9
|
-
flickr_attribute :icon_server, :
|
10
|
-
flickr_attribute :icon_farm,
|
7
|
+
flickr_attribute :id, :from => '@nsid'
|
8
|
+
flickr_attribute :username
|
9
|
+
flickr_attribute :icon_server, :from => '@iconserver'
|
10
|
+
flickr_attribute :icon_farm, :from => '@iconfarm'
|
11
11
|
|
12
12
|
# Retrieve a list of contacts for the specified user ID and return an initialized
|
13
13
|
# collection of #User objects
|
14
14
|
def self.find_all_by_user_id(user_id)
|
15
|
-
response = Fleakr::Api::
|
15
|
+
response = Fleakr::Api::MethodRequest.with_response!('contacts.getPublicList', :user_id => user_id)
|
16
16
|
(response.body/'contacts/contact').map {|c| Contact.new(c).to_user }
|
17
17
|
end
|
18
18
|
|
data/lib/fleakr/objects/error.rb
CHANGED
@@ -14,8 +14,8 @@ module Fleakr
|
|
14
14
|
|
15
15
|
include Fleakr::Support::Object
|
16
16
|
|
17
|
-
flickr_attribute :code, :
|
18
|
-
flickr_attribute :message, :
|
17
|
+
flickr_attribute :code, :from => 'err@code'
|
18
|
+
flickr_attribute :message, :from => 'err@msg'
|
19
19
|
|
20
20
|
end
|
21
21
|
end
|
data/lib/fleakr/objects/group.rb
CHANGED
@@ -12,8 +12,8 @@ module Fleakr
|
|
12
12
|
|
13
13
|
include Fleakr::Support::Object
|
14
14
|
|
15
|
-
flickr_attribute :id, :
|
16
|
-
flickr_attribute :name
|
15
|
+
flickr_attribute :id, :from => '@nsid'
|
16
|
+
flickr_attribute :name
|
17
17
|
|
18
18
|
find_all :by_user_id, :call => 'people.getPublicGroups', :path => 'groups/group'
|
19
19
|
|