flickr-objects 0.2.0 → 0.3.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.
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