fragmenter 1.0.0.rc2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/{HISTORY.md → CHANGELOG.md} +5 -0
- data/README.md +12 -0
- data/fragmenter.gemspec +4 -2
- data/lib/fragmenter.rb +1 -0
- data/lib/fragmenter/dummy_io.rb +13 -0
- data/lib/fragmenter/rails/controller.rb +5 -5
- data/lib/fragmenter/version.rb +1 -1
- data/lib/fragmenter/wrapper.rb +6 -0
- data/spec/fragmenter/dummy_io_spec.rb +39 -0
- data/spec/fragmenter/rails/controller_spec.rb +5 -4
- data/spec/fragmenter/wrapper_spec.rb +25 -10
- data/spec/requests/uploading_fragments_spec.rb +105 -0
- data/spec/spec_helper.rb +3 -0
- data/spec/support/resource.rb +9 -0
- data/spec/support/uploads_app.rb +30 -0
- data/spec/upload_server.rb +11 -0
- metadata +44 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 492790d602b011f85a9c59b2e7730e897b50b4c2
|
4
|
+
data.tar.gz: 15ce8668a07766801e7d1fbfa946e0a51f8481f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c5961781e29d00c847b1683e87f50daf5bff5998db3eb92c0acc61065c9a8a657c4a178b695f2002ae3fcc99a81db81e8047f5679403d058554ebb3d9e213f3
|
7
|
+
data.tar.gz: afe3e60454e3b2e304cd351562befcad35168fddc5d0624c4ca4c4284b174a0c318323bdecf09c6e631fb9d6f92b483ba77322bffd6e9099baea8073a3d8eb4e
|
data/{HISTORY.md → CHANGELOG.md}
RENAMED
data/README.md
CHANGED
@@ -143,6 +143,18 @@ curl -i
|
|
143
143
|
#=> { "content_type": "image/jpeg", "fragments": [1,2], "total": 2 }
|
144
144
|
```
|
145
145
|
|
146
|
+
If you need to customize the status codes for partial or complete `PUT`
|
147
|
+
requests you can override the `update_status` method within the controller:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
private
|
151
|
+
|
152
|
+
# Return 201 Created instead of 202 Accepted
|
153
|
+
def update_status
|
154
|
+
uploader.complete? ? 201 : 200
|
155
|
+
end
|
156
|
+
```
|
157
|
+
|
146
158
|
### Validation
|
147
159
|
|
148
160
|
Often you will want to be sure that all of the data is being stored without any
|
data/fragmenter.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
|
|
10
10
|
gem.email = ['parker@sorentwo.com']
|
11
11
|
gem.homepage = 'https://github.com/dscout/fragmenter'
|
12
12
|
gem.license = 'MIT'
|
13
|
-
gem.description =
|
13
|
+
gem.description = 'Fragmentize and rebuild data'
|
14
14
|
gem.summary = <<-SUMMARY
|
15
15
|
Multipart upload support backed by Redis. Fragmenter handles storing
|
16
16
|
multiple parts of a larger binary and rebuilding it back into the original
|
@@ -22,5 +22,7 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.require_paths = ['lib']
|
23
23
|
|
24
24
|
gem.add_dependency 'redis', '~> 3.0.0'
|
25
|
-
gem.add_development_dependency 'rspec',
|
25
|
+
gem.add_development_dependency 'rspec', '~> 2.14.0'
|
26
|
+
gem.add_development_dependency 'rack-test', '~> 0.6.2'
|
27
|
+
gem.add_development_dependency 'sinatra', '~> 1.4.3'
|
26
28
|
end
|
data/lib/fragmenter.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Fragmenter
|
2
|
+
class DummyIO < StringIO
|
3
|
+
attr_writer :content_type, :original_filename
|
4
|
+
|
5
|
+
def original_filename
|
6
|
+
@original_filename || ['dummy', content_type.split('/').last].join('.')
|
7
|
+
end
|
8
|
+
|
9
|
+
def content_type
|
10
|
+
@content_type || 'application/octet-stream'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -2,7 +2,7 @@ module Fragmenter
|
|
2
2
|
module Rails
|
3
3
|
module Controller
|
4
4
|
def show
|
5
|
-
render json: fragmenter.as_json
|
5
|
+
render json: fragmenter.as_json, status: 200
|
6
6
|
end
|
7
7
|
|
8
8
|
def update
|
@@ -11,14 +11,14 @@ module Fragmenter
|
|
11
11
|
else
|
12
12
|
render json: {
|
13
13
|
message: 'Upload of part failed.', errors: uploader.errors
|
14
|
-
}, status:
|
14
|
+
}, status: 422
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
def destroy
|
19
19
|
fragmenter.clean!
|
20
20
|
|
21
|
-
render nothing: true, status:
|
21
|
+
render nothing: true, status: 204
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
@@ -37,13 +37,13 @@ module Fragmenter
|
|
37
37
|
resource: resource,
|
38
38
|
fragmenter: fragmenter,
|
39
39
|
body: request.body,
|
40
|
-
headers: request.
|
40
|
+
headers: request.env
|
41
41
|
), validators
|
42
42
|
)
|
43
43
|
end
|
44
44
|
|
45
45
|
def update_status
|
46
|
-
uploader.complete? ?
|
46
|
+
uploader.complete? ? 202 : 200
|
47
47
|
end
|
48
48
|
end
|
49
49
|
end
|
data/lib/fragmenter/version.rb
CHANGED
data/lib/fragmenter/wrapper.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'fragmenter/dummy_io'
|
2
|
+
|
3
|
+
describe Fragmenter::DummyIO do
|
4
|
+
it 'provies IO like access' do
|
5
|
+
io = Fragmenter::DummyIO.new
|
6
|
+
|
7
|
+
expect(io).to respond_to(:read)
|
8
|
+
expect(io).to respond_to(:length)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '#content_type' do
|
12
|
+
it 'defaults to application/octet-stream' do
|
13
|
+
expect(Fragmenter::DummyIO.new.content_type).to eq('application/octet-stream')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can be overridden' do
|
17
|
+
io = Fragmenter::DummyIO.new
|
18
|
+
io.content_type = 'image/png'
|
19
|
+
|
20
|
+
expect(io.content_type).to eq('image/png')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#original_filename' do
|
25
|
+
it 'defaults to a fake mime comprised of dummy and the content type' do
|
26
|
+
io = Fragmenter::DummyIO.new
|
27
|
+
io.content_type = 'image/png'
|
28
|
+
|
29
|
+
expect(io.original_filename).to eq('dummy.png')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can be overriden' do
|
33
|
+
io = Fragmenter::DummyIO.new
|
34
|
+
io.original_filename = 'wonderful.png'
|
35
|
+
|
36
|
+
expect(io.original_filename).to eq('wonderful.png')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -22,7 +22,8 @@ describe Fragmenter::Rails::Controller do
|
|
22
22
|
controller.show
|
23
23
|
|
24
24
|
expect(controller).to have_received(:render).with(
|
25
|
-
json: { 'fragments' => [] }
|
25
|
+
json: { 'fragments' => [] },
|
26
|
+
status: 200
|
26
27
|
)
|
27
28
|
end
|
28
29
|
end
|
@@ -40,7 +41,7 @@ describe Fragmenter::Rails::Controller do
|
|
40
41
|
expect(resource.fragmenter).to have_received(:clean!)
|
41
42
|
expect(controller).to have_received(:render).with(
|
42
43
|
nothing: true,
|
43
|
-
status:
|
44
|
+
status: 204
|
44
45
|
)
|
45
46
|
end
|
46
47
|
end
|
@@ -59,7 +60,7 @@ describe Fragmenter::Rails::Controller do
|
|
59
60
|
expect(uploader).to have_received(:store)
|
60
61
|
expect(controller).to have_received(:render).with(
|
61
62
|
json: { 'fragments' => [] },
|
62
|
-
status:
|
63
|
+
status: 200
|
63
64
|
)
|
64
65
|
end
|
65
66
|
|
@@ -78,7 +79,7 @@ describe Fragmenter::Rails::Controller do
|
|
78
79
|
message: 'Upload of part failed.',
|
79
80
|
errors: []
|
80
81
|
},
|
81
|
-
status:
|
82
|
+
status: 422
|
82
83
|
)
|
83
84
|
end
|
84
85
|
end
|
@@ -1,15 +1,17 @@
|
|
1
|
-
require 'fragmenter
|
1
|
+
require 'fragmenter'
|
2
2
|
|
3
3
|
describe Fragmenter::Wrapper do
|
4
4
|
let(:object) { double(:object, id: 1001) }
|
5
5
|
let(:engine_class) { double(:engine_class, new: engine) }
|
6
6
|
let(:engine) { double(:engine) }
|
7
7
|
|
8
|
-
subject(:
|
8
|
+
subject(:wrapper) do
|
9
|
+
Fragmenter::Wrapper.new(object, engine_class)
|
10
|
+
end
|
9
11
|
|
10
12
|
describe '#key' do
|
11
13
|
it 'composes a key from the object class and id value' do
|
12
|
-
|
14
|
+
expect(wrapper.key).to match(/[a-z]+-\d+/)
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -18,14 +20,14 @@ describe Fragmenter::Wrapper do
|
|
18
20
|
let(:headers) { {} }
|
19
21
|
|
20
22
|
it 'delegates #store to the storage engine' do
|
21
|
-
engine.
|
23
|
+
expect(engine).to receive(:store).with(blob, headers)
|
22
24
|
|
23
|
-
|
25
|
+
wrapper.store(blob, headers)
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'delegates #fragments to the storage engine' do
|
27
|
-
engine.
|
28
|
-
|
29
|
+
expect(engine).to receive(:fragments)
|
30
|
+
wrapper.fragments
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
@@ -34,9 +36,22 @@ describe Fragmenter::Wrapper do
|
|
34
36
|
engine.stub('meta' => { 'content_type' => 'application/octet-stream' },
|
35
37
|
'fragments' => ['1', '2'])
|
36
38
|
|
37
|
-
|
38
|
-
json.
|
39
|
-
json.
|
39
|
+
wrapper.as_json.tap do |json|
|
40
|
+
expect(json).to have_key('content_type')
|
41
|
+
expect(json).to have_key('fragments')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#to_io' do
|
47
|
+
it 'wraps the rebuilt data in a Rack::Multipart::UploadedFile compatible IO object' do
|
48
|
+
engine.stub(meta: { 'content_type' => 'image/png' }, rebuild: '0101010')
|
49
|
+
|
50
|
+
wrapper.to_io.tap do |io|
|
51
|
+
expect(io).to be_instance_of(Fragmenter::DummyIO)
|
52
|
+
expect(io.read).to eq('0101010')
|
53
|
+
expect(io.content_type).to eq('image/png')
|
54
|
+
expect(io.original_filename).to eq('dummy.png')
|
40
55
|
end
|
41
56
|
end
|
42
57
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'json'
|
3
|
+
require 'rack/test'
|
4
|
+
require 'support/resource'
|
5
|
+
require 'support/uploads_app'
|
6
|
+
|
7
|
+
describe 'Uploading Fragments' do
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
let(:app) { UploadsApp }
|
11
|
+
let(:resource) { Resource.new(200) }
|
12
|
+
|
13
|
+
around do |example|
|
14
|
+
UploadsApp.resource = resource
|
15
|
+
Fragmenter.logger = Logger.new('/dev/null')
|
16
|
+
|
17
|
+
example.run
|
18
|
+
|
19
|
+
Fragmenter.logger = nil
|
20
|
+
UploadsApp.resource = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'Lists uploaded fragments' do
|
24
|
+
get '/'
|
25
|
+
|
26
|
+
expect(last_response.status).to eq(200)
|
27
|
+
expect(decoded_response).to eq('fragments' => [])
|
28
|
+
|
29
|
+
store_fragment(number: 1, total: 2)
|
30
|
+
|
31
|
+
get '/'
|
32
|
+
|
33
|
+
expect(last_response.status).to eq(200)
|
34
|
+
expect(decoded_response).to eq(
|
35
|
+
'content_type' => 'application/octet-stream',
|
36
|
+
'fragments' => %w[1],
|
37
|
+
'total' => '2'
|
38
|
+
)
|
39
|
+
|
40
|
+
clean_fragments!
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'Stores uploaded fragments' do
|
44
|
+
header 'Content-Type', 'image/gif'
|
45
|
+
header 'X-Fragment-Number', '1'
|
46
|
+
header 'X-Fragment-Total', '2'
|
47
|
+
|
48
|
+
put '/', file_data('micro.gif')
|
49
|
+
|
50
|
+
expect(last_response.status).to eq(200)
|
51
|
+
expect(decoded_response).to eq(
|
52
|
+
'content_type' => 'image/gif',
|
53
|
+
'fragments' => %w[1],
|
54
|
+
'total' => '2'
|
55
|
+
)
|
56
|
+
|
57
|
+
header 'X-Fragment-Number', '2'
|
58
|
+
header 'X-Fragment-Total', '2'
|
59
|
+
|
60
|
+
put '/', file_data('micro.gif')
|
61
|
+
|
62
|
+
expect(last_response.status).to eq(202)
|
63
|
+
expect(decoded_response).to eq('fragments' => [])
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'Destroys uploaded fragments' do
|
67
|
+
store_fragment(number: 1, total: 2)
|
68
|
+
|
69
|
+
delete '/'
|
70
|
+
|
71
|
+
expect(last_response.status).to eq(204)
|
72
|
+
expect(last_response.body).to eq('')
|
73
|
+
expect(fragmenter.fragments.length).to be_zero
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def file_data(file)
|
79
|
+
IO.read("spec/fixtures/#{file}")
|
80
|
+
end
|
81
|
+
|
82
|
+
def decoded_response
|
83
|
+
JSON.parse(last_response.body)
|
84
|
+
end
|
85
|
+
|
86
|
+
def fragmenter
|
87
|
+
resource.fragmenter
|
88
|
+
end
|
89
|
+
|
90
|
+
def store_fragment(options = {})
|
91
|
+
number = options[:number]
|
92
|
+
total = options[:total]
|
93
|
+
|
94
|
+
fragmenter.store(
|
95
|
+
'0101',
|
96
|
+
content_type: 'application/octet-stream',
|
97
|
+
number: number,
|
98
|
+
total: total
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
def clean_fragments!
|
103
|
+
resource.fragmenter.clean!
|
104
|
+
end
|
105
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'fragmenter/rails/controller'
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
class UploadsApp < Sinatra::Base
|
5
|
+
include Fragmenter::Rails::Controller
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :resource
|
9
|
+
end
|
10
|
+
|
11
|
+
get('/') { show }
|
12
|
+
put('/') { update }
|
13
|
+
delete('/') { destroy }
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def resource
|
18
|
+
self.class.resource
|
19
|
+
end
|
20
|
+
|
21
|
+
def render(options)
|
22
|
+
body = if options[:json]
|
23
|
+
JSON.dump(options[:json])
|
24
|
+
else
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
[options[:status], body]
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fragmenter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Parker Selbert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-07-
|
11
|
+
date: 2013-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -38,6 +38,34 @@ dependencies:
|
|
38
38
|
- - ~>
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 2.14.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rack-test
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.6.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.6.2
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sinatra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.4.3
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.4.3
|
41
69
|
description: Fragmentize and rebuild data
|
42
70
|
email:
|
43
71
|
- parker@sorentwo.com
|
@@ -48,13 +76,14 @@ files:
|
|
48
76
|
- .gitignore
|
49
77
|
- .rspec
|
50
78
|
- .travis.yml
|
79
|
+
- CHANGELOG.md
|
51
80
|
- Gemfile
|
52
|
-
- HISTORY.md
|
53
81
|
- LICENSE.txt
|
54
82
|
- README.md
|
55
83
|
- Rakefile
|
56
84
|
- fragmenter.gemspec
|
57
85
|
- lib/fragmenter.rb
|
86
|
+
- lib/fragmenter/dummy_io.rb
|
58
87
|
- lib/fragmenter/fragment.rb
|
59
88
|
- lib/fragmenter/rails/controller.rb
|
60
89
|
- lib/fragmenter/rails/model.rb
|
@@ -67,6 +96,7 @@ files:
|
|
67
96
|
- lib/fragmenter/version.rb
|
68
97
|
- lib/fragmenter/wrapper.rb
|
69
98
|
- spec/fixtures/micro.gif
|
99
|
+
- spec/fragmenter/dummy_io_spec.rb
|
70
100
|
- spec/fragmenter/fragment_spec.rb
|
71
101
|
- spec/fragmenter/rails/controller_spec.rb
|
72
102
|
- spec/fragmenter/rails/model_spec.rb
|
@@ -78,7 +108,11 @@ files:
|
|
78
108
|
- spec/fragmenter/validators/image_validator_spec.rb
|
79
109
|
- spec/fragmenter/wrapper_spec.rb
|
80
110
|
- spec/fragmenter_spec.rb
|
111
|
+
- spec/requests/uploading_fragments_spec.rb
|
81
112
|
- spec/spec_helper.rb
|
113
|
+
- spec/support/resource.rb
|
114
|
+
- spec/support/uploads_app.rb
|
115
|
+
- spec/upload_server.rb
|
82
116
|
homepage: https://github.com/dscout/fragmenter
|
83
117
|
licenses:
|
84
118
|
- MIT
|
@@ -94,9 +128,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
128
|
version: '0'
|
95
129
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
130
|
requirements:
|
97
|
-
- - '
|
131
|
+
- - '>='
|
98
132
|
- !ruby/object:Gem::Version
|
99
|
-
version:
|
133
|
+
version: '0'
|
100
134
|
requirements: []
|
101
135
|
rubyforge_project:
|
102
136
|
rubygems_version: 2.0.0
|
@@ -107,6 +141,7 @@ summary: Multipart upload support backed by Redis. Fragmenter handles storing mu
|
|
107
141
|
have been stored.
|
108
142
|
test_files:
|
109
143
|
- spec/fixtures/micro.gif
|
144
|
+
- spec/fragmenter/dummy_io_spec.rb
|
110
145
|
- spec/fragmenter/fragment_spec.rb
|
111
146
|
- spec/fragmenter/rails/controller_spec.rb
|
112
147
|
- spec/fragmenter/rails/model_spec.rb
|
@@ -118,4 +153,8 @@ test_files:
|
|
118
153
|
- spec/fragmenter/validators/image_validator_spec.rb
|
119
154
|
- spec/fragmenter/wrapper_spec.rb
|
120
155
|
- spec/fragmenter_spec.rb
|
156
|
+
- spec/requests/uploading_fragments_spec.rb
|
121
157
|
- spec/spec_helper.rb
|
158
|
+
- spec/support/resource.rb
|
159
|
+
- spec/support/uploads_app.rb
|
160
|
+
- spec/upload_server.rb
|