popuparchive 1.0.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.
- checksums.yaml +7 -0
- data/lib/popuparchive.rb +404 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f4df34b72d100f6c9dbe9a447ed3432eca71ca85
|
4
|
+
data.tar.gz: 4f5346cabc147f30659bb20c4d80272b3e396892
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 44d1b5a28bf0fd405d700e56a652ebd18340bf883175c60dc69a76ad980973e14ee7ba60bf6b6eb78f1e6b0fc234e4912df6ed4ec6999ba949586c66d8d39b23
|
7
|
+
data.tar.gz: bf52ef21e64941fadb907d3521a8fecd5063dfff6137babb728043506545607c1e3ffec58a2f98643843be4cd5d3ac94ad57f7cdf8658fe039ebc17b3167386e
|
data/lib/popuparchive.rb
ADDED
@@ -0,0 +1,404 @@
|
|
1
|
+
# Pop Up Archive Ruby SDK
|
2
|
+
# Copyright 2014 - Pop Up Archive
|
3
|
+
# Licensed under Apache 2 license - see LICENSE file
|
4
|
+
#
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'json'
|
9
|
+
require 'faraday_middleware'
|
10
|
+
require 'oauth2'
|
11
|
+
require 'uri'
|
12
|
+
require 'xmlsimple'
|
13
|
+
|
14
|
+
module PopUpArchive
|
15
|
+
|
16
|
+
module Error
|
17
|
+
class NotFound < StandardError
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class FaradayErrHandler < Faraday::Response::Middleware
|
23
|
+
def on_complete(env)
|
24
|
+
# Ignore any non-error response codes
|
25
|
+
return if (status = env[:status]) < 400
|
26
|
+
#puts "got response status #{status}"
|
27
|
+
case status
|
28
|
+
when 404
|
29
|
+
#raise Error::NotFound
|
30
|
+
# 404 errors not fatal
|
31
|
+
else
|
32
|
+
#puts pp(env)
|
33
|
+
super # let parent class deal with it
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Client
|
39
|
+
|
40
|
+
attr_accessor :host
|
41
|
+
attr_accessor :debug
|
42
|
+
attr_accessor :agent
|
43
|
+
attr_accessor :user_agent
|
44
|
+
attr_accessor :cookies
|
45
|
+
attr_accessor :api_endpoint
|
46
|
+
attr_accessor :croak_on_404
|
47
|
+
|
48
|
+
def version
|
49
|
+
return "1.0.0"
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(args)
|
53
|
+
#puts args.inspect
|
54
|
+
@un = args[:username]
|
55
|
+
@pw = args[:password]
|
56
|
+
@oauth_id = args[:id]
|
57
|
+
@oauth_secret = args[:secret]
|
58
|
+
@oauth_redir_uri = args[:redir_uri] || 'urn:ietf:wg:oauth:2.0:oob'
|
59
|
+
@host = args[:host] || 'https://www.popuparchive.com'
|
60
|
+
@debug = args[:debug]
|
61
|
+
@user_agent = args[:user_agent] || 'popuparchive-ruby-client/'+version()
|
62
|
+
@api_endpoint = args[:api_endpoint] || '/api'
|
63
|
+
@croak_on_404 = args[:croak_on_404] || false
|
64
|
+
|
65
|
+
# normalize host
|
66
|
+
@host.gsub!(/\/$/, '')
|
67
|
+
|
68
|
+
# sanity check
|
69
|
+
begin
|
70
|
+
uri = URI.parse(@host)
|
71
|
+
rescue URI::InvalidURIError => err
|
72
|
+
raise "Bad :host value " + err
|
73
|
+
end
|
74
|
+
if (!uri.host || !uri.port)
|
75
|
+
raise "Bad :host value " + @server
|
76
|
+
end
|
77
|
+
|
78
|
+
@agent = get_agent()
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
def get_oauth_token(options={})
|
83
|
+
oauth_options = {
|
84
|
+
site: @host + @api_endpoint,
|
85
|
+
authorize_url: @host + '/oauth/authorize',
|
86
|
+
token_url: @host + '/oauth/token',
|
87
|
+
redirect_uri: @oauth_redir_uri,
|
88
|
+
connection_opts: options.merge( { :ssl => {:verify => false}, } )
|
89
|
+
}
|
90
|
+
|
91
|
+
# TODO
|
92
|
+
|
93
|
+
client = OAuth2::Client.new(@oauth_id, @oauth_secret, oauth_options) do |faraday|
|
94
|
+
faraday.request :url_encoded
|
95
|
+
faraday.response :logger if @debug
|
96
|
+
faraday.adapter :excon
|
97
|
+
end
|
98
|
+
|
99
|
+
token = nil
|
100
|
+
if @un && @pw
|
101
|
+
# TODO 3-legged oauth to @authorize_url
|
102
|
+
else
|
103
|
+
token = client.client_credentials.get_token()
|
104
|
+
end
|
105
|
+
|
106
|
+
return token
|
107
|
+
end
|
108
|
+
|
109
|
+
def get_agent()
|
110
|
+
uri = @host + @api_endpoint
|
111
|
+
opts = {
|
112
|
+
:url => uri,
|
113
|
+
:ssl => {:verify => false},
|
114
|
+
:headers => {
|
115
|
+
'User-Agent' => @user_agent,
|
116
|
+
'Accept' => 'application/json',
|
117
|
+
'Cookie' => @cookies
|
118
|
+
}
|
119
|
+
}
|
120
|
+
@token = get_oauth_token
|
121
|
+
#puts "token="
|
122
|
+
#puts pp(@token)
|
123
|
+
conn = Faraday.new(opts) do |faraday|
|
124
|
+
faraday.request :url_encoded
|
125
|
+
[:mashify, :json].each{|mw| faraday.response(mw) }
|
126
|
+
if !@croak_on_404
|
127
|
+
faraday.use PopUpArchive::FaradayErrHandler
|
128
|
+
else
|
129
|
+
faraday.response(:raise_error)
|
130
|
+
end
|
131
|
+
faraday.request :authorization, 'Bearer', @token.token
|
132
|
+
faraday.response :logger if @debug
|
133
|
+
faraday.adapter :excon # IMPORTANT this is last
|
134
|
+
end
|
135
|
+
|
136
|
+
return conn
|
137
|
+
end
|
138
|
+
|
139
|
+
def get(path, params={})
|
140
|
+
resp = @agent.get @api_endpoint + path, params
|
141
|
+
@debug and puts pp(resp)
|
142
|
+
return PopUpArchive::Response.new resp
|
143
|
+
end
|
144
|
+
|
145
|
+
def post(path, body, content_type='application/json')
|
146
|
+
uri = @api_endpoint + path
|
147
|
+
resp = @agent.post do|req|
|
148
|
+
req.url uri
|
149
|
+
req.body = body
|
150
|
+
req.headers['Content-Type'] = content_type
|
151
|
+
end
|
152
|
+
return PopUpArchive::Response.new resp
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_or_create_item(filename)
|
156
|
+
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
def get_collection(coll_id)
|
161
|
+
resp = get('/collections/'+coll_id.to_s)
|
162
|
+
return resp.http_resp.body
|
163
|
+
end
|
164
|
+
|
165
|
+
def get_items(coll_id)
|
166
|
+
resp = get('/collections/'+coll_id.to_s+'/items')
|
167
|
+
return resp.http_resp.body.items
|
168
|
+
end
|
169
|
+
|
170
|
+
def get_item(coll_id, item_id)
|
171
|
+
resp = get('/collections/'+coll_id.to_s+'/items/'+item_id.to_s)
|
172
|
+
return resp.http_resp.body
|
173
|
+
end
|
174
|
+
|
175
|
+
def get_my_uploads
|
176
|
+
resp = get('/collections')
|
177
|
+
my_uploads = nil
|
178
|
+
resp.collections.each do|coll|
|
179
|
+
if coll.title == 'My Uploads'
|
180
|
+
my_uploads = coll
|
181
|
+
end
|
182
|
+
end
|
183
|
+
return my_uploads
|
184
|
+
end
|
185
|
+
|
186
|
+
def create_item(coll, attrs)
|
187
|
+
resp = post("/collections/#{coll.id}/items", JSON.generate(attrs))
|
188
|
+
return resp.http_resp.body
|
189
|
+
end
|
190
|
+
|
191
|
+
def create_audio_file(item, attrs={})
|
192
|
+
file = attrs.has_key?(:file) ? URI::encode(attrs.delete(:file)) : nil
|
193
|
+
uri = "/items/#{item.id}/audio_files"
|
194
|
+
uri += "?file=#{file}" if file
|
195
|
+
body = JSON.generate({:audio_file => attrs})
|
196
|
+
resp = post(uri, body)
|
197
|
+
#puts "audio_file:"
|
198
|
+
#puts pp resp
|
199
|
+
return resp.http_resp.body
|
200
|
+
end
|
201
|
+
|
202
|
+
def start_upload(item, af_id, fileattrs)
|
203
|
+
|
204
|
+
# workflow is:
|
205
|
+
# (1) GET upload_to with audio_file.id
|
206
|
+
# (2) using response, GET get_init_signature
|
207
|
+
# (example: "key"=>"$token/$filename", "mime_type"=>"image/jpeg", "filename"=>"$filename", "filesize"=>"$n", "last_modified"=>"$e", "item_id"=>"$iid", "audio_file_id"=>"$afid"}
|
208
|
+
# (3) using upload_id, GET get_all_signatures
|
209
|
+
# (example: {"key"=>"$token/$filename", "mime_type"=>"image/jpeg", "num_chunks"=>"1", "upload_id"=>"$upid", "filename"=>"$filename", "filesize"=>"$n", "last_modified"=>"$e", "item_id"=>"$iid", "audio_file_id"=>"$afid"}
|
210
|
+
# (4) foreach chunk, GET chunk_loaded (optional, since only UI needs this, not a SDK)
|
211
|
+
# (5) finally, GET upload_finished (see finish_upload() below)
|
212
|
+
|
213
|
+
# check 'item' for validity
|
214
|
+
if !item.has_key? :token
|
215
|
+
raise ":token missing on item"
|
216
|
+
end
|
217
|
+
if !item.has_key? :id
|
218
|
+
raise ":id missing on item"
|
219
|
+
end
|
220
|
+
|
221
|
+
# check 'fileattrs' for validity
|
222
|
+
if !fileattrs.has_key? :filename
|
223
|
+
raise ":filename missing on fileattrs"
|
224
|
+
end
|
225
|
+
if !fileattrs.has_key? :lastmod
|
226
|
+
raise ":lastmod missing on fileattrs"
|
227
|
+
end
|
228
|
+
if !fileattrs.has_key? :size
|
229
|
+
raise ":size missing on fileattrs"
|
230
|
+
end
|
231
|
+
if !fileattrs.has_key? :mime_type
|
232
|
+
raise ":mime_type missing on fileattrs"
|
233
|
+
end
|
234
|
+
|
235
|
+
base_uri = "/items/#{item.id}/audio_files/#{af_id}"
|
236
|
+
|
237
|
+
upload_to_uri = "#{base_uri}/upload_to"
|
238
|
+
upload_to_resp = get(upload_to_uri)
|
239
|
+
puts "upload_to_resp:"
|
240
|
+
puts pp upload_to_resp
|
241
|
+
|
242
|
+
sig_key = "#{item[:token]}/#{fileattrs[:filename]}"
|
243
|
+
sig_params = {
|
244
|
+
:key => sig_key,
|
245
|
+
:mime_type => fileattrs[:mime_type],
|
246
|
+
:filename => fileattrs[:filename],
|
247
|
+
:filesize => fileattrs[:size],
|
248
|
+
:last_modified => fileattrs[:lastmod],
|
249
|
+
:item_id => item.id,
|
250
|
+
:audio_file_id => af_id,
|
251
|
+
}
|
252
|
+
init_sig_uri = "#{base_uri}/get_init_signature?" + Faraday::FlatParamsEncoder.encode(sig_params)
|
253
|
+
init_sig_resp = get(init_sig_uri)
|
254
|
+
puts "init_sig_resp:"
|
255
|
+
puts pp init_sig_resp
|
256
|
+
|
257
|
+
# build the authorization key
|
258
|
+
upload_key = upload_to_resp['key']
|
259
|
+
authz_key = "#{upload_to_resp.provider} #{upload_key}:#{init_sig_resp.signature}"
|
260
|
+
|
261
|
+
# special agent for aws
|
262
|
+
aws_url = "https://#{upload_to_resp.bucket}.s3.amazonaws.com/#{sig_key}"
|
263
|
+
aws_opts = {
|
264
|
+
:headers => {
|
265
|
+
'User-Agent' => @user_agent,
|
266
|
+
'Authorization' => authz_key,
|
267
|
+
'x-amz-date' => init_sig_resp.date,
|
268
|
+
},
|
269
|
+
:ssl => { :verify => false }
|
270
|
+
}
|
271
|
+
aws_agent = Faraday.new(aws_opts) do |faraday|
|
272
|
+
[:mashify, :xml, :raise_error].each{|mw| faraday.response(mw) }
|
273
|
+
faraday.response :logger if @debug
|
274
|
+
faraday.adapter :excon # IMPORTANT this is last
|
275
|
+
end
|
276
|
+
|
277
|
+
# post to provider to get the upload_id
|
278
|
+
puts "aws_agent.post"
|
279
|
+
aws_resp = aws_agent.post do |req|
|
280
|
+
req.url "#{aws_url}?uploads"
|
281
|
+
req.headers['Content-Type'] = fileattrs[:mime_type]
|
282
|
+
req.headers['Content-Disposition'] = "attachment; filename=" + fileattrs[:filename]
|
283
|
+
req.headers['x-amz-acl'] = 'public-read' # TODO
|
284
|
+
end
|
285
|
+
puts "aws response:"
|
286
|
+
puts pp aws_resp
|
287
|
+
|
288
|
+
# pull out the AWS uploadId
|
289
|
+
sig_params[:upload_id] = aws_resp.body.InitiateMultipartUploadResult.UploadId
|
290
|
+
|
291
|
+
# how many chunks do we expect?
|
292
|
+
sig_params[:num_chunks] = fileattrs.has_key?(:num_chunks) ? fileattrs[:num_chunks] : 1
|
293
|
+
|
294
|
+
all_sig_resp = get("#{base_uri}/get_all_signatures?" + Faraday::FlatParamsEncoder.encode(sig_params))
|
295
|
+
puts "all_sig_resp"
|
296
|
+
puts pp all_sig_resp
|
297
|
+
|
298
|
+
return {
|
299
|
+
:signatures => all_sig_resp.http_resp.body,
|
300
|
+
:params => sig_params,
|
301
|
+
:fileattrs => fileattrs,
|
302
|
+
:upload_to => upload_to_resp.http_resp.body,
|
303
|
+
:init_sig => init_sig_resp.http_resp.body,
|
304
|
+
:aws_url => aws_url,
|
305
|
+
:aws_agent => aws_agent,
|
306
|
+
}
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
def finish_upload(upload)
|
311
|
+
puts "finish_upload:"
|
312
|
+
puts pp upload
|
313
|
+
|
314
|
+
# workflow assumes file has been successfully PUT to AWS
|
315
|
+
# (1) GET aws_url?uploadId=$uploadId
|
316
|
+
# parse xml response for ListPartsResult
|
317
|
+
# (2) POST aws_url?uplaodId=$uploadId with body
|
318
|
+
# <CompleteMultipartUpload>
|
319
|
+
# <Part><PartNumber>1</PartNumber><ETag>"$etag_from_ListPartsResult"</ETag></Part>
|
320
|
+
# </CompleteMultipartUpload>
|
321
|
+
# response to POST contains CompleteMultipartUploadResult with final location
|
322
|
+
# (3) GET pua_url/upload_finished/? with params:
|
323
|
+
# filename => $filename,
|
324
|
+
# filesize => $n,
|
325
|
+
# key => $sig_key,
|
326
|
+
# last_modified => $e,
|
327
|
+
# upload_id => $aws_uploadId,
|
328
|
+
|
329
|
+
# sanity check upload object
|
330
|
+
[:signatures, :params, :fileattrs, :upload_to, :init_sig, :aws_url, :aws_agent].each do|p|
|
331
|
+
if !upload.has_key? p
|
332
|
+
raise ":#{p} missing from upload"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
aws_upload_id = upload[:params][:upload_id]
|
337
|
+
aws_agent = upload[:aws_agent]
|
338
|
+
parts_resp = aws_agent.get(upload[:aws_url], {:uploadId => aws_upload_id})
|
339
|
+
|
340
|
+
aws_parts = []
|
341
|
+
parts_resp.ListPartsResult.Part.each do|part|
|
342
|
+
aws_parts.push( { PartNumber: part.PartNumber, ETag: part.ETag })
|
343
|
+
end
|
344
|
+
aws_parts_xml = XmlSimple.xml_out({Part: aws_parts}, {rootname: 'CompleteMultipartUpload', noattr: true})
|
345
|
+
puts "aws_parts_xml: #{aws_parts_xml}"
|
346
|
+
aws_finish_resp = aws_agent.post do|req|
|
347
|
+
req.url "#{upload[:aws_url]}?uploadId=#{aws_upload_id}"
|
348
|
+
req.headers['Content-Type'] = upload[:fileattrs][:mime_type]
|
349
|
+
req.headers['Content-Disposition'] = "attachment; filename=" + upload[:fileattrs][:filename]
|
350
|
+
req.body = aws_parts_xml
|
351
|
+
end
|
352
|
+
|
353
|
+
puts "aws_finish_resp:"
|
354
|
+
puts pp aws_finish_resp
|
355
|
+
|
356
|
+
end
|
357
|
+
|
358
|
+
end # end Client
|
359
|
+
|
360
|
+
# dependent classes
|
361
|
+
class Response
|
362
|
+
|
363
|
+
attr_accessor :http_resp
|
364
|
+
|
365
|
+
def initialize(http_resp)
|
366
|
+
@http_resp = http_resp
|
367
|
+
|
368
|
+
#warn http_resp.headers.inspect
|
369
|
+
#warn "code=" + http_resp.status.to_s
|
370
|
+
|
371
|
+
@is_ok = false
|
372
|
+
if http_resp.status.to_s =~ /^2\d\d/
|
373
|
+
@is_ok = true
|
374
|
+
end
|
375
|
+
|
376
|
+
end
|
377
|
+
|
378
|
+
def status()
|
379
|
+
return @http_resp.status
|
380
|
+
end
|
381
|
+
|
382
|
+
def is_success()
|
383
|
+
return @is_ok
|
384
|
+
end
|
385
|
+
|
386
|
+
def method_missing(meth, *args, &block)
|
387
|
+
if @http_resp.body.respond_to? meth
|
388
|
+
@http_resp.body.send(meth, *args, &block)
|
389
|
+
else
|
390
|
+
super
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def respond_to?(meth)
|
395
|
+
if @http_resp.body.respond_to? meth
|
396
|
+
true
|
397
|
+
else
|
398
|
+
super
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
end # end Response
|
403
|
+
|
404
|
+
end # end module
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: popuparchive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Karman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-09-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: faraday_middleware
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: excon
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: hashie
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: oauth2
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: xml-simple
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: dotenv
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: mime-types
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Ruby client for the Pop Up Archive API. See https://www.popuparchive.com/
|
140
|
+
email: peter@popuparchive.com
|
141
|
+
executables: []
|
142
|
+
extensions: []
|
143
|
+
extra_rdoc_files: []
|
144
|
+
files:
|
145
|
+
- lib/popuparchive.rb
|
146
|
+
homepage: https://github.com/popuparchive/pua-sdk-ruby
|
147
|
+
licenses:
|
148
|
+
- Apache
|
149
|
+
metadata: {}
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
requirements: []
|
165
|
+
rubyforge_project: nowarning
|
166
|
+
rubygems_version: 2.0.14
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: Ruby client for the Pop Up Archive API
|
170
|
+
test_files: []
|