crush-rb 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/crush +138 -0
- metadata +46 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0d986921915063502f6e16d91852096ade00ddff463a6970ce2be18b630ca2b6
|
4
|
+
data.tar.gz: 58cb0eba18e0513c3df831a1a1aa76188923e259352478220707cb41ed9d562a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3efb8b4698d71f3142e05ff360443ccb981358922f275e88b86a5b5fa08f4e6c8d65a5f2ceb9df93c4f14587b38279fa61eeffa754f6fc597c517c37757fdf8
|
7
|
+
data.tar.gz: a20ad3ac2b93e58a99d955382c3be501472cfbc71d49fd9144c4fd06befaac21865d636ba8b9c82ec48459b54f2c6946c42e004dbedda73f98668e5966965986
|
data/bin/crush
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'tmpdir'
|
4
|
+
|
5
|
+
VERBOSE = !%w[0 false].include?(ENV.fetch('VERBOSE', '0'))
|
6
|
+
|
7
|
+
def dbg(msg = nil)
|
8
|
+
warn(msg || yield) if VERBOSE
|
9
|
+
end
|
10
|
+
|
11
|
+
def usage!(exit_code: 0)
|
12
|
+
method(exit_code.zero? ? :puts : :warn)
|
13
|
+
.call("Usage: crush [SWITCHES] path/to/directory/to/compress\n\t-h,--help:\tShow this message")
|
14
|
+
exit(exit_code)
|
15
|
+
end
|
16
|
+
|
17
|
+
dbg { "VERBOSE=#{ENV.fetch('VERBOSE')}; verbose mode is enabled" }
|
18
|
+
|
19
|
+
usage! if %w[-h --help].any? { |help_opt| ARGV.include?(help_opt) }
|
20
|
+
usage!(exit_code: 1) unless ARGV.size == 1
|
21
|
+
|
22
|
+
source = ARGV.first
|
23
|
+
dbg("Source file/directory: #{source}")
|
24
|
+
unless File.exist?(source)
|
25
|
+
warn("File does not exist: #{source}")
|
26
|
+
exit(1)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Base class of all 3 archive options
|
30
|
+
class Archive
|
31
|
+
attr_accessor :target_directory, :source
|
32
|
+
|
33
|
+
def initialize(target_directory, source)
|
34
|
+
self.target_directory = target_directory
|
35
|
+
self.source = source
|
36
|
+
end
|
37
|
+
|
38
|
+
def basename
|
39
|
+
File.basename(source)
|
40
|
+
end
|
41
|
+
|
42
|
+
def ext
|
43
|
+
raise NotImplementedError
|
44
|
+
end
|
45
|
+
|
46
|
+
def target_file
|
47
|
+
"#{basename}.tar.#{ext}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def target_path
|
51
|
+
File.join(target_directory, target_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
def compression_option
|
55
|
+
raise NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
def log
|
59
|
+
File.join(target_directory, "#{ext}.log")
|
60
|
+
end
|
61
|
+
|
62
|
+
def size
|
63
|
+
File.size(target_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def compress!
|
67
|
+
dbg("[#{self.class}] Starting compression")
|
68
|
+
success = system("tar -c#{compression_option}vf #{target_path} #{source} 2> #{log}")
|
69
|
+
dbg("[#{self.class}] Compression attempt complete: #{success ? :success : :failure}")
|
70
|
+
return self if success
|
71
|
+
|
72
|
+
warn("Error compressing #{source} to #{self.class} format")
|
73
|
+
begin
|
74
|
+
warn(File.read(log))
|
75
|
+
rescue StandardError
|
76
|
+
warn("Could not read log file #{log}")
|
77
|
+
end
|
78
|
+
exit(2)
|
79
|
+
end
|
80
|
+
|
81
|
+
def move_to!(dir)
|
82
|
+
dbg("[#{self.class}] Moving #{target_path} to #{dir}")
|
83
|
+
File.rename(target_path, File.join(dir, File.basename(target_file)))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Compress using gzip
|
88
|
+
class Gzip < Archive
|
89
|
+
def ext
|
90
|
+
:gz
|
91
|
+
end
|
92
|
+
|
93
|
+
def compression_option
|
94
|
+
:z
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Compress using gzip
|
99
|
+
class Bzip2 < Archive
|
100
|
+
def ext
|
101
|
+
:bz2
|
102
|
+
end
|
103
|
+
|
104
|
+
def compression_option
|
105
|
+
:j
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Compress using gzip
|
110
|
+
class Xz < Archive
|
111
|
+
def ext
|
112
|
+
:xz
|
113
|
+
end
|
114
|
+
|
115
|
+
def compression_option
|
116
|
+
:J
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
FORMATS = [Gzip, Bzip2, Xz].freeze
|
121
|
+
|
122
|
+
result = nil
|
123
|
+
|
124
|
+
Dir.mktmpdir do |tempdir_path|
|
125
|
+
dbg("Working in temporary directory #{tempdir_path}")
|
126
|
+
|
127
|
+
archives = FORMATS.map { |archive_class| archive_class.new(tempdir_path, source) }
|
128
|
+
|
129
|
+
threads = archives.map { |archive| Thread.new { archive.compress! } }
|
130
|
+
threads.each(&:join)
|
131
|
+
archives.each { |archive| dbg("#{archive.target_file}:\tSize=#{archive.size}") }
|
132
|
+
smallest, _others = archives.sort_by(&:size)
|
133
|
+
dbg("Keeping #{smallest.target_file}")
|
134
|
+
smallest.move_to!(Dir.pwd)
|
135
|
+
result = smallest.target_file
|
136
|
+
end
|
137
|
+
|
138
|
+
puts result
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: crush-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexandre Ignjatovic
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-05-28 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Run several flavors of compression to keep only the most space efficient
|
14
|
+
one
|
15
|
+
email: alexnadre.ignjatovic@gmail.com
|
16
|
+
executables:
|
17
|
+
- crush
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/crush
|
22
|
+
homepage: https://github.com/bankair/crush-rb
|
23
|
+
licenses:
|
24
|
+
- MIT
|
25
|
+
metadata:
|
26
|
+
source_code_uri: https://github.com/bankair/crush-rb
|
27
|
+
post_install_message:
|
28
|
+
rdoc_options: []
|
29
|
+
require_paths:
|
30
|
+
- lib
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 3.2.0
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
requirements: []
|
42
|
+
rubygems_version: 3.5.7
|
43
|
+
signing_key:
|
44
|
+
specification_version: 4
|
45
|
+
summary: Optimize your archive storage efficiency
|
46
|
+
test_files: []
|