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.
Files changed (53) hide show
  1. data/LICENSE +21 -0
  2. data/README.rdoc +55 -0
  3. data/Rakefile +24 -0
  4. data/TODO +13 -0
  5. data/bin/zinflate +17 -0
  6. data/bin//302/265gem +14 -0
  7. data/lib/microgem.rb +56 -0
  8. data/lib/microgem/bare_specification.rb +40 -0
  9. data/lib/microgem/bin_wrapper_emitter.rb +58 -0
  10. data/lib/microgem/config.rb +96 -0
  11. data/lib/microgem/dependency.rb +37 -0
  12. data/lib/microgem/downloader.rb +48 -0
  13. data/lib/microgem/installer.rb +147 -0
  14. data/lib/microgem/options_parser.rb +67 -0
  15. data/lib/microgem/requirement.rb +36 -0
  16. data/lib/microgem/source.rb +108 -0
  17. data/lib/microgem/specification.rb +65 -0
  18. data/lib/microgem/specification_emitter.rb +75 -0
  19. data/lib/microgem/unpacker.rb +55 -0
  20. data/lib/microgem/utils.rb +51 -0
  21. data/lib/microgem/version.rb +35 -0
  22. data/lib/microgem/yamlable.rb +32 -0
  23. data/test/bare_specification_test.rb +43 -0
  24. data/test/bin_wrapper_emitter_test.rb +57 -0
  25. data/test/config_test.rb +131 -0
  26. data/test/dependency_test.rb +41 -0
  27. data/test/downloader_test.rb +100 -0
  28. data/test/fixtures/gems.github.com +0 -0
  29. data/test/fixtures/gems.rubyforge.org +0 -0
  30. data/test/fixtures/gems/rake-0.8.0/rake.rb +1 -0
  31. data/test/fixtures/gems/rake-0.8.1/rake.rb +1 -0
  32. data/test/fixtures/gems/test-spec-0.3.0/test-spec.rb +1 -0
  33. data/test/fixtures/gems/test-spec-mock-0.3.0/test-spec-mock.rb +1 -0
  34. data/test/fixtures/rails-2.1.1.gemspec +26 -0
  35. data/test/fixtures/rails-2.1.1.gemspec.marshal +0 -0
  36. data/test/fixtures/rake-0.8.1.gem +0 -0
  37. data/test/fixtures/rake-0.8.1.gemspec +20 -0
  38. data/test/fixtures/rake-0.8.1.gemspec.marshal +0 -0
  39. data/test/fixtures/rake-0.8.1.gemspec.rz +2 -0
  40. data/test/fixtures/specs.4.8.gz +0 -0
  41. data/test/installer_test.rb +198 -0
  42. data/test/microgem_test.rb +34 -0
  43. data/test/options_parser_test.rb +36 -0
  44. data/test/requirement_test.rb +40 -0
  45. data/test/source_test.rb +153 -0
  46. data/test/specification_emitter_test.rb +139 -0
  47. data/test/specification_test.rb +45 -0
  48. data/test/test_helper.rb +71 -0
  49. data/test/unpacker_test.rb +91 -0
  50. data/test/utils_test.rb +42 -0
  51. data/test/version_test.rb +27 -0
  52. data/test/yamlable_test.rb +15 -0
  53. 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