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,75 @@
1
+ module Gem
2
+ module Micro
3
+ class SpecificationEmitter
4
+ SPECIAL_INSTANCE_VARIABLES = [
5
+ 'name',
6
+ 'mocha',
7
+ 'source',
8
+ 'version',
9
+ 'dependencies',
10
+ 'new_platform',
11
+ 'specification_version',
12
+ 'required_rubygems_version'
13
+ ]
14
+
15
+ def initialize(gem_spec)
16
+ @gem_spec = gem_spec
17
+ end
18
+
19
+ # Returns a list of all instance variables sorted by their keys.
20
+ #
21
+ # gem_spec.gem_spec_variables # => [["author", ["Eloy Duran"]], ["version", "0.5.2"] …]
22
+ def gem_spec_variables
23
+ @gem_spec.instance_variables.sort.map do |ivar|
24
+ key = ivar[1..-1]
25
+ next if SPECIAL_INSTANCE_VARIABLES.include?(key)
26
+ value = format(ivar_get(key))
27
+ [key, value]
28
+ end.compact
29
+ end
30
+
31
+ # Properly formats objects so they can be written to a Ruby `.gemspec' file.
32
+ def format(obj)
33
+ case obj
34
+ when Time
35
+ obj.strftime("%Y-%m-%d")
36
+ when Date, Gem::Version, Gem::Requirement
37
+ obj.to_s
38
+ else
39
+ obj
40
+ end
41
+ end
42
+
43
+ # Returns a Ruby syntax representation of the Specification which is used
44
+ # to generate the gemspec files that RubyGems uses to check for gems.
45
+ #
46
+ # TODO: We are still missing the following values, but it might not be a
47
+ # real problem. So we need to figure out if they are important to RubyGems
48
+ # and if so where it gets these values from.
49
+ #
50
+ # * files
51
+ # * extra_rdoc_files
52
+ # * rdoc_options
53
+ # * test_files
54
+ def to_ruby
55
+ %{Gem::Specification.new do |s|
56
+ s.name = "#{@gem_spec.name}"
57
+ s.version = "#{@gem_spec.version}"
58
+
59
+ s.specification_version = #{ ivar_get(:specification_version) || 2 } if s.respond_to? :specification_version=
60
+ s.required_rubygems_version = Gem::Requirement.new("#{ivar_get(:required_rubygems_version) || '>= 0'}") if s.respond_to? :required_rubygems_version=
61
+
62
+ #{gem_spec_variables.map { |k,v| " s.#{k} = #{v.inspect}" }.join("\n") }
63
+
64
+ #{@gem_spec.dependencies.map { |dep| " s.add_dependency(\"#{dep.name}\", [\"#{dep.version_requirements.to_s}\"])" }.join("\n") }
65
+ end}
66
+ end
67
+
68
+ private
69
+
70
+ def ivar_get(name)
71
+ @gem_spec.instance_variable_get("@#{name}")
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,55 @@
1
+ module Gem
2
+ module Micro
3
+ class Unpacker
4
+ class UnpackError < StandardError; end
5
+
6
+ extend Utils
7
+
8
+ # Unpacks a tar +archive+ to +extract_to+ using the +tar+ commandline
9
+ # utility. If +gzip+ is +true+ the archive is expected to be a gzip
10
+ # archive and will be decompressed.
11
+ #
12
+ # Raises a Gem::Micro::Unpacker::UnpackError if it fails.
13
+ def self.tar(archive, extract_to, gzip = false)
14
+ log(:debug, "Unpacking `#{archive}' to `#{extract_to}'")
15
+ ensure_dir(extract_to)
16
+ unless system("/usr/bin/tar --directory='#{extract_to}' -#{ 'z' if gzip }xf '#{archive}' > /dev/null 2>&1")
17
+ raise UnpackError, "Failed to unpack `#{archive}' with `tar'"
18
+ end
19
+ end
20
+
21
+ # Unpacks a gzip +archive+ _in_ place using the +gunzip+ commandline
22
+ # utility.
23
+ #
24
+ # Raises a Gem::Micro::Unpacker::UnpackError if it fails.
25
+ #
26
+ # TODO: Make it work _not_ in place.
27
+ def self.gzip(archive)
28
+ unless system("/usr/bin/gunzip --quiet --force --decompress '#{archive}' > /dev/null 2>&1")
29
+ raise UnpackError, "Failed to unpack `#{archive}' with `gunzip'"
30
+ end
31
+ end
32
+
33
+ def self.inflate(archive, out_file)
34
+ if Config.simple_unpacker?
35
+ inflate_with_zinflate(archive, out_file)
36
+ else
37
+ inflate_with_zlib(archive, out_file)
38
+ end
39
+ end
40
+
41
+ def self.inflate_with_zlib(archive, out_file)
42
+ require "zlib"
43
+ out = Zlib::Inflate.inflate(File.read(archive))
44
+ File.open(out_file, 'w') { |f| f << out }
45
+ end
46
+
47
+ ZINFLATE_BIN = File.expand_path('../../../bin/zinflate', __FILE__)
48
+ def self.inflate_with_zinflate(archive, out_file)
49
+ unless system("ruby '#{ZINFLATE_BIN}' '#{archive}' '#{out_file}' > /dev/null 2>&1")
50
+ raise UnpackError, "Failed to unpack `#{archive}' with `zinflate'"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,51 @@
1
+ require 'fileutils'
2
+ require 'tmpdir'
3
+
4
+ module Gem
5
+ module Micro
6
+ module Utils
7
+ extend self
8
+
9
+ # Returns the Config.
10
+ def config
11
+ Config
12
+ end
13
+
14
+ # Prints a +message+ to +stdout+ with the specified log +level+.
15
+ #
16
+ # TODO: Add proper Logger.
17
+ def log(level, message)
18
+ unless level == :debug && config.log_level != :debug
19
+ puts "[#{level}] #{message}"
20
+ end
21
+ end
22
+
23
+ # Creates a directory if it does not yet exist and it.
24
+ def ensure_dir(dir)
25
+ unless File.exist?(dir)
26
+ log(:debug, "Creating directory `#{dir}'")
27
+ FileUtils.mkdir_p(dir)
28
+ end
29
+ dir
30
+ end
31
+
32
+ # Removes +new_path+, if it exists, before moving +old_path+ to +to+.
33
+ def replace(old_path, new_path)
34
+ log(:debug, "Moving `#{old_path}' to `#{new_path}'")
35
+ FileUtils.rm_rf(new_path) if File.exist?(new_path)
36
+ FileUtils.mv(old_path, new_path)
37
+ end
38
+
39
+ # Returns a temporary directory and ensures it exists.
40
+ #
41
+ # File.exist?('/path/to/tmp/microgem') # => false
42
+ # tmpdir # => '/path/to/tmp/microgem'
43
+ # File.exist?('/path/to/tmp/microgem') # => true
44
+ def tmpdir
45
+ tmpdir = File.join(Dir.tmpdir, 'microgem')
46
+ ensure_dir(tmpdir)
47
+ tmpdir
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,35 @@
1
+ module Gem
2
+ class Version < Micro::YAMLable
3
+ class Requirement < Micro::YAMLable
4
+ end
5
+
6
+ # Returns a Gem::Version from a string:
7
+ #
8
+ # Gem::Version.from_gem_dirname('rake-0.8.1').version # => "0.8.1"
9
+ def self.from_gem_dirname(dirname)
10
+ if dirname =~ /-([\d\.]+)$/
11
+ self[:version => $1]
12
+ end
13
+ end
14
+
15
+ attr_reader :version
16
+ alias_method :to_s, :version
17
+
18
+ include Comparable
19
+
20
+ # Compare two Version instances.
21
+ def <=>(other)
22
+ version <=> other.version
23
+ end
24
+
25
+ # Returns whether or not any version will do.
26
+ def any?
27
+ version == 0 || version == '0'
28
+ end
29
+
30
+ # TODO: really test this method, added for utils test
31
+ def marshal_load(version)
32
+ @version = version.first
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ require 'yaml'
2
+
3
+ module Gem
4
+ module Micro
5
+ # Mainly a MacRuby workaround, because yaml transfer is broken.
6
+ class YAMLable
7
+ class << self
8
+ def inherited(subklass) #:nodoc:
9
+ super
10
+
11
+ subklass.class_eval do
12
+ yaml_as "tag:ruby.yaml.org,2002:object:#{name}"
13
+
14
+ def self.yaml_new(klass, tag, values)
15
+ self[values]
16
+ end
17
+ end
18
+ end
19
+
20
+ # Initializes an instance and assigns the hash of +values+ as its
21
+ # instance variables.
22
+ #
23
+ # YAMLable[:foo => 'foo'] # => <#Gem::Micro::YAMLable:0x320154 @foo="foo">
24
+ def [](values)
25
+ object = new
26
+ values.each { |k,v| object.send(:instance_variable_set, "@#{k}", v) }
27
+ object
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ ##!/usr/bin/env macruby
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe "Gem::Micro::BareSpecification" do
6
+ include Gem::Micro::Utils
7
+
8
+ def setup
9
+ @source = Gem::Micro::Source.new('gems.rubyforge.org', @dir)
10
+ @spec = Gem::Micro::BareSpecification.new(@source, 'rake', Gem::Version[:version => '0.8.1'])
11
+ end
12
+
13
+ def teardown
14
+ remove_microgem_tmpdir!
15
+ end
16
+
17
+ it "should return the url to a gemspec" do
18
+ @spec.gem_spec_url.should ==
19
+ "http://gems.rubyforge.org/quick/Marshal.4.8/rake-0.8.1.gemspec.rz"
20
+ end
21
+
22
+ it "should return the path to the work gemspec file" do
23
+ @spec.gem_spec_work_file.should ==
24
+ File.join(tmpdir, 'rake-0.8.1.gemspec')
25
+ end
26
+
27
+ it "should return the path to the work archive of the gemspec file" do
28
+ @spec.gem_spec_work_archive_file.should ==
29
+ File.join(tmpdir, 'rake-0.8.1.gemspec.rz')
30
+ end
31
+
32
+ it "should return its gem spec and know from which source it came" do
33
+ Gem::Micro::Downloader.expects(:get).with(@spec.gem_spec_url, @spec.gem_spec_work_archive_file)
34
+ FileUtils.cp(fixture('rake-0.8.1.gemspec.rz'), tmpdir)
35
+
36
+ gem_spec = @spec.gem_spec
37
+ gem_spec.should.be.instance_of Gem::Specification
38
+ gem_spec.name.should == 'rake'
39
+ gem_spec.version.should == Gem::Version[:version => '0.8.1']
40
+ gem_spec.source.should == @source
41
+ end
42
+
43
+ end
@@ -0,0 +1,57 @@
1
+ ##!/usr/bin/env macruby
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe "Gem::Micro::BinWrapperEmitter, in general" do
6
+ def setup
7
+ @emitter = Gem::Micro::BinWrapperEmitter.new('rake', 'rake')
8
+ end
9
+
10
+ def teardown
11
+ remove_microgem_tmpdir!
12
+ end
13
+
14
+ it "should return the path to where the bin wrapper should be created" do
15
+ @emitter.bin_wrapper_file.should == File.join(Gem::Micro::Config.bin_dir, 'rake')
16
+ end
17
+
18
+ it "should return the path to where the bin wrapper should be created if there's a program prefix" do
19
+ def @emitter.rbconfig(name)
20
+ { 'ruby_install_name' => 'macruby' }[name]
21
+ end
22
+
23
+ @emitter.bin_wrapper_file.should == File.join(Gem::Micro::Config.bin_dir, 'macrake')
24
+ end
25
+
26
+ it "should return the path to where the bin wrapper should be created if there's a program suffix" do
27
+ def @emitter.rbconfig(name)
28
+ { 'ruby_install_name' => 'ruby19' }[name]
29
+ end
30
+
31
+ @emitter.bin_wrapper_file.should == File.join(Gem::Micro::Config.bin_dir, 'rake19')
32
+ end
33
+
34
+ it "should create an executable bin wrapper" do
35
+ bin_file = File.join(Gem::Micro::Utils.tmpdir, 'rake')
36
+ @emitter.stubs(:bin_wrapper_file).returns(bin_file)
37
+
38
+ @emitter.create_bin_wrapper!
39
+ File.read(bin_file).should == @emitter.to_ruby
40
+ File.should.be.executable bin_file
41
+ end
42
+ end
43
+
44
+ describe "Gem::Micro::BinWrapperEmitter, when emitting a wrapper" do
45
+ def setup
46
+ @emitter = Gem::Micro::BinWrapperEmitter.new('rake', 'raketest')
47
+ end
48
+
49
+ it "should have a shebang pointing to the current Ruby being used" do
50
+ @emitter.to_ruby.split("\n").first.should ==
51
+ "#!#{ File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) }"
52
+ end
53
+
54
+ it "should load the correct gem and bin file" do
55
+ @emitter.to_ruby.should.match /\ngem 'rake', version\nload 'raketest'/
56
+ end
57
+ end
@@ -0,0 +1,131 @@
1
+ ##!/usr/bin/env macruby
2
+
3
+ require File.expand_path('../test_helper', __FILE__)
4
+
5
+ describe "Gem::Micro::Config" do
6
+ include Gem::Micro::Utils
7
+
8
+ def setup
9
+ config.instance_variable_set(:@gem_home, nil)
10
+ config.instance_variable_set(:@bin_dir, nil)
11
+ ENV.delete('PRODUCTION')
12
+ end
13
+
14
+ def teardown
15
+ config.merge! :log_level => :info, :force => false, :simple_downloader => false, :simple_unpacker => false
16
+ end
17
+
18
+ it "should return the path to the development gem_home in development mode" do
19
+ path = File.expand_path("../../tmp/gem_home", __FILE__)
20
+
21
+ config.expects(:ensure_dir).with(path).returns(path)
22
+ config.gem_home.should == path
23
+ end
24
+
25
+ it "should return the path to the real rubygems gem_home in production mode" do
26
+ ENV['PRODUCTION'] = 'true'
27
+ path = rubygems_gem_paths.first
28
+
29
+ config.expects(:ensure_dir).with(path).returns(path)
30
+ config.gem_home.should == path
31
+ end
32
+
33
+ it "should return the path to the bin dir in development mode" do
34
+ path = File.expand_path("../../tmp/bin", __FILE__)
35
+
36
+ config.expects(:ensure_dir).with(path).returns(path)
37
+ config.bin_dir.should == path
38
+ end
39
+
40
+ it "should return the path to the bin dir in production mode on a regular Ruby install" do
41
+ ENV['PRODUCTION'] = 'true'
42
+
43
+ config.stubs(:macruby?).returns(false)
44
+ config.stubs(:osx_default_ruby?).returns(false)
45
+
46
+ path = Config::CONFIG['bindir']
47
+
48
+ config.expects(:ensure_dir).never
49
+ config.bin_dir.should == path
50
+ end
51
+
52
+ it "should return the path to the bin dir in production mode on a default Apple Ruby install" do
53
+ ENV['PRODUCTION'] = 'true'
54
+
55
+ config.stubs(:macruby?).returns(false)
56
+ config.stubs(:osx_default_ruby?).returns(true)
57
+
58
+ path = "/usr/bin"
59
+
60
+ config.expects(:ensure_dir).never
61
+ config.bin_dir.should == path
62
+ end
63
+
64
+ it "should return the path to the bin dir in production mode on MacRuby install" do
65
+ ENV['PRODUCTION'] = 'true'
66
+
67
+ config.stubs(:macruby?).returns(true)
68
+ config.stubs(:osx_default_ruby?).returns(true)
69
+
70
+ path = "/usr/local/bin"
71
+
72
+ config.expects(:ensure_dir).never
73
+ config.bin_dir.should == path
74
+ end
75
+
76
+ it "should return a list of the available sources" do
77
+ config.sources.should == %w{ gems.rubyforge.org gems.github.com }
78
+ end
79
+
80
+ it "should return the path to the gems path" do
81
+ path = File.join(config.gem_home, 'gems')
82
+
83
+ config.expects(:ensure_dir).with(path).returns(path)
84
+ config.gems_path.should == path
85
+ end
86
+
87
+ it "should return the path to the specifications path" do
88
+ path = File.join(config.gem_home, 'specifications')
89
+
90
+ config.expects(:ensure_dir).with(path).returns(path)
91
+ config.specifications_path.should == path
92
+ end
93
+
94
+ it "should return the path to the gem cache path" do
95
+ path = File.join(config.gem_home, 'cache')
96
+
97
+ config.expects(:ensure_dir).with(path).returns(path)
98
+ config.cache_path.should == path
99
+ end
100
+
101
+ it "should return the log_level" do
102
+ config.log_level.should == :info
103
+ end
104
+
105
+ it "should return whether or not to force actions" do
106
+ assert !config.force?
107
+ end
108
+
109
+ it "should return whether or not to use the simple (external) downloader" do
110
+ assert !config.simple_downloader?
111
+ end
112
+
113
+ it "should return whether or not to use the simple (external) unpacker" do
114
+ assert !config.simple_unpacker?
115
+ end
116
+
117
+ it "should merge a hash of config values" do
118
+ config.merge! :log_level => :debug, :force => true, :simple_downloader => true, :simple_unpacker => true
119
+
120
+ config.log_level.should == :debug
121
+ assert config.force?
122
+ assert config.simple_downloader?
123
+ assert config.simple_unpacker?
124
+ end
125
+
126
+ private
127
+
128
+ def rubygems_gem_paths
129
+ YAML.load(`gem env`)["RubyGems Environment"].find { |h| h.has_key? 'GEM PATHS' }['GEM PATHS']
130
+ end
131
+ end