omnizip 0.4.10-arm-linux → 0.4.12-arm-linux
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 +13 -0
- data/README.adoc +101 -1284
- data/docs/compatibility.adoc +1 -2
- data/docs/lychee.toml +1 -7
- data/lib/omnizip/version.rb +1 -1
- data/lib/tasks/docs.rake +146 -0
- data/readme-docs/rar-archives.adoc +505 -0
- data/scripts/generate_rar_oracle_fixtures.rb +177 -0
- data/vendor/libomnizip_ffi.so +0 -0
- metadata +4 -74
- data/TODO.refactor/00-overview.md +0 -67
- data/TODO.refactor/01-registry-base-class.md +0 -81
- data/TODO.refactor/02-error-hierarchy.md +0 -57
- data/TODO.refactor/03-autoload-entry-point.md +0 -69
- data/TODO.refactor/04-filter-base-consolidation.md +0 -49
- data/TODO.refactor/05-send-private-methods.md +0 -64
- data/TODO.refactor/06-instance-variable-access.md +0 -69
- data/TODO.refactor/07-respond-to-replacement.md +0 -237
- data/TODO.refactor/08-spec-doubles.md +0 -27
- data/TODO.refactor/09-cli-shared-module.md +0 -35
- data/TODO.refactor/10-format-detector-ocp.md +0 -34
- data/TODO.refactor/11-convenience-decoupling.md +0 -38
- data/TODO.refactor/12-thread-safety.md +0 -8
- data/TODO.refactor/13-lutaml-model-migration.md +0 -158
- data/TODO.refactor/14-add-missing-specs.md +0 -25
- data/docs/.github/workflows/docs.yml +0 -142
- data/docs/.gitignore +0 -21
- data/docs/Gemfile +0 -13
- data/docs/README.md +0 -101
- data/docs/_config.yml +0 -112
- data/docs/comparison/feature-matrix.adoc +0 -694
- data/docs/comparison/index.adoc +0 -113
- data/docs/comparison/vs-7zip.adoc +0 -309
- data/docs/comparison/vs-peazip.adoc +0 -77
- data/docs/comparison/vs-rubyzip.adoc +0 -342
- data/docs/comparison/vs-winrar.adoc +0 -100
- data/docs/concepts/index.adoc +0 -129
- data/docs/developer/architecture.adoc +0 -256
- data/docs/developer/contributing.adoc +0 -158
- data/docs/developer/index.adoc +0 -25
- data/docs/developer/testing.adoc +0 -212
- data/docs/getting-started/basic-usage.adoc +0 -263
- data/docs/getting-started/index.adoc +0 -42
- data/docs/getting-started/installation.adoc +0 -138
- data/docs/getting-started/quick-start.adoc +0 -183
- data/docs/getting-started/your-first-archive.adoc +0 -219
- data/docs/guides/advanced-features/encryption.adoc +0 -277
- data/docs/guides/advanced-features/index.adoc +0 -59
- data/docs/guides/advanced-features/parallel-processing.adoc +0 -144
- data/docs/guides/advanced-features/progress-tracking.adoc +0 -124
- data/docs/guides/advanced-features/streaming.adoc +0 -142
- data/docs/guides/archive-formats/gzip-format.adoc +0 -105
- data/docs/guides/archive-formats/index.adoc +0 -160
- data/docs/guides/archive-formats/ole-format.adoc +0 -151
- data/docs/guides/archive-formats/rar-format.adoc +0 -104
- data/docs/guides/archive-formats/rar5.adoc +0 -521
- data/docs/guides/archive-formats/rpm-format.adoc +0 -108
- data/docs/guides/archive-formats/seven-zip-format.adoc +0 -35
- data/docs/guides/archive-formats/tar-format.adoc +0 -106
- data/docs/guides/archive-formats/xz-format.adoc +0 -118
- data/docs/guides/archive-formats/zip-format.adoc +0 -35
- data/docs/guides/compression-algorithms/bzip2.adoc +0 -114
- data/docs/guides/compression-algorithms/deflate.adoc +0 -313
- data/docs/guides/compression-algorithms/index.adoc +0 -190
- data/docs/guides/compression-algorithms/lzma.adoc +0 -394
- data/docs/guides/compression-algorithms/lzma2.adoc +0 -244
- data/docs/guides/compression-algorithms/ppmd.adoc +0 -313
- data/docs/guides/compression-algorithms/zstandard.adoc +0 -296
- data/docs/guides/creating-archives.adoc +0 -348
- data/docs/guides/extracting-archives.adoc +0 -53
- data/docs/guides/format-conversion.adoc +0 -64
- data/docs/guides/index.adoc +0 -49
- data/docs/guides/migration-rubyzip.adoc +0 -217
- data/docs/guides/parity-archives.adoc +0 -605
- data/docs/guides/performance-tuning.adoc +0 -88
- data/docs/index.adoc +0 -228
- data/docs/reference/api/overview.adoc +0 -187
- data/docs/reference/cli/compress-command.adoc +0 -114
- data/docs/reference/cli/overview.adoc +0 -140
- data/docs/reference/index.adoc +0 -26
- data/docs/resources/faq.adoc +0 -185
- data/docs/resources/quick-reference.adoc +0 -222
- data/docs/troubleshooting/index.adoc +0 -207
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Regenerates the RAR oracle fixtures under spec/fixtures/rar/oracle/.
|
|
4
|
+
#
|
|
5
|
+
# The specs that used to shell out to `unrar` now assert against these
|
|
6
|
+
# committed files, so CI never needs the rar/unrar oracle. When the
|
|
7
|
+
# writers legitimately change their output, run:
|
|
8
|
+
#
|
|
9
|
+
# bundle exec ruby scripts/generate_rar_oracle_fixtures.rb
|
|
10
|
+
#
|
|
11
|
+
# and commit the refreshed fixtures. The script FAILS unless the local
|
|
12
|
+
# `unrar` oracle validates every generated archive, and unless each
|
|
13
|
+
# writer is byte-deterministic across two runs (the specs compare fresh
|
|
14
|
+
# writer output against these fixtures byte-for-byte).
|
|
15
|
+
|
|
16
|
+
require "digest"
|
|
17
|
+
require "fileutils"
|
|
18
|
+
require "tmpdir"
|
|
19
|
+
|
|
20
|
+
ROOT = File.expand_path("..", __dir__)
|
|
21
|
+
DEST = File.join(ROOT, "spec/fixtures/rar/oracle")
|
|
22
|
+
UNRAR = "unrar"
|
|
23
|
+
# RAR5 embeds mtime as epoch seconds; pin it so fixtures are reproducible
|
|
24
|
+
FIXED_TIME = Time.at(1_700_000_000)
|
|
25
|
+
|
|
26
|
+
def sh!(*cmd)
|
|
27
|
+
system(*cmd) or abort("FAILED: #{cmd.join(' ')}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def oracle_t(path)
|
|
31
|
+
sh!(UNRAR, "t", "-idq", path)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def oracle_listing(path)
|
|
35
|
+
out = `#{UNRAR} l #{path} 2>&1`
|
|
36
|
+
abort("unrar l failed for #{path}:\n#{out}") if out.include?("ERROR") || $?.success? == false
|
|
37
|
+
out
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_rar5_single(dir)
|
|
41
|
+
input = File.join(dir, "in.txt")
|
|
42
|
+
File.binwrite(input, "Test content for unrar")
|
|
43
|
+
archive = File.join(dir, "rar5_single.rar")
|
|
44
|
+
writer = Omnizip::Formats::Rar::Rar5::Writer.new(archive)
|
|
45
|
+
writer.add_file(input, "test.txt")
|
|
46
|
+
writer.write
|
|
47
|
+
archive
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def build_rar4_store_tree(dir)
|
|
51
|
+
big = File.join(dir, "big.txt")
|
|
52
|
+
File.binwrite(big, "omnizip RAR4 interop fixture. " * 800)
|
|
53
|
+
tree = File.join(dir, "tree")
|
|
54
|
+
FileUtils.mkdir_p(File.join(tree, "sub"))
|
|
55
|
+
File.binwrite(File.join(tree, "README.md"), "# tree\n")
|
|
56
|
+
File.binwrite(File.join(tree, "sub", "main.rb"), "puts 'hi'\n")
|
|
57
|
+
File.binwrite(File.join(tree, "empty.bin"), "")
|
|
58
|
+
archive = File.join(dir, "rar4_store_tree.rar")
|
|
59
|
+
writer = Omnizip::Formats::Rar::Writer.new(archive, compression: :store)
|
|
60
|
+
writer.add_file(big)
|
|
61
|
+
writer.add_directory(tree)
|
|
62
|
+
writer.write
|
|
63
|
+
archive
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def build_rar5_multivolume(dir)
|
|
67
|
+
%w[compat0.txt compat1.txt].each_with_index do |name, i|
|
|
68
|
+
File.binwrite(File.join(dir, name), "Compatible content #{i}\n" * 3000)
|
|
69
|
+
end
|
|
70
|
+
archive = File.join(dir, "rar5_mv.rar")
|
|
71
|
+
writer = Omnizip::Formats::Rar::Rar5::Writer.new(
|
|
72
|
+
archive, multi_volume: true, volume_size: 65_536, compression: :store
|
|
73
|
+
)
|
|
74
|
+
writer.add_file(File.join(dir, "compat0.txt"))
|
|
75
|
+
writer.add_file(File.join(dir, "compat1.txt"))
|
|
76
|
+
volumes = writer.write
|
|
77
|
+
raise "expected split volumes, got #{volumes.size}" unless volumes.size > 1
|
|
78
|
+
|
|
79
|
+
volumes
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_rar5_large(dir)
|
|
83
|
+
archive = File.join(dir, "rar5_large.rar")
|
|
84
|
+
writer = Omnizip::Formats::Rar5::Writer.new
|
|
85
|
+
io = StringIO.new
|
|
86
|
+
# RAR5 stores mtime as epoch seconds; the fixed constant keeps the bytes
|
|
87
|
+
# reproducible across machines and timezones.
|
|
88
|
+
writer.write_archive(io, [{ name: "large.txt", data: "X" * 10_000, time: FIXED_TIME }])
|
|
89
|
+
File.binwrite(archive, io.string)
|
|
90
|
+
archive
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def build_rar5_store(dir)
|
|
94
|
+
input = File.join(dir, "test.txt")
|
|
95
|
+
File.write(input, "Test content for unrar")
|
|
96
|
+
archive = File.join(dir, "rar5_store_omnizip.rar")
|
|
97
|
+
writer = Omnizip::Formats::Rar::Rar5::Writer.new(archive, compression: :store)
|
|
98
|
+
writer.add_file(input, "test.txt")
|
|
99
|
+
writer.write
|
|
100
|
+
archive
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def build_rar5_lzss_fallback(dir)
|
|
104
|
+
input = File.join(dir, "test.txt")
|
|
105
|
+
File.write(input, "Test content for unrar")
|
|
106
|
+
archive = File.join(dir, "rar5_lzss_fallback.rar")
|
|
107
|
+
writer = Omnizip::Formats::Rar::Rar5::Writer.new(archive,
|
|
108
|
+
compression: :lzss, level: 3)
|
|
109
|
+
writer.add_file(input, "test.txt")
|
|
110
|
+
writer.write
|
|
111
|
+
archive
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def build_rar5_multi_files(dir)
|
|
115
|
+
%w[file1.txt file2.txt].each_with_index do |name, i|
|
|
116
|
+
File.write(File.join(dir, name), "Content #{i + 1}\n" * 10)
|
|
117
|
+
end
|
|
118
|
+
archive = File.join(dir, "rar5_multi_files.rar")
|
|
119
|
+
writer = Omnizip::Formats::Rar::Rar5::Writer.new(archive,
|
|
120
|
+
compression: :lzma, level: 3)
|
|
121
|
+
writer.add_file(File.join(dir, "file1.txt"), "file1.txt")
|
|
122
|
+
writer.add_file(File.join(dir, "file2.txt"), "file2.txt")
|
|
123
|
+
writer.write
|
|
124
|
+
archive
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
builders = {
|
|
128
|
+
"rar5_single" => method(:build_rar5_single),
|
|
129
|
+
"rar4_store_tree" => method(:build_rar4_store_tree),
|
|
130
|
+
"rar5_multivolume" => method(:build_rar5_multivolume),
|
|
131
|
+
"rar5_large" => method(:build_rar5_large),
|
|
132
|
+
"rar5_store_omnizip" => method(:build_rar5_store),
|
|
133
|
+
"rar5_lzss_fallback" => method(:build_rar5_lzss_fallback),
|
|
134
|
+
"rar5_multi_files" => method(:build_rar5_multi_files),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
abort("unrar oracle not available — these fixtures can only be regenerated where unrar exists") unless system("which #{UNRAR} > /dev/null 2>&1")
|
|
138
|
+
require "omnizip"
|
|
139
|
+
|
|
140
|
+
FileUtils.mkdir_p(DEST)
|
|
141
|
+
manifest = ["# RAR oracle fixtures — regenerated by scripts/generate_rar_oracle_fixtures.rb",
|
|
142
|
+
"# (unrar validated each archive at generation time; specs assert against these bytes)",
|
|
143
|
+
"generated: #{Time.now.utc.iso8601}",
|
|
144
|
+
"unrar_version: (derived from the first frozen listing's banner)",
|
|
145
|
+
"fixtures:"]
|
|
146
|
+
|
|
147
|
+
builders.each do |name, builder|
|
|
148
|
+
puts "== #{name}"
|
|
149
|
+
Dir.mktmpdir("rar_oracle") do |dir|
|
|
150
|
+
built1 = builder.call(dir)
|
|
151
|
+
h1 = Array(built1).map { |p| Digest::SHA256.file(p).hexdigest }
|
|
152
|
+
h2 = Dir.mktmpdir("rar_oracle2") do |dir2|
|
|
153
|
+
Array(builder.call(dir2)).map { |p| Digest::SHA256.file(p).hexdigest }
|
|
154
|
+
end
|
|
155
|
+
abort("#{name}: writer not byte-deterministic across runs") unless h1 == h2
|
|
156
|
+
|
|
157
|
+
first = Array(built1).first
|
|
158
|
+
oracle_t(first)
|
|
159
|
+
listing = oracle_listing(first)
|
|
160
|
+
digest_pairs = Array(built1).zip(h1).map do |path, h|
|
|
161
|
+
base = name == "rar5_multivolume" ? File.basename(path) : "#{name}.rar"
|
|
162
|
+
dest = File.join(DEST, base)
|
|
163
|
+
FileUtils.cp(path, dest)
|
|
164
|
+
manifest << " - file: #{base}"
|
|
165
|
+
manifest << " sha256: #{h}"
|
|
166
|
+
base
|
|
167
|
+
end
|
|
168
|
+
listing_name = "#{name}.unrar-l.txt"
|
|
169
|
+
File.write(File.join(DEST, listing_name), listing)
|
|
170
|
+
manifest << " - file: #{listing_name}"
|
|
171
|
+
manifest << " oracle_listing_of: #{digest_pairs.first}"
|
|
172
|
+
puts " frozen: #{digest_pairs.join(', ')}"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
File.write(File.join(DEST, "MANIFEST.yml"), "#{manifest.join("\n")}\n")
|
|
177
|
+
puts "done → #{DEST}"
|
data/vendor/libomnizip_ffi.so
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: omnizip
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.12
|
|
5
5
|
platform: arm-linux
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -135,21 +135,6 @@ files:
|
|
|
135
135
|
- LICENSE
|
|
136
136
|
- README.adoc
|
|
137
137
|
- Rakefile
|
|
138
|
-
- TODO.refactor/00-overview.md
|
|
139
|
-
- TODO.refactor/01-registry-base-class.md
|
|
140
|
-
- TODO.refactor/02-error-hierarchy.md
|
|
141
|
-
- TODO.refactor/03-autoload-entry-point.md
|
|
142
|
-
- TODO.refactor/04-filter-base-consolidation.md
|
|
143
|
-
- TODO.refactor/05-send-private-methods.md
|
|
144
|
-
- TODO.refactor/06-instance-variable-access.md
|
|
145
|
-
- TODO.refactor/07-respond-to-replacement.md
|
|
146
|
-
- TODO.refactor/08-spec-doubles.md
|
|
147
|
-
- TODO.refactor/09-cli-shared-module.md
|
|
148
|
-
- TODO.refactor/10-format-detector-ocp.md
|
|
149
|
-
- TODO.refactor/11-convenience-decoupling.md
|
|
150
|
-
- TODO.refactor/12-thread-safety.md
|
|
151
|
-
- TODO.refactor/13-lutaml-model-migration.md
|
|
152
|
-
- TODO.refactor/14-add-missing-specs.md
|
|
153
138
|
- benchmark/README.md
|
|
154
139
|
- benchmark/benchmark_suite.rb
|
|
155
140
|
- benchmark/compression_bench.rb
|
|
@@ -162,71 +147,13 @@ files:
|
|
|
162
147
|
- benchmark/test_data.rb
|
|
163
148
|
- config/formats/rar3_spec.yml
|
|
164
149
|
- config/formats/rar5_spec.yml
|
|
165
|
-
- docs/.github/workflows/docs.yml
|
|
166
|
-
- docs/.gitignore
|
|
167
150
|
- docs/.lychee.toml
|
|
168
|
-
- docs/Gemfile
|
|
169
151
|
- docs/RAR_WRITE_SUPPORT.md
|
|
170
|
-
- docs/README.md
|
|
171
|
-
- docs/_config.yml
|
|
172
152
|
- docs/assets/logo.svg
|
|
173
153
|
- docs/assets/omnizip-logo.pdf
|
|
174
|
-
- docs/comparison/feature-matrix.adoc
|
|
175
|
-
- docs/comparison/index.adoc
|
|
176
|
-
- docs/comparison/vs-7zip.adoc
|
|
177
|
-
- docs/comparison/vs-peazip.adoc
|
|
178
|
-
- docs/comparison/vs-rubyzip.adoc
|
|
179
|
-
- docs/comparison/vs-winrar.adoc
|
|
180
154
|
- docs/compatibility.adoc
|
|
181
|
-
- docs/concepts/index.adoc
|
|
182
|
-
- docs/developer/architecture.adoc
|
|
183
|
-
- docs/developer/contributing.adoc
|
|
184
|
-
- docs/developer/index.adoc
|
|
185
|
-
- docs/developer/testing.adoc
|
|
186
|
-
- docs/getting-started/basic-usage.adoc
|
|
187
|
-
- docs/getting-started/index.adoc
|
|
188
|
-
- docs/getting-started/installation.adoc
|
|
189
|
-
- docs/getting-started/quick-start.adoc
|
|
190
|
-
- docs/getting-started/your-first-archive.adoc
|
|
191
|
-
- docs/guides/advanced-features/encryption.adoc
|
|
192
|
-
- docs/guides/advanced-features/index.adoc
|
|
193
|
-
- docs/guides/advanced-features/parallel-processing.adoc
|
|
194
|
-
- docs/guides/advanced-features/progress-tracking.adoc
|
|
195
|
-
- docs/guides/advanced-features/streaming.adoc
|
|
196
|
-
- docs/guides/archive-formats/gzip-format.adoc
|
|
197
|
-
- docs/guides/archive-formats/index.adoc
|
|
198
|
-
- docs/guides/archive-formats/ole-format.adoc
|
|
199
|
-
- docs/guides/archive-formats/rar-format.adoc
|
|
200
|
-
- docs/guides/archive-formats/rar5.adoc
|
|
201
|
-
- docs/guides/archive-formats/rpm-format.adoc
|
|
202
|
-
- docs/guides/archive-formats/seven-zip-format.adoc
|
|
203
|
-
- docs/guides/archive-formats/tar-format.adoc
|
|
204
|
-
- docs/guides/archive-formats/xz-format.adoc
|
|
205
|
-
- docs/guides/archive-formats/zip-format.adoc
|
|
206
|
-
- docs/guides/compression-algorithms/bzip2.adoc
|
|
207
|
-
- docs/guides/compression-algorithms/deflate.adoc
|
|
208
|
-
- docs/guides/compression-algorithms/index.adoc
|
|
209
|
-
- docs/guides/compression-algorithms/lzma.adoc
|
|
210
|
-
- docs/guides/compression-algorithms/lzma2.adoc
|
|
211
|
-
- docs/guides/compression-algorithms/ppmd.adoc
|
|
212
|
-
- docs/guides/compression-algorithms/zstandard.adoc
|
|
213
|
-
- docs/guides/creating-archives.adoc
|
|
214
|
-
- docs/guides/extracting-archives.adoc
|
|
215
|
-
- docs/guides/format-conversion.adoc
|
|
216
|
-
- docs/guides/index.adoc
|
|
217
|
-
- docs/guides/migration-rubyzip.adoc
|
|
218
|
-
- docs/guides/parity-archives.adoc
|
|
219
|
-
- docs/guides/performance-tuning.adoc
|
|
220
|
-
- docs/index.adoc
|
|
221
155
|
- docs/lychee.toml
|
|
222
|
-
- docs/reference/api/overview.adoc
|
|
223
|
-
- docs/reference/cli/compress-command.adoc
|
|
224
|
-
- docs/reference/cli/overview.adoc
|
|
225
|
-
- docs/reference/index.adoc
|
|
226
|
-
- docs/resources/faq.adoc
|
|
227
|
-
- docs/resources/quick-reference.adoc
|
|
228
156
|
- docs/rust-backend.md
|
|
229
|
-
- docs/troubleshooting/index.adoc
|
|
230
157
|
- docs/xar_format.md
|
|
231
158
|
- examples/api_comparison.rb
|
|
232
159
|
- examples/deflate64_example.rb
|
|
@@ -739,6 +666,7 @@ files:
|
|
|
739
666
|
- lib/omnizip/zip/file.rb
|
|
740
667
|
- lib/omnizip/zip/input_stream.rb
|
|
741
668
|
- lib/omnizip/zip/output_stream.rb
|
|
669
|
+
- lib/tasks/docs.rake
|
|
742
670
|
- lib/tasks/msi_test_files.rake
|
|
743
671
|
- lib/tasks/platform_gems.rake
|
|
744
672
|
- lib/tasks/rust_backend.rake
|
|
@@ -755,6 +683,8 @@ files:
|
|
|
755
683
|
- readme-docs/par2-archives.adoc
|
|
756
684
|
- readme-docs/performance-profiler.adoc
|
|
757
685
|
- readme-docs/preprocessing-filters.adoc
|
|
686
|
+
- readme-docs/rar-archives.adoc
|
|
687
|
+
- scripts/generate_rar_oracle_fixtures.rb
|
|
758
688
|
- vendor/libomnizip_ffi.so
|
|
759
689
|
- xz-file-format-1.2.1.txt
|
|
760
690
|
homepage: https://github.com/omnizip/omnizip
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
# 00 — Refactor Plan Overview
|
|
2
|
-
|
|
3
|
-
Date: 2026-07-20 (updated 2026-07-21)
|
|
4
|
-
Scope: `lib/` and `spec/` of the omnizip gem.
|
|
5
|
-
|
|
6
|
-
## Goals
|
|
7
|
-
|
|
8
|
-
Apply OCP, DRY, MECE, model-driven design, open/closed principle, and high
|
|
9
|
-
performance to the omnizip codebase. Eliminate the anti-patterns declared
|
|
10
|
-
as absolute rules:
|
|
11
|
-
|
|
12
|
-
- `require_relative` (and internal `require`) inside `lib/`
|
|
13
|
-
- `.send` against private methods (breaks encapsulation)
|
|
14
|
-
- `instance_variable_set` / `instance_variable_get` on other objects
|
|
15
|
-
- `respond_to?` as a substitute for proper typing
|
|
16
|
-
- `double()` in specs (use real instances or `Struct`)
|
|
17
|
-
- Hand-rolled `to_h` / `from_h` / `to_json` on model classes (use `lutaml-model`)
|
|
18
|
-
|
|
19
|
-
## Track status
|
|
20
|
-
|
|
21
|
-
| # | File | Priority | Status |
|
|
22
|
-
|---|---|---|---|
|
|
23
|
-
| 01 | `01-registry-base-class.md` | high | **DONE** |
|
|
24
|
-
| 02 | `02-error-hierarchy.md` | high | **DONE** |
|
|
25
|
-
| 03 | `03-autoload-entry-point.md` | high | **DONE** |
|
|
26
|
-
| 04 | `04-filter-base-consolidation.md` | medium | **DONE** |
|
|
27
|
-
| 05 | `05-send-private-methods.md` | high | **DONE** |
|
|
28
|
-
| 06 | `06-instance-variable-access.md` | high | **DONE** |
|
|
29
|
-
| 07 | `07-respond-to-replacement.md` | medium | **DONE** — 11 type checks converted; 45 feature-detection sites annotated `# allowed:` and enforced by a spec; 1 deferred type check in `algorithms/lzma.rb` pending `LzmaOptions` unification |
|
|
30
|
-
| 08 | `08-spec-doubles.md` | low | **DONE** |
|
|
31
|
-
| 09 | `09-cli-shared-module.md` | low | **DONE** |
|
|
32
|
-
| 10 | `10-format-detector-ocp.md` | medium | **DONE** |
|
|
33
|
-
| 11 | `11-convenience-decoupling.md` | medium | **DONE** (ArchiveHandler dispatcher + ZipHandler + TarHandler) |
|
|
34
|
-
| 12 | `12-thread-safety.md` | covered by 01 | n/a |
|
|
35
|
-
| 13 | `13-lutaml-model-migration.md` | medium | **DONE** — six models migrated; follow-ups 2+3 landed in 0.3.28; 1 and 4 decided (deferred rename / migration declined with reasons) |
|
|
36
|
-
| 14 | `14-add-missing-specs.md` | high | **DONE** |
|
|
37
|
-
|
|
38
|
-
## Anti-pattern counts (lib/)
|
|
39
|
-
|
|
40
|
-
| Pattern | Before | After |
|
|
41
|
-
|---|---|---|
|
|
42
|
-
| `require_relative` / `require "omnizip/..."` | ~290 | **0** |
|
|
43
|
-
| `.send` on private methods | 17 | **0** |
|
|
44
|
-
| `instance_variable_set`/`get` | 54 | **0** |
|
|
45
|
-
| `double()` in specs | 1 | **0** |
|
|
46
|
-
| `respond_to?` | 60 at track 07 start (111 originally) | 49 — 46 annotated `# allowed:` (45 feature detection + 1 deferred type check) plus 3 doc comments |
|
|
47
|
-
| Hand-rolled model `to_h` | 26 | 24 (2 migrated to lutaml-model) |
|
|
48
|
-
|
|
49
|
-
## Architecture summary
|
|
50
|
-
|
|
51
|
-
- **`Omnizip::Registry`** — thread-safe base class with lazy-load triggers.
|
|
52
|
-
- **`Omnizip::IO::Source` / `Sink`** — polymorphic adapters for
|
|
53
|
-
String/Path/IO/StringIO/Tempfile.
|
|
54
|
-
- **`Omnizip::ArchiveHandler`** — dispatcher decoupling Convenience from
|
|
55
|
-
ZIP. New formats plug in by registering a handler.
|
|
56
|
-
- **`Omnizip::Platform`** — predicates for symlink/hardlink/NTFS support.
|
|
57
|
-
- **Error hierarchy** consolidated with backward-compat aliases.
|
|
58
|
-
- **`Filter` / `FilterBase`** unified.
|
|
59
|
-
|
|
60
|
-
## Test coverage
|
|
61
|
-
|
|
62
|
-
3643 examples, 0 failures. New specs:
|
|
63
|
-
|
|
64
|
-
- `spec/omnizip/registry_spec.rb`
|
|
65
|
-
- `spec/omnizip/error_spec.rb`
|
|
66
|
-
- `spec/omnizip/io/source_spec.rb`
|
|
67
|
-
- `spec/omnizip/convenience_tar_spec.rb`
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
# 01 — Registry base class
|
|
2
|
-
|
|
3
|
-
Priority: **high**.
|
|
4
|
-
Status: TODO.
|
|
5
|
-
|
|
6
|
-
## Problem
|
|
7
|
-
|
|
8
|
-
Five registry classes duplicate an identical pattern:
|
|
9
|
-
|
|
10
|
-
- `Omnizip::AlgorithmRegistry` (`lib/omnizip/algorithm_registry.rb`)
|
|
11
|
-
- `Omnizip::FormatRegistry` (`lib/omnizip/format_registry.rb`)
|
|
12
|
-
- `Omnizip::FilterRegistry` (`lib/omnizip/filter_registry.rb`)
|
|
13
|
-
- `Omnizip::ChecksumRegistry` (`lib/omnizip/checksum_registry.rb`)
|
|
14
|
-
- `Omnizip::OptimizationRegistry` (`lib/omnizip/optimization_registry.rb`)
|
|
15
|
-
- `Omnizip::Password::EncryptionRegistry` (`lib/omnizip/password/encryption_registry.rb`)
|
|
16
|
-
- `Omnizip::Converter::ConversionRegistry` (`lib/omnizip/converter/conversion_registry.rb`)
|
|
17
|
-
|
|
18
|
-
Each implements a slightly different version of `register / get /
|
|
19
|
-
registered? / available / reset` with different naming, error classes, and
|
|
20
|
-
key-normalization rules.
|
|
21
|
-
|
|
22
|
-
Inconsistencies:
|
|
23
|
-
|
|
24
|
-
| Registry | Storage | Reset | Not-found error |
|
|
25
|
-
|---|---|---|---|
|
|
26
|
-
| AlgorithmRegistry | `@algorithms ||= {}` | `reset!` | `UnknownAlgorithmError` |
|
|
27
|
-
| FormatRegistry | `@registry ||= {}` | (none) | returns nil |
|
|
28
|
-
| FilterRegistry | `@filters = {}` | `reset!` | `UnknownFilterError` |
|
|
29
|
-
| ChecksumRegistry | `@checksums = {}` | `clear` | `UnknownAlgorithmError` |
|
|
30
|
-
| OptimizationRegistry | `@strategies ||= {}` | `clear!` | `OptimizationNotFound` |
|
|
31
|
-
| EncryptionRegistry | `@strategies = {}` | `reset` | `ArgumentError` |
|
|
32
|
-
| ConversionRegistry | array of classes | `reset` | returns nil |
|
|
33
|
-
|
|
34
|
-
None are thread-safe.
|
|
35
|
-
|
|
36
|
-
## Plan
|
|
37
|
-
|
|
38
|
-
1. Create `lib/omnizip/registry.rb` defining `Omnizip::Registry` — a
|
|
39
|
-
thread-safe generic registry with:
|
|
40
|
-
- `register(name, value, **opts)` (overrides per subclass)
|
|
41
|
-
- `get(name)` → raises `not_found_error` if missing
|
|
42
|
-
- `registered?(name)` → bool
|
|
43
|
-
- `available` / `all` → keys
|
|
44
|
-
- `reset!` → clears store (canonical name)
|
|
45
|
-
- `Mutex`-synchronized mutations
|
|
46
|
-
- Configurable `not_found_error` class and `normalize_key` hook.
|
|
47
|
-
|
|
48
|
-
2. Migrate each registry to inherit from `Omnizip::Registry`. Preserve the
|
|
49
|
-
existing public class names and method names so call sites still work.
|
|
50
|
-
For backward compatibility, alias `clear`/`reset` to `reset!`,
|
|
51
|
-
`supported_formats`/`strategies` to `available` where applicable.
|
|
52
|
-
|
|
53
|
-
3. FormatRegistry keeps `normalize_extension` as its `normalize_key` hook.
|
|
54
|
-
|
|
55
|
-
4. FilterRegistry keeps its `formats:` registry metadata and
|
|
56
|
-
`get_for_format` / `supports_format?` / `filters_for_format` extensions
|
|
57
|
-
on top of the base.
|
|
58
|
-
|
|
59
|
-
5. ConversionRegistry is array-based and is the only outlier; leave as-is
|
|
60
|
-
or convert to a key based on `[source, target]` tuple. Pragmatically:
|
|
61
|
-
leave as-is, document why.
|
|
62
|
-
|
|
63
|
-
## Acceptance
|
|
64
|
-
|
|
65
|
-
- All existing registry specs pass unchanged.
|
|
66
|
-
- `bundle exec rspec` failure count is ≤ baseline (2).
|
|
67
|
-
- No new rubocop offenses.
|
|
68
|
-
- `lib/omnizip/registry_spec.rb` covers register/get/registered?/reset! and
|
|
69
|
-
thread-safety smoke test.
|
|
70
|
-
|
|
71
|
-
## Files to touch
|
|
72
|
-
|
|
73
|
-
- create `lib/omnizip/registry.rb`
|
|
74
|
-
- create `spec/omnizip/registry_spec.rb`
|
|
75
|
-
- modify `lib/omnizip/algorithm_registry.rb`
|
|
76
|
-
- modify `lib/omnizip/format_registry.rb`
|
|
77
|
-
- modify `lib/omnizip/filter_registry.rb`
|
|
78
|
-
- modify `lib/omnizip/checksum_registry.rb`
|
|
79
|
-
- modify `lib/omnizip/optimization_registry.rb`
|
|
80
|
-
- modify `lib/omnizip/password/encryption_registry.rb`
|
|
81
|
-
- modify `lib/omnizip.rb` to autoload `:Registry`
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# 02 — Error hierarchy consolidation
|
|
2
|
-
|
|
3
|
-
Priority: **high**.
|
|
4
|
-
Status: TODO.
|
|
5
|
-
|
|
6
|
-
## Problems
|
|
7
|
-
|
|
8
|
-
1. `AlgorithmNotFoundError` and `UnknownAlgorithmError` both exist in
|
|
9
|
-
`lib/omnizip/error.rb` and are used interchangeably. The two registries
|
|
10
|
-
that raise on miss (`AlgorithmRegistry`, `ChecksumRegistry`) both raise
|
|
11
|
-
`UnknownAlgorithmError`.
|
|
12
|
-
2. `OptimizationNotFound` lacks the `Error` suffix convention.
|
|
13
|
-
3. `Omnizip::IOError` shadows Ruby's built-in `IOError` — confusing for
|
|
14
|
-
users who `rescue IOError`.
|
|
15
|
-
4. `UnknownFilterError` is defined at the bottom of
|
|
16
|
-
`lib/omnizip/filter_registry.rb` instead of `lib/omnizip/error.rb`.
|
|
17
|
-
5. `EncryptionRegistry` raises vanilla `ArgumentError` instead of a
|
|
18
|
-
domain-specific error.
|
|
19
|
-
6. `ConversionRegistry` returns nil instead of raising.
|
|
20
|
-
|
|
21
|
-
## Plan
|
|
22
|
-
|
|
23
|
-
1. In `lib/omnizip/error.rb`:
|
|
24
|
-
- Keep `AlgorithmNotFoundError` as canonical; make
|
|
25
|
-
`UnknownAlgorithmError` an alias (subclass) for backward compatibility.
|
|
26
|
-
Document the alias.
|
|
27
|
-
- Rename `OptimizationNotFound` → `OptimizationNotFoundError`. Keep the
|
|
28
|
-
old name as an alias for backward compatibility.
|
|
29
|
-
- Rename `IOError` → `IOOperationError`. Keep `IOError` as an alias for
|
|
30
|
-
backward compatibility (or deprecate it).
|
|
31
|
-
- Add `UnknownFilterError` to `error.rb` (and remove from
|
|
32
|
-
`filter_registry.rb`).
|
|
33
|
-
- Add `UnknownEncryptionStrategyError < Error`.
|
|
34
|
-
- Add `ConversionNotSupportedError < Error`.
|
|
35
|
-
|
|
36
|
-
2. Update all internal `raise` sites to use the canonical names.
|
|
37
|
-
|
|
38
|
-
3. Update `FormatRegistry`, `ConversionRegistry` to raise their new errors
|
|
39
|
-
instead of returning nil where the caller cannot reasonably handle nil.
|
|
40
|
-
|
|
41
|
-
4. Update `lib/omnizip.rb` autoload entries to include the new error names.
|
|
42
|
-
|
|
43
|
-
## Acceptance
|
|
44
|
-
|
|
45
|
-
- All existing specs pass.
|
|
46
|
-
- No new rubocop offenses.
|
|
47
|
-
- `lib/omnizip/error_spec.rb` confirms hierarchy and aliases.
|
|
48
|
-
|
|
49
|
-
## Files
|
|
50
|
-
|
|
51
|
-
- modify `lib/omnizip/error.rb`
|
|
52
|
-
- modify `lib/omnizip/filter_registry.rb` (remove inline error)
|
|
53
|
-
- modify `lib/omnizip/optimization_registry.rb` (raise renamed error)
|
|
54
|
-
- modify `lib/omnizip/password/encryption_registry.rb` (raise new error)
|
|
55
|
-
- modify `lib/omnizip/converter/conversion_registry.rb` (raise on miss)
|
|
56
|
-
- modify `lib/omnizip.rb` autoload list
|
|
57
|
-
- create `spec/omnizip/error_spec.rb`
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# 03 — Autoload entry point
|
|
2
|
-
|
|
3
|
-
Priority: **high**.
|
|
4
|
-
Status: TODO.
|
|
5
|
-
|
|
6
|
-
## Problem
|
|
7
|
-
|
|
8
|
-
`lib/omnizip.rb` lines 99–139 contain 31 `require_relative` calls for
|
|
9
|
-
algorithms, filters, checksums, and formats. Per the global rule,
|
|
10
|
-
`require_relative` is forbidden inside `lib/`. The justification given is
|
|
11
|
-
"these classes self-register at load time" — but self-registration only
|
|
12
|
-
needs to happen *before first lookup*, not at gem boot.
|
|
13
|
-
|
|
14
|
-
Additional inconsistency: line 179 uses `require "omnizip/convenience"`
|
|
15
|
-
while similar modules are autoloaded.
|
|
16
|
-
|
|
17
|
-
## Plan
|
|
18
|
-
|
|
19
|
-
1. Convert each `require_relative` line in `lib/omnizip.rb` to an autoload
|
|
20
|
-
declaration in the appropriate namespace:
|
|
21
|
-
|
|
22
|
-
```ruby
|
|
23
|
-
module Omnizip
|
|
24
|
-
module Algorithms
|
|
25
|
-
autoload :LZMA, "omnizip/algorithms/lzma"
|
|
26
|
-
autoload :LZMA2, "omnizip/algorithms/lzma2"
|
|
27
|
-
# ...
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
2. For self-registering classes, keep the registration call at the bottom
|
|
33
|
-
of each implementation file (`AlgorithmRegistry.register(:lzma, LZMA)`).
|
|
34
|
-
Autoload will load the file on first reference, which triggers
|
|
35
|
-
registration. Tests that pre-populate the registry should reference the
|
|
36
|
-
constant first (e.g., `Omnizip::Algorithms::LZMA`).
|
|
37
|
-
|
|
38
|
-
3. For files that are currently eager-required because *nothing else*
|
|
39
|
-
references them (only the registration matters), add an explicit
|
|
40
|
-
autoload entry in the right namespace *and* arrange for the constant to
|
|
41
|
-
be referenced on first registry miss. Concretely: change registries to
|
|
42
|
-
trigger autoload by referencing the constant rather than calling
|
|
43
|
-
`register` from the file body. See `08-registry-lazy-trigger.md` if a
|
|
44
|
-
fallback is needed.
|
|
45
|
-
|
|
46
|
-
4. Replace the trailing `require "omnizip/convenience"` with an autoload
|
|
47
|
-
entry — `autoload :Convenience, "omnizip/convenience"` — and reference
|
|
48
|
-
it lazily where used (or eager-load at the very end of `lib/omnizip.rb`
|
|
49
|
-
via `require "omnizip/convenience"` ONLY if Convenience must extend
|
|
50
|
-
Omnizip at boot; document why).
|
|
51
|
-
|
|
52
|
-
5. Run specs. If any spec fails because a class never autoloaded, fix by
|
|
53
|
-
adding a `require` at the top of the spec file (specs are NOT subject
|
|
54
|
-
to the no-`require_relative` rule for library code; they ARE subject to
|
|
55
|
-
it for sibling spec files but not for library files).
|
|
56
|
-
|
|
57
|
-
## Acceptance
|
|
58
|
-
|
|
59
|
-
- `grep -rn "require_relative" lib/` returns zero matches.
|
|
60
|
-
- `grep -rn '^require "omnizip/' lib/` is limited to the convenience
|
|
61
|
-
fallback (documented) and any in-file registration triggers.
|
|
62
|
-
- All specs pass.
|
|
63
|
-
|
|
64
|
-
## Files
|
|
65
|
-
|
|
66
|
-
- modify `lib/omnizip.rb` (replace lines 99–139 and line 179)
|
|
67
|
-
- ensure each `lib/omnizip/algorithms/*.rb`, `lib/omnizip/filters/*.rb`,
|
|
68
|
-
`lib/omnizip/checksums/*.rb`, `lib/omnizip/formats/*.rb` ends with its
|
|
69
|
-
own registration call (most already do).
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# 04 — Filter base consolidation
|
|
2
|
-
|
|
3
|
-
Priority: **medium**.
|
|
4
|
-
Status: TODO.
|
|
5
|
-
|
|
6
|
-
## Problem
|
|
7
|
-
|
|
8
|
-
Two parallel base classes exist:
|
|
9
|
-
|
|
10
|
-
- `Omnizip::Filter` (`lib/omnizip/filter.rb`) — has `architecture` and
|
|
11
|
-
`name` attrs plus `id_for_format(format)`. Used by `BCJ` and `Delta`.
|
|
12
|
-
- `Omnizip::Filters::FilterBase` (`lib/omnizip/filters/filter_base.rb`) —
|
|
13
|
-
minimal `encode`/`decode`/`.metadata` stub. Used by `BcjX86`, `BcjArm`,
|
|
14
|
-
`BcjArm64`, `BcjIa64`, `BcjPpc`, `BcjSparc`, `Bcj2`.
|
|
15
|
-
|
|
16
|
-
They share identical `encode(data, position=0)` / `decode(data,
|
|
17
|
-
position=0)` / `.metadata` stubs. `FilterBase` is a subset of `Filter`.
|
|
18
|
-
|
|
19
|
-
## Plan
|
|
20
|
-
|
|
21
|
-
1. Keep `Filter` as the canonical base, located at
|
|
22
|
-
`lib/omnizip/filters/base.rb`. Make `architecture` optional (defaults
|
|
23
|
-
to nil).
|
|
24
|
-
2. Make `Omnizip::Filters::FilterBase` an alias / subclass of the unified
|
|
25
|
-
base for backward compatibility.
|
|
26
|
-
3. Migrate subclasses of `FilterBase` to inherit from the unified base.
|
|
27
|
-
4. Delete the body of `lib/omnizip/filters/filter_base.rb`, leaving it as
|
|
28
|
-
a backward-compat alias:
|
|
29
|
-
```ruby
|
|
30
|
-
require "omnizip/filters/base"
|
|
31
|
-
module Omnizip::Filters
|
|
32
|
-
FilterBase = Filter # backward-compat alias
|
|
33
|
-
end
|
|
34
|
-
```
|
|
35
|
-
(or define `FilterBase < Filter` if a true subclass is preferred.)
|
|
36
|
-
|
|
37
|
-
## Acceptance
|
|
38
|
-
|
|
39
|
-
- All filter specs pass.
|
|
40
|
-
- No public API breaks.
|
|
41
|
-
- `lib/omnizip/filter.rb` removed or stubbed to alias.
|
|
42
|
-
|
|
43
|
-
## Files
|
|
44
|
-
|
|
45
|
-
- modify `lib/omnizip/filter.rb`
|
|
46
|
-
- create or modify `lib/omnizip/filters/base.rb`
|
|
47
|
-
- modify `lib/omnizip/filters/filter_base.rb` (alias)
|
|
48
|
-
- modify each subclass to inherit from the new base
|
|
49
|
-
- add autoload entry in `lib/omnizip/filters.rb`
|