upflickr 0.1.6
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.
- data/CHANGELOG +4 -0
- data/README +94 -0
- data/lib/multipartpost.rb +55 -0
- data/lib/upflickr.rb +295 -0
- metadata +83 -0
data/CHANGELOG
ADDED
data/README
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
#######################
|
2
|
+
# upflickr v0.1.6 #
|
3
|
+
#######################
|
4
|
+
#
|
5
|
+
# Upflickr makes it easier to upload files to flickr using the flickr api.
|
6
|
+
#
|
7
|
+
# you will need the api_key and secret values for your flickr application.
|
8
|
+
#
|
9
|
+
#######################
|
10
|
+
# ATTRIBUTES #
|
11
|
+
#######################
|
12
|
+
|
13
|
+
api_key
|
14
|
+
|
15
|
+
secret
|
16
|
+
|
17
|
+
token
|
18
|
+
|
19
|
+
#######################
|
20
|
+
# METHODS #
|
21
|
+
#######################
|
22
|
+
# (all methods return false on failure)
|
23
|
+
|
24
|
+
add_photo
|
25
|
+
args: photo_id, set_id
|
26
|
+
returns: true
|
27
|
+
|
28
|
+
Add a photo to a set.
|
29
|
+
|
30
|
+
check_token
|
31
|
+
args: none
|
32
|
+
returns: true
|
33
|
+
|
34
|
+
Check if the current authentication token is still valid.
|
35
|
+
|
36
|
+
create_set
|
37
|
+
args: photo_id, title, description (optional)
|
38
|
+
returns: set_id
|
39
|
+
|
40
|
+
Create a new photoset.
|
41
|
+
|
42
|
+
delete_photo
|
43
|
+
args: photo_id
|
44
|
+
returns: true
|
45
|
+
|
46
|
+
Delete a photo.
|
47
|
+
|
48
|
+
delete_set
|
49
|
+
args: set_id
|
50
|
+
returns: true
|
51
|
+
|
52
|
+
Delete a photoset.
|
53
|
+
|
54
|
+
get_auth_url
|
55
|
+
args: none
|
56
|
+
returns: authorization_url
|
57
|
+
|
58
|
+
Gets the unique authentication URL for confirming read/write/delete access. You must load this URL in a web browser prior to attempting get_token.
|
59
|
+
|
60
|
+
get_token
|
61
|
+
args: none
|
62
|
+
returns: token
|
63
|
+
|
64
|
+
Get an authentication token after visiting the authentication URL and clicking confirm. This token may be used with the above 'token' method to set the token in a new instance without re-authenticating.
|
65
|
+
|
66
|
+
license_photo
|
67
|
+
args: photo_id, license_id
|
68
|
+
returns: true
|
69
|
+
|
70
|
+
Specify a license for a photo. License IDs can be found at http://www.flickr.com/services/api/flickr.photos.licenses.getInfo.html
|
71
|
+
|
72
|
+
list_photos
|
73
|
+
args: set_id (optional)
|
74
|
+
returns: hash {photo_id => title}
|
75
|
+
|
76
|
+
Without argument, lists all photos not assigned to a set. With set_id argument, lists all photos in the given set.
|
77
|
+
|
78
|
+
list_sets
|
79
|
+
args: none
|
80
|
+
returns: hash {set_id => title}
|
81
|
+
|
82
|
+
List all the photosets for the current user.
|
83
|
+
|
84
|
+
remove_photo
|
85
|
+
args: photo_id, set_id
|
86
|
+
returns: true
|
87
|
+
|
88
|
+
Remove a photo from a set.
|
89
|
+
|
90
|
+
upload
|
91
|
+
args: hash {arg => value}
|
92
|
+
returns: photo_id
|
93
|
+
|
94
|
+
Upload a new image. Create a hash using any of the arguments from http://www.flickr.com/services/api/upload.api.html as keys. All arguments are optional except 'photo'. Therefore, the minimum required argument is: args{'photo' => "/path/to/image1.jpg"}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
#--
|
2
|
+
# Helper class to prepare an HTTP POST request with a file upload
|
3
|
+
# Mostly taken from http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/113774
|
4
|
+
# Anything that's broken and wrong probably the fault of Bill Stilwell (bill@marginalia.org)
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'mime/types'
|
9
|
+
require 'net/http'
|
10
|
+
require 'cgi'
|
11
|
+
|
12
|
+
class Param
|
13
|
+
attr_accessor :k, :v
|
14
|
+
def initialize( k, v )
|
15
|
+
@k = k
|
16
|
+
@v = v
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_multipart
|
20
|
+
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"\r\n\r\n#{v}\r\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class FileParam
|
25
|
+
attr_accessor :k, :filename, :content
|
26
|
+
def initialize( k, filename, content )
|
27
|
+
@k = k
|
28
|
+
@filename = filename
|
29
|
+
@content = content
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_multipart
|
33
|
+
return "Content-Disposition: form-data; name=\"#{CGI::escape(k)}\"; filename=\"#{filename}\"\r\n" +
|
34
|
+
"Content-Transfer-Encoding: binary\r\n" +
|
35
|
+
"Content-Type: #{MIME::Types.type_for(@filename)}\r\n\r\n" + content + "\r\n"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MultipartPost
|
40
|
+
BOUNDARY = 'flickrrocks-aaaaaabbbb0000'
|
41
|
+
HEADER = {"Content-type" => "multipart/form-data, boundary=" + BOUNDARY + " "}
|
42
|
+
|
43
|
+
def prepare_query ( params )
|
44
|
+
fp = []
|
45
|
+
params.each do |k,v|
|
46
|
+
if v.respond_to?(:read)
|
47
|
+
fp.push(FileParam.new(k,v.path,v.read))
|
48
|
+
else
|
49
|
+
fp.push(Param.new(k,v))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
query = fp.collect {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
|
53
|
+
return query, HEADER
|
54
|
+
end
|
55
|
+
end
|
data/lib/upflickr.rb
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'multipartpost'
|
7
|
+
|
8
|
+
class Upflickr
|
9
|
+
API_URL = 'http://api.flickr.com/services/rest/?'
|
10
|
+
attr_accessor :api_key
|
11
|
+
attr_accessor :secret
|
12
|
+
attr_accessor :token
|
13
|
+
|
14
|
+
def check_attr(token=false)
|
15
|
+
if token == false
|
16
|
+
if @api_key != nil && @secret != nil
|
17
|
+
return true
|
18
|
+
else
|
19
|
+
puts "UPFLICKR: api_key and/or secret not set"
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
else
|
23
|
+
if @api_key != nil && @secret != nil && @token != nil
|
24
|
+
return true
|
25
|
+
else
|
26
|
+
puts "UPFLICKR: api_key, secret, and/or token not set"
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def sign(s)
|
33
|
+
return Digest::MD5.hexdigest(s)
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_white(s)
|
37
|
+
return s.gsub(" ","+")
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_auth_url
|
41
|
+
if self.check_attr == true
|
42
|
+
sig = self.sign("#{@secret}api_key#{@api_key}methodflickr.auth.getFrob")
|
43
|
+
data = open("#{API_URL}method=flickr.auth.getFrob&api_key=#{@api_key}&api_sig=#{sig}")
|
44
|
+
doc = REXML::Document.new(data)
|
45
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
46
|
+
@frob = doc.elements["//frob"].text.strip
|
47
|
+
sig = self.sign("#{@secret}api_key#{@api_key}frob#{@frob}permsdelete")
|
48
|
+
url = "http://flickr.com/services/auth/?api_key=#{@api_key}&frob=#{@frob}&perms=delete&api_sig=#{sig}"
|
49
|
+
return url
|
50
|
+
else
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
else
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get_token
|
59
|
+
if self.check_attr == true
|
60
|
+
sig = self.sign("#{@secret}api_key#{@api_key}frob#{@frob}methodflickr.auth.getToken")
|
61
|
+
data = open("#{API_URL}method=flickr.auth.getToken&api_key=#{@api_key}&frob=#{@frob}&api_sig=#{sig}")
|
62
|
+
doc = REXML::Document.new(data)
|
63
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
64
|
+
self.token = doc.elements["//token"].text.strip
|
65
|
+
return @token
|
66
|
+
else
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
else
|
70
|
+
return false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def check_token
|
75
|
+
if self.check_attr(true) == true
|
76
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.auth.checkToken")
|
77
|
+
data = open("#{API_URL}method=flickr.auth.checkToken&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
|
78
|
+
doc = REXML::Document.new(data)
|
79
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
80
|
+
perms = doc.elements["//perms"].text.strip
|
81
|
+
if perms == "delete"
|
82
|
+
return true
|
83
|
+
else
|
84
|
+
puts "UPFLICKR: Full permissions not enabled."
|
85
|
+
return false
|
86
|
+
end
|
87
|
+
else
|
88
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
else
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def list_photos(set_id=false)
|
97
|
+
if self.check_attr(true) == true
|
98
|
+
if set_id == false
|
99
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photos.getNotInSet")
|
100
|
+
data = open("#{API_URL}method=flickr.photos.getNotInSet&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
|
101
|
+
doc = REXML::Document.new(data)
|
102
|
+
else
|
103
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.getPhotosphotoset_id#{set_id}")
|
104
|
+
data = open("#{API_URL}method=flickr.photosets.getPhotos&api_key=#{@api_key}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
|
105
|
+
doc = REXML::Document.new(data)
|
106
|
+
end
|
107
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
108
|
+
photos = {}
|
109
|
+
doc.elements.each("*/*/photo") do |e|
|
110
|
+
id = e.attributes['id']
|
111
|
+
name = e.attributes['title']
|
112
|
+
photos[id] = name
|
113
|
+
end
|
114
|
+
return photos
|
115
|
+
else
|
116
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
117
|
+
return false
|
118
|
+
end
|
119
|
+
else
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_photo(photo_id, set_id)
|
125
|
+
if self.check_attr(true) == true
|
126
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.addPhotophoto_id#{photo_id}photoset_id#{set_id}")
|
127
|
+
data = open("#{API_URL}method=flickr.photosets.addPhoto&api_key=#{@api_key}&photo_id=#{photo_id}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
|
128
|
+
doc = REXML::Document.new(data)
|
129
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
130
|
+
return true
|
131
|
+
else
|
132
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
133
|
+
return false
|
134
|
+
end
|
135
|
+
else
|
136
|
+
return false
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def remove_photo(photo_id, set_id)
|
141
|
+
if self.check_attr(true) == true
|
142
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.removePhotophoto_id#{photo_id}photoset_id#{set_id}")
|
143
|
+
data = open("#{API_URL}method=flickr.photosets.removePhoto&api_key=#{@api_key}&photo_id=#{photo_id}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
|
144
|
+
doc = REXML::Document.new(data)
|
145
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
146
|
+
return true
|
147
|
+
else
|
148
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
149
|
+
return false
|
150
|
+
end
|
151
|
+
else
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def delete_photo(photo_id)
|
157
|
+
if self.check_attr(true) == true
|
158
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photos.deletephoto_id#{photo_id}")
|
159
|
+
data = open("#{API_URL}method=flickr.photos.delete&api_key=#{@api_key}&photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
|
160
|
+
doc = REXML::Document.new(data)
|
161
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
162
|
+
return true
|
163
|
+
else
|
164
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
165
|
+
return false
|
166
|
+
end
|
167
|
+
else
|
168
|
+
return false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def license_photo(photo_id,license_id)
|
173
|
+
if self.check_attr(true) == true
|
174
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}license_id#{license_id}methodflickr.photos.licenses.setLicensephoto_id#{photo_id}")
|
175
|
+
data = open("#{API_URL}method=flickr.photos.licenses.setLicense&api_key=#{@api_key}&photo_id=#{photo_id}&license_id=#{license_id}&auth_token=#{@token}&api_sig=#{sig}")
|
176
|
+
doc = REXML::Document.new(data)
|
177
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
178
|
+
return true
|
179
|
+
else
|
180
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
181
|
+
return false
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def list_sets
|
187
|
+
if self.check_attr(true) == true
|
188
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.getList")
|
189
|
+
data = open("#{API_URL}method=flickr.photosets.getList&api_key=#{@api_key}&auth_token=#{@token}&api_sig=#{sig}")
|
190
|
+
doc = REXML::Document.new(data)
|
191
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
192
|
+
photosetids = {}
|
193
|
+
doc.elements.each("*/photosets/photoset") do |e|
|
194
|
+
id = e.attributes['id']
|
195
|
+
name = e.elements['title'].text.strip
|
196
|
+
photosetids[id] = name
|
197
|
+
end
|
198
|
+
return photosetids
|
199
|
+
else
|
200
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
else
|
204
|
+
return false
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def create_set(photo_id, title, description=false)
|
209
|
+
if self.check_attr(true) == true
|
210
|
+
if description != false
|
211
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}description#{description}methodflickr.photosets.createprimary_photo_id#{photo_id}title#{title}")
|
212
|
+
title = self.remove_white(title)
|
213
|
+
description = self.remove_white(description)
|
214
|
+
data = open("#{API_URL}method=flickr.photosets.create&api_key=#{@api_key}&title=#{title}&description=#{description}&primary_photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
|
215
|
+
else
|
216
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.createprimary_photo_id#{photo_id}title#{title}")
|
217
|
+
title = self.remove_white(title)
|
218
|
+
data = open("#{API_URL}method=flickr.photosets.create&api_key=#{@api_key}&title=#{title}&primary_photo_id=#{photo_id}&auth_token=#{@token}&api_sig=#{sig}")
|
219
|
+
end
|
220
|
+
doc = REXML::Document.new(data)
|
221
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
222
|
+
return doc.elements['//photoset'].attributes['id']
|
223
|
+
else
|
224
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
225
|
+
return false
|
226
|
+
end
|
227
|
+
else
|
228
|
+
return false
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def delete_set(set_id)
|
233
|
+
if self.check_attr(true) == true
|
234
|
+
sig = self.sign("#{@secret}api_key#{@api_key}auth_token#{@token}methodflickr.photosets.deletephotoset_id#{set_id}")
|
235
|
+
data = open("#{API_URL}method=flickr.photosets.delete&api_key=#{@api_key}&photoset_id=#{set_id}&auth_token=#{@token}&api_sig=#{sig}")
|
236
|
+
doc = REXML::Document.new(data)
|
237
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
238
|
+
return true
|
239
|
+
else
|
240
|
+
puts "UPFLICKR: #{doc.elements["//err"].attributes['msg']}"
|
241
|
+
return false
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def post_form(url, query, headers)
|
247
|
+
Net::HTTP.start(url.host, url.port) {|con|
|
248
|
+
con.read_timeout = 10
|
249
|
+
begin
|
250
|
+
return con.post(url.path, query, headers)
|
251
|
+
rescue => e
|
252
|
+
puts "UPFLICKR: POSTING Failed #{e}... #{Time.now}"
|
253
|
+
end
|
254
|
+
}
|
255
|
+
end
|
256
|
+
|
257
|
+
def upload(params)
|
258
|
+
if self.check_attr(true) == true
|
259
|
+
params['api_key'] = @api_key
|
260
|
+
params['auth_token'] = @token
|
261
|
+
p2s = {}
|
262
|
+
params.each do |k,v|
|
263
|
+
if k == 'photo'
|
264
|
+
params['photo'] = File.open(v, 'r')
|
265
|
+
else
|
266
|
+
p2s[k] = v
|
267
|
+
end
|
268
|
+
end
|
269
|
+
p2s = p2s.sort.to_s
|
270
|
+
sig = self.sign("#{@secret}#{p2s}")
|
271
|
+
params['api_sig'] = sig
|
272
|
+
|
273
|
+
mp = MultipartPost.new
|
274
|
+
|
275
|
+
# Get both the headers and the query ready,
|
276
|
+
# given the new MultipartPost and the params
|
277
|
+
# Hash
|
278
|
+
query, headers = mp.prepare_query(params)
|
279
|
+
|
280
|
+
# Make sure the URL is useable
|
281
|
+
url = URI.parse('http://flickr.com/services/upload/')
|
282
|
+
|
283
|
+
# Do the actual POST, given the right inputs
|
284
|
+
data = self.post_form(url, query, headers)
|
285
|
+
doc = REXML::Document.new(data.read_body)
|
286
|
+
if doc.elements["//rsp"].attributes['stat'] == "ok"
|
287
|
+
return doc.elements["//photoid"].text.strip
|
288
|
+
else
|
289
|
+
return false
|
290
|
+
end
|
291
|
+
else
|
292
|
+
return false
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: upflickr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 6
|
10
|
+
version: 0.1.6
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ben Baker-Smith
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-06-06 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: mime-types
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description:
|
36
|
+
email:
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
files:
|
44
|
+
- lib/multipartpost.rb
|
45
|
+
- lib/upflickr.rb
|
46
|
+
- README
|
47
|
+
- CHANGELOG
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://bitsynthesis.com/upflickr/
|
50
|
+
licenses: []
|
51
|
+
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options: []
|
54
|
+
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.7
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: a simple flickr uploader
|
82
|
+
test_files: []
|
83
|
+
|