omnizip 0.3.8 → 0.3.10

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +51 -50
  3. data/Gemfile +2 -0
  4. data/Rakefile +3 -0
  5. data/TODO.refactor/00-overview.md +67 -0
  6. data/TODO.refactor/01-registry-base-class.md +81 -0
  7. data/TODO.refactor/02-error-hierarchy.md +57 -0
  8. data/TODO.refactor/03-autoload-entry-point.md +69 -0
  9. data/TODO.refactor/04-filter-base-consolidation.md +49 -0
  10. data/TODO.refactor/05-send-private-methods.md +64 -0
  11. data/TODO.refactor/06-instance-variable-access.md +69 -0
  12. data/TODO.refactor/07-respond-to-replacement.md +91 -0
  13. data/TODO.refactor/08-spec-doubles.md +27 -0
  14. data/TODO.refactor/09-cli-shared-module.md +35 -0
  15. data/TODO.refactor/10-format-detector-ocp.md +34 -0
  16. data/TODO.refactor/11-convenience-decoupling.md +38 -0
  17. data/TODO.refactor/12-thread-safety.md +8 -0
  18. data/TODO.refactor/13-lutaml-model-migration.md +82 -0
  19. data/TODO.refactor/14-add-missing-specs.md +25 -0
  20. data/lib/omnizip/algorithm.rb +0 -2
  21. data/lib/omnizip/algorithm_registry.rb +29 -57
  22. data/lib/omnizip/algorithms/lzma/bit_model.rb +1 -1
  23. data/lib/omnizip/algorithms/lzma/dictionary.rb +4 -3
  24. data/lib/omnizip/algorithms/lzma/distance_coder.rb +5 -102
  25. data/lib/omnizip/algorithms/lzma/length_coder.rb +4 -92
  26. data/lib/omnizip/algorithms/lzma/literal_decoder.rb +4 -119
  27. data/lib/omnizip/algorithms/lzma/lzip_decoder.rb +2 -0
  28. data/lib/omnizip/algorithms/lzma/match_finder.rb +59 -29
  29. data/lib/omnizip/algorithms/lzma/optimal_encoder.rb +20 -12
  30. data/lib/omnizip/algorithms/lzma/range_decoder.rb +77 -269
  31. data/lib/omnizip/algorithms/lzma/range_encoder.rb +0 -9
  32. data/lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb +0 -15
  33. data/lib/omnizip/algorithms/lzma/xz_state.rb +0 -8
  34. data/lib/omnizip/algorithms/lzma/xz_utils_decoder.rb +189 -968
  35. data/lib/omnizip/algorithms/lzma2/encoder.rb +0 -2
  36. data/lib/omnizip/algorithms/lzma2/lzma2_chunk.rb +0 -2
  37. data/lib/omnizip/algorithms/lzma2/properties.rb +0 -2
  38. data/lib/omnizip/algorithms/ppmd7/model.rb +1 -1
  39. data/lib/omnizip/algorithms/ppmd8/context.rb +0 -2
  40. data/lib/omnizip/algorithms/ppmd8/model.rb +1 -1
  41. data/lib/omnizip/algorithms/ppmd8.rb +0 -2
  42. data/lib/omnizip/algorithms/ppmd_base.rb +0 -2
  43. data/lib/omnizip/algorithms/sevenzip_lzma2.rb +0 -5
  44. data/lib/omnizip/algorithms/xz_lzma2.rb +0 -5
  45. data/lib/omnizip/algorithms/zstandard/frame/header.rb +3 -5
  46. data/lib/omnizip/algorithms.rb +1 -0
  47. data/lib/omnizip/archive_handler.rb +75 -0
  48. data/lib/omnizip/archive_handlers/tar_handler.rb +51 -0
  49. data/lib/omnizip/archive_handlers/zip_handler.rb +73 -0
  50. data/lib/omnizip/archive_handlers.rb +11 -0
  51. data/lib/omnizip/checksum_registry.rb +23 -96
  52. data/lib/omnizip/cli/shared.rb +31 -0
  53. data/lib/omnizip/cli.rb +6 -43
  54. data/lib/omnizip/commands/archive_create_command.rb +0 -6
  55. data/lib/omnizip/commands/archive_extract_command.rb +0 -4
  56. data/lib/omnizip/commands/archive_list_command.rb +0 -4
  57. data/lib/omnizip/commands/archive_repair_command.rb +0 -2
  58. data/lib/omnizip/commands/archive_verify_command.rb +0 -2
  59. data/lib/omnizip/commands/compress_command.rb +0 -2
  60. data/lib/omnizip/commands/decompress_command.rb +0 -2
  61. data/lib/omnizip/commands/list_command.rb +0 -2
  62. data/lib/omnizip/commands/metadata_command.rb +0 -5
  63. data/lib/omnizip/commands/parity_create_command.rb +0 -2
  64. data/lib/omnizip/commands/parity_repair_command.rb +0 -2
  65. data/lib/omnizip/commands/parity_verify_command.rb +0 -2
  66. data/lib/omnizip/commands/profile_list_command.rb +0 -2
  67. data/lib/omnizip/commands/profile_show_command.rb +0 -2
  68. data/lib/omnizip/convenience.rb +111 -234
  69. data/lib/omnizip/converter/seven_zip_to_zip_strategy.rb +1 -1
  70. data/lib/omnizip/crypto/aes256/cipher.rb +6 -1
  71. data/lib/omnizip/error.rb +22 -18
  72. data/lib/omnizip/filter.rb +5 -3
  73. data/lib/omnizip/filter_registry.rb +74 -98
  74. data/lib/omnizip/filters/bcj2/decoder.rb +196 -130
  75. data/lib/omnizip/filters/bcj2.rb +0 -2
  76. data/lib/omnizip/filters/bcj_x86.rb +0 -2
  77. data/lib/omnizip/filters/filter_base.rb +4 -70
  78. data/lib/omnizip/filters/registry.rb +0 -2
  79. data/lib/omnizip/format_detector.rb +21 -12
  80. data/lib/omnizip/format_registry.rb +79 -38
  81. data/lib/omnizip/formats/bzip2_file.rb +0 -2
  82. data/lib/omnizip/formats/cpio/entry.rb +0 -2
  83. data/lib/omnizip/formats/cpio/reader.rb +0 -1
  84. data/lib/omnizip/formats/cpio/writer.rb +0 -1
  85. data/lib/omnizip/formats/cpio.rb +0 -1
  86. data/lib/omnizip/formats/format_spec_loader.rb +1 -1
  87. data/lib/omnizip/formats/gzip.rb +0 -2
  88. data/lib/omnizip/formats/iso/directory_record.rb +6 -3
  89. data/lib/omnizip/formats/iso/reader.rb +2 -2
  90. data/lib/omnizip/formats/iso.rb +0 -1
  91. data/lib/omnizip/formats/lzip.rb +5 -25
  92. data/lib/omnizip/formats/lzma_alone.rb +7 -25
  93. data/lib/omnizip/formats/msi/cab_extractor.rb +181 -0
  94. data/lib/omnizip/formats/msi/constants.rb +157 -0
  95. data/lib/omnizip/formats/msi/directory_resolver.rb +188 -0
  96. data/lib/omnizip/formats/msi/entry.rb +128 -0
  97. data/lib/omnizip/formats/msi/reader.rb +301 -0
  98. data/lib/omnizip/formats/msi/string_pool.rb +246 -0
  99. data/lib/omnizip/formats/msi/table_parser.rb +244 -0
  100. data/lib/omnizip/formats/msi.rb +84 -0
  101. data/lib/omnizip/formats/ole/allocation_table.rb +10 -1
  102. data/lib/omnizip/formats/ole/dirent.rb +20 -3
  103. data/lib/omnizip/formats/ole/header.rb +0 -1
  104. data/lib/omnizip/formats/ole/ranges_io.rb +21 -3
  105. data/lib/omnizip/formats/ole/storage.rb +19 -4
  106. data/lib/omnizip/formats/ole.rb +0 -2
  107. data/lib/omnizip/formats/rar/block_parser.rb +0 -1
  108. data/lib/omnizip/formats/rar/compression/ppmd/encoder.rb +1 -1
  109. data/lib/omnizip/formats/rar/decompressor.rb +38 -1
  110. data/lib/omnizip/formats/rar/header.rb +0 -1
  111. data/lib/omnizip/formats/rar/rar5/writer.rb +0 -2
  112. data/lib/omnizip/formats/rar/reader.rb +2 -3
  113. data/lib/omnizip/formats/rar/writer.rb +0 -1
  114. data/lib/omnizip/formats/rar.rb +2 -2
  115. data/lib/omnizip/formats/rpm/entry.rb +0 -1
  116. data/lib/omnizip/formats/rpm/header.rb +3 -8
  117. data/lib/omnizip/formats/rpm/lead.rb +37 -26
  118. data/lib/omnizip/formats/rpm/tag.rb +0 -1
  119. data/lib/omnizip/formats/rpm/writer.rb +0 -1
  120. data/lib/omnizip/formats/seven_zip/bcj2_stream_decompressor.rb +98 -61
  121. data/lib/omnizip/formats/seven_zip/coder_chain.rb +1 -2
  122. data/lib/omnizip/formats/seven_zip/encoded_header.rb +0 -1
  123. data/lib/omnizip/formats/seven_zip/encrypted_header.rb +0 -1
  124. data/lib/omnizip/formats/seven_zip/header.rb +0 -1
  125. data/lib/omnizip/formats/seven_zip/header_encryptor.rb +0 -1
  126. data/lib/omnizip/formats/seven_zip/header_writer.rb +22 -61
  127. data/lib/omnizip/formats/seven_zip/models/file_entry.rb +1 -1
  128. data/lib/omnizip/formats/seven_zip/parser.rb +14 -13
  129. data/lib/omnizip/formats/seven_zip/reader.rb +82 -19
  130. data/lib/omnizip/formats/seven_zip/split_archive_reader.rb +0 -15
  131. data/lib/omnizip/formats/seven_zip/split_archive_writer.rb +0 -1
  132. data/lib/omnizip/formats/seven_zip/writer.rb +22 -68
  133. data/lib/omnizip/formats/seven_zip.rb +63 -5
  134. data/lib/omnizip/formats/tar/entry.rb +0 -2
  135. data/lib/omnizip/formats/tar/header.rb +0 -2
  136. data/lib/omnizip/formats/tar/reader.rb +0 -2
  137. data/lib/omnizip/formats/tar/writer.rb +0 -2
  138. data/lib/omnizip/formats/tar.rb +0 -1
  139. data/lib/omnizip/formats/xar/entry.rb +0 -1
  140. data/lib/omnizip/formats/xar/header.rb +0 -1
  141. data/lib/omnizip/formats/xar/reader.rb +0 -1
  142. data/lib/omnizip/formats/xar/toc.rb +3 -3
  143. data/lib/omnizip/formats/xar/writer.rb +11 -3
  144. data/lib/omnizip/formats/xar.rb +0 -1
  145. data/lib/omnizip/formats/xz/reader.rb +7 -3
  146. data/lib/omnizip/formats/xz.rb +3 -40
  147. data/lib/omnizip/formats/xz_impl/block_decoder.rb +4 -10
  148. data/lib/omnizip/formats/xz_impl/block_encoder.rb +0 -2
  149. data/lib/omnizip/formats/xz_impl/block_header_parser.rb +0 -2
  150. data/lib/omnizip/formats/xz_impl/index_decoder.rb +0 -1
  151. data/lib/omnizip/formats/xz_impl/index_encoder.rb +0 -1
  152. data/lib/omnizip/formats/xz_impl/stream_encoder.rb +1 -1
  153. data/lib/omnizip/formats/xz_impl/stream_footer.rb +0 -1
  154. data/lib/omnizip/formats/xz_impl/stream_footer_parser.rb +0 -1
  155. data/lib/omnizip/formats/xz_impl/stream_header.rb +0 -1
  156. data/lib/omnizip/formats/xz_impl/stream_header_parser.rb +0 -2
  157. data/lib/omnizip/formats/zip/central_directory_header.rb +2 -3
  158. data/lib/omnizip/formats/zip/end_of_central_directory.rb +0 -2
  159. data/lib/omnizip/formats/zip/local_file_header.rb +0 -2
  160. data/lib/omnizip/formats/zip/reader.rb +12 -2
  161. data/lib/omnizip/formats/zip/writer.rb +73 -1
  162. data/lib/omnizip/formats/zip/zip64_end_of_central_directory.rb +0 -2
  163. data/lib/omnizip/formats/zip/zip64_end_of_central_directory_locator.rb +0 -2
  164. data/lib/omnizip/formats/zip/zip64_extra_field.rb +0 -2
  165. data/lib/omnizip/formats/zip.rb +0 -1
  166. data/lib/omnizip/formats.rb +9 -22
  167. data/lib/omnizip/implementations/seven_zip/lzma/range_decoder.rb +2 -2
  168. data/lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb +1 -1
  169. data/lib/omnizip/implementations/seven_zip/lzma2/encoder.rb +0 -6
  170. data/lib/omnizip/implementations/xz_utils/lzma2/decoder.rb +18 -185
  171. data/lib/omnizip/implementations/xz_utils/lzma2/encoder.rb +3 -3
  172. data/lib/omnizip/io/source.rb +136 -0
  173. data/lib/omnizip/io.rb +2 -0
  174. data/lib/omnizip/link_handler.rb +7 -3
  175. data/lib/omnizip/metadata/metadata_editor.rb +1 -1
  176. data/lib/omnizip/models/algorithm_metadata.rb +33 -60
  177. data/lib/omnizip/models/compression_options.rb +40 -53
  178. data/lib/omnizip/models/conversion_options.rb +9 -18
  179. data/lib/omnizip/models/parallel_options.rb +25 -0
  180. data/lib/omnizip/optimization_registry.rb +8 -44
  181. data/lib/omnizip/parallel/job_scheduler.rb +26 -8
  182. data/lib/omnizip/parallel/parallel_compressor.rb +10 -20
  183. data/lib/omnizip/parallel/parallel_extractor.rb +20 -11
  184. data/lib/omnizip/parity/par2_repairer.rb +5 -5
  185. data/lib/omnizip/parity/par2_verifier.rb +27 -22
  186. data/lib/omnizip/parity.rb +4 -4
  187. data/lib/omnizip/password/encryption_registry.rb +11 -50
  188. data/lib/omnizip/password/zip_crypto_strategy.rb +1 -4
  189. data/lib/omnizip/pipe/stream_decompressor.rb +2 -3
  190. data/lib/omnizip/profile/archive_profile.rb +0 -2
  191. data/lib/omnizip/profile/binary_profile.rb +0 -2
  192. data/lib/omnizip/profile/compression_profile.rb +0 -1
  193. data/lib/omnizip/profile/profile_detector.rb +0 -2
  194. data/lib/omnizip/profile/text_profile.rb +0 -2
  195. data/lib/omnizip/profile.rb +0 -2
  196. data/lib/omnizip/registry.rb +112 -0
  197. data/lib/omnizip/version.rb +1 -1
  198. data/lib/omnizip/zip/file.rb +20 -9
  199. data/lib/omnizip.rb +26 -55
  200. data/lib/tasks/msi_test_files.rake +66 -0
  201. metadata +57 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cb67186c4d0a7c3d4255db6fdfbc47895719621482f38ad7da46dcbdc5e9c901
4
- data.tar.gz: 7610eb0253e8ddfc3c98283d850b5939340295f3a755d8efe77677c4794aa756
3
+ metadata.gz: d688703887c5ac25ffa43a48582dbb0580c74a94eff91d663d8e42c86943c4bb
4
+ data.tar.gz: ebe8ffdfddb46f63a508b4bc95e3d17ad85eef0e64ffe8e512d5c45f094cfedd
5
5
  SHA512:
6
- metadata.gz: fcd49f7d89648284ed080479ca5cf24f9d28d0c11b01935d62c45575d9342341c4f6a86b6c28d11c9ff2c86d3bae70b166b8cad73cb5c64b8ea10c690778e299
7
- data.tar.gz: a1218222877e813dcb45ceaceb89e9aeb84726ce38fa65a5db37a727862b5e53ba70462a1ac8723a1bc22271793f5692b89dc6168dc6eee6080e47b8a1a4d6b7
6
+ metadata.gz: 067dbae2e0301457c15fdeed52dacbb3d85376aa3282d6fa09e519b3853252db4d2cfffe53d95d71129aaecaef6f0f566d89088cd0fb242300f7f294ab9a3d18
7
+ data.tar.gz: 2d5777ce1a3bd115a5084fa4f3b2bae7b566087043978506c105cdbb905d8575912304eb6814b7d8d3ace483b1cd39833a76e03182e4d22930f09b3d6cbae63c
data/.rubocop_todo.yml CHANGED
@@ -1,25 +1,23 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-02-23 11:56:10 UTC using RuboCop version 1.81.6.
3
+ # on 2026-03-19 08:28:00 UTC using RuboCop version 1.85.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 69
9
+ # Offense count: 1
10
+ Gemspec/RequiredRubyVersion:
11
+ Exclude:
12
+ - 'omnizip.gemspec'
13
+
14
+ # Offense count: 5
10
15
  # This cop supports safe autocorrection (--autocorrect).
11
16
  # Configuration parameters: EnforcedStyle, IndentationWidth.
12
17
  # SupportedStyles: with_first_argument, with_fixed_indentation
13
18
  Layout/ArgumentAlignment:
14
- Enabled: false
15
-
16
- # Offense count: 1
17
- # This cop supports safe autocorrection (--autocorrect).
18
- # Configuration parameters: EnforcedStyle, IndentationWidth.
19
- # SupportedStyles: with_first_element, with_fixed_indentation
20
- Layout/ArrayAlignment:
21
19
  Exclude:
22
- - 'lib/omnizip/implementations/seven_zip/lzma/decoder.rb'
20
+ - 'spec/omnizip/formats/seven_zip/embedded_search_spec.rb'
23
21
 
24
22
  # Offense count: 2
25
23
  # This cop supports safe autocorrection (--autocorrect).
@@ -30,31 +28,19 @@ Layout/BlockAlignment:
30
28
  - 'lib/omnizip/algorithms/lzma/lzip_decoder.rb'
31
29
  - 'lib/omnizip/formats/xz_impl/block_decoder.rb'
32
30
 
33
- # Offense count: 10
34
- # This cop supports safe autocorrection (--autocorrect).
35
- # Configuration parameters: EmptyLineBetweenMethodDefs, EmptyLineBetweenClassDefs, EmptyLineBetweenModuleDefs, DefLikeMacros, AllowAdjacentOneLineDefs, NumberOfEmptyLines.
36
- Layout/EmptyLineBetweenDefs:
37
- Exclude:
38
- - 'lib/omnizip.rb'
39
- - 'lib/omnizip/formats/rar.rb'
40
-
41
- # Offense count: 17
31
+ # Offense count: 1485
42
32
  # This cop supports safe autocorrection (--autocorrect).
43
- Layout/EmptyLines:
44
- Enabled: false
45
-
46
- # Offense count: 1831
47
- # This cop supports safe autocorrection (--autocorrect).
48
- # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
33
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
49
34
  # URISchemes: http, https
50
35
  Layout/LineLength:
51
36
  Enabled: false
52
37
 
53
- # Offense count: 69
38
+ # Offense count: 6
54
39
  # This cop supports safe autocorrection (--autocorrect).
55
40
  # Configuration parameters: AllowInHeredoc.
56
41
  Layout/TrailingWhitespace:
57
- Enabled: false
42
+ Exclude:
43
+ - 'spec/omnizip/formats/seven_zip/embedded_search_spec.rb'
58
44
 
59
45
  # Offense count: 13
60
46
  # Configuration parameters: AllowedMethods.
@@ -71,15 +57,16 @@ Lint/ConstantDefinitionInBlock:
71
57
  - 'spec/omnizip/seven_zip_tool_compatibility_spec.rb'
72
58
  - 'spec/omnizip/xz_tool_compatibility_spec.rb'
73
59
 
74
- # Offense count: 34
60
+ # Offense count: 36
75
61
  # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
76
62
  Lint/DuplicateBranch:
77
63
  Enabled: false
78
64
 
79
- # Offense count: 8
65
+ # Offense count: 9
80
66
  Lint/DuplicateMethods:
81
67
  Exclude:
82
68
  - 'lib/omnizip/algorithms/lzma/xz_range_encoder_exact.rb'
69
+ - 'lib/omnizip/formats/msi/reader.rb'
83
70
  - 'lib/omnizip/formats/xz.rb'
84
71
  - 'lib/omnizip/implementations/xz_utils/lzma2/encoder.rb'
85
72
  - 'lib/omnizip/models/filter_config.rb'
@@ -127,13 +114,6 @@ Lint/StructNewOverride:
127
114
  - 'lib/omnizip/parallel/job_queue.rb'
128
115
  - 'lib/omnizip/parity/par2_creator.rb'
129
116
 
130
- # Offense count: 1
131
- # Configuration parameters: AllowedPatterns.
132
- # AllowedPatterns: (?-mix:(exactly|at_least|at_most)\(\d+\)\.times)
133
- Lint/UnreachableLoop:
134
- Exclude:
135
- - 'lib/omnizip/algorithms/lzma/range_decoder.rb'
136
-
137
117
  # Offense count: 2
138
118
  # This cop supports safe autocorrection (--autocorrect).
139
119
  # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
@@ -148,38 +128,38 @@ Lint/UselessConstantScoping:
148
128
  Exclude:
149
129
  - 'lib/omnizip/algorithms/lzma/optimal_encoder.rb'
150
130
 
151
- # Offense count: 552
131
+ # Offense count: 553
152
132
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
153
133
  Metrics/AbcSize:
154
134
  Enabled: false
155
135
 
156
- # Offense count: 18
136
+ # Offense count: 20
157
137
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
158
138
  # AllowedMethods: refine
159
139
  Metrics/BlockLength:
160
- Max: 206
140
+ Max: 81
161
141
 
162
142
  # Offense count: 5
163
143
  # Configuration parameters: CountBlocks, CountModifierForms.
164
144
  Metrics/BlockNesting:
165
145
  Max: 6
166
146
 
167
- # Offense count: 266
147
+ # Offense count: 263
168
148
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
169
149
  Metrics/CyclomaticComplexity:
170
150
  Enabled: false
171
151
 
172
- # Offense count: 948
152
+ # Offense count: 961
173
153
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
174
154
  Metrics/MethodLength:
175
- Max: 434
155
+ Max: 238
176
156
 
177
157
  # Offense count: 33
178
158
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
179
159
  Metrics/ParameterLists:
180
160
  Max: 20
181
161
 
182
- # Offense count: 187
162
+ # Offense count: 182
183
163
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
184
164
  Metrics/PerceivedComplexity:
185
165
  Enabled: false
@@ -193,22 +173,19 @@ Naming/MethodParameterName:
193
173
  # Offense count: 38
194
174
  # Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
195
175
  # AllowedMethods: call
196
- # WaywardPredicates: nonzero?
176
+ # WaywardPredicates: infinite?, nonzero?
197
177
  Naming/PredicateMethod:
198
178
  Enabled: false
199
179
 
200
- # Offense count: 42
180
+ # Offense count: 37
201
181
  # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
202
182
  # SupportedStyles: snake_case, normalcase, non_integer
203
183
  # AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
204
184
  Naming/VariableNumber:
205
185
  Exclude:
206
186
  - 'lib/omnizip/algorithms/lzma/bit_model.rb'
207
- - 'lib/omnizip/algorithms/lzma/distance_coder.rb'
208
- - 'lib/omnizip/algorithms/lzma/literal_decoder.rb'
209
187
  - 'lib/omnizip/algorithms/lzma/range_encoder.rb'
210
188
  - 'lib/omnizip/algorithms/lzma/xz_buffered_range_encoder.rb'
211
- - 'lib/omnizip/algorithms/lzma/xz_utils_decoder.rb'
212
189
  - 'lib/omnizip/formats/rar/rar5/compression/lzss.rb'
213
190
  - 'lib/omnizip/formats/xz_impl/block_decoder.rb'
214
191
  - 'lib/omnizip/implementations/seven_zip/lzma/range_encoder.rb'
@@ -218,13 +195,14 @@ Naming/VariableNumber:
218
195
  - 'spec/omnizip/parity/reed_solomon_encoder_spec.rb'
219
196
  - 'spec/omnizip/parity/reed_solomon_matrix_spec.rb'
220
197
 
221
- # Offense count: 8
198
+ # Offense count: 9
222
199
  # Configuration parameters: MinSize.
223
200
  Performance/CollectionLiteralInLoop:
224
201
  Exclude:
225
202
  - 'lib/omnizip/commands/archive_create_command.rb'
226
203
  - 'lib/omnizip/formats/iso/directory_builder.rb'
227
204
  - 'lib/omnizip/formats/iso/writer.rb'
205
+ - 'lib/omnizip/formats/msi/constants.rb'
228
206
  - 'lib/omnizip/formats/seven_zip/header_writer.rb'
229
207
  - 'lib/omnizip/parity/models/file_description_packet.rb'
230
208
  - 'lib/omnizip/parity/par2_repairer.rb'
@@ -235,12 +213,14 @@ Performance/MapMethodChain:
235
213
  Exclude:
236
214
  - 'lib/omnizip/commands/archive_create_command.rb'
237
215
 
238
- # Offense count: 7
216
+ # Offense count: 11
239
217
  Security/Open:
240
218
  Exclude:
241
219
  - 'lib/omnizip/formats/cpio.rb'
242
220
  - 'lib/omnizip/formats/iso.rb'
221
+ - 'lib/omnizip/formats/msi.rb'
243
222
  - 'lib/omnizip/zip/file.rb'
223
+ - 'lib/tasks/msi_test_files.rake'
244
224
 
245
225
  # Offense count: 6
246
226
  # This cop supports safe autocorrection (--autocorrect).
@@ -260,6 +240,20 @@ Style/EmptyElse:
260
240
  - 'lib/omnizip/algorithms/ppmd7/encoder.rb'
261
241
  - 'lib/omnizip/formats/iso/volume_descriptor.rb'
262
242
 
243
+ # Offense count: 11
244
+ Style/FileOpen:
245
+ Exclude:
246
+ - 'lib/omnizip/chunked/writer.rb'
247
+ - 'lib/omnizip/formats/iso/reader.rb'
248
+ - 'lib/omnizip/formats/rar/rar5/multi_volume/volume_writer.rb'
249
+ - 'lib/omnizip/formats/rpm.rb'
250
+ - 'lib/omnizip/formats/seven_zip/split_archive_writer.rb'
251
+ - 'lib/omnizip/formats/tar/writer.rb'
252
+ - 'lib/omnizip/formats/xar/reader.rb'
253
+ - 'lib/omnizip/zip/input_stream.rb'
254
+ - 'lib/omnizip/zip/output_stream.rb'
255
+ - 'spec/omnizip/formats/rpm_spec.rb'
256
+
263
257
  # Offense count: 161
264
258
  # This cop supports safe autocorrection (--autocorrect).
265
259
  # Configuration parameters: MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
@@ -273,6 +267,13 @@ Style/MixinUsage:
273
267
  - 'spec/scripts/debug_7z_structure.rb'
274
268
  - 'spec/scripts/debug_writer_output.rb'
275
269
 
270
+ # Offense count: 3
271
+ # Configuration parameters: AllowedClasses.
272
+ Style/OneClassPerFile:
273
+ Exclude:
274
+ - 'lib/omnizip.rb'
275
+ - 'lib/omnizip/algorithms/lzma2.rb'
276
+
276
277
  # Offense count: 3
277
278
  # Configuration parameters: AllowedMethods.
278
279
  # AllowedMethods: respond_to_missing?
data/Gemfile CHANGED
@@ -5,6 +5,8 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in omnizip.gemspec
6
6
  gemspec
7
7
 
8
+ # For spec testing
9
+ gem "csv"
8
10
  gem "rake"
9
11
  gem "rspec"
10
12
  gem "rubocop"
data/Rakefile CHANGED
@@ -9,4 +9,7 @@ require "rubocop/rake_task"
9
9
 
10
10
  RuboCop::RakeTask.new
11
11
 
12
+ # Load custom rake tasks
13
+ Dir["lib/tasks/*.rake"].each { |task| load task }
14
+
12
15
  task default: %i[spec rubocop]
@@ -0,0 +1,67 @@
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 | **PARTIAL** — IO::Source/Sink adapter built; type-check sites converted; ~76 IO capability checks remain in xz_impl/* and ole/* (legitimate duck-typing for ungetbyte / set_encoding etc.) |
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 | **PARTIAL** — CompressionOptions and AlgorithmMetadata migrated; ConversionOptions pending lutaml symbol-type support |
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?` | 111 | 76 (mostly IO capability detection) |
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`
@@ -0,0 +1,81 @@
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`
@@ -0,0 +1,57 @@
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`
@@ -0,0 +1,69 @@
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).
@@ -0,0 +1,49 @@
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`
@@ -0,0 +1,64 @@
1
+ # 05 — Eliminate `.send` against private methods
2
+
3
+ Priority: **high**.
4
+ Status: TODO.
5
+
6
+ ## Rule
7
+
8
+ > NEVER use `send` to call private methods. Private methods are private
9
+ > for a reason. If you need to call it from outside, the API boundary is
10
+ > wrong — redesign, don't bypass.
11
+
12
+ ## Inventory (17 occurrences)
13
+
14
+ | File:Line | Call | Fix |
15
+ |---|---|---|
16
+ | `lib/omnizip/parity.rb:178` | `verifier.send(:parse_par2_file)` | Promote to public |
17
+ | `lib/omnizip/parity.rb:180` | `verifier.send(:calculate_total_blocks)` | Promote to public |
18
+ | `lib/omnizip/parity/par2_repairer.rb:174` | `@verifier.send(:find_file_path, ...)` | Promote to public |
19
+ | `lib/omnizip/parallel/parallel_compressor.rb:306` | `writer.send(:create_entry, ...)` | Promote to public |
20
+ | `lib/omnizip/parallel/parallel_compressor.rb:324` | `writer.send(:write_with_precompressed_data, ...)` | Promote |
21
+ | `lib/omnizip/parallel/parallel_compressor.rb:136` | `opts.send(:"#{k}=", v)` | Replace with `CompressionOptions#apply(hash)` |
22
+ | `lib/omnizip/parallel/parallel_extractor.rb:110` | `reader.send(:decompress_data, ...)` | Promote to public |
23
+ | `lib/omnizip/parallel/parallel_extractor.rb:135` | `opts.send(:"#{k}=", v)` | Same as above |
24
+ | `lib/omnizip/formats/format_spec_loader.rb:203` | `format.send(key)` | Replace with explicit dispatch |
25
+ | `lib/omnizip/converter/seven_zip_to_zip_strategy.rb:62` | `reader.send(:extract_entry_data, ...)` | Promote to public |
26
+ | `lib/omnizip/formats/seven_zip/parser.rb:573` | `entry.send(:"#{attr}=", ...)` | Add public writer or ctor arg |
27
+ | `lib/omnizip/formats/rpm/header.rb:36` | `header.send(:parse!, io)` | Promote `parse!` to public |
28
+ | `lib/omnizip/algorithms/zstandard/frame/header.rb:72,76,80` | `header.send(:parse_window_descriptor, ...)` etc. | Promote parse helpers to public, or inline |
29
+ | `lib/omnizip/zip/file.rb:419` | `@reader.send(:decompress_data, ...)` | Promote to public |
30
+ | `lib/omnizip/crypto/aes256/cipher.rb:90` | `cipher.send(mode)` | Replace with explicit `if encrypt? ... else ...` |
31
+
32
+ ## Plan
33
+
34
+ For each call site, choose ONE of:
35
+
36
+ 1. **Promote the method to public.** If callers legitimately need it, the
37
+ `private` declaration was wrong. Add a doc comment and make it public.
38
+ 2. **Add a public facade.** Keep the private method, expose a public
39
+ method with a clearer name that wraps it.
40
+ 3. **Inline the call.** Sometimes the private method is one line and the
41
+ `.send` is just laziness — inline the body.
42
+
43
+ `opts.send(:"#{k}=", v)` (parallel_compressor.rb:136 and
44
+ parallel_extractor.rb:135) deserves a real fix: add
45
+ `CompressionOptions#apply(attributes)` and `ParallelOptions#apply` methods
46
+ that take a hash and set attributes via `public_send` only on whitelisted
47
+ keys.
48
+
49
+ `format.send(key)` (format_spec_loader.rb:203) is data-driven dispatch on
50
+ a spec format — replace with a hash of known keys → lambdas, or with a
51
+ case statement that calls public methods explicitly.
52
+
53
+ `cipher.send(mode)` (crypto/aes256/cipher.rb:90) — replace with an
54
+ explicit branch on whether to call `encrypt` or `decrypt`.
55
+
56
+ ## Acceptance
57
+
58
+ - `grep -rn "\.send(" lib/` returns at most a handful of intentional
59
+ metaprogramming cases (e.g., `public_send` with documented reason).
60
+ - All specs pass.
61
+
62
+ ## Files
63
+
64
+ See table above. 11 files to modify.