flickr-objects 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +49 -63
  3. data/lib/flickr/api/abstract/params_processor.rb +68 -0
  4. data/lib/flickr/api/abstract.rb +113 -0
  5. data/lib/flickr/api/general.rb +75 -0
  6. data/lib/flickr/api/person.rb +91 -39
  7. data/lib/flickr/api/photo.rb +213 -108
  8. data/lib/flickr/api/set.rb +114 -55
  9. data/lib/flickr/api/upload_ticket.rb +17 -6
  10. data/lib/flickr/api.rb +90 -35
  11. data/lib/flickr/attributes.rb +200 -0
  12. data/lib/flickr/base_converter.rb +29 -0
  13. data/lib/flickr/client/data.rb +47 -0
  14. data/lib/flickr/client/oauth.rb +44 -0
  15. data/lib/flickr/client/upload.rb +55 -0
  16. data/lib/flickr/client.rb +77 -33
  17. data/lib/flickr/configuration.rb +85 -14
  18. data/lib/flickr/error.rb +58 -0
  19. data/lib/flickr/middleware.rb +52 -28
  20. data/lib/flickr/oauth.rb +89 -74
  21. data/lib/flickr/object/attribute_locations/list.rb +25 -0
  22. data/lib/flickr/object/attribute_locations/location.rb +29 -0
  23. data/lib/flickr/object/attribute_locations/permissions.rb +31 -0
  24. data/lib/flickr/object/attribute_locations/person/upload_status.rb +32 -0
  25. data/lib/flickr/object/attribute_locations/person.rb +78 -0
  26. data/lib/flickr/object/attribute_locations/photo/exif.rb +29 -0
  27. data/lib/flickr/object/attribute_locations/photo/note.rb +36 -0
  28. data/lib/flickr/object/attribute_locations/photo/tag.rb +23 -0
  29. data/lib/flickr/object/attribute_locations/photo.rb +164 -0
  30. data/lib/flickr/object/attribute_locations/set.rb +53 -0
  31. data/lib/flickr/object/attribute_locations/upload_ticket.rb +30 -0
  32. data/lib/flickr/object/attribute_locations/visibility.rb +24 -0
  33. data/lib/flickr/object/list/kaminari.rb +30 -0
  34. data/lib/flickr/object/list/normal.rb +27 -0
  35. data/lib/flickr/object/list/will_paginate.rb +31 -0
  36. data/lib/flickr/object/list.rb +87 -0
  37. data/lib/flickr/object/location.rb +35 -0
  38. data/lib/flickr/object/permissions.rb +18 -0
  39. data/lib/flickr/object/person/upload_status.rb +22 -0
  40. data/lib/flickr/object/person.rb +93 -0
  41. data/lib/flickr/object/photo/exif.rb +35 -0
  42. data/lib/flickr/object/photo/note.rb +20 -0
  43. data/lib/flickr/object/photo/size.rb +67 -0
  44. data/lib/flickr/object/photo/tag.rb +23 -0
  45. data/lib/flickr/object/photo.rb +349 -0
  46. data/lib/flickr/object/set.rb +114 -0
  47. data/lib/flickr/object/upload_ticket.rb +31 -0
  48. data/lib/flickr/object/visibility.rb +16 -0
  49. data/lib/flickr/object.rb +118 -27
  50. data/lib/flickr/sanitized_file.rb +70 -0
  51. data/lib/flickr/version.rb +4 -2
  52. data/lib/flickr.rb +69 -15
  53. metadata +89 -103
  54. data/lib/flickr/api/api_methods/flickr.rb +0 -5
  55. data/lib/flickr/api/api_methods/person.rb +0 -14
  56. data/lib/flickr/api/api_methods/photo.rb +0 -26
  57. data/lib/flickr/api/api_methods/set.rb +0 -18
  58. data/lib/flickr/api/api_methods/upload_ticket.rb +0 -5
  59. data/lib/flickr/api/flickr.rb +0 -35
  60. data/lib/flickr/api_caller.rb +0 -98
  61. data/lib/flickr/client/methods_client.rb +0 -22
  62. data/lib/flickr/client/upload_client.rb +0 -62
  63. data/lib/flickr/errors.rb +0 -19
  64. data/lib/flickr/helpers/base_58.rb +0 -15
  65. data/lib/flickr/helpers/boolean.rb +0 -4
  66. data/lib/flickr/helpers/core_ext.rb +0 -5
  67. data/lib/flickr/object/attribute/converter.rb +0 -49
  68. data/lib/flickr/object/attribute/finder.rb +0 -32
  69. data/lib/flickr/object/attribute.rb +0 -45
  70. data/lib/flickr/objects/attribute_values/list.rb +0 -10
  71. data/lib/flickr/objects/attribute_values/location.rb +0 -14
  72. data/lib/flickr/objects/attribute_values/note.rb +0 -11
  73. data/lib/flickr/objects/attribute_values/permissions.rb +0 -12
  74. data/lib/flickr/objects/attribute_values/person/upload_status.rb +0 -16
  75. data/lib/flickr/objects/attribute_values/person.rb +0 -24
  76. data/lib/flickr/objects/attribute_values/photo.rb +0 -84
  77. data/lib/flickr/objects/attribute_values/set.rb +0 -17
  78. data/lib/flickr/objects/attribute_values/tag.rb +0 -9
  79. data/lib/flickr/objects/attribute_values/upload_ticket.rb +0 -9
  80. data/lib/flickr/objects/attribute_values/visibility.rb +0 -10
  81. data/lib/flickr/objects/list.rb +0 -86
  82. data/lib/flickr/objects/location.rb +0 -26
  83. data/lib/flickr/objects/note.rb +0 -14
  84. data/lib/flickr/objects/permissions.rb +0 -14
  85. data/lib/flickr/objects/person/upload_status.rb +0 -14
  86. data/lib/flickr/objects/person.rb +0 -43
  87. data/lib/flickr/objects/photo.rb +0 -113
  88. data/lib/flickr/objects/set.rb +0 -29
  89. data/lib/flickr/objects/tag.rb +0 -17
  90. data/lib/flickr/objects/upload_ticket.rb +0 -18
  91. data/lib/flickr/objects/visibility.rb +0 -12
  92. data/lib/flickr/objects.rb +0 -25
@@ -0,0 +1,200 @@
1
+ require "date"
2
+
3
+ module Flickr
4
+
5
+ ##
6
+ # A helper class for true/false type.
7
+ #
8
+ # @private
9
+ #
10
+ class Boolean
11
+ end
12
+
13
+ ##
14
+ # A module that provides functionality of defining attributes of which locations
15
+ # can be found in the JSON response from Flickr.
16
+ #
17
+ # @private
18
+ #
19
+ module Attributes
20
+
21
+ ##
22
+ # Registers an attribute (name + type), defining the getter method (and in the
23
+ # boolean case an additional predicated alias).
24
+ #
25
+ def attribute(name, type)
26
+ new_attribute = Attribute.new(name, type)
27
+
28
+ attributes << new_attribute
29
+
30
+ define_method(name) do
31
+ self.class.attributes.find(name).value(self)
32
+ end
33
+ alias_method "#{name}?", name if type == Boolean
34
+
35
+ new_attribute
36
+ end
37
+
38
+ ##
39
+ # List of all registered attributes.
40
+ #
41
+ def attributes
42
+ @attributes ||= AttributeSet.new
43
+ end
44
+
45
+ end
46
+
47
+ ##
48
+ # This class stores the information about attributes. It stores the name, locations
49
+ # and type, and it is responsible for retrieving the attribute values from Flickr's
50
+ # JSON response, and optionally coercing them to the right type (for example, time
51
+ # can be represented in JSON only as a string, and here we convert it to actual
52
+ # instance of Ruby's `Time` class).
53
+ #
54
+ # @private
55
+ #
56
+ class Attribute
57
+
58
+ attr_reader :name, :type, :locations
59
+
60
+ def initialize(name, type)
61
+ @name, @type = name, type
62
+ @locations = []
63
+ end
64
+
65
+ def add_locations(locations)
66
+ @locations = locations + @locations
67
+ end
68
+
69
+ def value(object)
70
+ value = find_value(object)
71
+ coerce(value, object, @type)
72
+ end
73
+
74
+ private
75
+
76
+ ##
77
+ # Finds attribute value in the JSON response by looking at the given locations.
78
+ #
79
+ def find_value(context)
80
+ locations.each do |location|
81
+ begin
82
+ value = context.instance_exec(&location)
83
+ next if value.nil?
84
+ return value
85
+ rescue
86
+ end
87
+ end
88
+
89
+ nil
90
+ end
91
+
92
+ ##
93
+ # It coerces the found attribute value into a given type. For example, "boolean"
94
+ # type is represented in JSON as an integer (1/0), so values of this type need to
95
+ # be coerced the appropriate true/false values.
96
+ #
97
+ def coerce(value, object, type)
98
+ return value if value.nil?
99
+
100
+ if type.is_a?(Enumerable)
101
+ objects = value.map { |e| coerce(e, object, type.first) }
102
+ if type.respond_to?(:find_by)
103
+ return type.class.new({}).populate(objects)
104
+ else
105
+ return type.class.new(objects)
106
+ end
107
+ elsif type.ancestors.include? Flickr::Object
108
+ return type.new(value, object.access_token)
109
+ else
110
+ COERCIONS.fetch(type).each do |coercion|
111
+ begin
112
+ return coercion.call(value)
113
+ rescue
114
+ end
115
+ end
116
+ end
117
+
118
+ value
119
+ end
120
+
121
+ COERCIONS = {
122
+
123
+ String => [
124
+ ->(value) { String(value) },
125
+ ],
126
+
127
+ Time => [
128
+ ->(value) { Time.at(Integer(value)) },
129
+ ->(value) { DateTime.parse(value).to_time },
130
+ ],
131
+
132
+ Boolean => [
133
+ ->(value) { Integer(value) == 1 },
134
+ ],
135
+
136
+ Integer => [
137
+ ->(value) { Integer(value) },
138
+ ],
139
+
140
+ Float => [
141
+ ->(value) { Float(value) },
142
+ ],
143
+
144
+ Hash => [
145
+ ->(value) { value },
146
+ ],
147
+
148
+ Array => [
149
+ ->(value) { Array(value) },
150
+ ]
151
+
152
+ }
153
+
154
+ end
155
+
156
+ ##
157
+ # Container for the attributes.
158
+ #
159
+ # @private
160
+ #
161
+ class AttributeSet
162
+
163
+ include Enumerable
164
+
165
+ def initialize(*attributes)
166
+ @attributes = Array.new(attributes)
167
+ end
168
+
169
+ def each(*args, &block)
170
+ @attributes.each(*args, &block)
171
+ end
172
+
173
+ ##
174
+ # Shorthand for finding attributes by name.
175
+ #
176
+ def find(name = nil)
177
+ if name
178
+ super() { |attribute| attribute.name == name }
179
+ else
180
+ super()
181
+ end
182
+ end
183
+
184
+ ##
185
+ # Shorthand for adding locations to multiple attributes at once.
186
+ #
187
+ def add_locations(hash)
188
+ hash.each do |attribute_name, locations|
189
+ find(attribute_name).add_locations(locations)
190
+ end
191
+ end
192
+
193
+ def <<(attribute)
194
+ @attributes << attribute
195
+ self
196
+ end
197
+
198
+ end
199
+
200
+ end
@@ -0,0 +1,29 @@
1
+ module Flickr
2
+
3
+ ##
4
+ # Converts numbers to different bases, used for generating URLs in Flickr. For example,
5
+ # base 58 is used for generating short URLs.
6
+ #
7
+ # @private
8
+ #
9
+ module BaseConverter
10
+
11
+ extend self
12
+
13
+ BASE58_ALPHABET = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ".chars.to_a.freeze
14
+
15
+ def to_base58(number)
16
+ number = Integer(number)
17
+ result = ""
18
+
19
+ begin
20
+ number, remainder = number.divmod(58)
21
+ result = BASE58_ALPHABET[remainder] + result
22
+ end while number > 0
23
+
24
+ result
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,47 @@
1
+ module Flickr
2
+ class Client
3
+
4
+ ##
5
+ # Client for query and maniuplation of Flickr's photo data.
6
+ #
7
+ class Data < Flickr::Client
8
+
9
+ def initialize(access_token = nil)
10
+ access_token ||= Array.new(2, nil)
11
+
12
+ super() do |builder|
13
+ # Request
14
+ builder.use FaradayMiddleware::OAuth,
15
+ consumer_key: api_key,
16
+ consumer_secret: shared_secret,
17
+ token: access_token[0],
18
+ token_secret: access_token[1]
19
+
20
+ # Response
21
+ builder.use Flickr::Middleware::CheckStatus
22
+ builder.use FaradayMiddleware::ParseJson
23
+ builder.use Flickr::Middleware::CheckOAuth
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def do_request(http_method, flickr_method, params = {})
30
+ super(http_method, "rest") do |req|
31
+ req.params[:method] = flickr_method
32
+ req.params.update(params)
33
+ end
34
+ end
35
+
36
+ def url
37
+ if use_ssl?
38
+ "https://secure.flickr.com/services"
39
+ else
40
+ "http://api.flickr.com/services"
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,44 @@
1
+ module Flickr
2
+ class Client
3
+
4
+ ##
5
+ # Client for OAuth authentication.
6
+ #
7
+ class OAuth < Flickr::Client
8
+
9
+ NO_CALLBACK = "oob".freeze
10
+
11
+ def initialize(request_token = nil)
12
+ request_token ||= Array.new(2, nil)
13
+
14
+ super() do |builder|
15
+ builder.use FaradayMiddleware::OAuth,
16
+ consumer_key: api_key,
17
+ consumer_secret: shared_secret,
18
+ token: request_token[0],
19
+ token_secret: request_token[1]
20
+
21
+ builder.use Flickr::Middleware::ParseOAuth
22
+ builder.use Flickr::Middleware::CheckOAuth
23
+ end
24
+ end
25
+
26
+ def get_request_token(params = {})
27
+ params[:oauth_callback] ||= NO_CALLBACK
28
+ get "request_token", params
29
+ end
30
+
31
+ def get_access_token(params = {})
32
+ get "access_token", params
33
+ end
34
+
35
+ private
36
+
37
+ def url
38
+ "http://www.flickr.com/services/oauth"
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ module Flickr
2
+ class Client
3
+
4
+ ##
5
+ # Client for uploading photos.
6
+ #
7
+ class Upload < Flickr::Client
8
+
9
+ def initialize(access_token = nil)
10
+ access_token ||= Array.new(2, nil)
11
+
12
+ super() do |builder|
13
+ # Request
14
+ builder.use FaradayMiddleware::OAuth,
15
+ consumer_key: api_key,
16
+ consumer_secret: shared_secret,
17
+ token: access_token[0],
18
+ token_secret: access_token[1]
19
+ builder.use Faraday::Request::Multipart
20
+
21
+ # Response
22
+ builder.use Flickr::Middleware::CheckStatus
23
+ builder.use FaradayMiddleware::ParseXml
24
+ builder.use Flickr::Middleware::CheckOAuth
25
+ end
26
+ end
27
+
28
+ def upload(params = {})
29
+ params[:photo] = UploadIO(params[:photo])
30
+ post "upload", params
31
+ end
32
+
33
+ def replace(params = {})
34
+ params[:photo] = UploadIO(params[:photo])
35
+ post "replace", params
36
+ end
37
+
38
+ private
39
+
40
+ def UploadIO(file)
41
+ Faraday::UploadIO.new(file, file.content_type, file.path)
42
+ end
43
+
44
+ def url
45
+ if use_ssl?
46
+ "https://secure.flickr.com/services"
47
+ else
48
+ "http://api.flickr.com/services"
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+ end
data/lib/flickr/client.rb CHANGED
@@ -2,56 +2,100 @@ require "faraday"
2
2
  require "faraday_middleware"
3
3
  require "flickr/middleware"
4
4
 
5
- class Flickr
6
- class Client < Faraday::Connection
5
+ module Flickr
6
+
7
+ ##
8
+ # This abstract class is the base for the client classes which communicate with
9
+ # the Flickr API. For example, the part of Flickr API for uploading photos need
10
+ # requests to be marked as multipart, while the part for querying data of photos
11
+ # doesn't need it. So it's obvious that we need separate classes.
12
+ #
13
+ # This class just extracts the common behaviour, like including the API key
14
+ # in requests.
15
+ #
16
+ # @private
17
+ #
18
+ class Client
19
+
20
+ extend Flickr::AutoloadHelper
21
+
22
+ autoload_dir "flickr/client",
23
+ :Data => "data",
24
+ :Upload => "upload",
25
+ :OAuth => "oauth"
26
+
7
27
  DEFAULTS = {
8
- open_timeout: 4,
9
- timeout: 6
28
+ open_timeout: 8,
29
+ timeout: 10,
10
30
  }
11
31
 
12
- def initialize(access_token)
13
- api_key, shared_secret = Flickr.configuration.fetch(:api_key, :shared_secret)
32
+ def initialize
33
+ @connection = Faraday.new(url, connection_options) do |builder|
34
+ builder.use Flickr::Middleware::CatchTimeout
35
+ yield builder if block_given?
36
+
37
+ builder.adapter :net_http
38
+ end
39
+ end
40
+
41
+ def get(*args, &block)
42
+ do_request(:get, *args, &block)
43
+ end
14
44
 
15
- open_timeout = Flickr.configuration.open_timeout || DEFAULTS[:open_timeout]
16
- timeout = Flickr.configuration.timeout || DEFAULTS[:timeout]
45
+ def post(*args, &block)
46
+ do_request(:post, *args, &block)
47
+ end
17
48
 
18
- url = Flickr.configuration.secure ? "https://secure.flickr.com/services" : "http://api.flickr.com/services"
19
- proxy = Flickr.configuration.proxy
49
+ private
20
50
 
21
- params = {
22
- url: url,
51
+ def connection_options
52
+ {
23
53
  params: {
24
54
  format: "json",
25
55
  nojsoncallback: 1,
26
- api_key: api_key
56
+ api_key: api_key,
27
57
  },
28
58
  request: {
29
59
  open_timeout: open_timeout,
30
- timeout: timeout
60
+ timeout: timeout,
31
61
  },
32
- proxy: proxy
62
+ proxy: proxy,
33
63
  }
64
+ end
34
65
 
35
- super(params) do |builder|
36
- # Request
37
- builder.use Middleware::Retry
38
- builder.use FaradayMiddleware::OAuth,
39
- consumer_key: api_key,
40
- consumer_secret: shared_secret,
41
- token: access_token.first,
42
- token_secret: access_token.last
43
- yield builder if block_given?
66
+ def url
67
+ # Should be implemented in subclasses
68
+ end
44
69
 
45
- # Response
46
- builder.use Middleware::CheckStatus
47
- builder.use self.parser
48
- builder.use Middleware::CheckOAuth
70
+ def do_request(http_method, *args, &block)
71
+ response = @connection.send(http_method, *args, &block)
72
+ response.body
73
+ end
49
74
 
50
- builder.adapter :net_http
51
- end
75
+ def api_key
76
+ Flickr.api_key
77
+ end
78
+
79
+ def shared_secret
80
+ Flickr.shared_secret
81
+ end
82
+
83
+ def open_timeout
84
+ Flickr.open_timeout || DEFAULTS[:open_timeout]
85
+ end
86
+
87
+ def timeout
88
+ Flickr.timeout || DEFAULTS[:timeout]
52
89
  end
90
+
91
+ def use_ssl?
92
+ !!Flickr.use_ssl
93
+ end
94
+
95
+ def proxy
96
+ Flickr.proxy
97
+ end
98
+
53
99
  end
54
- end
55
100
 
56
- require "flickr/client/methods_client"
57
- require "flickr/client/upload_client"
101
+ end
@@ -1,25 +1,96 @@
1
- class Flickr
2
- class Configuration
3
- attr_accessor :api_key
4
- attr_accessor :shared_secret
1
+ module Flickr
5
2
 
6
- attr_accessor :access_token_key
7
- attr_accessor :access_token_secret
3
+ ##
4
+ # Provides general configuration options for the library.
5
+ #
6
+ module Configuration
8
7
 
8
+ ##
9
+ # @example
10
+ # Flickr.configure do |config|
11
+ # config.api_key = "..."
12
+ # config.shared_secret = "..."
13
+ # # ...
14
+ # end
15
+ #
16
+ def configure
17
+ yield self
18
+ self
19
+ end
20
+
21
+ ##
22
+ # API key and shared secret are necessary for making API requests. You can apply
23
+ # for them [here][api_key].
24
+ #
25
+ # [api_key]: http://www.flickr.com/services/apps/create/apply
26
+ #
27
+ attr_accessor :api_key, :shared_secret
28
+
29
+ ##
30
+ # Required for authenticated requests.
31
+ #
32
+ # config.access_token_key = "KEY"
33
+ # config.access_token_secret = "SECRET"
34
+ #
35
+ # For details on how to obtain it, take a look at the {Flickr::OAuth} module.
36
+ #
37
+ attr_accessor :access_token_key, :access_token_secret
38
+
39
+ ##
40
+ # @return [Array(string, string)]
41
+ # @private
42
+ #
43
+ def access_token
44
+ if access_token_key and access_token_secret
45
+ [access_token_key, access_token_secret]
46
+ end
47
+ end
48
+
49
+ ##
50
+ # Time to wait for the connection to Flickr to open. After that
51
+ # {Flickr::TimeoutError} is thrown.
52
+ #
53
+ # Default is `5` seconds.
54
+ #
9
55
  attr_accessor :open_timeout
56
+ ##
57
+ # Time to wait for the first block of response from Flickr. After that
58
+ # {Flickr::TimeoutError} is thrown.
59
+ #
60
+ # Default is `10` seconds.
61
+ #
10
62
  attr_accessor :timeout
11
63
 
12
- attr_accessor :secure
64
+ ##
65
+ # You can choose to make requests over a secure connection (SSL).
66
+ #
67
+ # config.use_ssl = true
68
+ #
69
+ # Default is `false`.
70
+ #
71
+ attr_accessor :use_ssl
72
+ alias secure= use_ssl=
73
+
74
+ ##
75
+ # You can choose to go over a proxy.
76
+ #
77
+ # config.proxy = "http://proxy.com"
78
+ #
13
79
  attr_accessor :proxy
14
80
 
81
+ ##
82
+ # When retrieving photos from Flickr, you can enable automatic compatibility
83
+ # with a pagination library.
84
+ #
85
+ # config.pagination = :will_paginate
86
+ #
87
+ # Supports [WillPaginate][will_paginate] and [Kaminari][kaminari].
88
+ #
89
+ # [will_paginate]: https://github.com/mislav/will_paginate
90
+ # [kaminari]: https://github.com/amatsuda/kaminari
91
+ #
15
92
  attr_accessor :pagination
16
93
 
17
- def fetch(*attributes)
18
- attributes.map { |attribute| send(attribute) }
19
- end
20
-
21
- def access_token
22
- [access_token_key, access_token_secret]
23
- end
24
94
  end
95
+
25
96
  end
@@ -0,0 +1,58 @@
1
+ module Flickr
2
+
3
+ class Error < StandardError
4
+ end
5
+
6
+ ##
7
+ # This error is raised when there is an OAuth error (for example when the
8
+ # access token is missing on requests which require authentication).
9
+ #
10
+ class OAuthError < Error
11
+ end
12
+
13
+ ##
14
+ # This error is raised when there is an error in the request.
15
+ #
16
+ # @see #code
17
+ #
18
+ class ApiError < Error
19
+ ##
20
+ # Flickr's code of the error. The list possible errors and their codes is
21
+ # shown below every API method on {http://flickr.com/services/api}.
22
+ # For example:
23
+ #
24
+ # - 100: There is no API key
25
+ # - 105: Service currently unavailable
26
+ # - ...
27
+ #
28
+ # @example
29
+ # begin
30
+ # Flickr.photos.get_recent
31
+ # rescue Flickr::ApiError => error
32
+ # puts "There is no API key" if error.code == 100
33
+ # end
34
+ # @return [Integer]
35
+ #
36
+ attr_reader :code
37
+
38
+ ##
39
+ # @private
40
+ #
41
+ def initialize(message, code = nil)
42
+ super(message)
43
+ @code = code.to_i
44
+ end
45
+
46
+ def message
47
+ "#{code}: #{super}"
48
+ end
49
+ end
50
+
51
+ ##
52
+ # @see Flickr::Configuration#open_timeout
53
+ # @see Flickr::Configuration#timeout
54
+ #
55
+ class TimeoutError < Error
56
+ end
57
+
58
+ end