libzip-ruby 0.1.0-aarch64-linux-musl

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.
data/doc/api.rb ADDED
@@ -0,0 +1,577 @@
1
+ # doc/api.rb - the API reference, and only that.
2
+ #
3
+ # The extension is written in Zig (src/*.zig). RDoc can read a C extension but
4
+ # not a Zig one, so the Ruby surface is written out here: each body is empty,
5
+ # and the working code is whatever rb_define_method points at.
6
+ #
7
+ # This file is outside lib/, off the $LOAD_PATH, so it can't be required by
8
+ # mistake; loading it would shadow the extension with empty methods. It goes
9
+ # into the gem (spec.extra_rdoc_files) so that `ri LibZip::File` works from an
10
+ # installed gem, and `script/doc --check` compares it with the loaded
11
+ # extension, so the 2 can't fall out of step.
12
+
13
+ # Ruby bindings for libzip[https://libzip.org], with libzip, zlib and mbedTLS
14
+ # linked into the extension.
15
+ #
16
+ # require "libzip"
17
+ #
18
+ # LibZip::File.open("archive.zip", create: true) do |zip|
19
+ # zip.add("hello.txt", "hello.txt")
20
+ # end
21
+ #
22
+ # Start at LibZip::File. A listing gives LibZip::Entry snapshots,
23
+ # LibZip::InputStream and LibZip::OutputStream move entry bytes in and out, and
24
+ # anything that fails is a LibZip::Error.
25
+ module LibZip
26
+ # The gem version, written into the extension at build time from
27
+ # libzip-ruby.gemspec.
28
+ VERSION = "0.1.0"
29
+
30
+ # A zip archive, open for reading, writing, or both.
31
+ #
32
+ # An archive is a transaction: entries added, removed and renamed are noted
33
+ # as you go, and #close writes them to disk. The block form of ::open commits
34
+ # on normal exit and throws the archive away if the block fails, so a failed
35
+ # write can't leave a truncated zip behind.
36
+ #
37
+ # LibZip::File.open("archive.zip", create: true) do |zip|
38
+ # zip.add("a.txt", "a.txt")
39
+ # zip.get_output_stream("b.txt") { |out| out.write("from memory") }
40
+ # end
41
+ #
42
+ # LibZip::File.open("archive.zip") do |zip|
43
+ # zip.names # => ["a.txt", "b.txt"]
44
+ # zip.read("b.txt") # => "from memory"
45
+ # end
46
+ #
47
+ # File includes Enumerable over its LibZip::Entry records, so #map, #select
48
+ # and the rest work on a listing.
49
+ #
50
+ # Once the archive is closed, all of these fail with LibZip::EntryError.
51
+ class File
52
+ include Enumerable
53
+
54
+ # Opens the archive at +path+.
55
+ #
56
+ # With +create+ the archive is created when +path+ doesn't exist; an
57
+ # existing file is opened as it is and isn't truncated, so +create+ is safe
58
+ # to pass whenever you intend to write. Without it, a missing +path+ fails
59
+ # with LibZip::NotFoundError.
60
+ #
61
+ # +password+ becomes the archive default: the password each read of an
62
+ # encrypted entry falls back to. See #password=, and the call-level
63
+ # +password+ of #read and #get_input_stream.
64
+ #
65
+ # Without a block you get the archive and you call #close yourself. With a
66
+ # block the archive is yielded; on normal exit it's closed and committed
67
+ # and the block's value comes back from ::open. If the block fails, the
68
+ # archive is thrown away: no bytes are written, and an archive that ::open
69
+ # had just created is removed from disk.
70
+ #
71
+ # LibZip::File.open("archive.zip") # read
72
+ # LibZip::File.open("archive.zip", create: true) # read and write
73
+ # LibZip::File.open("secrets.zip", password: "hunter2") { |zip| ... }
74
+ #
75
+ # A 2nd argument that isn't a Hash is a TypeError.
76
+ def self.open(path, create: false, password: nil) # :yields: zip
77
+ end
78
+
79
+ # Writes the central directory, flushes the archive to disk, and releases
80
+ # it. Live input streams on this archive are closed first.
81
+ #
82
+ # Returns +nil+. Closing an already closed archive fails with
83
+ # LibZip::EntryError, so a double #close is a visible bug.
84
+ #
85
+ # An archive that was created and then not written to produces no file:
86
+ # libzip won't write an empty archive, and removing the last remaining
87
+ # entry deletes the file instead.
88
+ def close
89
+ end
90
+
91
+ # Whether the archive has been closed.
92
+ def closed?
93
+ end
94
+
95
+ # Adds +source_path+ from disk as the entry +name+, and returns +self+, so
96
+ # calls chain.
97
+ #
98
+ # zip.add("a.txt", "a.txt").add("docs/b.txt", "b.txt")
99
+ #
100
+ # The file is read when the archive is written, not now, so a source that
101
+ # disappears, or that can't be read any more, shows up as an error from
102
+ # #close. An entry of the same name is replaced.
103
+ #
104
+ # +encryption+ is one of +:aes128+, +:aes192+, +:aes256+, +:none+ or +nil+,
105
+ # and needs a +password+, either here or as the archive default. +:pkware+
106
+ # (ZipCrypto) isn't allowed for writing.
107
+ #
108
+ # Fails with LibZip::NotFoundError when +source_path+ doesn't exist,
109
+ # LibZip::PermissionError when it can't be read, and
110
+ # LibZip::InvalidArgumentError when it isn't a regular file, when
111
+ # +password+ comes without +encryption+, or when +encryption+ isn't one of
112
+ # the symbols above.
113
+ def add(name, source_path, encryption: nil, password: nil)
114
+ end
115
+
116
+ # Loads the entry +name+ and returns its bytes as an ASCII-8BIT String.
117
+ #
118
+ # +password+ decrypts this one entry and overrides the archive default, so
119
+ # an incorrect password here fails even when the archive was opened with
120
+ # the correct one.
121
+ #
122
+ # Fails with LibZip::NotFoundError when there's no such entry,
123
+ # LibZip::PasswordError when the password is missing or incorrect,
124
+ # LibZip::DecompressionError when the data doesn't match its CRC or its AES
125
+ # HMAC, and LibZip::CorruptArchiveError when the entry is shorter or longer
126
+ # than the size in the central directory.
127
+ #
128
+ # Use #get_input_stream for an entry too large to keep in memory.
129
+ def read(name, password: nil)
130
+ end
131
+
132
+ # Returns a LibZip::InputStream over the entry +name+, which pulls the entry
133
+ # in chunks instead of building 1 String.
134
+ #
135
+ # zip.get_input_stream("big.csv") do |stream|
136
+ # stream.read(64 * 1024) until stream.eof?
137
+ # end
138
+ #
139
+ # With a block the stream is yielded and closed on the way out, including
140
+ # when the block fails, and the block's value comes back. Without one you
141
+ # get the stream; close it with LibZip::InputStream#close, or let #close on
142
+ # the archive do it.
143
+ #
144
+ # +password+ works as in #read. Fails with LibZip::NotFoundError when
145
+ # there's no such entry.
146
+ def get_input_stream(name, password: nil) # :yields: stream
147
+ end
148
+
149
+ # Returns a LibZip::OutputStream that writes the entry +name+ from memory.
150
+ #
151
+ # zip.get_output_stream("data.csv") do |out|
152
+ # out.write("a,b,c\n")
153
+ # out << "1,2,3\n"
154
+ # end
155
+ #
156
+ # The stream buffers what you give it and adds the entry only when it's
157
+ # closed. A stream left to the garbage collector, or one where the block
158
+ # failed, contributes no entry, while entries written before it remain.
159
+ # An entry of the same name is replaced.
160
+ #
161
+ # With a block the stream is yielded, committed on normal exit, and the
162
+ # block's value comes back. Without one you get the stream, and
163
+ # LibZip::OutputStream#close commits it.
164
+ #
165
+ # +encryption+ and +password+ work as in #add.
166
+ def get_output_stream(name, encryption: nil, password: nil) # :yields: stream
167
+ end
168
+
169
+ # Deletes an entry, named by String or by the LibZip::Entry, and returns it
170
+ # as a snapshot taken before the deletion, so its metadata remains readable
171
+ # after the deletion and after the archive closes.
172
+ #
173
+ # removed = zip.remove("old.txt")
174
+ # removed.size # => 1234
175
+ #
176
+ # The entry disappears from the listing at once; #close writes the file
177
+ # again. Removing the last remaining entry deletes the archive from disk
178
+ # instead of writing an empty one.
179
+ #
180
+ # Fails with LibZip::NotFoundError when the entry isn't in the archive,
181
+ # including on a 2nd #remove of the same name, and
182
+ # LibZip::InvalidArgumentError for a LibZip::Entry belonging to another
183
+ # archive. There's no 3rd outcome: you get the entry back, or you get an
184
+ # exception.
185
+ def remove(name)
186
+ end
187
+
188
+ # Renames an entry, named by String or by the LibZip::Entry, and returns a
189
+ # snapshot taken after the rename. Size, CRC, time and compression are
190
+ # preserved.
191
+ #
192
+ # zip.rename("draft.txt", "final.txt")
193
+ #
194
+ # A rename moves 1 record: the children of a directory entry keep the names
195
+ # they were stored with, whatever the directory is called now.
196
+ #
197
+ # Fails with LibZip::NotFoundError when +old_name+ isn't in the archive,
198
+ # LibZip::AlreadyExistsError when +new_name+ already is, and
199
+ # LibZip::InvalidArgumentError when 1 name has the trailing +/+ of a
200
+ # directory entry and the other doesn't, or for a LibZip::Entry belonging
201
+ # to another archive.
202
+ def rename(old_name, new_name)
203
+ end
204
+
205
+ # Returns the central directory as an Array of LibZip::Entry snapshots.
206
+ #
207
+ # No extraction happens, and no entry bytes are read. The array is built
208
+ # fresh on each call, so it reflects entries added or removed since the
209
+ # previous one.
210
+ def entries
211
+ end
212
+
213
+ # Passes each LibZip::Entry to the block, and returns the full Array.
214
+ # Without a block, returns an Enumerator.
215
+ #
216
+ # This is what File's Enumerable methods run on.
217
+ #
218
+ # zip.map(&:name)
219
+ # zip.select(&:directory?)
220
+ def each # :yields: entry
221
+ end
222
+
223
+ # Same as #each. Named for what it passes to the block, and the method an
224
+ # Enumerator from #each calls.
225
+ def each_entry # :yields: entry
226
+ end
227
+
228
+ # Returns the entry names as an Array of UTF-8 Strings, directory entries
229
+ # included, with the trailing +/+ they're stored with.
230
+ def names
231
+ end
232
+
233
+ # Number of entries in the archive, not counting the ones removed since it
234
+ # was opened.
235
+ def size
236
+ end
237
+ alias length size
238
+
239
+ # Whether +name+ is an entry of this archive. Accepts a String or a
240
+ # LibZip::Entry.
241
+ def include?(name)
242
+ end
243
+
244
+ # Returns the LibZip::Entry named +name+, or +nil+ when the archive has no
245
+ # such entry. See #get_entry to fail instead.
246
+ def find_entry(name)
247
+ end
248
+ alias [] find_entry
249
+
250
+ # Returns the LibZip::Entry named +name+, or fails with
251
+ # LibZip::NotFoundError. See #find_entry to get +nil+ instead.
252
+ def get_entry(name)
253
+ end
254
+
255
+ # Returns the LibZip::Entry records matching +pattern+, and passes each of
256
+ # them to the block if one is given.
257
+ #
258
+ # zip.glob("*.txt") # => the .txt entries at the top level
259
+ # zip.glob("**/*.txt") # => the .txt entries at any depth
260
+ # zip.glob("docs") # => the "docs/" entry
261
+ #
262
+ # Matching is ::File.fnmatch? with +FNM_PATHNAME+, +FNM_DOTMATCH+ and
263
+ # +FNM_EXTGLOB+: <tt>*</tt> doesn't cross a +/+, <tt>**</tt> matches 0 or
264
+ # more directories, <tt>?</tt>, <tt>[...]</tt> and <tt>{a,b}</tt> all work,
265
+ # and a leading dot isn't special. A directory entry matches without the
266
+ # trailing +/+ it's stored with.
267
+ #
268
+ # Unlike Dir.glob on macOS, matching is case-sensitive on all platforms.
269
+ def glob(pattern) # :yields: entry
270
+ end
271
+
272
+ # The archive comment, as a UTF-8 String, or +nil+ when the archive has
273
+ # none. A comment that isn't valid UTF-8 comes back as ASCII-8BIT, instead
274
+ # of as a broken String.
275
+ def comment
276
+ end
277
+
278
+ # Sets the archive comment, which #close writes. +nil+ clears it.
279
+ #
280
+ # A comment longer than 65535 bytes, the limit of the field in the zip
281
+ # format, fails with LibZip::InvalidArgumentError.
282
+ def comment=(comment)
283
+ end
284
+
285
+ # Sets the comment of the entry +name+, and returns +self+. +nil+ clears
286
+ # it. Read it back with LibZip::Entry#comment.
287
+ #
288
+ # The comment is stored as UTF-8 when its bytes are valid UTF-8, and as raw
289
+ # bytes otherwise. Fails with LibZip::NotFoundError when there's no such
290
+ # entry, and LibZip::InvalidArgumentError past the 65535-byte limit.
291
+ def set_comment(name, comment)
292
+ end
293
+
294
+ # Sets the archive default password: the one used for an encrypted entry
295
+ # read with no +password+ argument. +nil+ clears it.
296
+ #
297
+ # There's no reader; a password that goes in doesn't come back out.
298
+ #
299
+ # Zip encryption covers entry contents only. Names, comments and the
300
+ # central directory remain in the clear whatever the password.
301
+ def password=(password)
302
+ end
303
+ end
304
+
305
+ # One record of the central directory: what the archive stores about an entry,
306
+ # without reading the entry.
307
+ #
308
+ # An Entry is an immutable snapshot taken when it was listed, so its metadata
309
+ # remains readable after the entry is removed and after the archive is
310
+ # closed. It isn't a handle: pass the name to LibZip::File#read for the
311
+ # bytes.
312
+ #
313
+ # entry = zip["docs/a.txt"]
314
+ # entry.name # => "docs/a.txt"
315
+ # entry.size # => 1234
316
+ # entry.compression_method # => LibZip::Entry::DEFLATED
317
+ #
318
+ # Each accessor except #name, #index and #directory? returns +nil+ when the
319
+ # archive didn't store that field, which a zip written by another tool is
320
+ # free to skip.
321
+ #
322
+ # Entries come from LibZip::File#entries, #each, #[], #find_entry,
323
+ # #get_entry, #glob, #remove and #rename; there's no public constructor.
324
+ class Entry
325
+ # Compression method: stored, no compression.
326
+ STORED = 0
327
+
328
+ # Compression method: deflate.
329
+ DEFLATED = 8
330
+
331
+ # Encryption method: none. What #encryption_method gives for a plain entry.
332
+ NONE = 0
333
+
334
+ # Encryption method: traditional PKWARE (ZipCrypto). Readable, and not
335
+ # allowed for writing, since libzip documents it as broken.
336
+ TRAD_PKWARE = 1
337
+
338
+ # Encryption method: AES-128 (Winzip AE-2).
339
+ AES_128 = 257
340
+
341
+ # Encryption method: AES-192 (Winzip AE-2).
342
+ AES_192 = 258
343
+
344
+ # Encryption method: AES-256 (Winzip AE-2).
345
+ AES_256 = 259
346
+
347
+ # The entry name, as a UTF-8 String. Directory entries keep the trailing
348
+ # +/+ they're stored with.
349
+ def name
350
+ end
351
+
352
+ # The entry's position in the archive, counting from 0.
353
+ def index
354
+ end
355
+
356
+ # The uncompressed size in bytes, or +nil+ when the archive didn't store
357
+ # it.
358
+ def size
359
+ end
360
+
361
+ # The size in bytes as stored, after compression, or +nil+ when the archive
362
+ # didn't store it.
363
+ def compressed_size
364
+ end
365
+
366
+ # The CRC-32 of the uncompressed data as an Integer, or +nil+ when the
367
+ # archive didn't store it.
368
+ #
369
+ # AES entries use AE-2, which keeps no CRC, so this can be 0 for them;
370
+ # their integrity check is the HMAC, verified when the entry is read to the
371
+ # end.
372
+ def crc
373
+ end
374
+
375
+ # The modification time, as a Time, or +nil+ when the archive didn't store
376
+ # it.
377
+ def time
378
+ end
379
+ alias mtime time
380
+
381
+ # STORED or DEFLATED, or +nil+ when the archive didn't store it. Directory
382
+ # entries give STORED.
383
+ def compression_method
384
+ end
385
+
386
+ # libzip's numeric encryption method: NONE, TRAD_PKWARE, AES_128, AES_192
387
+ # or AES_256. +nil+ when the archive didn't store it.
388
+ def encryption_method
389
+ end
390
+
391
+ # Whether the entry's contents are encrypted, which means #encryption_method
392
+ # is anything but NONE. +false+ when the archive didn't store the method.
393
+ def encrypted?
394
+ end
395
+
396
+ # Whether this entry is a directory.
397
+ #
398
+ # True for a name ending in +/+, and for an entry with external attributes
399
+ # that agree: a UNIX mode with +S_IFDIR+, or the MS-DOS directory bit.
400
+ # That's how a directory written by another tool is identified.
401
+ def directory?
402
+ end
403
+
404
+ # The entry comment, as a UTF-8 String, or +nil+ when it has none. A
405
+ # comment that isn't valid UTF-8 comes back as ASCII-8BIT. Set it with
406
+ # LibZip::File#set_comment.
407
+ def comment
408
+ end
409
+
410
+ # The entry name, so an Entry interpolates into a String as its name.
411
+ def to_s
412
+ end
413
+
414
+ # <tt>#&lt;LibZip::Entry name="docs/a.txt" size=1234&gt;</tt>
415
+ def inspect
416
+ end
417
+ end
418
+
419
+ # A read handle on 1 entry, from LibZip::File#get_input_stream.
420
+ #
421
+ # The entry is decompressed and decrypted in chunks as you read, so memory
422
+ # remains flat whatever the entry's size.
423
+ #
424
+ # LibZip::File.open("archive.zip") do |zip|
425
+ # zip.get_input_stream("big.csv") do |stream|
426
+ # stream.read(64 * 1024) until stream.eof?
427
+ # end
428
+ # end
429
+ #
430
+ # A stream keeps a libzip handle and has to be closed. The block form of
431
+ # LibZip::File#get_input_stream does that for you; otherwise #close does, and
432
+ # LibZip::File#close closes whatever is still open on that archive.
433
+ class InputStream
434
+ # Returns +length+ bytes, or the rest of the entry when +length+ is
435
+ # omitted.
436
+ #
437
+ # Returns an ASCII-8BIT String. A short read means the entry ended: fewer
438
+ # than +length+ bytes come back, and the call after that gives +nil+. With
439
+ # no +length+, a stream already at EOF gives <tt>""</tt>, the same
440
+ # distinction IO makes. <tt>read(0)</tt> gives <tt>""</tt> and consumes no
441
+ # bytes, even at EOF.
442
+ #
443
+ # Fails with ArgumentError for a negative +length+, LibZip::EntryError once
444
+ # the stream is closed, and LibZip::DecompressionError when the data
445
+ # doesn't match its CRC or its AES HMAC, which is only known once the entry
446
+ # has been read to the end.
447
+ def read(length = nil)
448
+ end
449
+
450
+ # Whether the entry has been read to the end. Fails with
451
+ # LibZip::EntryError once the stream is closed.
452
+ def eof?
453
+ end
454
+
455
+ # Whether the stream has been closed, by #close or by closing the archive.
456
+ def closed?
457
+ end
458
+
459
+ # Releases the entry handle. Returns +nil+, and has no effect on a stream
460
+ # that's already closed.
461
+ def close
462
+ end
463
+ end
464
+
465
+ # A write handle on 1 entry, from LibZip::File#get_output_stream.
466
+ #
467
+ # zip.get_output_stream("data.csv") do |out|
468
+ # out.write("a,b,c\n")
469
+ # out << "1,2,3\n"
470
+ # end
471
+ #
472
+ # What you write is buffered in memory, and becomes an entry only on #close,
473
+ # which the block form calls for you. A stream that isn't closed, because the
474
+ # block failed or because the garbage collector took it, adds no entry. The
475
+ # entry is written to disk when the archive is closed.
476
+ class OutputStream
477
+ # Appends +string+ to the buffer, and returns the number of bytes written.
478
+ # Binary data, NUL bytes included, is written as given.
479
+ #
480
+ # Fails with LibZip::EntryError once the stream has been closed, or the
481
+ # archive has.
482
+ def write(string)
483
+ end
484
+
485
+ # Appends +string+ and returns +self+, so writes chain.
486
+ #
487
+ # out << "Hello, " << "world!\n"
488
+ def <<(string)
489
+ end
490
+
491
+ # Adds the buffered bytes to the archive as the entry, and returns +nil+.
492
+ # LibZip::File#close writes the archive to disk.
493
+ #
494
+ # Encryption is set up here, so a bad encryption setup shows up on #close
495
+ # instead of on #write.
496
+ def close
497
+ end
498
+ end
499
+
500
+ # Base class for anything this gem fails with, and a StandardError, so a
501
+ # single +rescue LibZip::Error+ handles all of them.
502
+ #
503
+ # begin
504
+ # LibZip::File.open("archive.zip") { |zip| zip.read("nope.txt") }
505
+ # rescue LibZip::NotFoundError => e
506
+ # warn "missing entry: #{e.message}"
507
+ # rescue LibZip::Error => e
508
+ # warn "libzip error: #{e.message}"
509
+ # end
510
+ #
511
+ # Each libzip error code maps to one of the subclasses below; a code with no
512
+ # better home comes back as LibZip::Error.
513
+ class Error < StandardError
514
+ end
515
+
516
+ # An operation or an encryption method libzip can't perform.
517
+ class UnsupportedError < Error
518
+ end
519
+
520
+ # An open, seek, tell, close, rename or remove of an underlying file failed,
521
+ # or a file ended sooner than expected.
522
+ class IoError < Error
523
+ end
524
+
525
+ # Reading archive data failed.
526
+ class ReadError < Error
527
+ end
528
+
529
+ # Writing archive data failed.
530
+ class WriteError < Error
531
+ end
532
+
533
+ # No such archive, entry, or source file.
534
+ class NotFoundError < Error
535
+ end
536
+
537
+ # An entry exists where one must not, as when renaming to a name that's
538
+ # taken.
539
+ class AlreadyExistsError < Error
540
+ end
541
+
542
+ # Not a zip file, truncated, inconsistent, multi-disk, or with compressed
543
+ # data that makes no sense.
544
+ class CorruptArchiveError < Error
545
+ end
546
+
547
+ # The compression method isn't supported.
548
+ class CompressionError < Error
549
+ end
550
+
551
+ # Decompression failed: the data doesn't match its CRC or its AES HMAC.
552
+ class DecompressionError < Error
553
+ end
554
+
555
+ # An argument libzip or this gem turns away: a directory as a source file, a
556
+ # comment past 65535 bytes, an unknown +encryption+, a password without
557
+ # encryption, an entry from another archive.
558
+ class InvalidArgumentError < Error
559
+ end
560
+
561
+ # The archive is read-only, or the operation isn't allowed on it.
562
+ class PermissionError < Error
563
+ end
564
+
565
+ # The password is missing or incorrect. Comes when the entry is read, not
566
+ # when the archive is opened.
567
+ class PasswordError < Error
568
+ end
569
+
570
+ # An entry, stream or archive used after it was closed, deleted or changed.
571
+ class EntryError < Error
572
+ end
573
+
574
+ # libzip or zlib failed internally, or ran out of memory.
575
+ class InternalError < Error
576
+ end
577
+ end
@@ -0,0 +1,4 @@
1
+ # This file exists only so that `gem "libzip-ruby"` works without a `require:`
2
+ # option: Bundler auto-requires the gem name, and RubyGems' default file for
3
+ # `libzip-ruby` is `libzip-ruby.rb`. The real entry point is `libzip.rb`.
4
+ require_relative "libzip"
data/lib/libzip.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "rbconfig"
2
+
3
+ # The entry point: `require "libzip"`.
4
+ #
5
+ # A precompiled gem ships one extension per Ruby ABI under
6
+ # lib/libzip_ruby/<abi>/, <abi> being the major.minor of the interpreter it was
7
+ # built for (an extension built for 4.0 loads on any 4.0.x). In a source
8
+ # checkout that directory does not exist and we fall through to $LOAD_PATH,
9
+ # which `zig build`/`script/package` point at zig-out/lib.
10
+ abi = RbConfig::CONFIG["ruby_version"][/\A\d+\.\d+/]
11
+ precompiled = File.expand_path("libzip_ruby/#{abi}", __dir__)
12
+ $LOAD_PATH.unshift(precompiled) if File.directory?(precompiled)
13
+
14
+ require "libzip_ruby"
15
+
16
+ # Everything this gem defines lives under LibZip. There is deliberately no
17
+ # top-level `Zip` constant: rubyzip owns that name, and defining a second, much
18
+ # smaller `Zip` would hijack any transitive `require "zip"` that resolves to us.
Binary file
Binary file
Binary file
@@ -0,0 +1,41 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "libzip-ruby"
3
+ # Single source of truth: build.zig reads this file and stamps the value into
4
+ # LibZip::VERSION, script/package tags the release, so they cannot disagree.
5
+ spec.version = "0.1.0"
6
+ spec.summary = "libzip bindings for Ruby, with libzip and zlib linked in"
7
+ spec.description = <<~TEXT
8
+ Ruby bindings for libzip, the C library for reading and writing zip
9
+ archives.
10
+ TEXT
11
+ spec.authors = ["Jean-Paul Pierre Louis Fiorini"]
12
+ spec.email = ["fiorini751@proton.me"]
13
+ spec.license = "Apache-2.0"
14
+
15
+ # The oldest interpreter we ship a compiled extension for (script/package).
16
+ spec.required_ruby_version = ">= 3.3"
17
+
18
+ # script/package sets this while assembling a precompiled gem, so the .gem
19
+ # carries its own platform; a plain `gem build` stays platform-independent.
20
+ spec.platform = Gem::Platform.new(ENV["LIBZIP_RUBY_PLATFORM"]) if ENV["LIBZIP_RUBY_PLATFORM"]
21
+
22
+ spec.homepage = "https://github.com/Tipuch/libzip-ruby"
23
+ # No homepage_uri: spec.homepage already is that URL, and RubyGems complains
24
+ # when 2 metadata keys point at the same URL.
25
+ spec.metadata = {
26
+ "source_code_uri" => spec.homepage,
27
+ "bug_tracker_uri" => "#{spec.homepage}/issues",
28
+ "documentation_uri" => "https://tipuch.github.io/libzip-ruby/",
29
+ "rubygems_mfa_required" => "true",
30
+ }
31
+
32
+ spec.files = Dir["lib/**/*"] + Dir["doc/*.rb"] +
33
+ ["libzip-ruby.gemspec", "LICENSE", "README.md"]
34
+ spec.require_paths = ["lib"]
35
+
36
+ # RubyGems documents require_paths + extra_rdoc_files at install time, and
37
+ # the entire API is written out in doc/api.rb (the extension is Zig, which
38
+ # RDoc can't parse), so `ri LibZip::File` works only if it's listed here.
39
+ spec.extra_rdoc_files = Dir["doc/*.rb"] + ["README.md"]
40
+ spec.rdoc_options = ["--main", "README.md", "--title", "libzip-ruby #{spec.version}"]
41
+ end