fragmenter 1.0.0.rc2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b5931e8212db98c33d0d78dc0b857214a27f8c33
4
- data.tar.gz: 9cc17e75a60b88e87f75e561a7f203874e769898
3
+ metadata.gz: 492790d602b011f85a9c59b2e7730e897b50b4c2
4
+ data.tar.gz: 15ce8668a07766801e7d1fbfa946e0a51f8481f2
5
5
  SHA512:
6
- metadata.gz: 8f48a59ba0829ad7f7366a3e32c3ccb6c9639cabf6cae23fdf54bc6f877e5f184e20bc34e84f83c6cfc9a013923abf3c9deffc1eaf9502e0915fadfdc3bbd6c0
7
- data.tar.gz: 6b15e275846e7eb2d3199016f09f3a7e60a766850bdd17e3db49f0bfce624492b9721ea3c17b3dbb6bbd466ab78982fe98d6161cc05dafc4745b9ea525ee759c
6
+ metadata.gz: 5c5961781e29d00c847b1683e87f50daf5bff5998db3eb92c0acc61065c9a8a657c4a178b695f2002ae3fcc99a81db81e8047f5679403d058554ebb3d9e213f3
7
+ data.tar.gz: afe3e60454e3b2e304cd351562befcad35168fddc5d0624c4ca4c4284b174a0c318323bdecf09c6e631fb9d6f92b483ba77322bffd6e9099baea8073a3d8eb4e
@@ -1,3 +1,8 @@
1
+ # 1.0.0
2
+
3
+ * Provide a convenience IO wrapper for rebuilt data with `Wrapper#to_io`.
4
+ * Add integration specs for testing against the rack request/response interface
5
+
1
6
  # 1.0.0.rc2
2
7
 
3
8
  * Automatically rewind IO objects between reads. This fixes the issue of
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 = %q{Fragmentize and rebuild data}
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', '~> 2.14.0'
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
@@ -2,6 +2,7 @@ require 'logger'
2
2
  require 'redis'
3
3
  require 'fragmenter/redis'
4
4
  require 'fragmenter/version'
5
+ require 'fragmenter/dummy_io'
5
6
  require 'fragmenter/wrapper'
6
7
  require 'fragmenter/rails/controller'
7
8
  require 'fragmenter/rails/model'
@@ -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: :unprocessable_entity
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: :no_content
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.headers
40
+ headers: request.env
41
41
  ), validators
42
42
  )
43
43
  end
44
44
 
45
45
  def update_status
46
- uploader.complete? ? :accepted : :ok
46
+ uploader.complete? ? 202 : 200
47
47
  end
48
48
  end
49
49
  end
@@ -1,3 +1,3 @@
1
1
  module Fragmenter
2
- VERSION = '1.0.0.rc2'
2
+ VERSION = '1.0.0'
3
3
  end
@@ -23,5 +23,11 @@ module Fragmenter
23
23
  def as_json
24
24
  engine.meta.merge('fragments' => engine.fragments)
25
25
  end
26
+
27
+ def to_io
28
+ Fragmenter::DummyIO.new(rebuild).tap do |io|
29
+ io.content_type = meta['content_type']
30
+ end
31
+ end
26
32
  end
27
33
  end
@@ -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: :no_content
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: :ok
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: :unprocessable_entity
82
+ status: 422
82
83
  )
83
84
  end
84
85
  end
@@ -1,15 +1,17 @@
1
- require 'fragmenter/wrapper'
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(:base) { Fragmenter::Wrapper.new(object, engine_class) }
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
- base.key.should match(/[a-z]+-\d+/)
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.should_receive(:store).with(blob, headers)
23
+ expect(engine).to receive(:store).with(blob, headers)
22
24
 
23
- base.store(blob, headers)
25
+ wrapper.store(blob, headers)
24
26
  end
25
27
 
26
28
  it 'delegates #fragments to the storage engine' do
27
- engine.should_receive(:fragments)
28
- base.fragments
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
- base.as_json.tap do |json|
38
- json.should have_key('content_type')
39
- json.should have_key('fragments')
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
@@ -1,5 +1,8 @@
1
+ require 'bundler'
1
2
  require 'fragmenter'
2
3
 
4
+ Bundler.setup
5
+
3
6
  RSpec.configure do |config|
4
7
  config.treat_symbols_as_metadata_keys_with_true_values = true
5
8
  config.run_all_when_everything_filtered = true
@@ -0,0 +1,9 @@
1
+ require 'fragmenter/rails/model'
2
+
3
+ Resource = Struct.new(:id) do
4
+ include Fragmenter::Rails::Model
5
+
6
+ def rebuild_fragments
7
+ fragmenter.rebuild && fragmenter.clean!
8
+ end
9
+ end
@@ -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
@@ -0,0 +1,11 @@
1
+ require 'fragmenter'
2
+
3
+ class Uploads < Sinatra::Base
4
+ include Fragmenter::Rails::Controller
5
+
6
+ put '/' do
7
+ show
8
+ end
9
+ end
10
+
11
+ run Uploads
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.rc2
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-19 00:00:00.000000000 Z
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: 1.3.1
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