massive 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f8833bbf616ef0395fd5e89b3911aacb5af0418e
4
- data.tar.gz: 9a23e1181998a64e1bfc3aa43fa71f136f253967
3
+ metadata.gz: 6add55169f9bbe782d2ee00b8a033a21aae4d9cb
4
+ data.tar.gz: 3e7ef6017201515489a3e8f04fb41b8669b701cd
5
5
  SHA512:
6
- metadata.gz: dc17619c526974f5e6d762e6d55756b55d39b234c02edc95c4b7b8ef6365e2ca0109514e921bd4d89887849d2cd725bf5eaac2bb304914cb2b026a6d8a6c5638
7
- data.tar.gz: 1948810ce93bb7c160833cc7b2a10ab38386afd41735f59f72df185a050db990fbf5acc2c3bdeab598bf2b1d8884290b8d71c377a2919ec95dd41458adb318a7
6
+ metadata.gz: a2470c8e5230e4b2f4e12a47e93722e16da720971c15c67179aa8f053a1907c1322309f19fdef0572c43211d0b56f62a7d159d6a0c25422ab61cd339deab9a18
7
+ data.tar.gz: 2e48d557a63fa84092f83acabca19ba70b3d21559bbb0f96f25dee3ee97c16b96e0daac07ccf26c40377046787184d8c5810ec1f00b9831ad54f100073a73b5f
data/Gemfile CHANGED
@@ -4,8 +4,6 @@ gemspec
4
4
 
5
5
  gem 'rake'
6
6
 
7
- gem 'fog'
8
-
9
7
  group(:development) do
10
8
  gem 'debugger'
11
9
  gem 'guard-rspec', require: false
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- massive (0.2.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 [Fog::Storage][http://fog.io/storage/]. To use it yoy must define the `fog_credentials` and optionally the `fog_directory` and `fog_authenticated_url_expiration`:
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.fog_credentials = {
175
- provider: 'AWS',
176
- aws_access_key_id: 'INSERT-YOUR-AWS-KEY-HERE',
177
- aws_secret_access_key: 'INSERT-YOUR-AWS-SECRET-HERE'
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: '/path/to/my/file.csv' })
185
+ process = Massive::FileProcess.new(file_attributes: { filename: 'path/to/my/file.csv' })
188
186
  ```
189
187
 
190
188
  ## Contributing
@@ -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.fog_credentials
33
- @fog_credentials
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.fog_directory=(directory=nil)
53
- @fog_directory = directory
40
+ def self.storage_config=(value)
41
+ @storage_config ||= {}
42
+ @storage_config.merge!(value)
54
43
  end
55
44
 
56
- self.fog_directory = 'massive'
57
- self.fog_authenticated_url_expiration = 1 * 60 * 60
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
@@ -40,7 +40,7 @@ module Massive
40
40
  end
41
41
 
42
42
  def url
43
- read_attribute(:url).presence || authenticated_url
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 authenticated_url
63
- if can_use_fog?
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
@@ -1,3 +1,3 @@
1
1
  module Massive
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -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
- before { FileProcessor::CSV.stub(:new).with(file.url, expected_options).and_return(processor) }
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 using Fog" do
54
- let(:filename) { 'my-file.txt' }
55
- let(:fog_connection) { double(Fog::Storage) }
56
- let(:fog_directory) { double('Directory') }
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) { Massive::File.new(filename: filename, encoding: encoding, col_sep: col_sep) }
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
- Fog::Storage.stub(:new).with(Massive.fog_credentials).and_return(fog_connection)
68
- fog_connection.stub_chain(:directories, :get).with(Massive.fog_directory).and_return(fog_directory)
69
- fog_directory.stub_chain(:files, :get).with(filename).and_return(fog_file)
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 authenticated fog url" do
74
- FileProcessor::CSV.should_receive(:new).with(authenticated_url, expected_options).and_return(processor)
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.2.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