alloy-microgem 0.1
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/LICENSE +21 -0
- data/README.rdoc +55 -0
- data/Rakefile +24 -0
- data/TODO +13 -0
- data/bin/zinflate +17 -0
- data/bin//302/265gem +14 -0
- data/lib/microgem.rb +56 -0
- data/lib/microgem/bare_specification.rb +40 -0
- data/lib/microgem/bin_wrapper_emitter.rb +58 -0
- data/lib/microgem/config.rb +96 -0
- data/lib/microgem/dependency.rb +37 -0
- data/lib/microgem/downloader.rb +48 -0
- data/lib/microgem/installer.rb +147 -0
- data/lib/microgem/options_parser.rb +67 -0
- data/lib/microgem/requirement.rb +36 -0
- data/lib/microgem/source.rb +108 -0
- data/lib/microgem/specification.rb +65 -0
- data/lib/microgem/specification_emitter.rb +75 -0
- data/lib/microgem/unpacker.rb +55 -0
- data/lib/microgem/utils.rb +51 -0
- data/lib/microgem/version.rb +35 -0
- data/lib/microgem/yamlable.rb +32 -0
- data/test/bare_specification_test.rb +43 -0
- data/test/bin_wrapper_emitter_test.rb +57 -0
- data/test/config_test.rb +131 -0
- data/test/dependency_test.rb +41 -0
- data/test/downloader_test.rb +100 -0
- data/test/fixtures/gems.github.com +0 -0
- data/test/fixtures/gems.rubyforge.org +0 -0
- data/test/fixtures/gems/rake-0.8.0/rake.rb +1 -0
- data/test/fixtures/gems/rake-0.8.1/rake.rb +1 -0
- data/test/fixtures/gems/test-spec-0.3.0/test-spec.rb +1 -0
- data/test/fixtures/gems/test-spec-mock-0.3.0/test-spec-mock.rb +1 -0
- data/test/fixtures/rails-2.1.1.gemspec +26 -0
- data/test/fixtures/rails-2.1.1.gemspec.marshal +0 -0
- data/test/fixtures/rake-0.8.1.gem +0 -0
- data/test/fixtures/rake-0.8.1.gemspec +20 -0
- data/test/fixtures/rake-0.8.1.gemspec.marshal +0 -0
- data/test/fixtures/rake-0.8.1.gemspec.rz +2 -0
- data/test/fixtures/specs.4.8.gz +0 -0
- data/test/installer_test.rb +198 -0
- data/test/microgem_test.rb +34 -0
- data/test/options_parser_test.rb +36 -0
- data/test/requirement_test.rb +40 -0
- data/test/source_test.rb +153 -0
- data/test/specification_emitter_test.rb +139 -0
- data/test/specification_test.rb +45 -0
- data/test/test_helper.rb +71 -0
- data/test/unpacker_test.rb +91 -0
- data/test/utils_test.rb +42 -0
- data/test/version_test.rb +27 -0
- data/test/yamlable_test.rb +15 -0
- metadata +114 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Gem
|
4
|
+
module Micro
|
5
|
+
class Downloader
|
6
|
+
class DownloadError < StandardError; end
|
7
|
+
|
8
|
+
def self.get_with_curl(remote, local)
|
9
|
+
unless system("#{curl_binary} --silent --location --output '#{local}' '#{remote}'")
|
10
|
+
raise DownloadError, "Failed to download `#{remote}' to `#{local}'"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.get_with_net_http(remote, local)
|
15
|
+
require 'net/http'
|
16
|
+
require 'uri'
|
17
|
+
|
18
|
+
uri = URI.parse(remote)
|
19
|
+
response = Net::HTTP.start(uri.host, uri.port) do |http|
|
20
|
+
http.get(uri.path)
|
21
|
+
end
|
22
|
+
|
23
|
+
case response.code
|
24
|
+
when '200'
|
25
|
+
FileUtils.mkdir_p(File.dirname(local))
|
26
|
+
File.open(local, 'w') { |file| file.write(response.body) }
|
27
|
+
response
|
28
|
+
when '302'
|
29
|
+
get_with_net_http(response.header['Location'], local)
|
30
|
+
else
|
31
|
+
raise DownloadError, "Failed to download `#{remote}' to `#{local}' (#{response.code})"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.get(remote, local)
|
36
|
+
if Config.simple_downloader?
|
37
|
+
get_with_curl(remote, local)
|
38
|
+
else
|
39
|
+
get_with_net_http(remote, local)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.curl_binary
|
44
|
+
'/usr/bin/curl'
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Gem
|
2
|
+
module Micro
|
3
|
+
class Installer
|
4
|
+
include Utils
|
5
|
+
|
6
|
+
attr_reader :gem_spec
|
7
|
+
|
8
|
+
def initialize(gem_spec)
|
9
|
+
@gem_spec = gem_spec
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the download url for the gem.
|
13
|
+
#
|
14
|
+
# installer.url # => "http://gems.rubyforge.org/gems/rake-0.8.1.gem"
|
15
|
+
def url
|
16
|
+
"http://#{File.join(@gem_spec.source.host, 'gems', @gem_spec.gem_filename)}"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the full path to the temporary gem directory.
|
20
|
+
#
|
21
|
+
# installer.work_dir # => "/path/to/tmp/microgem/rake-0.8.1"
|
22
|
+
def work_dir
|
23
|
+
File.join(tmpdir, @gem_spec.gem_dirname)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the full path to the gem in the temporary directory.
|
27
|
+
#
|
28
|
+
# installer.gem_file # => "/path/to/tmp/microgem/rake-0.8.1.gem"
|
29
|
+
def gem_file
|
30
|
+
File.join(tmpdir, @gem_spec.gem_filename)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns the full path to the gems data directory in the temporary
|
34
|
+
# directory.
|
35
|
+
#
|
36
|
+
# installer.data_dir # => "/path/to/tmp/microgem/rake-0.8.1/data"
|
37
|
+
def data_dir
|
38
|
+
File.join(work_dir, 'data')
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns the full path to the gems data archive in the temporary
|
42
|
+
# directory.
|
43
|
+
#
|
44
|
+
# installer.data_file # => "/path/to/tmp/microgem/rake-0.8.1/data.tar.gz"
|
45
|
+
def data_file
|
46
|
+
"#{data_dir}.tar.gz"
|
47
|
+
end
|
48
|
+
|
49
|
+
def metadata_file
|
50
|
+
File.join(work_dir, 'metadata')
|
51
|
+
end
|
52
|
+
|
53
|
+
def metadata_archive_file
|
54
|
+
"#{metadata_file}.gz"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the full path to the gems Ruby `.gemspec' file. This file is
|
58
|
+
# needed by RubyGems to find the gem.
|
59
|
+
def ruby_gemspec_file
|
60
|
+
ensure_dir Config.specifications_path
|
61
|
+
File.join(Config.specifications_path, "#{@gem_spec.gem_dirname}.gemspec")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the full path to the RubyGems gem file cache directory.
|
65
|
+
def gem_cache_file
|
66
|
+
ensure_dir Config.cache_path
|
67
|
+
File.join(Config.cache_path, @gem_spec.gem_filename)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the path to where the gem should be installed.
|
71
|
+
#
|
72
|
+
# installer.install_path # => "/usr/local/lib/ruby/gems/1.8/gems/rake-0.8.1"
|
73
|
+
def install_path
|
74
|
+
File.join(Config.gems_path, @gem_spec.gem_dirname)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Downloads the gem to gem_file.
|
78
|
+
#
|
79
|
+
# Raises a Gem::Micro::Downloader::DownloadError if downloading fails.
|
80
|
+
def download
|
81
|
+
Downloader.get(url, gem_file)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Unpacks the gem to work_dir.
|
85
|
+
#
|
86
|
+
# Raises a Gem::Micro::Installer::UnpackError if unpacking fails.
|
87
|
+
def unpack
|
88
|
+
Unpacker.tar(gem_file, work_dir, false)
|
89
|
+
Unpacker.tar(data_file, data_dir, true)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Loads the full metadata gemspec from the unpacked gem and returns the
|
93
|
+
# Gem::Specification instance.
|
94
|
+
def load_full_spec!
|
95
|
+
Unpacker.gzip(metadata_archive_file)
|
96
|
+
@gem_spec = YAML.load(File.read(metadata_file))
|
97
|
+
end
|
98
|
+
|
99
|
+
# Creates the Ruby `.gemspec' used by RubyGems to find a gem at
|
100
|
+
# ruby_gemspec_file.
|
101
|
+
def create_ruby_gemspec!
|
102
|
+
log(:debug, "Creating gem spec file `#{ruby_gemspec_file}'")
|
103
|
+
File.open(ruby_gemspec_file, 'w') { |f| f << @gem_spec.to_ruby }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Creates the executables for this gem in the Ruby bin dir.
|
107
|
+
def create_bin_wrappers!
|
108
|
+
@gem_spec.executables.each { |bin| BinWrapperEmitter.new(@gem_spec.name, bin).create_bin_wrapper! }
|
109
|
+
end
|
110
|
+
|
111
|
+
# Installs all dependencies and then the gem itself. Skips installation
|
112
|
+
# if after installing the dependencies the gem is already installed.
|
113
|
+
#
|
114
|
+
# You can force the gem to be installed even if the gem is already
|
115
|
+
# installed by setting <tt>Config.force?</tt> to true.
|
116
|
+
def install!
|
117
|
+
install_dependencies!
|
118
|
+
|
119
|
+
if !Config.force? && File.exist?(install_path)
|
120
|
+
log(:debug, "Already installed `#{@gem_spec}'")
|
121
|
+
else
|
122
|
+
log(:info, "Installing `#{@gem_spec}'")
|
123
|
+
download
|
124
|
+
unpack
|
125
|
+
|
126
|
+
load_full_spec!
|
127
|
+
|
128
|
+
replace(data_dir, install_path)
|
129
|
+
replace(gem_file, gem_cache_file)
|
130
|
+
|
131
|
+
create_bin_wrappers!
|
132
|
+
create_ruby_gemspec!
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def install_dependencies!
|
139
|
+
log(:debug, "Checking dependencies of `#{@gem_spec}'")
|
140
|
+
@gem_spec.dependencies.each do |dep|
|
141
|
+
log(:debug, "Checking dependency requirements of `#{dep}'")
|
142
|
+
dep.gem_spec.install! unless dep.meets_requirements?
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Gem
|
4
|
+
module Micro
|
5
|
+
class OptionsParser
|
6
|
+
attr_accessor :command, :arguments, :options
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@options = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def parser
|
13
|
+
@parser ||= OptionParser.new do |opts|
|
14
|
+
opts.banner = "Microgem is an unsophisticated package manager for Ruby."
|
15
|
+
opts.separator "And the first commandline utility to start with a multibyte character; µ"
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator " Usage:"
|
18
|
+
opts.separator " µgem [command] [arguments…] [options…]"
|
19
|
+
opts.separator ""
|
20
|
+
opts.separator " Example:"
|
21
|
+
opts.separator " µgem install rake"
|
22
|
+
opts.separator " µgem install rails --force"
|
23
|
+
opts.separator " µgem cache update --debug"
|
24
|
+
opts.separator ""
|
25
|
+
opts.separator " Options:"
|
26
|
+
|
27
|
+
opts.on("--debug", "Raises the log level to `debug'") do
|
28
|
+
@options[:log_level] = :debug
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("--force", "Forces a command") do
|
32
|
+
@options[:force] = true
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("--simple-downloader", "Use curl to download files instead of Net::HTTP") do
|
36
|
+
@options[:simple_downloader] = true
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--simple-unpacker", "Use external tools to unpack archives instead of Zlib") do
|
40
|
+
@options[:simple_unpacker] = true
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("--simple", "Enables --simple-downloader and --simple-unpacker") do
|
44
|
+
@options[:simple_downloader] = @options[:simple_unpacker] = true
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--help", "Show help information") do
|
48
|
+
puts opts
|
49
|
+
exit
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def banner
|
56
|
+
parser.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse(arguments)
|
60
|
+
parser.parse! arguments
|
61
|
+
self.command = arguments.shift
|
62
|
+
self.arguments = arguments
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Gem
|
2
|
+
class Requirement < Micro::YAMLable
|
3
|
+
attr_reader :requirements
|
4
|
+
|
5
|
+
def ==(other)
|
6
|
+
@requirements == other.requirements
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns the operator for this the highest version.
|
10
|
+
#
|
11
|
+
# requirement.operator # => ">="
|
12
|
+
def operator
|
13
|
+
@requirements.first.first
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the version that's required at minimum.
|
17
|
+
#
|
18
|
+
# TODO: Haven't actually encountered the case where there are multiple
|
19
|
+
# required version yet, so that's not implemented.
|
20
|
+
def version
|
21
|
+
@requirements.first.last
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns a ‘pretty’ string representation of the Requirement instance:
|
25
|
+
#
|
26
|
+
# requirement.to_s # => ">= 0.8.1"
|
27
|
+
def to_s
|
28
|
+
"#{operator} #{version}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO: really test this method
|
32
|
+
def marshal_load(requirements)
|
33
|
+
@requirements = requirements.first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Gem
|
2
|
+
module Micro
|
3
|
+
class Source
|
4
|
+
# Returns an array of available Source instances as specified on
|
5
|
+
# Gem::Micro::Config.instance.
|
6
|
+
def self.sources
|
7
|
+
@sources ||= Config.sources.map do |source|
|
8
|
+
new(source, Config.gem_home)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns a gemspec from any of the sources matching the given +name+ and
|
13
|
+
# +version+.
|
14
|
+
def self.gem_spec(name, version)
|
15
|
+
sources.each do |source|
|
16
|
+
if spec = source.gem_spec(name, version)
|
17
|
+
return spec
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Calls #update! on all sources.
|
23
|
+
def self.update!
|
24
|
+
sources.each { |source| source.update! }
|
25
|
+
end
|
26
|
+
|
27
|
+
include Utils
|
28
|
+
|
29
|
+
SPECS_FILE = "specs.4.8"
|
30
|
+
SPECS_ARCHIVE_FILE = "#{SPECS_FILE}.gz"
|
31
|
+
|
32
|
+
attr_reader :host
|
33
|
+
|
34
|
+
# Initializes a Source with +host+ and the +directory+ where the index
|
35
|
+
# should be.
|
36
|
+
#
|
37
|
+
# s = Source.new('gems.rubyforge.org', '/path/to/gem/indices/')
|
38
|
+
# s.specs_url # => "http://gems.rubyforge.org/specs.4.8.gz"
|
39
|
+
# s.index_file # => "/path/to/indices/gems.rubyforge.org"
|
40
|
+
def initialize(host, directory)
|
41
|
+
@host, @directory = host, directory
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns the full path to the index file.
|
45
|
+
def index_file
|
46
|
+
File.join(@directory, @host)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns the full path to the temporary work directory to where the
|
50
|
+
# index should be unpacked.
|
51
|
+
def work_index_file
|
52
|
+
File.join(tmpdir, SPECS_FILE)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the full path to the temporary work directory to where the
|
56
|
+
# index archive should be downloaded.
|
57
|
+
def work_archive_file
|
58
|
+
File.join(tmpdir, SPECS_ARCHIVE_FILE)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the url for the complete marshalled list of specs.
|
62
|
+
def specs_url
|
63
|
+
"http://#{@host}/#{SPECS_ARCHIVE_FILE}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns whether or not the index exists at index_file.
|
67
|
+
def exist?
|
68
|
+
File.exist?(index_file)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Downloads and unpacks a index file to index_file.
|
72
|
+
def update!
|
73
|
+
log(:info, "Updating source: #{@host}")
|
74
|
+
Downloader.get(specs_url, work_archive_file)
|
75
|
+
Unpacker.gzip(work_archive_file)
|
76
|
+
FileUtils.mv(work_index_file, index_file)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Loads and returns an array of all gem names and versions.
|
80
|
+
#
|
81
|
+
# Creates an index with update! if the index doesn't exist yet.
|
82
|
+
def specs
|
83
|
+
unless @specs
|
84
|
+
update! unless exist?
|
85
|
+
@specs = Marshal.load(File.read(index_file))
|
86
|
+
end
|
87
|
+
@specs
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a gem name and it's version matching the given +name+ and
|
91
|
+
# +version+.
|
92
|
+
def spec(name, version)
|
93
|
+
specs.select { |spec| spec[0] == name && (version.any? || spec[1] == version) }.last
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns a Gem::Specification matching the given +name+ and +version+.
|
97
|
+
def gem_spec(name, version)
|
98
|
+
if spec = spec(name, version)
|
99
|
+
BareSpecification.new(self, name, spec[1]).gem_spec
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def inspect
|
104
|
+
"<#<Gem::Micro::Source:#{object_id} host=\"#{@host}\" index=\"#{index_file}\">"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Gem
|
2
|
+
class Specification < Micro::YAMLable
|
3
|
+
attr_reader :name, :version, :dependencies, :executables
|
4
|
+
|
5
|
+
# The Gem::Micro::Source from which this Specification originates.
|
6
|
+
attr_accessor :source
|
7
|
+
|
8
|
+
# Returns the Specification's dirname:
|
9
|
+
#
|
10
|
+
# rake.gem_dirname # => "rake-0.8.1"
|
11
|
+
def gem_dirname
|
12
|
+
"#{name}-#{version}"
|
13
|
+
end
|
14
|
+
alias_method :to_s, :gem_dirname
|
15
|
+
|
16
|
+
# Returns the Specification's filename:
|
17
|
+
#
|
18
|
+
# rake.gem_filename # => "rake-0.8.1.gem"
|
19
|
+
def gem_filename
|
20
|
+
"#{gem_dirname}.gem"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Installs the gem for this Specification.
|
24
|
+
def install!
|
25
|
+
Micro::Installer.new(self).install!
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
"#<Gem::Specification:#{object_id} name=\"#{name}\" version=\"#{version}\">"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a Ruby syntax representation of the Specification which is used
|
33
|
+
# to generate the gemspec files that RubyGems uses to check for gems.
|
34
|
+
def to_ruby
|
35
|
+
Micro::SpecificationEmitter.new(self).to_ruby
|
36
|
+
end
|
37
|
+
|
38
|
+
# TODO: For marshal, need to actually fix this, copied array elements code from RubyGems, cleanup
|
39
|
+
def self._load(val)
|
40
|
+
array = Marshal.load(val)
|
41
|
+
spec = new
|
42
|
+
|
43
|
+
spec.instance_variable_set :@rubygems_version, array[0]
|
44
|
+
# spec version
|
45
|
+
spec.instance_variable_set :@name, array[2]
|
46
|
+
spec.instance_variable_set :@version, array[3]
|
47
|
+
spec.instance_variable_set :@date, array[4]
|
48
|
+
spec.instance_variable_set :@summary, array[5]
|
49
|
+
spec.instance_variable_set :@required_ruby_version, array[6]
|
50
|
+
spec.instance_variable_set :@required_rubygems_version, array[7]
|
51
|
+
spec.instance_variable_set :@original_platform, array[8]
|
52
|
+
spec.instance_variable_set :@dependencies, array[9]
|
53
|
+
spec.instance_variable_set :@rubyforge_project, array[10]
|
54
|
+
spec.instance_variable_set :@email, array[11]
|
55
|
+
spec.instance_variable_set :@authors, array[12]
|
56
|
+
spec.instance_variable_set :@description, array[13]
|
57
|
+
spec.instance_variable_set :@homepage, array[14]
|
58
|
+
spec.instance_variable_set :@has_rdoc, array[15]
|
59
|
+
spec.instance_variable_set :@new_platform, array[16]
|
60
|
+
spec.instance_variable_set :@platform, array[16].to_s
|
61
|
+
|
62
|
+
spec
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|