fleakr 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +32 -5
- data/Rakefile +1 -1
- data/lib/fleakr.rb +34 -6
- data/lib/fleakr/api.rb +1 -0
- data/lib/fleakr/api/method_request.rb +9 -4
- data/lib/fleakr/api/option.rb +175 -0
- data/lib/fleakr/api/parameter_list.rb +7 -6
- data/lib/fleakr/api/upload_request.rb +17 -11
- data/lib/fleakr/core_ext.rb +3 -1
- data/lib/fleakr/core_ext/false_class.rb +7 -0
- data/lib/fleakr/core_ext/true_class.rb +7 -0
- data/lib/fleakr/objects/authentication_token.rb +25 -8
- data/lib/fleakr/objects/photo.rb +21 -6
- data/lib/fleakr/support/object.rb +4 -2
- data/lib/fleakr/version.rb +1 -1
- data/test/fixtures/auth.getToken.xml +8 -0
- data/test/unit/fleakr/api/method_request_test.rb +0 -10
- data/test/unit/fleakr/api/option_test.rb +179 -0
- data/test/unit/fleakr/api/parameter_list_test.rb +33 -18
- data/test/unit/fleakr/api/upload_request_test.rb +21 -9
- data/test/unit/fleakr/core_ext/false_class_test.rb +13 -0
- data/test/unit/fleakr/core_ext/true_class_test.rb +13 -0
- data/test/unit/fleakr/objects/authentication_token_test.rb +16 -2
- data/test/unit/fleakr/objects/photo_test.rb +22 -8
- data/test/unit/fleakr_test.rb +72 -4
- metadata +10 -3
data/README.rdoc
CHANGED
@@ -169,7 +169,33 @@ Searches can also be scoped to other entities in the system (namely Users and Gr
|
|
169
169
|
=> [#<Fleakr::Objects::Photo:0x18a6960 @server_id="41", @id="81370156",
|
170
170
|
@farm_id="1", @title="Clear and Serpent Danger", @secret="013091582a">]
|
171
171
|
|
172
|
-
===
|
172
|
+
=== Uploading Files
|
173
|
+
|
174
|
+
Before you can upload files, you need to be able to make authenticated calls to the Flickr
|
175
|
+
API. Skip to the next section (Authenticated Calls) for details on how to make this work.
|
176
|
+
|
177
|
+
Uploading single files is simple:
|
178
|
+
|
179
|
+
>> Fleakr.upload('/path/to/image.jpg')
|
180
|
+
=> [#<Fleakr::Objects::Photo:0x217fb54 @updated="1236133594", @server_id="3266", ...>]
|
181
|
+
|
182
|
+
Notice that the newly-uploaded image is returned. You can further inspect / modify this as
|
183
|
+
necessary. The real magic is in uploading multiple files - the upload method takes a file
|
184
|
+
glob:
|
185
|
+
|
186
|
+
>> Fleakr.upload('/path/to/images/*.jpg')
|
187
|
+
=> [#<Fleakr::Objects::Photo:0x217faa0 ...>,
|
188
|
+
#<Fleakr::Objects::Photo:0x212fb18 ...>,
|
189
|
+
#<Fleakr::Objects::Photo:0x20e09c8 ...>]
|
190
|
+
|
191
|
+
You can also set options on the file(s) that you're uploading:
|
192
|
+
|
193
|
+
>> Fleakr.upload('/path/to/party/images/*.jpg', :viewable_by => :everyone,
|
194
|
+
:title => 'Party Pics')
|
195
|
+
|
196
|
+
The full list of options can be found in the Fleakr::Objects::Photo documentation.
|
197
|
+
|
198
|
+
=== Authenticated Calls
|
173
199
|
|
174
200
|
While read-only access to the API gets you quite a bit of data, you'll need to generate an
|
175
201
|
authentication token if you want access to the more powerful features (like uploading your
|
@@ -197,7 +223,9 @@ confirm access to get your mini-token. Now you're ready to make authenticated re
|
|
197
223
|
Fleakr.token.value # => "34132412341235-12341234ef34"
|
198
224
|
|
199
225
|
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.
|
226
|
+
for future requests, just set Fleakr.auth_token to the generated value. Similarly, if you have
|
227
|
+
an authenticated frob from Flickr (using authentication for desktop applications, for example)
|
228
|
+
you can also set <tt>Fleakr.frob</tt> to the frob value returned from the API.
|
201
229
|
|
202
230
|
=== What Went Wrong?
|
203
231
|
|
@@ -223,20 +251,19 @@ API requests.
|
|
223
251
|
|
224
252
|
=== 0.4.x
|
225
253
|
|
226
|
-
* Allow passing of parameters to file uploads to allow for access control / naming
|
227
254
|
* Implement remaining bits of person and photo-related API calls (read-only)
|
228
|
-
* Automatically sign all calls (if we have a secret), authenticate all calls (if we have a token)
|
229
255
|
|
230
256
|
=== 0.5.x
|
231
257
|
|
232
258
|
* Implement asynchronous file upload / replacement w/ ticket checking
|
233
|
-
* Provide a better searching interface
|
259
|
+
* Provide a better searching interface with ruby-like option syntax
|
234
260
|
|
235
261
|
=== Future
|
236
262
|
|
237
263
|
* Implement save-able search results (e.g. Fleakr.search('ponies').save_to('/path', :medium))
|
238
264
|
* Implement deeper associations for core elements (e.g. tags / etc..)
|
239
265
|
* Implement write methods for photos & photosets
|
266
|
+
* Implement flickr.places.* portion of API
|
240
267
|
|
241
268
|
== License
|
242
269
|
|
data/Rakefile
CHANGED
@@ -19,7 +19,7 @@ spec = Gem::Specification.new do |s|
|
|
19
19
|
s.files = %w(README.rdoc Rakefile) + Dir.glob("{lib,test}/**/*")
|
20
20
|
|
21
21
|
s.add_dependency('hpricot', '~> 0.6.0')
|
22
|
-
s.add_dependency('activesupport', '~> 2.
|
22
|
+
s.add_dependency('activesupport', '~> 2.0')
|
23
23
|
s.add_dependency('loggable', '~> 0.2.0')
|
24
24
|
end
|
25
25
|
|
data/lib/fleakr.rb
CHANGED
@@ -5,7 +5,15 @@ require 'uri'
|
|
5
5
|
require 'cgi'
|
6
6
|
require 'net/http'
|
7
7
|
require 'hpricot'
|
8
|
-
|
8
|
+
|
9
|
+
# Require only what we need from ActiveSupport
|
10
|
+
require 'active_support/core_ext/array'
|
11
|
+
require 'active_support/core_ext/module'
|
12
|
+
require 'active_support/core_ext/blank'
|
13
|
+
require 'active_support/core_ext/time'
|
14
|
+
require 'active_support/inflector'
|
15
|
+
require 'active_support/core_ext/string'
|
16
|
+
|
9
17
|
require 'md5'
|
10
18
|
require 'loggable'
|
11
19
|
|
@@ -75,7 +83,7 @@ module Fleakr
|
|
75
83
|
# Generic catch-all exception for any API errors
|
76
84
|
class ApiError < StandardError; end
|
77
85
|
|
78
|
-
mattr_accessor :api_key, :shared_secret, :mini_token, :auth_token
|
86
|
+
mattr_accessor :api_key, :shared_secret, :mini_token, :auth_token, :frob
|
79
87
|
|
80
88
|
# Find a user based on some unique user data. This method will try to find
|
81
89
|
# the user based on username and will fall back to email if that fails. Example:
|
@@ -113,8 +121,12 @@ module Fleakr
|
|
113
121
|
# Fleakr.upload('/path/to/my/mug.jpg')
|
114
122
|
# Fleakr.upload('/User/Pictures/Party/*.jpg')
|
115
123
|
#
|
116
|
-
|
117
|
-
|
124
|
+
# Additionally, options can be supplied as part of the upload that will apply to all files
|
125
|
+
# that are matched by the pattern passed to <tt>glob</tt>. For a full list, see
|
126
|
+
# Fleakr::Objects::Photo.
|
127
|
+
#
|
128
|
+
def self.upload(glob, options = {})
|
129
|
+
Dir[glob].map {|file| Fleakr::Objects::Photo.upload(file, options) }
|
118
130
|
end
|
119
131
|
|
120
132
|
# Get the authentication token needed for authenticated requests. Will either use
|
@@ -122,13 +134,29 @@ module Fleakr
|
|
122
134
|
#
|
123
135
|
def self.token
|
124
136
|
@token ||= begin
|
125
|
-
if
|
137
|
+
if Fleakr.auth_token
|
126
138
|
Fleakr::Objects::AuthenticationToken.from_auth_token(Fleakr.auth_token)
|
127
|
-
|
139
|
+
elsif Fleakr.frob
|
140
|
+
Fleakr::Objects::AuthenticationToken.from_frob(Fleakr.frob)
|
141
|
+
elsif Fleakr.mini_token
|
128
142
|
Fleakr::Objects::AuthenticationToken.from_mini_token(Fleakr.mini_token)
|
129
143
|
end
|
130
144
|
end
|
131
145
|
end
|
146
|
+
|
147
|
+
# Reset the cached token whenever setting a new value for the mini_token, auth_token, or frob
|
148
|
+
#
|
149
|
+
[:mini_token, :auth_token, :frob].each do |attribute|
|
150
|
+
class_eval <<-ACCESSOR
|
151
|
+
def self.#{attribute}=(#{attribute})
|
152
|
+
reset_token
|
153
|
+
@@#{attribute} = #{attribute}
|
154
|
+
end
|
155
|
+
ACCESSOR
|
156
|
+
end
|
132
157
|
|
158
|
+
def self.reset_token # :nodoc: #
|
159
|
+
@token = nil
|
160
|
+
end
|
133
161
|
|
134
162
|
end
|
data/lib/fleakr/api.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
module Fleakr
|
2
2
|
module Api # :nodoc:
|
3
3
|
|
4
|
+
# = MethodRequest
|
5
|
+
#
|
6
|
+
# Handles all API requests that are non-upload related. For upload requests see the
|
7
|
+
# UploadRequest class.
|
8
|
+
#
|
4
9
|
class MethodRequest
|
5
10
|
attr_reader :parameters, :method
|
6
11
|
|
7
12
|
# Makes a request to the Flickr API and returns a valid Response object. If
|
8
13
|
# there are errors on the response it will raise an ApiError exception. See
|
9
|
-
# #
|
14
|
+
# MethodRequest#new for details about the additional parameters
|
10
15
|
#
|
11
16
|
def self.with_response!(method, additional_parameters = {})
|
12
17
|
request = self.new(method, additional_parameters)
|
@@ -25,9 +30,9 @@ module Fleakr
|
|
25
30
|
# see (#Fleakr.api_key=)
|
26
31
|
#
|
27
32
|
# The <tt>additional_parameters</tt> is a list of parameters to pass directly to
|
28
|
-
# the Flickr API call.
|
29
|
-
#
|
30
|
-
#
|
33
|
+
# the Flickr API call. The exception to this is the <tt>:authenticate?</tt> option
|
34
|
+
# that will force the call to not be authenticated if it is set to false (The default
|
35
|
+
# behavior is to authenticate all calls when we have a token).
|
31
36
|
#
|
32
37
|
def initialize(method, additional_parameters = {})
|
33
38
|
@parameters = ParameterList.new(additional_parameters)
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module Fleakr
|
2
|
+
module Api # :nodoc:
|
3
|
+
|
4
|
+
# = Option
|
5
|
+
#
|
6
|
+
# Top-level class for creating specific option instances based on type
|
7
|
+
#
|
8
|
+
class Option
|
9
|
+
|
10
|
+
MAPPING = {
|
11
|
+
:tags => 'TagOption',
|
12
|
+
:viewable_by => 'ViewOption',
|
13
|
+
:level => 'LevelOption',
|
14
|
+
:type => 'TypeOption',
|
15
|
+
:hide? => 'HiddenOption'
|
16
|
+
}
|
17
|
+
|
18
|
+
# Initialize a new option for the specified type and value
|
19
|
+
#
|
20
|
+
def self.for(type, value)
|
21
|
+
class_for(type).new(type, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.class_for(type) # :nodoc:
|
25
|
+
class_name = MAPPING[type] || 'SimpleOption'
|
26
|
+
"Fleakr::Api::#{class_name}".constantize
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# = SimpleOption
|
32
|
+
#
|
33
|
+
# Simple name / value option pair
|
34
|
+
#
|
35
|
+
class SimpleOption
|
36
|
+
|
37
|
+
attr_reader :type, :value
|
38
|
+
|
39
|
+
# Create an option of the specified type and value
|
40
|
+
#
|
41
|
+
def initialize(type, value)
|
42
|
+
@type = type
|
43
|
+
@value = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate hash representation of this option
|
47
|
+
#
|
48
|
+
def to_hash
|
49
|
+
{type => value}
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# = TagOption
|
55
|
+
#
|
56
|
+
# Represents values for tags
|
57
|
+
#
|
58
|
+
class TagOption < SimpleOption
|
59
|
+
|
60
|
+
# Tag with specified values. Value passed will be converted to an array if it isn't
|
61
|
+
# already
|
62
|
+
#
|
63
|
+
def initialize(type, value)
|
64
|
+
super type, value
|
65
|
+
@value = Array(self.value)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Hash representation of tag values (separated by spaces). Handles multi-word tags
|
69
|
+
# by enclosing each tag in double quotes.
|
70
|
+
#
|
71
|
+
def to_hash
|
72
|
+
tags = value.map {|tag| "\"#{tag}\"" }
|
73
|
+
{type => tags.join(' ')}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# = ViewOption
|
79
|
+
#
|
80
|
+
# Specify who is able to view the photo.
|
81
|
+
#
|
82
|
+
class ViewOption < SimpleOption
|
83
|
+
|
84
|
+
# Specify who this is viewable by (e.g. everyone / friends / family).
|
85
|
+
#
|
86
|
+
def initialize(type, value)
|
87
|
+
super type, Array(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Is this publicly viewable? (i.e. :everyone)
|
91
|
+
#
|
92
|
+
def public?
|
93
|
+
value == [:everyone]
|
94
|
+
end
|
95
|
+
|
96
|
+
# Is this viewable by friends? (i.e. :friends)
|
97
|
+
#
|
98
|
+
def friends?
|
99
|
+
value.include?(:friends)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Is this viewable by family? (i.e. :family)
|
103
|
+
#
|
104
|
+
def family?
|
105
|
+
value.include?(:family)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Hash representation of photo permissions
|
109
|
+
#
|
110
|
+
def to_hash
|
111
|
+
{:is_public => public?.to_i, :is_friend => friends?.to_i, :is_family => family?.to_i}
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# = LevelOption
|
117
|
+
#
|
118
|
+
# Specify the "safety level" of this photo (e.g. safe / moderate / restricted)
|
119
|
+
#
|
120
|
+
class LevelOption < SimpleOption
|
121
|
+
|
122
|
+
def value # :nodoc:
|
123
|
+
case @value
|
124
|
+
when :safe: 1
|
125
|
+
when :moderate: 2
|
126
|
+
when :restricted: 3
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Hash representation of the safety_level for this photo
|
131
|
+
#
|
132
|
+
def to_hash
|
133
|
+
{:safety_level => value}
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
# = TypeOption
|
139
|
+
#
|
140
|
+
# Specify the type of this photo (e.g. photo / screenshot / other)
|
141
|
+
#
|
142
|
+
class TypeOption < SimpleOption
|
143
|
+
|
144
|
+
def value # :nodoc:
|
145
|
+
case @value
|
146
|
+
when :photo: 1
|
147
|
+
when :screenshot: 2
|
148
|
+
when :other: 3
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Hash representation of this type
|
153
|
+
#
|
154
|
+
def to_hash
|
155
|
+
{:content_type => value}
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
# = HiddenOption
|
161
|
+
#
|
162
|
+
# Specify whether this photo should be hidden from search
|
163
|
+
#
|
164
|
+
class HiddenOption < SimpleOption
|
165
|
+
|
166
|
+
# Hash representation of whether to hide this photo
|
167
|
+
#
|
168
|
+
def to_hash
|
169
|
+
{:hidden => (value ? 2 : 1)}
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
@@ -10,8 +10,8 @@ module Fleakr
|
|
10
10
|
class ParameterList
|
11
11
|
|
12
12
|
# Create a new parameter list with optional parameters:
|
13
|
-
# [:
|
14
|
-
#
|
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
16
|
# Any additional name / value pairs will be created as individual
|
17
17
|
# ValueParameters as part of the list. Example:
|
@@ -22,7 +22,8 @@ module Fleakr
|
|
22
22
|
# => #<Fleakr::Api::ValueParameter:0x1656da4 @include_in_signature=true, @name="foo", @value="bar">
|
23
23
|
#
|
24
24
|
def initialize(options = {})
|
25
|
-
|
25
|
+
# TODO: need to find a way to move the unexpected behavior in Fleakr.token elsewhere
|
26
|
+
@api_options = options.extract!(:authenticate?)
|
26
27
|
|
27
28
|
@list = Hash.new
|
28
29
|
|
@@ -41,13 +42,13 @@ module Fleakr
|
|
41
42
|
# Should this parameter list be signed?
|
42
43
|
#
|
43
44
|
def sign?
|
44
|
-
|
45
|
+
!Fleakr.shared_secret.blank?
|
45
46
|
end
|
46
47
|
|
47
48
|
# Should we send the auth_token with the request?
|
48
49
|
#
|
49
50
|
def authenticate?
|
50
|
-
|
51
|
+
@api_options.has_key?(:authenticate?) ? @api_options[:authenticate?] : !Fleakr.token.blank?
|
51
52
|
end
|
52
53
|
|
53
54
|
# Access an individual parameter by key (symbol or string)
|
@@ -64,7 +65,7 @@ module Fleakr
|
|
64
65
|
# list - e.g. <tt>foo=bar&blee=baz</tt>
|
65
66
|
#
|
66
67
|
def to_query
|
67
|
-
list.values.map
|
68
|
+
list.values.map {|element| element.to_query }.join('&')
|
68
69
|
end
|
69
70
|
|
70
71
|
# Generate the form representation of this parameter list including the
|
@@ -19,8 +19,8 @@ module Fleakr
|
|
19
19
|
# a Fleakr::ApiError with the reason for the error. See UploadRequest#new for more
|
20
20
|
# details.
|
21
21
|
#
|
22
|
-
def self.with_response!(filename, options = {})
|
23
|
-
request = self.new(filename, options)
|
22
|
+
def self.with_response!(filename, type = :create, options = {})
|
23
|
+
request = self.new(filename, type, options)
|
24
24
|
response = request.send
|
25
25
|
|
26
26
|
raise(Fleakr::ApiError, "Code: #{response.error.code} - #{response.error.message}") if response.error?
|
@@ -28,21 +28,27 @@ module Fleakr
|
|
28
28
|
response
|
29
29
|
end
|
30
30
|
|
31
|
-
# Create a new UploadRequest with the specified filename and options
|
31
|
+
# Create a new UploadRequest with the specified filename, type, and options. Type
|
32
|
+
# is one of <tt>:create</tt> or <tt>:update</tt> to specify whether we are saving a new
|
33
|
+
# image or replacing an existing one.
|
32
34
|
#
|
33
|
-
#
|
34
|
-
# photos or replacing existing ones
|
35
|
+
# For a list of available options, see the documentation in Fleakr::Objects::Photo
|
35
36
|
#
|
36
|
-
def initialize(filename, options = {})
|
37
|
-
|
38
|
-
options
|
37
|
+
def initialize(filename, type = :create, options = {})
|
38
|
+
@type = type
|
39
|
+
@options = options
|
39
40
|
|
40
|
-
@
|
41
|
-
|
42
|
-
@parameters = ParameterList.new(options)
|
41
|
+
@parameters = ParameterList.new(upload_options)
|
43
42
|
@parameters << FileParameter.new('photo', filename)
|
44
43
|
end
|
45
44
|
|
45
|
+
# A list of upload options for this upload request (see Fleakr::Api::Option)
|
46
|
+
#
|
47
|
+
def upload_options
|
48
|
+
option_list = @options.map {|key, value| Option.for(key, value) }
|
49
|
+
option_list.inject({}) {|hash, option| hash.merge(option.to_hash)}
|
50
|
+
end
|
51
|
+
|
46
52
|
def headers # :nodoc:
|
47
53
|
{'Content-Type' => "multipart/form-data; boundary=#{self.parameters.boundary}"}
|
48
54
|
end
|