Added a bunch of documentation

This commit is contained in:
thomas 2005-02-17 20:27:02 +00:00
parent 93b7add002
commit 3e1aa6d7e5
6 changed files with 175 additions and 47 deletions

31
README
View File

@ -4,29 +4,25 @@ rubyzip is a ruby library for reading and writing zip (pkzip format)
files, with the restriction that only uncompressed and deflated zip
entries are supported. All this library does is handling of the zip
file format. the actual compression/decompression is handled by
zlib. zlib is accessible from ruby thanks to ruby/zlib (see below)
zlib. zlib is accessible from ruby thanks to ruby/zlib.
To run the unit tests you need to have rubyunit or test::unit
installed.
To run the unit tests you need to have test::unit installed.
= Install
ruby install.rb
To install from source run
ruby install.rb
= Prerequisites
If you have Rake installed you can build a rubyzip gem with
This library requires ruby/zlib version 0.5.0 or newer. ruby/zlib is
included in most recent ruby distributions.
* zlib http://www.gzip.org/zlib/
* ruby-zlib: http://www.blue.sky.or.jp/atelier/ruby/
rake package
= Documentation
There is more than one way to access or create a zip archive with
rubyzip. The basic API is modelled after the classes in
rubyzip. The basic API is modeled after the classes in
java.util.zip.* from the Java SDK. This means there are classes such
as Zip::ZipInputStream, Zip::ZipOutputStream and
Zip::ZipFile. Zip::ZipInputStream provides a basic interface for
@ -40,9 +36,9 @@ java.util.zip.ZipFile rubyzip's Zip::ZipFile is mutable, which means
it can be used to change zip files as well.
Another way to access a zip archive with rubyzip is to use rubyzip's
lib/zip/zipfilesystem.rb API. Using this API files can be read from and written
to the archive in much the same manner as ruby's builtin classes
allows files to be read from and written to the file system.
Zip::ZipFileSystem API. Using this API files can be read from and
written to the archive in much the same manner as ruby's builtin
classes allows files to be read from and written to the file system.
The samples/ directory is a good place to start to get a feel for
using the library. For details about the specific behaviour of classes
@ -59,9 +55,12 @@ http://www.ruby-lang.org/en/LICENSE.txt
http://rubyzip.sourceforge.net
== Download (tarballs and gems)
= Author
http://sourceforge.net/project/showfiles.php?group_id=43107&package_id=35377
Thomas Sondergaard (thomas at thomassondergaard.com)
= Authors
Thomas Sondergaard (thomas at sondergaard.cc)
extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)

View File

@ -60,7 +60,7 @@ end
Rake::RDocTask.new do |rd|
rd.main = "README"
rd.rdoc_files.add %W{ README NEWS TODO lib/** }
rd.rdoc_files.add %W{ lib/zip/*.rb README NEWS TODO }
rd.options << "--title 'rubyzip documentation' --webcvs http://cvs.sourceforge.net/viewcvs.py/rubyzip/rubyzip/"
# rd.options << "--all"
end

5
TODO
View File

@ -1,9 +1,4 @@
* Add an extensively documented sample in samples/ that shows most of
what rubyzip does
* Add doc target to Rakefile that builds rdoc documentation and which
turns the samples in the sample directory into html which is included
in the output site
* Add web target to Rakefile to update rubyzip.sourceforge.net website
* Add upload target which uploads dist to sourceforge and gem to where-ever
* Release 0.5.7

View File

@ -24,7 +24,7 @@ unless Object.method_defined?(:object_id)
end
unless File.respond_to?(:read)
class File
class File # :nodoc:all
# singleton method read does not exist in 1.6.x
def self.read(fileName)
open(fileName) { |f| f.read }

View File

@ -735,7 +735,7 @@ module Zip
end
class ZipCentralDirectory #:nodoc:all
class ZipCentralDirectory
include Enumerable
END_OF_CENTRAL_DIRECTORY_SIGNATURE = 0x06054b50
@ -743,24 +743,25 @@ module Zip
STATIC_EOCD_SIZE = 22
attr_reader :comment
# Returns an Enumerable containing the entries.
def entries
@entrySet.entries
end
def initialize(entries = ZipEntrySet.new, comment = "")
def initialize(entries = ZipEntrySet.new, comment = "") #:nodoc:
super()
@entrySet = entries.kind_of?(ZipEntrySet) ? entries : ZipEntrySet.new(entries)
@comment = comment
end
def write_to_stream(io)
def write_to_stream(io) #:nodoc:
offset = io.tell
@entrySet.each { |entry| entry.write_c_dir_entry(io) }
write_e_o_c_d(io, offset)
end
def write_e_o_c_d(io, offset)
def write_e_o_c_d(io, offset) #:nodoc:
io <<
[END_OF_CENTRAL_DIRECTORY_SIGNATURE,
0 , # @numberOfThisDisk
@ -774,13 +775,13 @@ module Zip
end
private :write_e_o_c_d
def cdir_size
def cdir_size #:nodoc:
# does not include eocd
@entrySet.inject(0) { |value, entry| entry.cdir_header_size + value }
end
private :cdir_size
def read_e_o_c_d(io)
def read_e_o_c_d(io) #:nodoc:
buf = get_e_o_c_d(io)
@numberOfThisDisk = ZipEntry::read_zip_short(buf)
@numberOfDiskWithStartOfCDir = ZipEntry::read_zip_short(buf)
@ -793,7 +794,7 @@ module Zip
raise ZipError, "Zip consistency problem while reading eocd structure" unless buf.size == 0
end
def read_central_directory_entries(io)
def read_central_directory_entries(io) #:nodoc:
begin
io.seek(@cdirOffset, IO::SEEK_SET)
rescue Errno::EINVAL
@ -805,12 +806,12 @@ module Zip
}
end
def read_from_stream(io)
def read_from_stream(io) #:nodoc:
read_e_o_c_d(io)
read_central_directory_entries(io)
end
def get_e_o_c_d(io)
def get_e_o_c_d(io) #:nodoc:
begin
io.seek(-MAX_END_OF_CENTRAL_DIRECTORY_STRUCTURE_SIZE, IO::SEEK_END)
rescue Errno::EINVAL
@ -827,16 +828,19 @@ module Zip
end
return buf
end
# For iterating over the entries.
def each(&proc)
@entrySet.each(&proc)
end
# Returns the number of entries in the central directory (and
# consequently in the zip archive).
def size
@entrySet.size
end
def ZipCentralDirectory.read_from_stream(io)
def ZipCentralDirectory.read_from_stream(io) #:nodoc:
cdir = new
cdir.read_from_stream(io)
return cdir
@ -844,7 +848,7 @@ module Zip
return nil
end
def == (other)
def == (other) #:nodoc:
return false unless other.kind_of?(ZipCentralDirectory)
@entrySet.entries.sort == other.entries.sort && comment == other.comment
end
@ -858,19 +862,64 @@ module Zip
class ZipCompressionMethodError < ZipError; end
class ZipEntryNameError < ZipError; end
# ZipFile is modeled after java.util.zip.ZipFile from the Java SDK.
# The most important methods are those inherited from
# ZipCentralDirectory for accessing information about the entries in
# the archive and methods such as get_input_stream and
# get_output_stream for reading from and writing entries to the
# archive. The class includes a few convenience methods such as
# #extract for extracting entries to the filesystem, and #remove,
# #replace, #rename and #mkdir for making simple modifications to
# the archive.
#
# Modifications to a zip archive are not committed until #commit or
# #close is called. The method #open accepts a block following
# the pattern from File.open offering a simple way to
# automatically close the archive when the block returns.
#
# The following example opens zip archive <code>my.zip</code>
# (creating it if it doesn't exist) and adds an entry
# <code>first.txt</code> and a directory entry <code>a_dir</code>
# to it.
#
# require 'zip/zip'
#
# Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) {
# |zipfile|
# zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" }
# zipfile.mkdir("a_dir")
# }
#
# The next example reopens <code>my.zip</code> writes the contents of
# <code>first.txt</code> to standard out and deletes the entry from
# the archive.
#
# require 'zip/zip'
#
# Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) {
# |zipfile|
# puts zipfile.read("first.txt")
# zipfile.remove("first.txt")
# }
#
# ZipFileSystem offers an alternative API that emulates ruby's
# interface for accessing the filesystem, ie. the File and Dir classes.
class ZipFile < ZipCentralDirectory
CREATE = 1
attr_reader :name
# Opens a zip archive. Pass true as the second parameter to create
# a new archive if it doesn't exist already.
def initialize(fileName, create = nil)
super()
@name = fileName
@comment = ""
if (File.exists?(fileName))
File.open(name, "rb") { |f| read_from_stream(f) }
elsif (create == ZipFile::CREATE)
elsif (create)
@entrySet = ZipEntrySet.new
else
raise ZipError, "File #{fileName} not found"
@ -878,7 +927,10 @@ module Zip
@create = create
@storedEntries = @entrySet.dup
end
# Same as #new. If a block is passed the ZipFile object is passed
# to the block and is automatically closed afterwards just as with
# ruby's builtin File.open method.
def ZipFile.open(fileName, create = nil)
zf = ZipFile.new(fileName, create)
if block_given?
@ -892,8 +944,15 @@ module Zip
end
end
# Returns the zip files comment, if it has one
attr_accessor :comment
# Iterates over the contents of the ZipFile. This is more efficient
# than using a ZipInputStream since this methods simply iterates
# through the entries in the central directory structure in the archive
# whereas ZipInputStream jumps through the entire archive accessing the
# local entry headers (which contain the same information as the
# central directory).
def ZipFile.foreach(aZipFileName, &block)
ZipFile.open(aZipFileName) {
|zipFile|
@ -901,10 +960,16 @@ module Zip
}
end
# Returns an input stream to the specified entry. If a block is passed
# the stream object is passed to the block and the stream is automatically
# closed afterwards just as with ruby's builtin File.open method.
def get_input_stream(entry, &aProc)
get_entry(entry).get_input_stream(&aProc)
end
# Returns an output stream to the specified entry. If a block is passed
# the stream object is passed to the block and the stream is automatically
# closed afterwards just as with ruby's builtin File.open method.
def get_output_stream(entry, &aProc)
newEntry = entry.kind_of?(ZipEntry) ? entry : ZipEntry.new(@name, entry.to_s)
if newEntry.directory?
@ -916,14 +981,17 @@ module Zip
zipStreamableEntry.get_output_stream(&aProc)
end
# Returns the name of the zip archive
def to_s
@name
end
# Returns a string containing the contents of the specified entry
def read(entry)
get_input_stream(entry) { |is| is.read }
end
# Convenience method for adding the contents of a file to the archive
def add(entry, srcPath, &continueOnExistsProc)
continueOnExistsProc ||= proc { false }
check_entry_exists(entry, continueOnExistsProc, "add")
@ -935,21 +1003,26 @@ module Zip
end
end
# Removes the specified entry.
def remove(entry)
@entrySet.delete(get_entry(entry))
end
# Renames the specified entry.
def rename(entry, newName, &continueOnExistsProc)
foundEntry = get_entry(entry)
check_entry_exists(newName, continueOnExistsProc, "rename")
foundEntry.name=newName
end
# Replaces the specified entry with the contents of srcPath (from
# the file system).
def replace(entry, srcPath)
check_file(srcPath)
add(remove(entry), srcPath)
end
# Extracts entry to file destPath.
def extract(entry, destPath, &onExistsProc)
onExistsProc ||= proc { false }
foundEntry = get_entry(entry)
@ -959,7 +1032,9 @@ module Zip
write_file(foundEntry, destPath, &onExistsProc)
end
end
# Commits changes that has been made since the previous commit to
# the zip archive.
def commit
return if ! commit_required?
on_success_replace(name) {
@ -974,22 +1049,29 @@ module Zip
}
initialize(name)
end
# Closes the zip file committing any changes that has been made.
def close
commit
end
# Returns true if any changes has been made to this archive since
# the previous commit
def commit_required?
return @entrySet != @storedEntries || @create == ZipFile::CREATE
end
# Searches for entry with the specified name. Returns nil if
# no entry is found. See also get_entry
def find_entry(entry)
@entrySet.detect {
|e|
e.name.sub(/\/$/, "") == entry.to_s.sub(/\/$/, "")
}
end
# Searches for an entry just as find_entry, but throws Errno::ENOENT
# if no entry is found.
def get_entry(entry)
selectedEntry = find_entry(entry)
unless selectedEntry
@ -998,6 +1080,7 @@ module Zip
return selectedEntry
end
# Creates a directory
def mkdir(entryName, permissionInt = 0) #permissionInt ignored
if find_entry(entryName)
raise Errno::EEXIST, "File exists - #{entryName}"

View File

@ -1,9 +1,42 @@
require 'zip/zip'
module Zip
# The ZipFileSystem API provides an API for accessing entries in
# a zip archive that is similar to ruby's builtin File and Dir
# classes.
#
# Requiring 'zip/zipfilesystem' includes this module in ZipFile
# making the methods in this module available on ZipFile objects.
#
# Using this API the following example creates a new zip file
# <code>my.zip</code> containing a normal entry with the name
# <code>first.txt</code>, a directory entry named <code>mydir</code>
# and finally another normal entry named <code>second.txt</code>
#
# require 'zip/zipfilesystem'
#
# Zip::ZipFile.open("my.zip", Zip::ZipFile::CREATE) {
# |zipfile|
# zipfile.file.open("first.txt", "w") { |f| f.puts "Hello world" }
# zipfile.dir.mkdir("mydir")
# zipfile.file.open("mydir/second.txt", "w") { |f| f.puts "Hello again" }
# }
#
# Reading is as easy as writing, as the following example shows. The
# example writes the contents of <code>first.txt</code> from zip archive
# <code>my.zip</code> to standard out.
#
# require 'zip/zipfilesystem'
#
# Zip::ZipFile.open("my.zip") {
# |zipfile|
# puts zipfile.file.read("first.txt")
# }
module ZipFileSystem
def initialize
def initialize # :nodoc:
mappedZip = ZipFileNameMapper.new(self)
@zipFsDir = ZipFsDir.new(mappedZip)
@zipFsFile = ZipFsFile.new(mappedZip)
@ -11,14 +44,26 @@ module Zip
@zipFsFile.dir = @zipFsDir
end
# Returns a ZipFsDir which is much like ruby's builtin Dir (class)
# object, except it works on the ZipFile on which this method is
# invoked
def dir
@zipFsDir
end
# Returns a ZipFsFile which is much like ruby's builtin File (class)
# object, except it works on the ZipFile on which this method is
# invoked
def file
@zipFsFile
end
# Instances of this class are normally accessed via the accessor
# ZipFile::file. An instance of ZipFsFile behaves like ruby's
# builtin File (class) object, except it works on ZipFile entries.
#
# The individual methods are not documented due to their
# similarity with the methods in File
class ZipFsFile
attr_writer :dir
@ -198,7 +243,7 @@ module Zip
@mappedZip.get_entry(fileName).size
end
# nil for not found and nil for directories
# Returns nil for not found and nil for directories
def size?(fileName)
entry = @mappedZip.find_entry(fileName)
return (entry == nil || entry.directory?) ? nil : entry.size
@ -367,6 +412,12 @@ module Zip
end
end
# Instances of this class are normally accessed via the accessor
# ZipFile::dir. An instance of ZipFsDir behaves like ruby's
# builtin Dir (class) object, except it works on ZipFile entries.
#
# The individual methods are not documented due to their
# similarity with the methods in Dir
class ZipFsDir
def initialize(mappedZip)
@ -441,7 +492,7 @@ module Zip
end
class ZipFsDirIterator
class ZipFsDirIterator # :nodoc:all
include Enumerable
def initialize(arrayOfFileNames)
@ -481,7 +532,7 @@ module Zip
# All access to ZipFile from ZipFsFile and ZipFsDir goes through a
# ZipFileNameMapper, which has one responsibility: ensure
class ZipFileNameMapper
class ZipFileNameMapper # :nodoc:all
include Enumerable
def initialize(zipFile)