omnizip 0.4.9-arm-linux → 0.4.11-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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-oss.yml +814 -0
  3. data/.rubocop.yml +15 -1
  4. data/.rubocop_todo.yml +7 -1
  5. data/CHANGELOG.md +17 -0
  6. data/Gemfile +23 -1
  7. data/README.adoc +101 -1282
  8. data/docs/compatibility.adoc +1 -2
  9. data/docs/lychee.toml +1 -7
  10. data/lib/omnizip/algorithms/deflate/constants.rb +5 -3
  11. data/lib/omnizip/implementations/rust/archive.rb +9 -41
  12. data/lib/omnizip/implementations/rust/ffi_library.rb +127 -0
  13. data/lib/omnizip/implementations/rust/fiddle_library.rb +144 -0
  14. data/lib/omnizip/implementations/rust/library.rb +39 -104
  15. data/lib/omnizip/implementations/rust.rb +2 -0
  16. data/lib/omnizip/version.rb +1 -1
  17. data/lib/tasks/docs.rake +146 -0
  18. data/readme-docs/rar-archives.adoc +505 -0
  19. data/scripts/generate_rar_oracle_fixtures.rb +177 -0
  20. data/vendor/libomnizip_ffi.so +0 -0
  21. metadata +21 -74
  22. data/TODO.refactor/00-overview.md +0 -67
  23. data/TODO.refactor/01-registry-base-class.md +0 -81
  24. data/TODO.refactor/02-error-hierarchy.md +0 -57
  25. data/TODO.refactor/03-autoload-entry-point.md +0 -69
  26. data/TODO.refactor/04-filter-base-consolidation.md +0 -49
  27. data/TODO.refactor/05-send-private-methods.md +0 -64
  28. data/TODO.refactor/06-instance-variable-access.md +0 -69
  29. data/TODO.refactor/07-respond-to-replacement.md +0 -237
  30. data/TODO.refactor/08-spec-doubles.md +0 -27
  31. data/TODO.refactor/09-cli-shared-module.md +0 -35
  32. data/TODO.refactor/10-format-detector-ocp.md +0 -34
  33. data/TODO.refactor/11-convenience-decoupling.md +0 -38
  34. data/TODO.refactor/12-thread-safety.md +0 -8
  35. data/TODO.refactor/13-lutaml-model-migration.md +0 -158
  36. data/TODO.refactor/14-add-missing-specs.md +0 -25
  37. data/docs/.github/workflows/docs.yml +0 -142
  38. data/docs/.gitignore +0 -21
  39. data/docs/Gemfile +0 -13
  40. data/docs/README.md +0 -101
  41. data/docs/_config.yml +0 -112
  42. data/docs/comparison/feature-matrix.adoc +0 -694
  43. data/docs/comparison/index.adoc +0 -113
  44. data/docs/comparison/vs-7zip.adoc +0 -309
  45. data/docs/comparison/vs-peazip.adoc +0 -77
  46. data/docs/comparison/vs-rubyzip.adoc +0 -342
  47. data/docs/comparison/vs-winrar.adoc +0 -100
  48. data/docs/concepts/index.adoc +0 -129
  49. data/docs/developer/architecture.adoc +0 -256
  50. data/docs/developer/contributing.adoc +0 -158
  51. data/docs/developer/index.adoc +0 -25
  52. data/docs/developer/testing.adoc +0 -212
  53. data/docs/getting-started/basic-usage.adoc +0 -263
  54. data/docs/getting-started/index.adoc +0 -42
  55. data/docs/getting-started/installation.adoc +0 -138
  56. data/docs/getting-started/quick-start.adoc +0 -183
  57. data/docs/getting-started/your-first-archive.adoc +0 -219
  58. data/docs/guides/advanced-features/encryption.adoc +0 -277
  59. data/docs/guides/advanced-features/index.adoc +0 -59
  60. data/docs/guides/advanced-features/parallel-processing.adoc +0 -144
  61. data/docs/guides/advanced-features/progress-tracking.adoc +0 -124
  62. data/docs/guides/advanced-features/streaming.adoc +0 -142
  63. data/docs/guides/archive-formats/gzip-format.adoc +0 -105
  64. data/docs/guides/archive-formats/index.adoc +0 -160
  65. data/docs/guides/archive-formats/ole-format.adoc +0 -151
  66. data/docs/guides/archive-formats/rar-format.adoc +0 -104
  67. data/docs/guides/archive-formats/rar5.adoc +0 -521
  68. data/docs/guides/archive-formats/rpm-format.adoc +0 -108
  69. data/docs/guides/archive-formats/seven-zip-format.adoc +0 -35
  70. data/docs/guides/archive-formats/tar-format.adoc +0 -106
  71. data/docs/guides/archive-formats/xz-format.adoc +0 -118
  72. data/docs/guides/archive-formats/zip-format.adoc +0 -35
  73. data/docs/guides/compression-algorithms/bzip2.adoc +0 -114
  74. data/docs/guides/compression-algorithms/deflate.adoc +0 -313
  75. data/docs/guides/compression-algorithms/index.adoc +0 -190
  76. data/docs/guides/compression-algorithms/lzma.adoc +0 -394
  77. data/docs/guides/compression-algorithms/lzma2.adoc +0 -244
  78. data/docs/guides/compression-algorithms/ppmd.adoc +0 -313
  79. data/docs/guides/compression-algorithms/zstandard.adoc +0 -296
  80. data/docs/guides/creating-archives.adoc +0 -348
  81. data/docs/guides/extracting-archives.adoc +0 -53
  82. data/docs/guides/format-conversion.adoc +0 -64
  83. data/docs/guides/index.adoc +0 -49
  84. data/docs/guides/migration-rubyzip.adoc +0 -217
  85. data/docs/guides/parity-archives.adoc +0 -605
  86. data/docs/guides/performance-tuning.adoc +0 -88
  87. data/docs/index.adoc +0 -228
  88. data/docs/reference/api/overview.adoc +0 -187
  89. data/docs/reference/cli/compress-command.adoc +0 -114
  90. data/docs/reference/cli/overview.adoc +0 -140
  91. data/docs/reference/index.adoc +0 -26
  92. data/docs/resources/faq.adoc +0 -185
  93. data/docs/resources/quick-reference.adoc +0 -222
  94. 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}"
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.9
4
+ version: 0.4.11
5
5
  platform: arm-linux
6
6
  authors:
7
7
  - Ribose Inc.
@@ -57,6 +57,20 @@ dependencies:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
59
  version: 0.2.4
60
+ - !ruby/object:Gem::Dependency
61
+ name: ffi
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.15'
67
+ type: :runtime
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.15'
60
74
  - !ruby/object:Gem::Dependency
61
75
  name: lutaml-model
62
76
  requirement: !ruby/object:Gem::Requirement
@@ -111,6 +125,7 @@ extensions: []
111
125
  extra_rdoc_files: []
112
126
  files:
113
127
  - ".rspec"
128
+ - ".rubocop-oss.yml"
114
129
  - ".rubocop.yml"
115
130
  - ".rubocop_todo.yml"
116
131
  - CHANGELOG.md
@@ -120,21 +135,6 @@ files:
120
135
  - LICENSE
121
136
  - README.adoc
122
137
  - Rakefile
123
- - TODO.refactor/00-overview.md
124
- - TODO.refactor/01-registry-base-class.md
125
- - TODO.refactor/02-error-hierarchy.md
126
- - TODO.refactor/03-autoload-entry-point.md
127
- - TODO.refactor/04-filter-base-consolidation.md
128
- - TODO.refactor/05-send-private-methods.md
129
- - TODO.refactor/06-instance-variable-access.md
130
- - TODO.refactor/07-respond-to-replacement.md
131
- - TODO.refactor/08-spec-doubles.md
132
- - TODO.refactor/09-cli-shared-module.md
133
- - TODO.refactor/10-format-detector-ocp.md
134
- - TODO.refactor/11-convenience-decoupling.md
135
- - TODO.refactor/12-thread-safety.md
136
- - TODO.refactor/13-lutaml-model-migration.md
137
- - TODO.refactor/14-add-missing-specs.md
138
138
  - benchmark/README.md
139
139
  - benchmark/benchmark_suite.rb
140
140
  - benchmark/compression_bench.rb
@@ -147,71 +147,13 @@ files:
147
147
  - benchmark/test_data.rb
148
148
  - config/formats/rar3_spec.yml
149
149
  - config/formats/rar5_spec.yml
150
- - docs/.github/workflows/docs.yml
151
- - docs/.gitignore
152
150
  - docs/.lychee.toml
153
- - docs/Gemfile
154
151
  - docs/RAR_WRITE_SUPPORT.md
155
- - docs/README.md
156
- - docs/_config.yml
157
152
  - docs/assets/logo.svg
158
153
  - docs/assets/omnizip-logo.pdf
159
- - docs/comparison/feature-matrix.adoc
160
- - docs/comparison/index.adoc
161
- - docs/comparison/vs-7zip.adoc
162
- - docs/comparison/vs-peazip.adoc
163
- - docs/comparison/vs-rubyzip.adoc
164
- - docs/comparison/vs-winrar.adoc
165
154
  - docs/compatibility.adoc
166
- - docs/concepts/index.adoc
167
- - docs/developer/architecture.adoc
168
- - docs/developer/contributing.adoc
169
- - docs/developer/index.adoc
170
- - docs/developer/testing.adoc
171
- - docs/getting-started/basic-usage.adoc
172
- - docs/getting-started/index.adoc
173
- - docs/getting-started/installation.adoc
174
- - docs/getting-started/quick-start.adoc
175
- - docs/getting-started/your-first-archive.adoc
176
- - docs/guides/advanced-features/encryption.adoc
177
- - docs/guides/advanced-features/index.adoc
178
- - docs/guides/advanced-features/parallel-processing.adoc
179
- - docs/guides/advanced-features/progress-tracking.adoc
180
- - docs/guides/advanced-features/streaming.adoc
181
- - docs/guides/archive-formats/gzip-format.adoc
182
- - docs/guides/archive-formats/index.adoc
183
- - docs/guides/archive-formats/ole-format.adoc
184
- - docs/guides/archive-formats/rar-format.adoc
185
- - docs/guides/archive-formats/rar5.adoc
186
- - docs/guides/archive-formats/rpm-format.adoc
187
- - docs/guides/archive-formats/seven-zip-format.adoc
188
- - docs/guides/archive-formats/tar-format.adoc
189
- - docs/guides/archive-formats/xz-format.adoc
190
- - docs/guides/archive-formats/zip-format.adoc
191
- - docs/guides/compression-algorithms/bzip2.adoc
192
- - docs/guides/compression-algorithms/deflate.adoc
193
- - docs/guides/compression-algorithms/index.adoc
194
- - docs/guides/compression-algorithms/lzma.adoc
195
- - docs/guides/compression-algorithms/lzma2.adoc
196
- - docs/guides/compression-algorithms/ppmd.adoc
197
- - docs/guides/compression-algorithms/zstandard.adoc
198
- - docs/guides/creating-archives.adoc
199
- - docs/guides/extracting-archives.adoc
200
- - docs/guides/format-conversion.adoc
201
- - docs/guides/index.adoc
202
- - docs/guides/migration-rubyzip.adoc
203
- - docs/guides/parity-archives.adoc
204
- - docs/guides/performance-tuning.adoc
205
- - docs/index.adoc
206
155
  - docs/lychee.toml
207
- - docs/reference/api/overview.adoc
208
- - docs/reference/cli/compress-command.adoc
209
- - docs/reference/cli/overview.adoc
210
- - docs/reference/index.adoc
211
- - docs/resources/faq.adoc
212
- - docs/resources/quick-reference.adoc
213
156
  - docs/rust-backend.md
214
- - docs/troubleshooting/index.adoc
215
157
  - docs/xar_format.md
216
158
  - examples/api_comparison.rb
217
159
  - examples/deflate64_example.rb
@@ -601,6 +543,8 @@ files:
601
543
  - lib/omnizip/implementations/base/state_machine_base.rb
602
544
  - lib/omnizip/implementations/rust.rb
603
545
  - lib/omnizip/implementations/rust/archive.rb
546
+ - lib/omnizip/implementations/rust/ffi_library.rb
547
+ - lib/omnizip/implementations/rust/fiddle_library.rb
604
548
  - lib/omnizip/implementations/rust/library.rb
605
549
  - lib/omnizip/implementations/seven_zip.rb
606
550
  - lib/omnizip/implementations/seven_zip/lzma.rb
@@ -722,6 +666,7 @@ files:
722
666
  - lib/omnizip/zip/file.rb
723
667
  - lib/omnizip/zip/input_stream.rb
724
668
  - lib/omnizip/zip/output_stream.rb
669
+ - lib/tasks/docs.rake
725
670
  - lib/tasks/msi_test_files.rake
726
671
  - lib/tasks/platform_gems.rake
727
672
  - lib/tasks/rust_backend.rake
@@ -738,6 +683,8 @@ files:
738
683
  - readme-docs/par2-archives.adoc
739
684
  - readme-docs/performance-profiler.adoc
740
685
  - readme-docs/preprocessing-filters.adoc
686
+ - readme-docs/rar-archives.adoc
687
+ - scripts/generate_rar_oracle_fixtures.rb
741
688
  - vendor/libomnizip_ffi.so
742
689
  - xz-file-format-1.2.1.txt
743
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`