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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d849a0a1482e71d93c6c90ea5a9336e2ced2be6
4
- data.tar.gz: 1d87cae0a38cc7dddce3d76a08a5181a7d11587b
3
+ metadata.gz: b5931e8212db98c33d0d78dc0b857214a27f8c33
4
+ data.tar.gz: 9cc17e75a60b88e87f75e561a7f203874e769898
5
5
  SHA512:
6
- metadata.gz: 14a4a61707cf76f83ceff137473dd8343b2ec7a1a0f5c645900c78dbb472516a943075cdae551be02efdf2e7e527e87e7a01afe62e21299738d23f04914382e0
7
- data.tar.gz: 3720a5b353199f1be826f663b758873d0311d0fa4a032a9ebdb99bcfeedcad5059bf4e9b180fea48e2b359dd5726551e6093a89bd718a3bb9b9c6c1b0ebaa47d
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 default validators. You can
175
- control with validators are used by overriding the `validators` method within
176
- the controller:
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?` with a boolean value and
192
- provides a list of errors. See the [ImageValidator][1] for an example validator
193
- that only performs validation when all fragments are complete.
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
@@ -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
@@ -8,6 +8,7 @@ require 'fragmenter/rails/model'
8
8
  require 'fragmenter/services/uploader'
9
9
  require 'fragmenter/services/storer'
10
10
  require 'fragmenter/validators/checksum_validator'
11
+ require 'fragmenter/validators/image_validator'
11
12
 
12
13
  module Fragmenter
13
14
  class << self
@@ -11,7 +11,7 @@ module Fragmenter
11
11
 
12
12
  def body
13
13
  if @body.respond_to?(:read)
14
- @body.read
14
+ @body.read.tap { |_| @body.rewind }
15
15
  else
16
16
  @body
17
17
  end
@@ -16,7 +16,7 @@ module Fragmenter
16
16
  end
17
17
 
18
18
  def store
19
- stored = valid? && storer.store
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 valid?
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
@@ -10,6 +10,10 @@ module Fragmenter
10
10
  @errors = []
11
11
  end
12
12
 
13
+ def part?
14
+ true
15
+ end
16
+
13
17
  def valid?
14
18
  matches = expected.nil? || expected == calculated
15
19
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Fragmenter
2
- VERSION = '1.0.0.rc1'
2
+ VERSION = '1.0.0.rc2'
3
3
  end
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.valid?
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
- Validator = Fragmenter::Validators::ChecksumValidator
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(Validator.new(double(headers: {}))).to be_valid
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(Validator.new(request)).to be_valid
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(Validator.new(request)).to_not be_valid
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
- validator = Validator.new(request)
36
- validator.valid?
37
+ instance = validator.new(request)
38
+ instance.valid?
37
39
 
38
- expect(validator.errors.length).to be_nonzero
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.rc1
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-18 00:00:00.000000000 Z
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