fleakr 0.5.2 → 0.6.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.
@@ -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