ruby_archive 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +24 -0
- data/README.rdoc +106 -0
- data/README.rubyzip +72 -0
- data/Rakefile +53 -0
- data/bin/rba_launch +62 -0
- data/lib/ruby_archive.rb +79 -0
- data/lib/ruby_archive/handler.rb +55 -0
- data/lib/ruby_archive/handlers/rubyzip/zip/ioextras.rb +165 -0
- data/lib/ruby_archive/handlers/rubyzip/zip/stdrubyext.rb +111 -0
- data/lib/ruby_archive/handlers/rubyzip/zip/tempfile_bugfixed.rb +195 -0
- data/lib/ruby_archive/handlers/rubyzip/zip/zip.rb +1880 -0
- data/lib/ruby_archive/handlers/rubyzip/zip/zipfilesystem.rb +630 -0
- data/lib/ruby_archive/handlers/zip_handler.rb +42 -0
- data/lib/ruby_archive/patch.rb +3 -0
- data/lib/ruby_archive/patch/dir.rb +185 -0
- data/lib/ruby_archive/patch/file.rb +301 -0
- data/lib/ruby_archive/patch/kernel.rb +144 -0
- data/test/archive.zip +0 -0
- data/test/does_it_work.rb +4 -0
- data/test/helper.rb +10 -0
- data/test/test_ruby_archive.rb +8 -0
- metadata +104 -0
data/.document
ADDED
data/.gitignore
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
= ruby_archive
|
2
|
+
|
3
|
+
Seamless access to Ruby source and other files inside zip archives
|
4
|
+
|
5
|
+
Works with all rubies I have tested it with... ruby-1.8.7, rubinius, jruby, and ruby-head.
|
6
|
+
|
7
|
+
Includes a modified version of rubyzip. Readme and license for rubyzip are
|
8
|
+
available in README.rubyzip. Note that ruby_archive may fail if the program uses
|
9
|
+
a different version of rubyzip.
|
10
|
+
|
11
|
+
A Ruby Summer of Code 2010 Project.
|
12
|
+
|
13
|
+
== Usage
|
14
|
+
|
15
|
+
Using ruby_archive in your project is easy! It allows you to use zip or jar archives
|
16
|
+
much the same way you already use normal operating system directories.
|
17
|
+
|
18
|
+
To use it, simply <code>require 'ruby_archive'</code> in your project. After this, you can:
|
19
|
+
* open files for reading or writing within an archive using <code>Kernel#open</code> or
|
20
|
+
<code>File.open</code>
|
21
|
+
* <code>load</code> or <code>require</code> Ruby source files from within an archive
|
22
|
+
* <code>glob</code> files within an archive directory
|
23
|
+
* much more...
|
24
|
+
|
25
|
+
The format for accessing files within an archive is: "(archive_file)!/(file_within_archive)"
|
26
|
+
So to open <code>information.txt</code> inside <code>archive.jar</code>, you might:
|
27
|
+
|
28
|
+
<code>f = File.open('./archive.jar!/information.txt','r')</code>
|
29
|
+
|
30
|
+
Or to load <code>source.rb</code> inside <code>program.zip</code>, you might:
|
31
|
+
|
32
|
+
<code>require './program.zip!/source'</code>
|
33
|
+
|
34
|
+
You can also add archive directories to the load path:
|
35
|
+
|
36
|
+
<code>$LOAD_PATH << 'program.zip!/' ; require 'source'</code>
|
37
|
+
|
38
|
+
Note that if the specified path (including the exclamation point) exists as a
|
39
|
+
file on the filesystem (i.e. not an archive), it will load the file instead of
|
40
|
+
the archive. So, if you have an archive named <code>archive.zip</code> and a
|
41
|
+
directory name <code>archive.zip!</code> with a file named <code>text.txt</code>
|
42
|
+
inside, a request for <code>'archive.zip!/text.txt'</code> will load the file
|
43
|
+
rather than look for text.txt inside <code>archive.zip</code>.
|
44
|
+
|
45
|
+
== Launcher
|
46
|
+
|
47
|
+
There is also a simple launcher for programs packaged as archive files in the
|
48
|
+
bin folder. If you include a file named <code>start.rb</code> in your archive,
|
49
|
+
this launcher will run this file. The launcher always adds the base path of the
|
50
|
+
archive to the load path.
|
51
|
+
|
52
|
+
The launcher can be used from the shell as follows:
|
53
|
+
|
54
|
+
<code>rba_launch program.zip</code>
|
55
|
+
|
56
|
+
You can also specify a specific file to load with the launcher, though often on some
|
57
|
+
shells you may need to escape the '!' or single-quote the filename:
|
58
|
+
|
59
|
+
<code>rba_launch 'program.zip!/alternate_start.rb'</code>
|
60
|
+
|
61
|
+
<code>rba_launch program.zip\!/alternate_start.rb</code>
|
62
|
+
|
63
|
+
== To-do
|
64
|
+
|
65
|
+
A lot of work has gone into making this work great, but there are still many features
|
66
|
+
that we should be able to implement.
|
67
|
+
|
68
|
+
* <b>autoload</b> - initially I thought it would not be possible to make it work without
|
69
|
+
patches to the Ruby interpreter, but some research on the topic has led me to
|
70
|
+
believe it is indeed possible.
|
71
|
+
* <b>launcher</b> - the included launcher is extremely basic. We would like one that can
|
72
|
+
do things like read jar manifests and load configuration options from the archive.
|
73
|
+
* <b>File.*** and Dir.***</b> - while most common methods work, there are a couple that may
|
74
|
+
come up from time to time that are currently marked as forward_method_unsupported
|
75
|
+
(meaning they will fail if called on an archive location)
|
76
|
+
* <b>More archive handlers</b> - zip_handler.rb works great for zip and jar files, but I'd like
|
77
|
+
to add more supported formats. A handler for gem files, for example, would be great.
|
78
|
+
* <b>Various fixes to zip_handler/rubyzip</b> - for example, currently you can only load files
|
79
|
+
within a zip archive with string modes ('r','w',etc). Trying to use a constant mode (File::RDONLY,
|
80
|
+
etc) will raise an exception.
|
81
|
+
|
82
|
+
== Bugs?
|
83
|
+
|
84
|
+
Please put bug reports on the issue tracker. Include any error messages, exceptions,
|
85
|
+
and (if applicable) source code that is causing the problem.
|
86
|
+
|
87
|
+
== Note on Patches/Pull Requests
|
88
|
+
|
89
|
+
* Fork the project.
|
90
|
+
* Make your feature addition or bug fix.
|
91
|
+
* Add tests for it. This is important so I don't break it in a
|
92
|
+
future version unintentionally.
|
93
|
+
* Commit, do not mess with rakefile, version, or history.
|
94
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
95
|
+
* Send me a pull request. Bonus points for topic branches.
|
96
|
+
|
97
|
+
== Thanks
|
98
|
+
|
99
|
+
* <b>Ruby Summer of Code</b> and all those involved in it, for being awesome - http://rubysoc.org
|
100
|
+
* <b>Evan Phoenix</b> for being my mentor on this project
|
101
|
+
* <b>Authors of rubyzip</b> for making a really great way to work with zip files in Ruby
|
102
|
+
|
103
|
+
== Copyright
|
104
|
+
|
105
|
+
Copyright (c) 2010 Jonathan Nielsen.
|
106
|
+
Released under Ruby's license, including GPL option.
|
data/README.rubyzip
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
= rubyzip
|
2
|
+
|
3
|
+
rubyzip is a ruby library for reading and writing zip files.
|
4
|
+
|
5
|
+
= Install
|
6
|
+
|
7
|
+
If you have rubygems you can install rubyzip directly from the gem
|
8
|
+
repository
|
9
|
+
|
10
|
+
gem install rubyzip
|
11
|
+
|
12
|
+
Otherwise obtain the source (see below) and run
|
13
|
+
|
14
|
+
ruby install.rb
|
15
|
+
|
16
|
+
To run the unit tests you need to have test::unit installed
|
17
|
+
|
18
|
+
rake test
|
19
|
+
|
20
|
+
|
21
|
+
= Documentation
|
22
|
+
|
23
|
+
There is more than one way to access or create a zip archive with
|
24
|
+
rubyzip. The basic API is modeled after the classes in
|
25
|
+
java.util.zip from the Java SDK. This means there are classes such
|
26
|
+
as Zip::ZipInputStream, Zip::ZipOutputStream and
|
27
|
+
Zip::ZipFile. Zip::ZipInputStream provides a basic interface for
|
28
|
+
iterating through the entries in a zip archive and reading from the
|
29
|
+
entries in the same way as from a regular File or IO
|
30
|
+
object. ZipOutputStream is the corresponding basic output
|
31
|
+
facility. Zip::ZipFile provides a mean for accessing the archives
|
32
|
+
central directory and provides means for accessing any entry without
|
33
|
+
having to iterate through the archive. Unlike Java's
|
34
|
+
java.util.zip.ZipFile rubyzip's Zip::ZipFile is mutable, which means
|
35
|
+
it can be used to change zip files as well.
|
36
|
+
|
37
|
+
Another way to access a zip archive with rubyzip is to use rubyzip's
|
38
|
+
Zip::ZipFileSystem API. Using this API files can be read from and
|
39
|
+
written to the archive in much the same manner as ruby's builtin
|
40
|
+
classes allows files to be read from and written to the file system.
|
41
|
+
|
42
|
+
rubyzip also features the
|
43
|
+
zip/ziprequire.rb[link:files/lib/zip/ziprequire_rb.html] module which
|
44
|
+
allows ruby to load ruby modules from zip archives.
|
45
|
+
|
46
|
+
For details about the specific behaviour of classes and methods refer
|
47
|
+
to the test suite. Finally you can generate the rdoc documentation or
|
48
|
+
visit http://rubyzip.sourceforge.net.
|
49
|
+
|
50
|
+
= License
|
51
|
+
|
52
|
+
rubyzip is distributed under the same license as ruby. See
|
53
|
+
http://www.ruby-lang.org/en/LICENSE.txt
|
54
|
+
|
55
|
+
|
56
|
+
= Website and Project Home
|
57
|
+
|
58
|
+
http://rubyzip.sourceforge.net
|
59
|
+
|
60
|
+
http://sourceforge.net/projects/rubyzip
|
61
|
+
|
62
|
+
== Download (tarballs and gems)
|
63
|
+
|
64
|
+
http://sourceforge.net/project/showfiles.php?group_id=43107&package_id=35377
|
65
|
+
|
66
|
+
= Authors
|
67
|
+
|
68
|
+
Thomas Sondergaard (thomas at sondergaard.cc)
|
69
|
+
|
70
|
+
Technorama Ltd. (oss-ruby-zip at technorama.net)
|
71
|
+
|
72
|
+
extra-field support contributed by Tatsuki Sugiura (sugi at nemui.org)
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "ruby_archive"
|
8
|
+
gem.summary = %Q{Seamless access to ruby source and other files inside zip archives}
|
9
|
+
gem.description = %Q{Allows loading applications, libraries, and data from easily distributable archive files}
|
10
|
+
gem.email = "jonathan@jmnet.us"
|
11
|
+
gem.homepage = "http://github.com/byuni/ruby_archive"
|
12
|
+
gem.authors = ["Jonathan Nielsen"]
|
13
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "rubyarchive-pure #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/bin/rba_launch
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
# default options
|
5
|
+
options = {
|
6
|
+
:entry_point => 'start.rb'
|
7
|
+
}
|
8
|
+
|
9
|
+
# overrides in archive
|
10
|
+
options_archive = {}
|
11
|
+
|
12
|
+
# overrides by command line
|
13
|
+
options_cmdline = {}
|
14
|
+
|
15
|
+
help_banner = nil
|
16
|
+
opts = OptionParser.new do |opts|
|
17
|
+
opts.banner =
|
18
|
+
%{Usage: #{$0} [options] [archive] [arguments]
|
19
|
+
To run a specific file within an archive, use archive.zip!/ruby_file.rb
|
20
|
+
|
21
|
+
}
|
22
|
+
|
23
|
+
#opts.on("-v", "--[no-]verbose", "Verbose errors/warnings") do |v|
|
24
|
+
# options[:verbose] = v
|
25
|
+
#end
|
26
|
+
|
27
|
+
opts.on("-e", "--entry-point=FILE", String, "Force a specific default file to load within the archive") do |l|
|
28
|
+
options_cmdline[:entry_point] = l
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on_tail("-h", "--help", "Print this message") do
|
32
|
+
puts opts
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.order! # parse up to archive name
|
38
|
+
|
39
|
+
if ARGV.empty? # no archive was specified, print banner and exit
|
40
|
+
puts opts
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
require File.expand_path("../../lib/ruby_archive",__FILE__)
|
45
|
+
|
46
|
+
archive_file = File.expand_path(ARGV.shift,Dir.getwd)
|
47
|
+
file_to_load = nil
|
48
|
+
|
49
|
+
location_info = File.in_archive?(archive_file)
|
50
|
+
unless location_info == false
|
51
|
+
archive_file = location_info[0]
|
52
|
+
file_to_load = location_info[1]
|
53
|
+
end
|
54
|
+
|
55
|
+
options.merge!(options_archive)
|
56
|
+
options.merge!(options_cmdline)
|
57
|
+
# TODO: load archive specific options
|
58
|
+
|
59
|
+
file_to_load = options[:entry_point] if file_to_load.nil?
|
60
|
+
|
61
|
+
$LOAD_PATH << "#{archive_file}!/"
|
62
|
+
load("#{archive_file}!#{file_to_load}",false)
|
data/lib/ruby_archive.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path('../ruby_archive/patch.rb',__FILE__)
|
2
|
+
|
3
|
+
module RubyArchive
|
4
|
+
class Handler
|
5
|
+
# ruby_archive/handler.rb
|
6
|
+
require File.expand_path('../ruby_archive/handler.rb',__FILE__)
|
7
|
+
end
|
8
|
+
|
9
|
+
module Handlers
|
10
|
+
# ruby_archive/handlers/*.rb
|
11
|
+
# loaded at end of file
|
12
|
+
end
|
13
|
+
|
14
|
+
@@archive_handlers ||= []
|
15
|
+
# Adds an archive handler to the list used. Provided handler must be a
|
16
|
+
# subclass of +RubyArchive::Handler+
|
17
|
+
def add_handler_class handler_class
|
18
|
+
unless (handler_class.is_a? Class) && (handler_class <= RubyArchive::Handler)
|
19
|
+
raise TypeError, "#{handler_class} is not a RubyArchive::Handler"
|
20
|
+
end
|
21
|
+
@@archive_handlers << handler_class
|
22
|
+
true
|
23
|
+
end
|
24
|
+
module_function :add_handler_class
|
25
|
+
|
26
|
+
# Finds the appropriate +RubyArchive::Handler+ subclass for a given location.
|
27
|
+
# Returns nil if no supported handler found.
|
28
|
+
def find_handler_class location
|
29
|
+
@@archive_handlers.each do |h|
|
30
|
+
return h if h.handles?(location)
|
31
|
+
end
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
module_function :find_handler_class
|
35
|
+
|
36
|
+
@@loaded_archives ||= {}
|
37
|
+
# Retrieves an archive from the loaded_archives cache, or automatically
|
38
|
+
# loads the archive. Returns the archive on success. Returns nil if
|
39
|
+
# archive is not available and autoload is false. Raises +LoadError+
|
40
|
+
# if autoload is attempted and fails.
|
41
|
+
def get location, autoload=true
|
42
|
+
@@archive_handlers.each do |h|
|
43
|
+
normalized = h.normalize_path(location)
|
44
|
+
return @@loaded_archives[normalized] if @@loaded_archives.has_key?(normalized)
|
45
|
+
end
|
46
|
+
return load(location) if autoload
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
module_function :get
|
50
|
+
|
51
|
+
# Loads the specified archive location. Returns the handler object on
|
52
|
+
# success. Raises +LoadError+ if no handler can be found or it does not
|
53
|
+
# exist. May also pass exceptions passed along by creating the handler.
|
54
|
+
def load location
|
55
|
+
handler_class = find_handler_class(location)
|
56
|
+
if handler_class.nil?
|
57
|
+
raise LoadError, "No handler found or does not exist for archive -- #{location}"
|
58
|
+
end
|
59
|
+
archive = handler_class.new(location)
|
60
|
+
@@loaded_archives[archive.name] = archive
|
61
|
+
end
|
62
|
+
module_function :load
|
63
|
+
|
64
|
+
def close_all_archives
|
65
|
+
@@loaded_archives.each_value do |archive|
|
66
|
+
archive.close
|
67
|
+
end
|
68
|
+
@@loaded_archives.clear
|
69
|
+
true
|
70
|
+
end
|
71
|
+
module_function :close_all_archives
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# Set RubyArchive::close_all_archives to at_exit
|
76
|
+
at_exit { RubyArchive::close_all_archives }
|
77
|
+
|
78
|
+
# load builtin handlers
|
79
|
+
require File.expand_path('../ruby_archive/handlers/zip_handler.rb',__FILE__)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module RubyArchive
|
2
|
+
# RubyArchive::Handler
|
3
|
+
class Handler
|
4
|
+
# Should return true if the class can handle the given location as an
|
5
|
+
# archive. Returns false otherwise. The default implementation always
|
6
|
+
# returns false, this must be overridden in your subclasses.
|
7
|
+
#
|
8
|
+
# This method must NOT raise an exception.
|
9
|
+
def self.handles? location
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
# Should return a normalized version of the location specified.
|
14
|
+
# +File.expand_path+ works well in many cases, and is provided as the
|
15
|
+
# default.
|
16
|
+
def self.normalize_path location
|
17
|
+
File.expand_path(location)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Initialize a handler object for the location given. The default
|
21
|
+
# implementation raises a +NotImplementedError+.
|
22
|
+
#
|
23
|
+
# Your implementation needs to set +@name+ to identify the archive
|
24
|
+
def initialize location
|
25
|
+
raise NotImplementedError, "Cannot initialize a handler of class #{self.class}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Close the handler. This will be executed when +RubyArchive::close_all_archives+
|
29
|
+
# is executed, or at_exit
|
30
|
+
#
|
31
|
+
# Should always return nil
|
32
|
+
def close
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Should return a +File+-like object for the archive. The default
|
37
|
+
# implementation raises a +NotImplentedError+, must be overridden.
|
38
|
+
def file
|
39
|
+
raise NotImplementedError, "Cannot retrieve a file object for class #{self.class}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Should return a +Dir+-like object for the archive (optional)
|
43
|
+
# Should return +nil+ if not supported.
|
44
|
+
def dir
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# reader for +@name+, which should be set in +initialize+
|
49
|
+
attr_reader :name
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
"<#{self.class}:#{self.name}>"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|