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