s3_zipper 1.0.5 → 2.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.
data/Gemfile.lock CHANGED
@@ -1,34 +1,38 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- s3_zipper (1.0.4)
5
- aws-sdk-s3 (~> 1)
4
+ s3_zipper (1.0.9)
5
+ aws-sdk (~> 2)
6
+ concurrent-ruby (~> 1.1)
7
+ multiblock (~> 0.2.1)
6
8
  ruby-progressbar (~> 1)
7
9
  rubyzip (>= 1.0.0)
8
10
 
9
11
  GEM
10
12
  remote: https://rubygems.org/
11
13
  specs:
12
- aws-eventstream (1.0.3)
13
- aws-partitions (1.159.0)
14
- aws-sdk-core (3.50.0)
15
- aws-eventstream (~> 1.0, >= 1.0.2)
16
- aws-partitions (~> 1.0)
17
- aws-sigv4 (~> 1.1)
18
- jmespath (~> 1.0)
19
- aws-sdk-kms (1.18.0)
20
- aws-sdk-core (~> 3, >= 3.48.2)
21
- aws-sigv4 (~> 1.1)
22
- aws-sdk-s3 (1.36.1)
23
- aws-sdk-core (~> 3, >= 3.48.2)
24
- aws-sdk-kms (~> 1)
14
+ ast (2.4.0)
15
+ aws-eventstream (1.1.0)
16
+ aws-sdk (2.11.632)
17
+ aws-sdk-resources (= 2.11.632)
18
+ aws-sdk-core (2.11.632)
25
19
  aws-sigv4 (~> 1.0)
26
- aws-sigv4 (1.1.0)
27
- aws-eventstream (~> 1.0, >= 1.0.2)
20
+ jmespath (~> 1.0)
21
+ aws-sdk-resources (2.11.632)
22
+ aws-sdk-core (= 2.11.632)
23
+ aws-sigv4 (1.2.2)
24
+ aws-eventstream (~> 1, >= 1.0.2)
25
+ concurrent-ruby (1.1.5)
28
26
  diff-lcs (1.3)
29
27
  docile (1.3.1)
28
+ jaro_winkler (1.5.2)
30
29
  jmespath (1.4.0)
31
30
  json (2.2.0)
31
+ multiblock (0.2.1)
32
+ parallel (1.17.0)
33
+ parser (2.6.3.0)
34
+ ast (~> 2.4.0)
35
+ rainbow (3.0.0)
32
36
  rake (10.5.0)
33
37
  rake-compiler (1.0.7)
34
38
  rake
@@ -45,14 +49,22 @@ GEM
45
49
  diff-lcs (>= 1.2.0, < 2.0)
46
50
  rspec-support (~> 3.8.0)
47
51
  rspec-support (3.8.0)
52
+ rubocop (0.70.0)
53
+ jaro_winkler (~> 1.5.1)
54
+ parallel (~> 1.10)
55
+ parser (>= 2.6)
56
+ rainbow (>= 2.2.2, < 4.0)
57
+ ruby-progressbar (~> 1.7)
58
+ unicode-display_width (>= 1.4.0, < 1.7)
48
59
  ruby-progressbar (1.10.0)
49
- rubyzip (1.2.2)
60
+ rubyzip (1.2.3)
50
61
  simplecov (0.16.1)
51
62
  docile (~> 1.1)
52
63
  json (>= 1.8, < 3)
53
64
  simplecov-html (~> 0.10.0)
54
65
  simplecov-html (0.10.2)
55
66
  thor (0.20.3)
67
+ unicode-display_width (1.6.0)
56
68
 
57
69
  PLATFORMS
58
70
  ruby
@@ -62,9 +74,10 @@ DEPENDENCIES
62
74
  rake (~> 10.0)
63
75
  rake-compiler
64
76
  rspec (~> 3.0)
77
+ rubocop (~> 0.70.0)
65
78
  s3_zipper!
66
79
  simplecov
67
80
  thor
68
81
 
69
82
  BUNDLED WITH
70
- 2.0.1
83
+ 2.2.12
data/README.md CHANGED
@@ -46,6 +46,25 @@ zipper.zip_to_s3(keys)
46
46
  # }
47
47
  ```
48
48
 
49
+ ### Event Handling
50
+ ```ruby
51
+ keys = ["documents/files/790/306/985/original/background-10.jpg", "documents/files/790/307/076/original/background-10.jpg"]
52
+ zipper.zip_to_s3 keys do
53
+ on.start { puts 'Starting to zip' }
54
+ on.progress { |progress| puts progress.percentage }
55
+ on.finish { puts 'Finished zipping' }
56
+ on.upload { puts 'Upload complete' }
57
+ end
58
+ # {
59
+ # :key=>"3dc29e9ba0a069eb5d0783f07b12e1b3.zip",
60
+ # :url => "https://bucket_name.s3.us-west-2.amazonaws.com/35b6f0e2ee91aa0e3c0640c7a4b2b7db.zip"
61
+ # :zipped=>["documents/files/790/306/985/original/background-10.jpg", "documents/files/790/307/076/original/background-10.jpg"],
62
+ # :failed=>[]
63
+ # }
64
+ ```
65
+
66
+
67
+
49
68
  ## Development
50
69
 
51
70
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -54,7 +73,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
54
73
 
55
74
  ## Contributing
56
75
 
57
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/s3_zipper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
76
+ Bug reports and pull requests are welcome on GitHub at https://github.com/capshareinc/s3_zipper. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
58
77
 
59
78
  ## License
60
79
 
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "s3_zipper"
data/exe/s3_zipper CHANGED
@@ -1,24 +1,25 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "s3_zipper"
4
- require 'thor'
5
- require 'awesome_print'
5
+ require "thor"
6
+ require "awesome_print"
6
7
  class CLI < Thor
7
- desc 'local_file FILES', 'zip files from s3 to a local file'
8
- method_option :filename, type: :string, aliases: 'f', desc: 'Name of the created zip archive'
9
- method_option :bucket, type: :string, default: ENV['AWS_BUCKET'], required: !ENV['AWS_BUCKET'].nil?, aliases: 'b', desc: 'Name of the bucket the files are in'
8
+ desc "local_file FILES", "zip files from s3 to a local file"
9
+ method_option :filename, type: :string, aliases: "f", desc: "Name of the created zip archive"
10
+ method_option :bucket, type: :string, default: ENV["AWS_BUCKET"], required: !ENV["AWS_BUCKET"].nil?, aliases: "b", desc: "Name of the bucket the files are in"
10
11
 
11
- def local_file(*files)
12
+ def local_file *files
12
13
  S3Zipper.new(options[:bucket]).zip_to_local_file(files, file: options[:filename] || SecureRandom.hex)
13
14
  end
14
15
 
15
- desc 's3 FILES', 'zip files from s3 to s3'
16
- method_option :filename, type: :string, aliases: 'f', desc: 'Name of the created zip archive'
17
- method_option :path, type: :string, aliases: 'p', desc: 'Path to the file in s3'
18
- method_option :bucket, type: :string, default: ENV['AWS_BUCKET'], required: !ENV['AWS_BUCKET'].nil?, aliases: 'b', desc: 'Name of the bucket the files are in'
16
+ desc "s3 FILES", "zip files from s3 to s3"
17
+ method_option :filename, type: :string, aliases: "f", desc: "Name of the created zip archive"
18
+ method_option :path, type: :string, aliases: "p", desc: "Path to the file in s3"
19
+ method_option :bucket, type: :string, default: ENV["AWS_BUCKET"], required: !ENV["AWS_BUCKET"].nil?, aliases: "b", desc: "Name of the bucket the files are in"
19
20
 
20
- def s3(*files)
21
+ def s3 *files
21
22
  S3Zipper.new(options[:bucket]).zip_to_s3(files, filename: options[:filename] || SecureRandom.hex, path: options[:path])
22
23
  end
23
24
  end
24
- CLI.start(ARGV)
25
+ CLI.start(ARGV)
data/lib/s3_zipper.rb CHANGED
@@ -1,37 +1,46 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "s3_zipper/version"
2
- require 's3_zipper/progress'
4
+ require "s3_zipper/progress"
5
+ require "s3_zipper/spinner"
3
6
  require "s3_zipper/client"
4
7
  require "zip"
8
+ require "multiblock"
5
9
 
6
10
  class S3Zipper
7
- attr_accessor :client, :options, :progress
11
+ attr_accessor :client, :options, :progress, :zip_client, :wrapper
8
12
 
9
13
  # @param [String] bucket - bucket that files exist in
10
14
  # @param [Hash] options - options for zipper
11
15
  # @option options [Boolean] :progress - toggles progress tracking
12
16
  # @return [S3Zipper]
13
17
  def initialize bucket, options = {}
14
- @options = options
15
- @client = Client.new(bucket, options)
16
- @progress = Progress.new(enabled: options[:progress], format: "%e %c/%C %t", total: nil, length: 80, autofinish: false)
18
+ @options = options
19
+ @wrapper = Multiblock.wrapper
20
+ @progress = Progress.new(enabled: options[:progress], format: "%e %c/%C %t", total: nil, length: 80, autofinish: false)
21
+ @client = Client.new(bucket, options)
22
+ @zip_client = Client.new(options[:zip_bucket], options) if options[:zip_bucket]
23
+ @zip_client ||= @client
17
24
  end
18
25
 
19
26
  # Zips files from s3 to a local zip
20
27
  # @param [Array] keys - Array of s3 keys to zip
21
28
  # @param [String, File] file - Filename or file object for the zip, defaults to a random string
22
29
  # @return [Hash]
23
- def zip_to_local_file(keys, file: SecureRandom.hex, &block)
24
- file = file.is_a?(File) ? file : File.open("#{file}.zip", 'w')
25
- zip(keys, file.path, &block)
30
+ def zip_to_local_file keys, file: SecureRandom.hex
31
+ yield(wrapper) if block_given?
32
+ file = file.is_a?(File) ? file : File.open("#{file}.zip", "w")
33
+ zip(keys, file.path)
26
34
  end
27
35
 
28
36
  # Zips files from s3 to a temporary zip
29
37
  # @param [Array] keys - Array of s3 keys to zip
30
38
  # @param [String, File] filename - Name of file, defaults to a random string
31
39
  # @return [Hash]
32
- def zip_to_tempfile(keys, filename: SecureRandom.hex, cleanup: false, &block)
33
- zipfile = Tempfile.new([filename, '.zip'])
34
- result = zip(keys, zipfile.path, &block)
40
+ def zip_to_tempfile keys, filename: SecureRandom.hex, cleanup: false
41
+ yield(wrapper) if block_given?
42
+ zipfile = Tempfile.new([filename, ".zip"])
43
+ result = zip(keys, zipfile.path)
35
44
  zipfile.unlink if cleanup
36
45
  result
37
46
  end
@@ -41,36 +50,45 @@ class S3Zipper
41
50
  # @param [String, File] filename - Name of file, defaults to a random string
42
51
  # @param [String] path - path for file in s3
43
52
  # @return [Hash]
44
- def zip_to_s3 keys, filename: SecureRandom.hex, path: nil, s3_options: {}, &block
45
- progress.update :total, 1
53
+ def zip_to_s3 keys, filename: SecureRandom.hex, path: nil, s3_options: {}
54
+ yield(wrapper) if block_given?
46
55
  filename = "#{path ? "#{path}/" : ''}#{filename}.zip"
47
- result = zip_to_tempfile(keys, filename: filename, cleanup: false, &block)
48
- progress.update_attrs title: "Uploading zip to s3", total: nil
49
- client.upload(result.delete(:filename), filename, options: s3_options)
50
- progress.finish(title: "Uploaded zip to #{filename}")
56
+ result = zip_to_tempfile(keys, filename: filename, cleanup: false)
57
+ zip_client.upload(result.delete(:filename), filename, options: s3_options)
51
58
  result[:key] = filename
52
- result[:url] = client.get_url(result[:key])
59
+ result[:url] = zip_client.get_url(result[:key])
60
+ wrapper.call(:upload, result)
53
61
  result
54
62
  end
55
63
 
56
64
  private
57
65
 
66
+ def add_to_zip zipfile, filename, file, n = 0
67
+ existing_file = zipfile.find_entry(filename)
68
+ if existing_file
69
+ filename = "#{File.basename(filename, ".*").split('(').first}(#{n})#{File.extname(filename)}"
70
+ add_to_zip(zipfile, filename, file, n + 1)
71
+ else
72
+ zipfile.add(filename, file.path)
73
+ end
74
+ end
75
+
58
76
  # @param [Array] keys - Array of s3 keys to zip
59
77
  # @param [String] path - path to zip
60
78
  # @yield [progress]
61
79
  # @return [Hash]
62
- def zip(keys, path)
63
- total = progress.total || 0
64
- total += keys.size
65
- progress.update_attrs total: total, title: "Zipping Keys to #{path}"
80
+ def zip keys, path
81
+ progress.reset total: keys.size, title: "Zipping Keys to #{path}"
66
82
  Zip::File.open(path, Zip::File::CREATE) do |zipfile|
83
+ wrapper.call(:start, zipfile)
67
84
  @failed, @successful = client.download_keys keys do |file, key|
68
- progress.increment
69
- progress.update :title, "Zipping #{key} to #{path}"
70
- yield(zipfile, progress) if block_given?
85
+ progress.increment title: "Zipping #{key} to #{path}"
86
+ wrapper.call(:progress, progress)
71
87
  next if file.nil?
72
- zipfile.add(File.basename(key), file.path)
88
+ add_to_zip(zipfile, File.basename(key), file)
73
89
  end
90
+ progress.finish(title: "Zipped keys to #{path}")
91
+ wrapper.call(:finish, zipfile)
74
92
  end
75
93
  @successful.each { |_, temp| temp.unlink }
76
94
  {
@@ -1,4 +1,6 @@
1
- require 'aws-sdk-s3'
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk"
2
4
 
3
5
  class S3Zipper
4
6
  class Client
@@ -42,18 +44,26 @@ class S3Zipper
42
44
  temp.binmode
43
45
  temp = download_to_file key, temp
44
46
  return if temp.nil?
47
+
45
48
  yield(temp) if block_given?
46
49
  temp
47
50
  ensure
48
51
  temp&.unlink if cleanup
49
52
  end
50
53
 
51
- def get_url(key)
54
+ def get_url key
52
55
  resource.bucket(bucket_name).object(key).public_url
53
56
  end
54
57
 
55
58
  def upload local_path, repo_path, options: {}
56
- client.put_object(options.merge!(bucket: bucket_name, key: repo_path, body: File.open(local_path).read))
59
+ spinner = Spinner.new(
60
+ enabled: options[:progress],
61
+ title: "Uploading zip to #{bucket_name}/#{repo_path}",
62
+ )
63
+ spinner.start
64
+ object = client.put_object(options.merge!(bucket: bucket_name, key: repo_path, body: File.open(local_path).read))
65
+ spinner.finish title: "Uploaded zip to #{bucket_name}/#{repo_path}"
66
+ object
57
67
  end
58
68
  end
59
69
  end
@@ -1,38 +1,61 @@
1
- require 'ruby-progressbar'
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-progressbar"
2
4
  class Progress
5
+
3
6
  def initialize options = {}
4
7
  return unless options[:enabled] || true
8
+
5
9
  @options = options
6
10
  @format = options[:format]
7
11
  @progress_bar = ProgressBar.create(@options)
8
12
  end
9
13
 
10
- def reset(title: nil, total: nil, format: nil)
14
+ def reset title: nil, total: nil, format: nil
15
+ return unless @progress_bar
16
+
11
17
  @progress_bar.progress = 0
12
- @progress_bar.title = title
13
- @progress_bar.total = total
14
- @progress_bar.format = format
18
+ @progress_bar.title = title if title
19
+ @progress_bar.total = total if total
20
+ @progress_bar.format = format if format
15
21
  refresh
16
22
  end
17
23
 
24
+ def spin
25
+ until @progress_bar.finished?
26
+ increment
27
+ end
28
+ end
29
+
18
30
  def total
19
- @progress_bar&.total
31
+ return unless @progress_bar
32
+
33
+ @progress_bar.total
20
34
  end
21
35
 
22
36
  def percentage
23
- @progress_bar&.to_h['percentage']
37
+ return unless @progress_bar
38
+
39
+ @progress_bar.to_h["percentage"]
24
40
  end
25
41
 
26
42
  def refresh
27
- @progress_bar&.refresh
43
+ return unless @progress_bar
44
+
45
+ @progress_bar.refresh
28
46
  end
29
47
 
30
48
  def progress
31
- @progress_bar&.progress
49
+ return unless @progress_bar
50
+
51
+ @progress_bar.progress
32
52
  end
33
53
 
34
- def increment
35
- @progress_bar&.increment
54
+ def increment attrs = {}
55
+ return unless @progress_bar
56
+
57
+ @progress_bar.increment
58
+ update_attrs(attrs) unless attrs.empty?
36
59
  end
37
60
 
38
61
  def update_attrs attrs
@@ -41,11 +64,13 @@ class Progress
41
64
 
42
65
  def update attr, value
43
66
  return unless @progress_bar
67
+
44
68
  @progress_bar.send("#{attr}=", value)
45
69
  end
46
70
 
47
71
  def finish title: nil, format: nil
48
72
  return unless @progress_bar
73
+
49
74
  @progress_bar.title = title if title
50
75
  @progress_bar.format = format if format
51
76
  @progress_bar.finish
@@ -57,6 +82,7 @@ class Progress
57
82
 
58
83
  def get_attr attr
59
84
  return unless @progress_bar
85
+
60
86
  @progress_bar.send(attr)
61
87
  end
62
- end
88
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-progressbar"
4
+ require "concurrent-ruby"
5
+ class S3Zipper
6
+ class Spinner
7
+ include Concurrent::Async
8
+
9
+ def initialize title: "", enabled: true, steps: %w[▸▹▹▹▹▹ ▹▸▹▹▹▹ ▹▹▸▹▹▹ ▹▹▹▸▹▹ ▹▹▹▹▸▹ ▹▹▹▹▹▸ ▹▹▹▹▹]
10
+ return unless enabled || true
11
+
12
+ @progress_bar = ProgressBar.create(
13
+ format: "[%B] %t",
14
+ total: nil,
15
+ length: 100,
16
+ title: title,
17
+ autofinish: false,
18
+ unknown_progress_animation_steps: steps,
19
+ )
20
+ end
21
+
22
+ def reset title: nil, total: nil, format: nil
23
+ return unless @progress_bar
24
+
25
+ @progress_bar.progress = 0
26
+ @progress_bar.title = title if title
27
+ @progress_bar.total = total if total
28
+ @progress_bar.format = format if format
29
+ refresh
30
+ end
31
+
32
+ def start
33
+ async.spin
34
+ end
35
+
36
+ def spin
37
+ return unless @progress_bar
38
+ until @progress_bar.finished?
39
+ increment
40
+ sleep(2)
41
+ end
42
+ end
43
+
44
+ def increment
45
+ return unless @progress_bar
46
+
47
+ @progress_bar.increment
48
+ end
49
+
50
+ def finish title: nil
51
+ return unless @progress_bar
52
+
53
+ @progress_bar.title = title if title
54
+ @progress_bar.format = "[✔] %t"
55
+ @progress_bar.finish
56
+ end
57
+ end
58
+ end