massive 0.2.0 → 0.3.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 +4 -4
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -27
- data/README.md +7 -9
- data/lib/massive.rb +16 -22
- data/lib/massive/authenticators/s3.rb +37 -0
- data/lib/massive/file.rb +3 -21
- data/lib/massive/version.rb +1 -1
- data/spec/models/massive/authenticators/s3_spec.rb +119 -0
- data/spec/models/massive/file_spec.rb +15 -19
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6add55169f9bbe782d2ee00b8a033a21aae4d9cb
|
4
|
+
data.tar.gz: 3e7ef6017201515489a3e8f04fb41b8669b701cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2470c8e5230e4b2f4e12a47e93722e16da720971c15c67179aa8f053a1907c1322309f19fdef0572c43211d0b56f62a7d159d6a0c25422ab61cd339deab9a18
|
7
|
+
data.tar.gz: 2e48d557a63fa84092f83acabca19ba70b3d21559bbb0f96f25dee3ee97c16b96e0daac07ccf26c40377046787184d8c5810ec1f00b9831ad54f100073a73b5f
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
massive (0.
|
4
|
+
massive (0.3.0)
|
5
5
|
active_model_serializers
|
6
6
|
file_processor (= 0.2.0)
|
7
7
|
mongoid (~> 4.0.0.beta)
|
@@ -37,26 +37,8 @@ GEM
|
|
37
37
|
debugger-ruby_core_source (1.3.4)
|
38
38
|
diff-lcs (1.2.5)
|
39
39
|
docile (1.1.3)
|
40
|
-
excon (0.33.0)
|
41
40
|
ffi (1.9.3)
|
42
41
|
file_processor (0.2.0)
|
43
|
-
fog (1.22.0)
|
44
|
-
fog-brightbox
|
45
|
-
fog-core (~> 1.21, >= 1.21.1)
|
46
|
-
fog-json
|
47
|
-
nokogiri (~> 1.5, >= 1.5.11)
|
48
|
-
fog-brightbox (0.0.2)
|
49
|
-
fog-core
|
50
|
-
fog-json
|
51
|
-
fog-core (1.22.0)
|
52
|
-
builder
|
53
|
-
excon (~> 0.33)
|
54
|
-
formatador (~> 0.2)
|
55
|
-
mime-types
|
56
|
-
net-scp (~> 1.1)
|
57
|
-
net-ssh (>= 2.1.3)
|
58
|
-
fog-json (1.0.0)
|
59
|
-
multi_json (~> 1.0)
|
60
42
|
formatador (0.2.4)
|
61
43
|
guard (2.6.1)
|
62
44
|
formatador (>= 0.2.4)
|
@@ -75,8 +57,6 @@ GEM
|
|
75
57
|
rb-inotify (>= 0.9)
|
76
58
|
lumberjack (1.0.5)
|
77
59
|
method_source (0.8.2)
|
78
|
-
mime-types (2.2)
|
79
|
-
mini_portile (0.6.0)
|
80
60
|
minitest (5.3.3)
|
81
61
|
mongoid (4.0.0.beta1)
|
82
62
|
activemodel (>= 4.0.0)
|
@@ -89,11 +69,6 @@ GEM
|
|
89
69
|
connection_pool (~> 2.0)
|
90
70
|
optionable (~> 0.2.0)
|
91
71
|
multi_json (1.10.0)
|
92
|
-
net-scp (1.2.1)
|
93
|
-
net-ssh (>= 2.6.5)
|
94
|
-
net-ssh (2.9.1)
|
95
|
-
nokogiri (1.6.2.1)
|
96
|
-
mini_portile (= 0.6.0)
|
97
72
|
optionable (0.2.0)
|
98
73
|
origin (2.1.1)
|
99
74
|
pry (0.9.12.6)
|
@@ -150,7 +125,6 @@ PLATFORMS
|
|
150
125
|
DEPENDENCIES
|
151
126
|
database_cleaner
|
152
127
|
debugger
|
153
|
-
fog
|
154
128
|
guard-rspec
|
155
129
|
massive!
|
156
130
|
rake
|
data/README.md
CHANGED
@@ -168,23 +168,21 @@ Notice that we didn't had to specify how the step would calculate the total coun
|
|
168
168
|
|
169
169
|
We also didn't have to specify how the job would iterate through each row, it is already defined. We just get a CSV::Row, which will be a Hash-like structure where the header of the CSV is the key, so we can just pass it to `User.create`. Of course this is a simple example, you should protect the attributes, or even pass only the ones you want from the CSV.
|
170
170
|
|
171
|
-
The `Massive::File` has support for
|
171
|
+
The `Massive::File` has support for getting files from Amazon AWS S3 service. To use it you must define the `storage_config`:
|
172
172
|
|
173
173
|
```ruby
|
174
|
-
Massive.
|
175
|
-
|
176
|
-
|
177
|
-
|
174
|
+
Massive.storage_config = {
|
175
|
+
key: 'INSERT-YOUR-AWS-KEY-HERE',
|
176
|
+
secret: 'INSERT-YOUR-AWS-SECRET-HERE'
|
177
|
+
directory: 'your-bucket-here', # defaults to 'massive'
|
178
|
+
expiration: 30.minutes # defaults to 1.hour
|
178
179
|
}
|
179
|
-
|
180
|
-
Massive.fog_directory = 'your-bucket-here' # defaults to 'massive'
|
181
|
-
Massive.fog_authenticated_url_expiration = 30.minutes # defaults to 1.hour
|
182
180
|
```
|
183
181
|
|
184
182
|
Then set the `filename` field when creating the `Massive::File` instead of setting its `url`. Notice that the filename should point to the full path within the bucket, not just the actual filename.
|
185
183
|
|
186
184
|
```ruby
|
187
|
-
process = Massive::FileProcess.new(file_attributes: { filename: '
|
185
|
+
process = Massive::FileProcess.new(file_attributes: { filename: 'path/to/my/file.csv' })
|
188
186
|
```
|
189
187
|
|
190
188
|
## Contributing
|
data/lib/massive.rb
CHANGED
@@ -23,38 +23,32 @@ module Massive
|
|
23
23
|
autoload :ProcessSerializer, 'massive/process_serializer'
|
24
24
|
autoload :StepSerializer, 'massive/step_serializer'
|
25
25
|
|
26
|
+
module Authenticators
|
27
|
+
autoload :S3, 'massive/authenticators/s3'
|
28
|
+
end
|
29
|
+
|
26
30
|
class Cancelled < StandardError; end
|
27
31
|
|
28
32
|
def self.redis
|
29
33
|
@redis ||= Resque.redis
|
30
34
|
end
|
31
35
|
|
32
|
-
def self.
|
33
|
-
@
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.fog_credentials=(values=nil)
|
37
|
-
@fog_credentials = values
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.fog_authenticated_url_expiration
|
41
|
-
@fog_authenticated_url_expiration
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.fog_authenticated_url_expiration=(value=nil)
|
45
|
-
@fog_authenticated_url_expiration = value
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.fog_directory
|
49
|
-
@fog_directory
|
36
|
+
def self.storage_config
|
37
|
+
@storage_config
|
50
38
|
end
|
51
39
|
|
52
|
-
def self.
|
53
|
-
@
|
40
|
+
def self.storage_config=(value)
|
41
|
+
@storage_config ||= {}
|
42
|
+
@storage_config.merge!(value)
|
54
43
|
end
|
55
44
|
|
56
|
-
self.
|
57
|
-
|
45
|
+
self.storage_config = {
|
46
|
+
directory: 'massive',
|
47
|
+
provider: Massive::Authenticators::S3,
|
48
|
+
key: nil,
|
49
|
+
secret: nil,
|
50
|
+
expiration: 1 * 60 * 60 # 1 hour
|
51
|
+
}
|
58
52
|
end
|
59
53
|
|
60
54
|
require "resque"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Massive
|
2
|
+
module Authenticators
|
3
|
+
class S3
|
4
|
+
def initialize(filename)
|
5
|
+
@filename = filename
|
6
|
+
end
|
7
|
+
|
8
|
+
def url
|
9
|
+
"https://#{Massive.storage_config[:directory]}.s3.amazonaws.com/#{@filename}#{authentication_params}"
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def authentication_params
|
15
|
+
if Massive.storage_config[:key] && Massive.storage_config[:secret]
|
16
|
+
"?Expires=#{expiration}&AWSAccessKeyId=#{Massive.storage_config[:key]}&Signature=#{signature}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def expiration
|
21
|
+
@expiration ||= Time.now.to_i + Massive.storage_config[:expiration]
|
22
|
+
end
|
23
|
+
|
24
|
+
def signature
|
25
|
+
CGI.escape(
|
26
|
+
Base64.encode64(
|
27
|
+
OpenSSL::HMAC.digest(
|
28
|
+
OpenSSL::Digest.new('sha1'),
|
29
|
+
Massive.storage_config[:secret],
|
30
|
+
"GET\n\n\n#{expiration}\n/#{Massive.storage_config[:directory]}/#{@filename}".encode("UTF-8")
|
31
|
+
)
|
32
|
+
).gsub("\n", "")
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/massive/file.rb
CHANGED
@@ -40,7 +40,7 @@ module Massive
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def url
|
43
|
-
read_attribute(:url).presence ||
|
43
|
+
read_attribute(:url).presence || authenticator.url
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
@@ -59,26 +59,8 @@ module Massive
|
|
59
59
|
}
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
63
|
-
|
64
|
-
fog_file.respond_to?(:url) ? fog_file.url(Time.current.to_i + Massive.fog_authenticated_url_expiration) : fog_file.public_url
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def can_use_fog?
|
69
|
-
filename && Massive.fog_credentials.present?
|
70
|
-
end
|
71
|
-
|
72
|
-
def fog_connection
|
73
|
-
@fog_connection ||= Fog::Storage.new(Massive.fog_credentials)
|
74
|
-
end
|
75
|
-
|
76
|
-
def fog_directory
|
77
|
-
@fog_directory ||= fog_connection.directories.get(Massive.fog_directory)
|
78
|
-
end
|
79
|
-
|
80
|
-
def fog_file
|
81
|
-
@fog_file ||= fog_directory.files.get(filename)
|
62
|
+
def authenticator
|
63
|
+
@authenticator ||= Massive.storage_config[:provider].new(filename)
|
82
64
|
end
|
83
65
|
end
|
84
66
|
end
|
data/lib/massive/version.rb
CHANGED
@@ -0,0 +1,119 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Massive::Authenticators::S3 do
|
4
|
+
let(:filename) { 'some/path/my-filename.png' }
|
5
|
+
subject(:authenticator) { described_class.new(filename) }
|
6
|
+
|
7
|
+
after do
|
8
|
+
Massive.storage_config = {
|
9
|
+
key: nil,
|
10
|
+
secret: nil
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'when no key/secret are set' do
|
15
|
+
it "returns the given url without any authentication params" do
|
16
|
+
expect(authenticator.url).to eq("https://#{Massive.storage_config[:directory]}.s3.amazonaws.com/#{filename}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when key/secret are set' do
|
21
|
+
before do
|
22
|
+
Massive.storage_config[:key] = 'some-key'
|
23
|
+
Massive.storage_config[:secret] = 'some-secret'
|
24
|
+
Time.stub(:now).and_return(Time.parse("2014-05-15T13:25:45Z"))
|
25
|
+
end
|
26
|
+
|
27
|
+
def parsed_query(url)
|
28
|
+
CGI.parse(URI.parse(url).query || '').with_indifferent_access
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns a url pointing to the proper bucket" do
|
32
|
+
expect(URI.parse(authenticator.url).host).to eq("#{Massive.storage_config[:directory]}.s3.amazonaws.com")
|
33
|
+
end
|
34
|
+
|
35
|
+
it "returns a url using https" do
|
36
|
+
expect(URI.parse(authenticator.url).scheme).to eq("https")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns a url with the filename as path" do
|
40
|
+
expect(URI.parse(authenticator.url).path).to eq("/#{filename}")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns a url with the expiration as a query string parameter using a timestamped format" do
|
44
|
+
now = Time.now.tap { |now| Time.stub(:now).and_return(now) }
|
45
|
+
|
46
|
+
expect(parsed_query(authenticator.url)[:Expires].first.to_i).to eq(now.to_i + Massive.storage_config[:expiration])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns a url with the AWSAccessKeyId as a query string parameter using the configured key" do
|
50
|
+
expect(parsed_query(authenticator.url)[:AWSAccessKeyId].first).to eq(Massive.storage_config[:key])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns a url with the Signature as a query string parameter properly signing the GET request" do
|
54
|
+
expect(parsed_query(authenticator.url)[:Signature].first).to eq("FD38leXqSdFYGYrAXgNF8cX98os=")
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when changing the expiration" do
|
58
|
+
before do
|
59
|
+
Time.stub(:now).and_return(Time.parse("2009-08-01T20:03:27Z"))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns a url with the Signature properly signing based on the current time" do
|
63
|
+
expect(parsed_query(authenticator.url)[:Signature].first).to eq("ehM0n71BUPduV9WwWE73PIMmQYM=")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when changing the secret" do
|
68
|
+
before do
|
69
|
+
Massive.storage_config[:secret] = 'other-secret'
|
70
|
+
end
|
71
|
+
|
72
|
+
it "returns a url with the Signature based the configured secret" do
|
73
|
+
expect(parsed_query(authenticator.url)[:Signature].first).to eq("mE6pvT9pRLOUcufs+fX45vbbATQ=")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "when changing the key" do
|
78
|
+
before do
|
79
|
+
Massive.storage_config[:key] = 'other-key'
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns a url with the AWSAccessKeyId as a query string parameter using the configured key" do
|
83
|
+
expect(parsed_query(authenticator.url)[:AWSAccessKeyId].first).to eq(Massive.storage_config[:key])
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when changing the directory" do
|
88
|
+
before do
|
89
|
+
Massive.storage_config[:directory] = 'other-directory'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns a url with the expiration as a query string parameter using a timestamped format" do
|
93
|
+
now = Time.now.tap { |now| Time.stub(:now).and_return(now) }
|
94
|
+
|
95
|
+
expect(parsed_query(authenticator.url)[:Expires].first.to_i).to eq(now.to_i + Massive.storage_config[:expiration])
|
96
|
+
end
|
97
|
+
|
98
|
+
it "returns a url pointing to the proper bucket" do
|
99
|
+
expect(URI.parse(authenticator.url).host).to eq("#{Massive.storage_config[:directory]}.s3.amazonaws.com")
|
100
|
+
end
|
101
|
+
|
102
|
+
it "returns a url with the Signature based the configured directory" do
|
103
|
+
expect(parsed_query(authenticator.url)[:Signature].first).to eq("f37FDYE7lcewcpApQhFYBEUQjhs=")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when changing the filename" do
|
108
|
+
let(:filename) { 'some/other/file.txt' }
|
109
|
+
|
110
|
+
it "returns a url with the filename as path" do
|
111
|
+
expect(URI.parse(authenticator.url).path).to eq("/#{filename}")
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns a url with the Signature based the configured directory" do
|
115
|
+
expect(parsed_query(authenticator.url)[:Signature].first).to eq("fQCrfdk1FhSRZnecyZ2+Jye2HUY=")
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -18,7 +18,11 @@ describe Massive::File do
|
|
18
18
|
|
19
19
|
subject(:file) { process.file = Massive::File.new(url: url, encoding: encoding, col_sep: col_sep) }
|
20
20
|
|
21
|
-
|
21
|
+
def stub_processor
|
22
|
+
FileProcessor::CSV.stub(:new).with(file.url, expected_options).and_return(processor)
|
23
|
+
end
|
24
|
+
|
25
|
+
before { stub_processor }
|
22
26
|
|
23
27
|
describe "#processor" do
|
24
28
|
it "creates a new instance of the CSV file processor, enabling headers but without encoding and separator" do
|
@@ -50,28 +54,20 @@ describe Massive::File do
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
53
|
-
describe "when
|
54
|
-
let(:filename) { 'my-file.txt' }
|
55
|
-
let(:
|
56
|
-
let(:
|
57
|
-
let(:fog_file) { double('File') }
|
58
|
-
let(:authenticated_url) { 'http://my-auth.url.com' }
|
57
|
+
describe "when passing a filename" do
|
58
|
+
let(:filename) { 'my/path/my-file.txt' }
|
59
|
+
let(:url) { 'http://my-auth.url.com' }
|
60
|
+
let(:provider) { double(Massive::Authenticators::S3, url: url) }
|
59
61
|
|
60
|
-
subject(:file)
|
61
|
-
|
62
|
-
before do
|
63
|
-
Massive.fog_credentials = { provider: 'AWS', aws_access_key_id: 'some-key', aws_secret_access_key: 'some-secret' }
|
64
|
-
Massive.fog_authenticated_url_expiration = 1.hour
|
65
|
-
Massive.fog_directory = 'my-bucket'
|
62
|
+
subject(:file) { Massive::File.new(filename: filename, encoding: encoding, col_sep: col_sep) }
|
66
63
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
fog_file.stub(:url).with(Time.current.to_i + Massive.fog_authenticated_url_expiration).and_return(authenticated_url)
|
64
|
+
def stub_processor
|
65
|
+
Massive.storage_config[:provider].stub(:new).with(filename).and_return(provider)
|
66
|
+
FileProcessor::CSV.stub(:new).with(file.url, expected_options).and_return(processor)
|
71
67
|
end
|
72
68
|
|
73
|
-
it "creates a new instance of the CSV file processor, pointing its URL to the
|
74
|
-
FileProcessor::CSV.should_receive(:new).with(
|
69
|
+
it "creates a new instance of the CSV file processor, pointing its URL to the authenticator provider url" do
|
70
|
+
FileProcessor::CSV.should_receive(:new).with(url, expected_options).and_return(processor)
|
75
71
|
file.processor.should eq(processor)
|
76
72
|
end
|
77
73
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: massive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vicente Mundim
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- README.md
|
87
87
|
- Rakefile
|
88
88
|
- lib/massive.rb
|
89
|
+
- lib/massive/authenticators/s3.rb
|
89
90
|
- lib/massive/cancelling.rb
|
90
91
|
- lib/massive/file.rb
|
91
92
|
- lib/massive/file_job.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- massive.gemspec
|
110
111
|
- spec/fixtures/custom_job.rb
|
111
112
|
- spec/fixtures/custom_step.rb
|
113
|
+
- spec/models/massive/authenticators/s3_spec.rb
|
112
114
|
- spec/models/massive/cancelling_spec.rb
|
113
115
|
- spec/models/massive/file_job_spec.rb
|
114
116
|
- spec/models/massive/file_spec.rb
|
@@ -155,6 +157,7 @@ summary: Parallelize processing of large files and/or data using Resque, Redis a
|
|
155
157
|
test_files:
|
156
158
|
- spec/fixtures/custom_job.rb
|
157
159
|
- spec/fixtures/custom_step.rb
|
160
|
+
- spec/models/massive/authenticators/s3_spec.rb
|
158
161
|
- spec/models/massive/cancelling_spec.rb
|
159
162
|
- spec/models/massive/file_job_spec.rb
|
160
163
|
- spec/models/massive/file_spec.rb
|