philiprehberger-gzip_kit 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +55 -5
- data/lib/philiprehberger/gzip_kit/version.rb +1 -1
- data/lib/philiprehberger/gzip_kit.rb +117 -15
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc38727681cc1557d80be3bdf12969c479c53bdf2b67d48b88c35286ed2bfed2
|
|
4
|
+
data.tar.gz: 723760091ce30d192a0bc0fe8a921dc2c5da331b3c068c7d3e5bc70edcd71c76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4db3b719e3188b6c0011eecf3e26ea2e4f2386fb76d0ed0a2aac0f82d43a383107e2acdec7cc8adeee288aa5716ce9114678b0d468afadf991741e29a9a13f11
|
|
7
|
+
data.tar.gz: 363c8fdd0ca90ace3235e6c27e3f8b014391709d8208f2d5bb64a881832dfd8159159412ae2abe848a1175b46f324df183f52383f1838990262a7b40fbf05800
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-04-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Optional `chunk_size:` keyword on `compress_stream`, `decompress_stream`, `compress_file`, `decompress_file` (defaults to 64 KB).
|
|
14
|
+
- Optional `stats:` keyword on `decompress` — returns `{ data:, ratio: }` when true.
|
|
15
|
+
- Module-level YARD overview and `@example` blocks on primary methods.
|
|
16
|
+
|
|
17
|
+
## [0.3.0] - 2026-04-16
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
- `GzipKit.equivalent?(blob_a, blob_b)` — returns true iff both gzip-compressed inputs decompress to equal byte strings; raises `GzipKit::Error` if either input is not valid gzip
|
|
21
|
+
|
|
10
22
|
## [0.2.1] - 2026-03-31
|
|
11
23
|
|
|
12
24
|
### Changed
|
data/README.md
CHANGED
|
@@ -108,6 +108,41 @@ File.open("output.gz", "rb") do |input|
|
|
|
108
108
|
end
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
### Custom chunk size
|
|
112
|
+
|
|
113
|
+
Streaming and file methods read in 64 KB chunks by default. Override with `chunk_size:` to tune for memory constraints or throughput:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
require "philiprehberger/gzip_kit"
|
|
117
|
+
|
|
118
|
+
# Smaller chunks for memory-constrained environments
|
|
119
|
+
File.open("input.txt", "rb") do |input|
|
|
120
|
+
File.open("output.gz", "wb") do |output|
|
|
121
|
+
Philiprehberger::GzipKit.compress_stream(input, output, chunk_size: 4 * 1024)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Larger chunks for bulk throughput
|
|
126
|
+
Philiprehberger::GzipKit.compress_file("input.bin", "output.gz", chunk_size: 1 * 1024 * 1024)
|
|
127
|
+
Philiprehberger::GzipKit.decompress_file("output.gz", "restored.bin", chunk_size: 1 * 1024 * 1024)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`chunk_size:` must be a positive `Integer`; any other value raises `ArgumentError`.
|
|
131
|
+
|
|
132
|
+
### Decompress with stats
|
|
133
|
+
|
|
134
|
+
Mirror of `compress(..., stats: true)` — returns a hash with the decompressed payload and the compressed-to-decompressed size ratio:
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
require "philiprehberger/gzip_kit"
|
|
138
|
+
|
|
139
|
+
compressed = Philiprehberger::GzipKit.compress("a" * 10_000)
|
|
140
|
+
result = Philiprehberger::GzipKit.decompress(compressed, stats: true)
|
|
141
|
+
# => { data: "aaaa...", ratio: 0.0041 }
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The ratio is `compressed_size / decompressed_size` (inverse of compression ratio). For zero-length output, ratio is `0.0`.
|
|
145
|
+
|
|
111
146
|
### Stream Concatenation
|
|
112
147
|
|
|
113
148
|
```ruby
|
|
@@ -120,6 +155,20 @@ combined = Philiprehberger::GzipKit.concat(part_a, part_b)
|
|
|
120
155
|
Philiprehberger::GzipKit.decompress(combined) # => "Hello, world!"
|
|
121
156
|
```
|
|
122
157
|
|
|
158
|
+
### Equivalence Check
|
|
159
|
+
|
|
160
|
+
Compare two gzip blobs by their decompressed payload — useful when the same source is recompressed at different levels or with different metadata:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
require "philiprehberger/gzip_kit"
|
|
164
|
+
|
|
165
|
+
fast = Philiprehberger::GzipKit.compress("hello", level: Zlib::BEST_SPEED)
|
|
166
|
+
best = Philiprehberger::GzipKit.compress("hello", level: Zlib::BEST_COMPRESSION)
|
|
167
|
+
|
|
168
|
+
Philiprehberger::GzipKit.equivalent?(fast, best) # => true
|
|
169
|
+
Philiprehberger::GzipKit.equivalent?(fast, Philiprehberger::GzipKit.compress("world")) # => false
|
|
170
|
+
```
|
|
171
|
+
|
|
123
172
|
### Header Inspection
|
|
124
173
|
|
|
125
174
|
```ruby
|
|
@@ -134,13 +183,14 @@ header = Philiprehberger::GzipKit.inspect_header(gzip_data)
|
|
|
134
183
|
| Method | Description |
|
|
135
184
|
|--------|-------------|
|
|
136
185
|
| `GzipKit.compress(string, level:, stats:)` | Compress a string to gzip bytes; returns stats hash when `stats: true` |
|
|
137
|
-
| `GzipKit.decompress(data)` | Decompress gzip bytes to a string |
|
|
186
|
+
| `GzipKit.decompress(data, stats:)` | Decompress gzip bytes to a string; returns `{ data:, ratio: }` when `stats: true` |
|
|
138
187
|
| `GzipKit.compressed?(data)` | Check if data is gzip-compressed via magic bytes |
|
|
139
|
-
| `GzipKit.compress_file(src, dest, level:, &block)` | Compress a file to a gzip file with optional progress callback |
|
|
140
|
-
| `GzipKit.decompress_file(src, dest, &block)` | Decompress a gzip file with optional progress callback |
|
|
141
|
-
| `GzipKit.compress_stream(io_in, io_out, level:)` | Streaming compression
|
|
142
|
-
| `GzipKit.decompress_stream(io_in, io_out)` | Streaming decompression
|
|
188
|
+
| `GzipKit.compress_file(src, dest, level:, chunk_size:, &block)` | Compress a file to a gzip file with optional progress callback and chunk size |
|
|
189
|
+
| `GzipKit.decompress_file(src, dest, chunk_size:, &block)` | Decompress a gzip file with optional progress callback and chunk size |
|
|
190
|
+
| `GzipKit.compress_stream(io_in, io_out, level:, chunk_size:)` | Streaming compression (64 KB chunks by default) |
|
|
191
|
+
| `GzipKit.decompress_stream(io_in, io_out, chunk_size:)` | Streaming decompression (64 KB chunks by default) |
|
|
143
192
|
| `GzipKit.concat(data_a, data_b)` | Concatenate two gzip-compressed strings |
|
|
193
|
+
| `GzipKit.equivalent?(blob_a, blob_b)` | Check whether two gzip blobs decompress to equal byte strings |
|
|
144
194
|
| `GzipKit.inspect_header(data)` | Read gzip header metadata without decompressing |
|
|
145
195
|
|
|
146
196
|
## Development
|
|
@@ -5,6 +5,22 @@ require 'stringio'
|
|
|
5
5
|
require_relative 'gzip_kit/version'
|
|
6
6
|
|
|
7
7
|
module Philiprehberger
|
|
8
|
+
# GzipKit provides gzip compression and decompression with streaming support.
|
|
9
|
+
#
|
|
10
|
+
# The module exposes both string-oriented and IO-oriented entry points:
|
|
11
|
+
#
|
|
12
|
+
# - {GzipKit.compress} / {GzipKit.decompress} for in-memory string data
|
|
13
|
+
# - {GzipKit.compress_stream} / {GzipKit.decompress_stream} for IO-to-IO streaming
|
|
14
|
+
# - {GzipKit.compress_file} / {GzipKit.decompress_file} for file-to-file transforms
|
|
15
|
+
# - {GzipKit.compressed?} / {GzipKit.inspect_header} for gzip detection and header inspection
|
|
16
|
+
# - {GzipKit.concat} / {GzipKit.equivalent?} for combining and comparing gzip blobs
|
|
17
|
+
#
|
|
18
|
+
# Streaming and file methods read in 64 KB chunks by default. The chunk size can be
|
|
19
|
+
# tuned via the +chunk_size:+ keyword when dealing with very small or very large payloads.
|
|
20
|
+
#
|
|
21
|
+
# @example Compress and decompress a string
|
|
22
|
+
# compressed = Philiprehberger::GzipKit.compress('hello')
|
|
23
|
+
# Philiprehberger::GzipKit.decompress(compressed) # => "hello"
|
|
8
24
|
module GzipKit
|
|
9
25
|
class Error < StandardError; end
|
|
10
26
|
|
|
@@ -17,6 +33,14 @@ module Philiprehberger
|
|
|
17
33
|
# @param level [Integer] compression level (Zlib::DEFAULT_COMPRESSION by default)
|
|
18
34
|
# @param stats [Boolean] when true, return a hash with compression statistics
|
|
19
35
|
# @return [String, Hash] gzip-compressed bytes, or a stats hash when stats: true
|
|
36
|
+
#
|
|
37
|
+
# @example Compress a string
|
|
38
|
+
# Philiprehberger::GzipKit.compress('hello, world!')
|
|
39
|
+
# # => "\x1F\x8B\b\x00..." (binary gzip bytes)
|
|
40
|
+
#
|
|
41
|
+
# @example Compress with stats
|
|
42
|
+
# Philiprehberger::GzipKit.compress('a' * 10_000, stats: true)
|
|
43
|
+
# # => { data: "...", ratio: 0.99, original_size: 10000, compressed_size: 41 }
|
|
20
44
|
def self.compress(string, level: Zlib::DEFAULT_COMPRESSION, stats: false)
|
|
21
45
|
io_out = StringIO.new
|
|
22
46
|
io_out.binmode
|
|
@@ -43,9 +67,19 @@ module Philiprehberger
|
|
|
43
67
|
# Decompress gzip bytes to a string.
|
|
44
68
|
#
|
|
45
69
|
# @param data [String] gzip-compressed bytes
|
|
46
|
-
# @
|
|
70
|
+
# @param stats [Boolean] when true, return a hash with decompression statistics
|
|
71
|
+
# @return [String, Hash] decompressed string, or a stats hash when stats: true
|
|
47
72
|
# @raise [Zlib::GzipFile::Error] if the data is not valid gzip
|
|
48
|
-
|
|
73
|
+
#
|
|
74
|
+
# @example Decompress gzip bytes
|
|
75
|
+
# compressed = Philiprehberger::GzipKit.compress('hello')
|
|
76
|
+
# Philiprehberger::GzipKit.decompress(compressed) # => "hello"
|
|
77
|
+
#
|
|
78
|
+
# @example Decompress with stats
|
|
79
|
+
# compressed = Philiprehberger::GzipKit.compress('a' * 10_000)
|
|
80
|
+
# Philiprehberger::GzipKit.decompress(compressed, stats: true)
|
|
81
|
+
# # => { data: "aaaa...", ratio: 0.0041 }
|
|
82
|
+
def self.decompress(data, stats: false)
|
|
49
83
|
io_in = StringIO.new(data)
|
|
50
84
|
io_in.binmode
|
|
51
85
|
result = String.new(encoding: Encoding::BINARY)
|
|
@@ -62,7 +96,16 @@ module Philiprehberger
|
|
|
62
96
|
end
|
|
63
97
|
end
|
|
64
98
|
|
|
65
|
-
result.force_encoding(Encoding::UTF_8)
|
|
99
|
+
decompressed = result.force_encoding(Encoding::UTF_8)
|
|
100
|
+
|
|
101
|
+
if stats
|
|
102
|
+
decompressed_size = decompressed.bytesize
|
|
103
|
+
compressed_size = data.bytesize
|
|
104
|
+
ratio = decompressed_size.zero? ? 0.0 : compressed_size.to_f / decompressed_size
|
|
105
|
+
{ data: decompressed, ratio: ratio }
|
|
106
|
+
else
|
|
107
|
+
decompressed
|
|
108
|
+
end
|
|
66
109
|
end
|
|
67
110
|
|
|
68
111
|
# Check if data is gzip-compressed by inspecting magic bytes.
|
|
@@ -81,25 +124,29 @@ module Philiprehberger
|
|
|
81
124
|
# @param src [String] path to the source file
|
|
82
125
|
# @param dest [String] path to the destination gzip file
|
|
83
126
|
# @param level [Integer] compression level (Zlib::DEFAULT_COMPRESSION by default)
|
|
127
|
+
# @param chunk_size [Integer] bytes per read chunk (defaults to 64 KB)
|
|
84
128
|
# @yield [bytes_processed, total_bytes] progress callback
|
|
85
129
|
# @yieldparam bytes_processed [Integer] bytes processed so far
|
|
86
130
|
# @yieldparam total_bytes [Integer] total file size
|
|
87
131
|
# @return [void]
|
|
88
|
-
|
|
132
|
+
# @raise [ArgumentError] if chunk_size is not a positive Integer
|
|
133
|
+
def self.compress_file(src, dest, level: Zlib::DEFAULT_COMPRESSION, chunk_size: CHUNK_SIZE, &block)
|
|
134
|
+
validate_chunk_size!(chunk_size)
|
|
135
|
+
|
|
89
136
|
File.open(src, 'rb') do |io_in|
|
|
90
137
|
File.open(dest, 'wb') do |io_out|
|
|
91
138
|
if block
|
|
92
139
|
total_bytes = File.size(src)
|
|
93
140
|
bytes_processed = 0
|
|
94
141
|
gz = Zlib::GzipWriter.new(io_out, level)
|
|
95
|
-
while (chunk = io_in.read(
|
|
142
|
+
while (chunk = io_in.read(chunk_size))
|
|
96
143
|
gz.write(chunk)
|
|
97
144
|
bytes_processed += chunk.bytesize
|
|
98
145
|
block.call(bytes_processed, total_bytes)
|
|
99
146
|
end
|
|
100
147
|
gz.finish
|
|
101
148
|
else
|
|
102
|
-
compress_stream(io_in, io_out, level: level)
|
|
149
|
+
compress_stream(io_in, io_out, level: level, chunk_size: chunk_size)
|
|
103
150
|
end
|
|
104
151
|
end
|
|
105
152
|
end
|
|
@@ -109,24 +156,28 @@ module Philiprehberger
|
|
|
109
156
|
#
|
|
110
157
|
# @param src [String] path to the gzip source file
|
|
111
158
|
# @param dest [String] path to the destination file
|
|
159
|
+
# @param chunk_size [Integer] bytes per read chunk (defaults to 64 KB)
|
|
112
160
|
# @yield [bytes_processed, total_bytes] progress callback
|
|
113
161
|
# @yieldparam bytes_processed [Integer] bytes decompressed so far
|
|
114
162
|
# @yieldparam total_bytes [nil] always nil (total unknown until decompression completes)
|
|
115
163
|
# @return [void]
|
|
116
|
-
|
|
164
|
+
# @raise [ArgumentError] if chunk_size is not a positive Integer
|
|
165
|
+
def self.decompress_file(src, dest, chunk_size: CHUNK_SIZE, &block)
|
|
166
|
+
validate_chunk_size!(chunk_size)
|
|
167
|
+
|
|
117
168
|
File.open(src, 'rb') do |io_in|
|
|
118
169
|
File.open(dest, 'wb') do |io_out|
|
|
119
170
|
if block
|
|
120
171
|
gz = Zlib::GzipReader.new(io_in)
|
|
121
172
|
bytes_processed = 0
|
|
122
|
-
while (chunk = gz.read(
|
|
173
|
+
while (chunk = gz.read(chunk_size))
|
|
123
174
|
io_out.write(chunk)
|
|
124
175
|
bytes_processed += chunk.bytesize
|
|
125
176
|
block.call(bytes_processed, nil)
|
|
126
177
|
end
|
|
127
178
|
gz.close
|
|
128
179
|
else
|
|
129
|
-
decompress_stream(io_in, io_out)
|
|
180
|
+
decompress_stream(io_in, io_out, chunk_size: chunk_size)
|
|
130
181
|
end
|
|
131
182
|
end
|
|
132
183
|
end
|
|
@@ -149,6 +200,24 @@ module Philiprehberger
|
|
|
149
200
|
result
|
|
150
201
|
end
|
|
151
202
|
|
|
203
|
+
# Check whether two gzip-compressed blobs decompress to equal byte strings.
|
|
204
|
+
#
|
|
205
|
+
# Useful for comparing gzip outputs produced at different compression levels
|
|
206
|
+
# or with different metadata — only the decompressed payloads are compared.
|
|
207
|
+
#
|
|
208
|
+
# @param blob_a [String] first gzip-compressed string
|
|
209
|
+
# @param blob_b [String] second gzip-compressed string
|
|
210
|
+
# @return [Boolean] true iff both blobs decompress to equal byte strings
|
|
211
|
+
# @raise [Error] if either input is not valid gzip
|
|
212
|
+
def self.equivalent?(blob_a, blob_b)
|
|
213
|
+
raise Error, 'first argument is not valid gzip data' unless compressed?(blob_a)
|
|
214
|
+
raise Error, 'second argument is not valid gzip data' unless compressed?(blob_b)
|
|
215
|
+
|
|
216
|
+
decompress(blob_a).b == decompress(blob_b).b
|
|
217
|
+
rescue Zlib::GzipFile::Error => e
|
|
218
|
+
raise Error, "failed to decompress gzip data: #{e.message}"
|
|
219
|
+
end
|
|
220
|
+
|
|
152
221
|
# Inspect the gzip header without decompressing.
|
|
153
222
|
#
|
|
154
223
|
# @param data [String] gzip-compressed data
|
|
@@ -173,32 +242,65 @@ module Philiprehberger
|
|
|
173
242
|
gz&.close
|
|
174
243
|
end
|
|
175
244
|
|
|
176
|
-
# Streaming compression from one IO to another
|
|
245
|
+
# Streaming compression from one IO to another.
|
|
177
246
|
#
|
|
178
247
|
# @param io_in [IO] readable input stream
|
|
179
248
|
# @param io_out [IO] writable output stream
|
|
180
249
|
# @param level [Integer] compression level (Zlib::DEFAULT_COMPRESSION by default)
|
|
250
|
+
# @param chunk_size [Integer] bytes per read chunk (defaults to 64 KB)
|
|
181
251
|
# @return [void]
|
|
182
|
-
|
|
252
|
+
# @raise [ArgumentError] if chunk_size is not a positive Integer
|
|
253
|
+
#
|
|
254
|
+
# @example Compress from one IO to another
|
|
255
|
+
# File.open('input.txt', 'rb') do |io_in|
|
|
256
|
+
# File.open('output.gz', 'wb') do |io_out|
|
|
257
|
+
# Philiprehberger::GzipKit.compress_stream(io_in, io_out)
|
|
258
|
+
# end
|
|
259
|
+
# end
|
|
260
|
+
#
|
|
261
|
+
# @example Tune the chunk size for small payloads
|
|
262
|
+
# Philiprehberger::GzipKit.compress_stream(io_in, io_out, chunk_size: 4 * 1024)
|
|
263
|
+
def self.compress_stream(io_in, io_out, level: Zlib::DEFAULT_COMPRESSION, chunk_size: CHUNK_SIZE)
|
|
264
|
+
validate_chunk_size!(chunk_size)
|
|
265
|
+
|
|
183
266
|
gz = Zlib::GzipWriter.new(io_out, level)
|
|
184
|
-
while (chunk = io_in.read(
|
|
267
|
+
while (chunk = io_in.read(chunk_size))
|
|
185
268
|
gz.write(chunk)
|
|
186
269
|
end
|
|
187
270
|
gz.finish
|
|
188
271
|
end
|
|
189
272
|
|
|
190
|
-
# Streaming decompression from one IO to another
|
|
273
|
+
# Streaming decompression from one IO to another.
|
|
191
274
|
#
|
|
192
275
|
# @param io_in [IO] readable input stream containing gzip data
|
|
193
276
|
# @param io_out [IO] writable output stream
|
|
277
|
+
# @param chunk_size [Integer] bytes per read chunk (defaults to 64 KB)
|
|
194
278
|
# @return [void]
|
|
195
|
-
|
|
279
|
+
# @raise [ArgumentError] if chunk_size is not a positive Integer
|
|
280
|
+
#
|
|
281
|
+
# @example Decompress from one IO to another
|
|
282
|
+
# File.open('output.gz', 'rb') do |io_in|
|
|
283
|
+
# File.open('restored.txt', 'wb') do |io_out|
|
|
284
|
+
# Philiprehberger::GzipKit.decompress_stream(io_in, io_out)
|
|
285
|
+
# end
|
|
286
|
+
# end
|
|
287
|
+
def self.decompress_stream(io_in, io_out, chunk_size: CHUNK_SIZE)
|
|
288
|
+
validate_chunk_size!(chunk_size)
|
|
289
|
+
|
|
196
290
|
gz = Zlib::GzipReader.new(io_in)
|
|
197
|
-
while (chunk = gz.read(
|
|
291
|
+
while (chunk = gz.read(chunk_size))
|
|
198
292
|
io_out.write(chunk)
|
|
199
293
|
end
|
|
200
294
|
ensure
|
|
201
295
|
gz&.close
|
|
202
296
|
end
|
|
297
|
+
|
|
298
|
+
# @api private
|
|
299
|
+
def self.validate_chunk_size!(chunk_size)
|
|
300
|
+
return if chunk_size.is_a?(Integer) && chunk_size.positive?
|
|
301
|
+
|
|
302
|
+
raise ArgumentError, "chunk_size must be a positive Integer, got: #{chunk_size.inspect}"
|
|
303
|
+
end
|
|
304
|
+
private_class_method :validate_chunk_size!
|
|
203
305
|
end
|
|
204
306
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-gzip_kit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Simple API for gzip compression and decompression with support for strings,
|
|
14
14
|
files, and IO streams. Configurable compression level. Built on Ruby stdlib zlib.
|
|
@@ -23,11 +23,11 @@ files:
|
|
|
23
23
|
- README.md
|
|
24
24
|
- lib/philiprehberger/gzip_kit.rb
|
|
25
25
|
- lib/philiprehberger/gzip_kit/version.rb
|
|
26
|
-
homepage: https://
|
|
26
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-gzip_kit
|
|
27
27
|
licenses:
|
|
28
28
|
- MIT
|
|
29
29
|
metadata:
|
|
30
|
-
homepage_uri: https://
|
|
30
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-gzip_kit
|
|
31
31
|
source_code_uri: https://github.com/philiprehberger/rb-gzip-kit
|
|
32
32
|
changelog_uri: https://github.com/philiprehberger/rb-gzip-kit/blob/main/CHANGELOG.md
|
|
33
33
|
bug_tracker_uri: https://github.com/philiprehberger/rb-gzip-kit/issues
|