fleakr 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,10 +12,6 @@ A small, yet powerful, gem to interface with Flickr photostreams
12
12
 
13
13
  === Bleeding Edge
14
14
 
15
- sudo gem install reagent-fleakr --source=http://gems.github.com
16
-
17
- Or ...
18
-
19
15
  $ git clone git://github.com/reagent/fleakr.git
20
16
  $ cd fleakr
21
17
  $ rake gem && sudo gem install pkg/fleakr-<version>.gem
@@ -306,8 +302,13 @@ While read-only access to the API gets you quite a bit of data, you'll need to g
306
302
  authentication token if you want access to the more powerful features (like uploading your
307
303
  own photos).
308
304
 
309
- Assuming you've already applied for a key, go back and make sure you have the right settings
310
- to get your auth token. Click on the 'Edit key details' link and ensure that:
305
+ Depending on how you intend to use the Flickr API, there are 2 methods for performing
306
+ authenticated calls.
307
+
308
+ === Single User
309
+
310
+ You'll need to configure your API key to to use Mobile authentication. If you're viewing your
311
+ list of keys on the Flickr site, click on the 'Edit key details' link and ensure that:
311
312
 
312
313
  1. Your application description and notes are up-to-date
313
314
  1. The value for 'Authentication Type' is set to 'Mobile Application'
@@ -315,22 +316,56 @@ to get your auth token. Click on the 'Edit key details' link and ensure that:
315
316
 
316
317
  Once this is set, you'll see your Authentication URL on the key details page (it will look
317
318
  something like http://www.flickr.com/auth-534525246245). Paste this URL into your browser and
318
- confirm access to get your mini-token. Now you're ready to make authenticated requests:
319
-
320
- require 'rubygems'
321
- require 'fleakr'
319
+ confirm access to get your mini-token. Now you're ready to configure your authentication token:
322
320
 
323
321
  Fleakr.api_key = 'ABC123'
324
322
  Fleakr.shared_secret = 'sekrit' # Available with your key details on the Flickr site
325
- Fleakr.mini_token = '362-133-214'
326
-
323
+
324
+ token = Fleakr.token_from_mini_token('294-585-410')
325
+ Fleakr.auth_token = token.value
326
+
327
327
  Fleakr.upload('/path/to/my/photo.jpg')
328
- Fleakr.token.value # => "34132412341235-12341234ef34"
329
328
 
330
- Once you use the mini-token once, it is no longer available. To use the generated auth_token
331
- for future requests, just set Fleakr.auth_token to the generated value. Similarly, if you have
332
- an authenticated frob from Flickr (using authentication for desktop applications, for example)
333
- you can also set <tt>Fleakr.frob</tt> to the frob value returned from the API.
329
+ Once you use the mini-token once it is no longer available. To use the generated auth_token
330
+ for future requests, you'll need to make sure that you set the value permanently:
331
+
332
+ Fleakr.auth_token = '72157622657341094-41241e527f325abb'
333
+
334
+ === Multiple Users
335
+
336
+ If you need to have your application access other users photos on their behalf, you'll want to
337
+ use the Web authentication method. Edit your key details and make sure that:
338
+
339
+ 1. Your application description and notes are up-to-date
340
+ 1. The value for 'Authentication Type' is set to 'Web Application'
341
+ 1. You configure your callback URL to point to something valid and accessible
342
+
343
+ Make sure that your key and secret are set as above. You can then begin the process by requesting
344
+ that the user authorize access to his Flickr account by redirecting to an authorization URL. I'm
345
+ assuming Rails conventions here, but this should work with any Ruby web framework:
346
+
347
+ redirect_to Fleakr.authorization_url
348
+
349
+ By default, we request read permission for access. This doesn't really do much more than what the
350
+ public API allows, so you can request different permissions when asking for authorization:
351
+
352
+ # The values :read, :write, and :delete are supported
353
+ redirect_to Fleakr.authorization_url(:delete)
354
+
355
+ One the user authorizes your application, he will be redirected back to your callback URL with a
356
+ <tt>frob</tt> parameter as part of the query string. You'll need to exchange this for a token:
357
+
358
+ token = Fleakr.token_from_frob(params[:frob])
359
+
360
+ The actual authentication token is available by calling <tt>token.value</tt> in the above
361
+ example. You'll want to store this value somewhere to make future API calls on behalf of this
362
+ user. To make that process easier, there is a method that you can use that will allow you to
363
+ automatically scope your requests to the authenticated user:
364
+
365
+ user = Fleakr.user_for_token('72157622657341094-41241e527f325abb')
366
+
367
+ From there, you can make any of the usual calls available with the API. See
368
+ Fleakr::Objects::User for more information.
334
369
 
335
370
  === What Went Wrong?
336
371
 
@@ -1,5 +1,4 @@
1
1
  $:.unshift(File.dirname(__FILE__))
2
- require 'rubygems'
3
2
 
4
3
  require 'uri'
5
4
  require 'cgi'
@@ -19,9 +18,9 @@ require 'digest/md5'
19
18
  require 'fileutils'
20
19
  require 'loggable'
21
20
 
21
+ require 'fleakr/support'
22
22
  require 'fleakr/api'
23
23
  require 'fleakr/core_ext'
24
- require 'fleakr/support'
25
24
  require 'fleakr/objects'
26
25
 
27
26
  # = Fleakr: A small, yet powerful, gem to interface with Flickr photostreams
@@ -85,7 +84,7 @@ module Fleakr
85
84
  # Generic catch-all exception for any API errors
86
85
  class ApiError < StandardError; end
87
86
 
88
- mattr_accessor :api_key, :shared_secret, :mini_token, :auth_token, :frob
87
+ mattr_accessor :api_key, :shared_secret, :auth_token
89
88
 
90
89
  # Find a user based on some unique user data. This method will try to find
91
90
  # the user based on several methods, starting with username and falling back to email and URL
@@ -96,11 +95,11 @@ module Fleakr
96
95
  # Fleakr.user('user@host.com')
97
96
  # Fleakr.user('http://www.flickr.com/photos/the_decapitator/')
98
97
  #
99
- def self.user(user_data)
98
+ def self.user(user_data, options = {})
100
99
  user = nil
101
100
  [:username, :email, :url].each do |attribute|
102
101
  if user.nil?
103
- user = Objects::User.send("find_by_#{attribute}", user_data) rescue nil
102
+ user = Objects::User.send("find_by_#{attribute}", user_data, options) rescue nil
104
103
  end
105
104
  end
106
105
  user
@@ -156,34 +155,43 @@ module Fleakr
156
155
  Fleakr::Objects::Contact.find_all(options)
157
156
  end
158
157
 
159
- # Get the authentication token needed for authenticated requests. Will either use
160
- # a valid auth_token (if available) or a mini-token to generate the auth_token.
161
- #
162
- def self.token
163
- @token ||= begin
164
- if Fleakr.auth_token
165
- Fleakr::Objects::AuthenticationToken.from_auth_token(Fleakr.auth_token)
166
- elsif Fleakr.frob
167
- Fleakr::Objects::AuthenticationToken.from_frob(Fleakr.frob)
168
- elsif Fleakr.mini_token
169
- Fleakr::Objects::AuthenticationToken.from_mini_token(Fleakr.mini_token)
170
- end
171
- end
158
+ # Generate an authorization URL to redirect users to. This defaults to
159
+ # 'read' permission, but others are available when passed to this method:
160
+ #
161
+ # * :read - permission to read private information (default)
162
+ # * :write - permission to add, edit and delete photo metadata (includes 'read')
163
+ # * :delete - permission to delete photos (includes 'write' and 'read')
164
+ #
165
+ def self.authorization_url(permissions = :read)
166
+ request = Fleakr::Api::AuthenticationRequest.new(:perms => permissions)
167
+ request.authorization_url
168
+ end
169
+
170
+ # Exchange a frob for an authentication token. See Fleakr.authorization_url for
171
+ # more information.
172
+ #
173
+ def self.token_from_frob(frob)
174
+ Fleakr::Objects::AuthenticationToken.from_frob(frob)
172
175
  end
173
176
 
174
- # Reset the cached token whenever setting a new value for the mini_token, auth_token, or frob
177
+ # Exchange a mini token for an authentication token.
175
178
  #
176
- [:mini_token, :auth_token, :frob].each do |attribute|
177
- class_eval <<-ACCESSOR
178
- def self.#{attribute}=(#{attribute})
179
- reset_token
180
- @@#{attribute} = #{attribute}
181
- end
182
- ACCESSOR
179
+ def self.token_from_mini_token(mini_token)
180
+ Fleakr::Objects::AuthenticationToken.from_mini_token(mini_token)
183
181
  end
184
182
 
185
- def self.reset_token # :nodoc: #
186
- @token = nil
183
+ # Get the user that this authentication token belongs to. Useful for pulling
184
+ # relationships scoped to this user.
185
+ #
186
+ def self.user_for_token(auth_token)
187
+ token = Fleakr::Objects::AuthenticationToken.from_auth_token(auth_token)
188
+ token.user
187
189
  end
188
-
190
+
191
+ end
192
+
193
+ # Alias Fleakr methods as Flickr if possible
194
+ if defined?(Flickr).nil?
195
+ Flickr = Fleakr
189
196
  end
197
+
@@ -1,4 +1,5 @@
1
1
  require "fleakr/api/response"
2
+ require "fleakr/api/authentication_request"
2
3
  require "fleakr/api/method_request"
3
4
  require "fleakr/api/option"
4
5
  require "fleakr/api/upload_request"
@@ -0,0 +1,32 @@
1
+ module Fleakr
2
+ module Api # :nodoc:
3
+
4
+ # = AuthenticationRequest
5
+ #
6
+ # Handles authentication requests for the web authentication method.
7
+ # Requires that Fleakr.api_key and Fleakr.shared_secret both be set.
8
+ #
9
+ class AuthenticationRequest
10
+
11
+ include Fleakr::Support::Request
12
+
13
+ # The endpoint for the authentication request
14
+ #
15
+ def endpoint_url
16
+ 'http://flickr.com/services/auth/'
17
+ end
18
+
19
+ def response # :nodoc:
20
+ Net::HTTP.get_response(endpoint_uri)
21
+ end
22
+
23
+ # The authorization URL that the user should be redirected to.
24
+ #
25
+ def authorization_url
26
+ @authorization_url ||= response.header['Location']
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -5,19 +5,21 @@ module Fleakr
5
5
  #
6
6
  # Parameter class to encapsulate file data sent to the Flickr upload API
7
7
  #
8
- class FileParameter < Parameter
8
+ class FileParameter
9
9
 
10
10
  MIME_TYPES = {
11
11
  '.jpg' => 'image/jpeg',
12
12
  '.png' => 'image/png',
13
13
  '.gif' => 'image/gif'
14
14
  }
15
+
16
+ attr_reader :name
15
17
 
16
18
  # Create a parameter with name and specified filename
17
19
  #
18
20
  def initialize(name, filename)
21
+ @name = name
19
22
  @filename = filename
20
- super(name, false)
21
23
  end
22
24
 
23
25
  # Discover MIME type by file extension using MIME_TYPES constant
@@ -7,7 +7,10 @@ module Fleakr
7
7
  # UploadRequest class.
8
8
  #
9
9
  class MethodRequest
10
- attr_reader :parameters, :method
10
+
11
+ include Fleakr::Support::Request
12
+
13
+ attr_reader :method
11
14
 
12
15
  # Makes a request to the Flickr API and returns a valid Response object. If
13
16
  # there are errors on the response it will raise an ApiError exception. See
@@ -16,7 +19,7 @@ module Fleakr
16
19
  def self.with_response!(method, additional_parameters = {})
17
20
  request = self.new(method, additional_parameters)
18
21
  response = request.send
19
-
22
+
20
23
  raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
21
24
 
22
25
  response
@@ -35,14 +38,17 @@ module Fleakr
35
38
  # behavior is to authenticate all calls when we have a token).
36
39
  #
37
40
  def initialize(method, additional_parameters = {})
38
- @parameters = ParameterList.new(additional_parameters)
39
-
41
+ super(additional_parameters)
40
42
  self.method = method
41
43
  end
42
44
 
43
45
  def method=(method) # :nodoc:
44
46
  @method = method.sub(/^(flickr\.)?/, 'flickr.')
45
- @parameters << ValueParameter.new('method', @method)
47
+ parameters.add_option(:method, @method)
48
+ end
49
+
50
+ def endpoint_url
51
+ 'http://api.flickr.com/services/rest/'
46
52
  end
47
53
 
48
54
  def send # :nodoc:
@@ -53,13 +59,6 @@ module Fleakr
53
59
  Response.new(response_xml)
54
60
  end
55
61
 
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
62
  end
64
63
 
65
64
  end
@@ -9,89 +9,116 @@ module Fleakr
9
9
  #
10
10
  class ParameterList
11
11
 
12
+ attr_reader :upload_options # :nodoc:
13
+
12
14
  # Create a new parameter list with optional parameters:
13
- # [:authenticate?] Request will automatically be authenticated if Fleakr.token is available
14
- # set this to false to force it not to authenticate
15
15
  #
16
- # Any additional name / value pairs will be created as individual
17
- # ValueParameters as part of the list. Example:
16
+ # list = Fleakr::Api::ParameterList.new(:username => 'reagent')
17
+ #
18
+ # You can also disable the sending of the authentication token using
19
+ # the second parameter:
18
20
  #
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">
21
+ # list = Fleakr::Api::ParameterList.new({}, false)
23
22
  #
24
- def initialize(options = {})
25
- # TODO: need to find a way to move the unexpected behavior in Fleakr.token elsewhere
26
- @api_options = options.extract!(:authenticate?)
27
-
28
- @list = Hash.new
29
-
30
- options.each {|k,v| self << ValueParameter.new(k.to_s, v) }
23
+ def initialize(options = {}, send_authentication_token = true)
24
+ @send_authentication_token = send_authentication_token
25
+ @options = options
26
+ @upload_options = {}
27
+ end
31
28
 
32
- self << ValueParameter.new('api_key', Fleakr.api_key)
33
- self << ValueParameter.new('auth_token', Fleakr.token.value) if authenticate?
29
+ # Should we send an authentication token as part of this list of parameters?
30
+ # By default this is true if the token is available as a global value or if
31
+ # the :auth_token key/value is part of the initial list. You can override this
32
+ # in the constructor.
33
+ #
34
+ def send_authentication_token?
35
+ @send_authentication_token && !authentication_token.nil?
34
36
  end
35
37
 
36
- # Add a new parameter (ValueParameter / FileParameter) to the list
38
+ # The default options to send as part of the parameter list, defaults to
39
+ # sending the API key
37
40
  #
38
- def <<(parameter)
39
- @list.merge!(parameter.name => parameter)
41
+ def default_options
42
+ {:api_key => Fleakr.api_key}
40
43
  end
41
44
 
42
- # Should this parameter list be signed?
45
+ # Should this parameter list be signed? This will be true if Fleakr.shared_secret
46
+ # is set, false if not.
43
47
  #
44
48
  def sign?
45
49
  !Fleakr.shared_secret.blank?
46
50
  end
47
51
 
48
- # Should we send the auth_token with the request?
49
- #
50
- def authenticate?
51
- @api_options.has_key?(:authenticate?) ? @api_options[:authenticate?] : !Fleakr.token.blank?
52
- end
53
-
54
- # Access an individual parameter by key (symbol or string)
55
- #
56
- def [](key)
57
- list[key.to_s]
58
- end
59
-
60
- def boundary # :nodoc:
61
- @boundary ||= Digest::MD5.hexdigest(rand.to_s)
62
- end
63
-
64
52
  # Generate the query string representation of this parameter
65
53
  # list - e.g. <tt>foo=bar&blee=baz</tt>
66
54
  #
67
55
  def to_query
68
- list.values.map {|element| element.to_query }.join('&')
56
+ list.map {|element| element.to_query }.join('&')
69
57
  end
70
58
 
71
59
  # Generate the form representation of this parameter list including the
72
60
  # boundary
73
61
  #
74
62
  def to_form
75
- form = list.values.map {|p| "--#{self.boundary}\r\n#{p.to_form}" }.join
76
- form << "--#{self.boundary}--"
63
+ form = list.map {|p| "--#{boundary}\r\n#{p.to_form}" }.join
64
+ form << "--#{boundary}--"
77
65
 
78
66
  form
79
67
  end
80
68
 
69
+ # Retrieve the authentication token from either the list of parameters
70
+ # or the global value (e.g. Fleakr.auth_token)
71
+ #
72
+ def authentication_token
73
+ Fleakr.auth_token.nil? ? @options[:auth_token] : Fleakr.auth_token
74
+ end
75
+
76
+ # Add an option to the list that should be sent with a request.
77
+ #
78
+ def add_option(name, value)
79
+ @options.merge!(name => value)
80
+ end
81
+
82
+ # Add an option that should be sent with an upload request.
83
+ #
84
+ def add_upload_option(name, value)
85
+ @upload_options.merge!(name => value)
86
+ end
87
+
88
+ def options # :nodoc:
89
+ options = default_options.merge(@options)
90
+ options.merge!(:auth_token => authentication_token) if send_authentication_token?
91
+
92
+ options
93
+ end
94
+
95
+ def boundary # :nodoc:
96
+ @boundary ||= Digest::MD5.hexdigest(rand.to_s)
97
+ end
98
+
81
99
  def signature # :nodoc:
82
- parameters_to_sign = @list.values.reject {|p| !p.include_in_signature? }
83
- signature_text = parameters_to_sign.sort.map {|p| "#{p.name}#{p.value}" }.join
100
+ sorted_options = options_without_signature.sort {|a,b| a[0].to_s <=> b[0].to_s }
101
+ signature_text = sorted_options.map {|o| "#{o[0]}#{o[1]}" }.join
84
102
 
85
103
  Digest::MD5.hexdigest("#{Fleakr.shared_secret}#{signature_text}")
86
104
  end
87
105
 
88
- private
89
- def list
90
- list = @list
91
- list.merge!('api_sig' => ValueParameter.new('api_sig', signature, false)) if self.sign?
92
-
93
- list
106
+ def options_without_signature # :nodoc:
107
+ options.reject {|k,v| k.to_s == 'api_sig'}
108
+ end
109
+
110
+ def options_with_signature # :nodoc:
111
+ options_without_signature.merge(:api_sig => signature)
94
112
  end
113
+
114
+ def list # :nodoc:
115
+ options_for_list = sign? ? options_with_signature : options_without_signature
116
+ value_parameters = options_for_list.map {|k,v| ValueParameter.new(k, v) }
117
+ file_parameters = upload_options.map {|k,v| FileParameter.new(k, v) }
118
+
119
+ value_parameters + file_parameters
120
+ end
121
+
95
122
  end
96
123
  end
97
124
  end