fragmenter 1.0.0.rc1 → 1.0.0.rc2
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 +4 -4
- data/HISTORY.md +9 -0
- data/README.md +11 -6
- data/fragmenter.gemspec +1 -0
- data/lib/fragmenter.rb +1 -0
- data/lib/fragmenter/request.rb +1 -1
- data/lib/fragmenter/services/uploader.rb +8 -4
- data/lib/fragmenter/validators/checksum_validator.rb +4 -0
- data/lib/fragmenter/validators/image_validator.rb +42 -0
- data/lib/fragmenter/version.rb +1 -1
- data/spec/fixtures/micro.gif +0 -0
- data/spec/fragmenter/request_spec.rb +5 -0
- data/spec/fragmenter/services/uploader_spec.rb +29 -4
- data/spec/fragmenter/validators/checksum_validator_spec.rb +9 -7
- data/spec/fragmenter/validators/image_validator_spec.rb +35 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5931e8212db98c33d0d78dc0b857214a27f8c33
|
4
|
+
data.tar.gz: 9cc17e75a60b88e87f75e561a7f203874e769898
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f48a59ba0829ad7f7366a3e32c3ccb6c9639cabf6cae23fdf54bc6f877e5f184e20bc34e84f83c6cfc9a013923abf3c9deffc1eaf9502e0915fadfdc3bbd6c0
|
7
|
+
data.tar.gz: 6b15e275846e7eb2d3199016f09f3a7e60a766850bdd17e3db49f0bfce624492b9721ea3c17b3dbb6bbd466ab78982fe98d6161cc05dafc4745b9ea525ee759c
|
data/HISTORY.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# 1.0.0.rc2
|
2
|
+
|
3
|
+
* Automatically rewind IO objects between reads. This fixes the issue of
|
4
|
+
multiple validations preventing storage.
|
5
|
+
* Expose storage errors along with validation errors when using the uploader.
|
6
|
+
* Make good on the documented `ImageValidator`. The implemented version has no
|
7
|
+
additional gem dependencies, but does rely on ImageMagick for its `identify`
|
8
|
+
command.
|
9
|
+
|
1
10
|
# 1.0.0.rc1
|
2
11
|
|
3
12
|
* Add modules for easiy integration with Rails controllers and models.
|
data/README.md
CHANGED
@@ -170,10 +170,11 @@ response with an accompanying message and errors:
|
|
170
170
|
]
|
171
171
|
}
|
172
172
|
```
|
173
|
+
|
173
174
|
As images uploads are a common use-case for fragmented uploading an
|
174
|
-
ImageValidator is included, but not one of the
|
175
|
-
|
176
|
-
|
175
|
+
ImageValidator is included, but not as one of the defaults. You can control
|
176
|
+
which validators are used by overriding the `validators` method within the
|
177
|
+
controller:
|
177
178
|
|
178
179
|
```ruby
|
179
180
|
class AvatarUploader < ApplicationController
|
@@ -188,8 +189,12 @@ end
|
|
188
189
|
```
|
189
190
|
|
190
191
|
To add a custom validator you must add it at some point in the validator chain.
|
191
|
-
A validator can be any class that responds to `valid
|
192
|
-
|
193
|
-
|
192
|
+
A validator can be any class that responds to `valid?`, `part?`, and provides a
|
193
|
+
list of errors. See the [ImageValidator][1] for an example validator that only
|
194
|
+
performs validation when all fragments are complete.
|
195
|
+
|
196
|
+
Note that [ImageMagick][2] is required for the ImageValidator to work, but it
|
197
|
+
doesn't require `RMagick` or `MiniMagick`.
|
194
198
|
|
195
199
|
[1]:lib/fragmenter/validators/image
|
200
|
+
[2]:http://www.imagemagick.org/script/identify.php
|
data/fragmenter.gemspec
CHANGED
@@ -9,6 +9,7 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.authors = ['Parker Selbert']
|
10
10
|
gem.email = ['parker@sorentwo.com']
|
11
11
|
gem.homepage = 'https://github.com/dscout/fragmenter'
|
12
|
+
gem.license = 'MIT'
|
12
13
|
gem.description = %q{Fragmentize and rebuild data}
|
13
14
|
gem.summary = <<-SUMMARY
|
14
15
|
Multipart upload support backed by Redis. Fragmenter handles storing
|
data/lib/fragmenter.rb
CHANGED
data/lib/fragmenter/request.rb
CHANGED
@@ -16,7 +16,7 @@ module Fragmenter
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def store
|
19
|
-
stored =
|
19
|
+
stored = parts_valid? && storer.store && rebuilt_valid?
|
20
20
|
@complete = fragmenter.complete?
|
21
21
|
|
22
22
|
if stored && complete?
|
@@ -27,15 +27,19 @@ module Fragmenter
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def errors
|
30
|
-
validator_instances.map(&:errors).flatten
|
30
|
+
[validator_instances.map(&:errors), storer.errors].flatten
|
31
31
|
end
|
32
32
|
|
33
33
|
def complete?
|
34
34
|
!!@complete
|
35
35
|
end
|
36
36
|
|
37
|
-
def
|
38
|
-
validator_instances.all?(&:valid?)
|
37
|
+
def parts_valid?
|
38
|
+
validator_instances.select(&:part?).all?(&:valid?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def rebuilt_valid?
|
42
|
+
validator_instances.reject(&:part?).all?(&:valid?)
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Fragmenter
|
2
|
+
module Validators
|
3
|
+
class ImageValidator
|
4
|
+
attr_reader :errors, :request
|
5
|
+
|
6
|
+
def initialize(request)
|
7
|
+
@request = request
|
8
|
+
@errors = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def part?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid?
|
16
|
+
return true unless fragmenter.complete?
|
17
|
+
|
18
|
+
identifiable = identifiable?
|
19
|
+
|
20
|
+
unless identifiable
|
21
|
+
errors << 'Rebuilt fragments are not a valid image'
|
22
|
+
end
|
23
|
+
|
24
|
+
identifiable
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def fragmenter
|
30
|
+
request.fragmenter
|
31
|
+
end
|
32
|
+
|
33
|
+
def identifiable?
|
34
|
+
IO.popen('identify -', 'w', err: '/dev/null', out: '/dev/null') do |io|
|
35
|
+
io << fragmenter.rebuild
|
36
|
+
end
|
37
|
+
|
38
|
+
$?.success?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/fragmenter/version.rb
CHANGED
Binary file
|
@@ -7,6 +7,11 @@ describe Fragmenter::Request do
|
|
7
7
|
expect(request.body).to eq('blob')
|
8
8
|
end
|
9
9
|
|
10
|
+
it 'automaticaly rewinds after reading IO' do
|
11
|
+
request = Fragmenter::Request.new(body: StringIO.new('blob'))
|
12
|
+
expect([request.body, request.body]).to eq(%w[blob blob])
|
13
|
+
end
|
14
|
+
|
10
15
|
it 'does not attempt to read the body if it is not IO' do
|
11
16
|
request = Fragmenter::Request.new(body: 'blob')
|
12
17
|
expect(request.body).to eq('blob')
|
@@ -13,8 +13,12 @@ describe Fragmenter::Services::Uploader do
|
|
13
13
|
expect(uploader.store).to be_true
|
14
14
|
end
|
15
15
|
|
16
|
-
it 'does not attempt to store fragments if any validators are invalid' do
|
16
|
+
it 'does not attempt to store fragments if any part validators are invalid' do
|
17
17
|
validator = Struct.new(:request) do
|
18
|
+
def part?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
|
18
22
|
def valid?
|
19
23
|
false
|
20
24
|
end
|
@@ -42,6 +46,25 @@ describe Fragmenter::Services::Uploader do
|
|
42
46
|
|
43
47
|
uploader.store
|
44
48
|
end
|
49
|
+
|
50
|
+
it 'does not instruct the resource to rebuild if the full content is invalid' do
|
51
|
+
validator = Struct.new(:request) do
|
52
|
+
def part?; false; end
|
53
|
+
def valid?; false; end
|
54
|
+
end
|
55
|
+
|
56
|
+
resource = double(:resource)
|
57
|
+
fragmenter = double(:fragmenter, complete?: true)
|
58
|
+
storer = double(:storer, store: true)
|
59
|
+
request = Fragmenter::Request.new(resource: resource, fragmenter: fragmenter)
|
60
|
+
uploader = Uploader.new(request, [validator])
|
61
|
+
|
62
|
+
uploader.storer = storer
|
63
|
+
|
64
|
+
expect(resource).to_not receive(:rebuild_fragments)
|
65
|
+
|
66
|
+
uploader.store
|
67
|
+
end
|
45
68
|
end
|
46
69
|
|
47
70
|
describe '#complete?' do
|
@@ -53,21 +76,23 @@ describe Fragmenter::Services::Uploader do
|
|
53
76
|
end
|
54
77
|
|
55
78
|
describe '#errors' do
|
56
|
-
it 'merges the errors from all validators' do
|
79
|
+
it 'merges the errors from all validators and the storer' do
|
57
80
|
validator_a = Struct.new(:request) do
|
81
|
+
def part?; true; end
|
58
82
|
def valid?; false; end
|
59
83
|
def errors; ['bad']; end
|
60
84
|
end
|
61
85
|
|
62
86
|
validator_b = Struct.new(:request) do
|
87
|
+
def part?; true; end
|
63
88
|
def valid?; false; end
|
64
89
|
def errors; ['invalid']; end
|
65
90
|
end
|
66
91
|
|
67
92
|
uploader = Uploader.new({}, [validator_a, validator_b])
|
68
|
-
uploader.
|
93
|
+
uploader.storer = double(:storer, errors: ['horrible'])
|
69
94
|
|
70
|
-
expect(uploader.errors).to eq(%w[bad invalid])
|
95
|
+
expect(uploader.errors).to eq(%w[bad invalid horrible])
|
71
96
|
end
|
72
97
|
end
|
73
98
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'fragmenter/validators/checksum_validator'
|
2
2
|
|
3
3
|
describe Fragmenter::Validators::ChecksumValidator do
|
4
|
-
|
4
|
+
let(:validator) do
|
5
|
+
Fragmenter::Validators::ChecksumValidator
|
6
|
+
end
|
5
7
|
|
6
8
|
describe '#valid?' do
|
7
9
|
it 'is always valid if no expected checksum was given' do
|
8
|
-
expect(
|
10
|
+
expect(validator.new(double(headers: {}))).to be_valid
|
9
11
|
end
|
10
12
|
|
11
13
|
it 'is valid if the expected checksum matches the body checksum' do
|
@@ -14,7 +16,7 @@ describe Fragmenter::Validators::ChecksumValidator do
|
|
14
16
|
headers: { 'HTTP_CONTENT_MD5' => '4ac8660969d304047daa9c3539f63682' }
|
15
17
|
)
|
16
18
|
|
17
|
-
expect(
|
19
|
+
expect(validator.new(request)).to be_valid
|
18
20
|
end
|
19
21
|
|
20
22
|
it 'is not valid if the expected checksum does not match the body checksum' do
|
@@ -23,7 +25,7 @@ describe Fragmenter::Validators::ChecksumValidator do
|
|
23
25
|
headers: { 'HTTP_CONTENT_MD5' => 'a9c3539f636824ac8660969d304047da' }
|
24
26
|
)
|
25
27
|
|
26
|
-
expect(
|
28
|
+
expect(validator.new(request)).to_not be_valid
|
27
29
|
end
|
28
30
|
|
29
31
|
it 'records an error when the checksums do not match' do
|
@@ -32,10 +34,10 @@ describe Fragmenter::Validators::ChecksumValidator do
|
|
32
34
|
headers: { 'HTTP_CONTENT_MD5' => 'a9c3539f636824ac8660969d304047da' }
|
33
35
|
)
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
+
instance = validator.new(request)
|
38
|
+
instance.valid?
|
37
39
|
|
38
|
-
expect(
|
40
|
+
expect(instance.errors.length).to be_nonzero
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fragmenter/request'
|
2
|
+
require 'fragmenter/validators/image_validator'
|
3
|
+
|
4
|
+
describe Fragmenter::Validators::ImageValidator do
|
5
|
+
let(:validator) do
|
6
|
+
Fragmenter::Validators::ImageValidator
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#valid?' do
|
10
|
+
it 'bypasses validation checking if the fragmenter is incomplete' do
|
11
|
+
fragmenter = double(:fragmenter, complete?: false)
|
12
|
+
request = Fragmenter::Request.new(fragmenter: fragmenter)
|
13
|
+
|
14
|
+
expect(validator.new(request)).to be_valid
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'is invalid with a body that can not be parsed as an image' do
|
18
|
+
fragmenter = double(:fragmenter, complete?: true, rebuild: '01010101')
|
19
|
+
request = Fragmenter::Request.new(fragmenter: fragmenter)
|
20
|
+
|
21
|
+
instance = validator.new(request)
|
22
|
+
|
23
|
+
expect(instance).to_not be_valid
|
24
|
+
expect(instance.errors.length).to be_nonzero
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'is valid with a body that can be parsed as an image' do
|
28
|
+
image = IO.read('spec/fixtures/micro.gif')
|
29
|
+
fragmenter = double(:fragmenter, complete?: true, rebuild: image)
|
30
|
+
request = Fragmenter::Request.new(fragmenter: fragmenter)
|
31
|
+
|
32
|
+
expect(validator.new(request)).to be_valid
|
33
|
+
end
|
34
|
+
end
|
35
|
+
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.rc2
|
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-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -63,8 +63,10 @@ files:
|
|
63
63
|
- lib/fragmenter/services/storer.rb
|
64
64
|
- lib/fragmenter/services/uploader.rb
|
65
65
|
- lib/fragmenter/validators/checksum_validator.rb
|
66
|
+
- lib/fragmenter/validators/image_validator.rb
|
66
67
|
- lib/fragmenter/version.rb
|
67
68
|
- lib/fragmenter/wrapper.rb
|
69
|
+
- spec/fixtures/micro.gif
|
68
70
|
- spec/fragmenter/fragment_spec.rb
|
69
71
|
- spec/fragmenter/rails/controller_spec.rb
|
70
72
|
- spec/fragmenter/rails/model_spec.rb
|
@@ -73,11 +75,13 @@ files:
|
|
73
75
|
- spec/fragmenter/services/storer_spec.rb
|
74
76
|
- spec/fragmenter/services/uploader_spec.rb
|
75
77
|
- spec/fragmenter/validators/checksum_validator_spec.rb
|
78
|
+
- spec/fragmenter/validators/image_validator_spec.rb
|
76
79
|
- spec/fragmenter/wrapper_spec.rb
|
77
80
|
- spec/fragmenter_spec.rb
|
78
81
|
- spec/spec_helper.rb
|
79
82
|
homepage: https://github.com/dscout/fragmenter
|
80
|
-
licenses:
|
83
|
+
licenses:
|
84
|
+
- MIT
|
81
85
|
metadata: {}
|
82
86
|
post_install_message:
|
83
87
|
rdoc_options: []
|
@@ -102,6 +106,7 @@ summary: Multipart upload support backed by Redis. Fragmenter handles storing mu
|
|
102
106
|
parts of a larger binary and rebuilding it back into the original after all parts
|
103
107
|
have been stored.
|
104
108
|
test_files:
|
109
|
+
- spec/fixtures/micro.gif
|
105
110
|
- spec/fragmenter/fragment_spec.rb
|
106
111
|
- spec/fragmenter/rails/controller_spec.rb
|
107
112
|
- spec/fragmenter/rails/model_spec.rb
|
@@ -110,6 +115,7 @@ test_files:
|
|
110
115
|
- spec/fragmenter/services/storer_spec.rb
|
111
116
|
- spec/fragmenter/services/uploader_spec.rb
|
112
117
|
- spec/fragmenter/validators/checksum_validator_spec.rb
|
118
|
+
- spec/fragmenter/validators/image_validator_spec.rb
|
113
119
|
- spec/fragmenter/wrapper_spec.rb
|
114
120
|
- spec/fragmenter_spec.rb
|
115
121
|
- spec/spec_helper.rb
|