alloy-microgem 0.1

Sign up to get free protection for your applications and to get access to all the features.
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
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) <2009> Eloy Duran <eloy.de.enige@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,55 @@
1
+ == MicroGem
2
+ The aim of this project is, at first, to create a re-implementation of the
3
+ RubyGems +install+ command, which should be easier to get up and running on
4
+ Ruby implementations such as MacRuby.
5
+
6
+ See the TODO file for a list of todo's.
7
+
8
+ === Installation
9
+
10
+ $ sudo gem install alloy-microgem -s http://gems.github.com
11
+
12
+ === µgem banner
13
+ Microgem is an unsophisticated package manager for Ruby.
14
+ And the first commandline utility to start with a multibyte character; µ
15
+
16
+ Usage:
17
+ µgem [command] [arguments…] [options…]
18
+
19
+ Example:
20
+ µgem install rake
21
+ µgem install rails --force
22
+ µgem cache update --debug
23
+
24
+ Options:
25
+ --debug Raises the log level to `debug'
26
+ --force Forces a command
27
+ --simple-downloader Use curl to download files instead of Net::HTTP
28
+ --simple-unpacker Use external tools to unpack archives instead of Zlib
29
+ --simple Enables --simple-downloader and --simple-unpacker
30
+ --help Show help information
31
+
32
+ === Development
33
+ The way it's being developed is in a test and RubyGems data driven manner.
34
+ We are using the `quick' marshalled specs, so we use those fixtures to run the tests.
35
+
36
+ ==== Getting started
37
+
38
+ Get the source:
39
+
40
+ $ git clone git://github.com/alloy/microgem.git
41
+
42
+ Install a gem:
43
+
44
+ $ ./bin/µgem install rake
45
+
46
+ Note that unless you set the PRODUCTION environment variable everything is installed in ./tmp.
47
+
48
+ The current default sources are rubyforge and github.
49
+
50
+ === Goals
51
+ * Install gems.
52
+ * Small.
53
+ * Test driven.
54
+ * Clean room design.
55
+ * Naive, no more code than necessary should be written. (YAGNI)
data/Rakefile ADDED
@@ -0,0 +1,24 @@
1
+ require 'rake/testtask'
2
+
3
+ HOME = File.expand_path('../tmp/gem_home', __FILE__)
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.test_files = FileList['test/*_test.rb']
7
+ end
8
+
9
+ task :default => :test
10
+
11
+ desc "Cleans the temporary development directory"
12
+ task :clean do
13
+ rm_rf 'tmp'
14
+ rm_rf 'pkg'
15
+ end
16
+
17
+ directory 'pkg'
18
+
19
+ desc "Builds and installs the gem"
20
+ task :install => :pkg do
21
+ # TODO: install gem with µgem, this is why we don't use the rake gem tasks.
22
+ sh 'gem build microgem.gemspec && mv microgem-*.gem pkg/'
23
+ sh 'cd pkg && sudo gem install microgem-*.gem'
24
+ end
data/TODO ADDED
@@ -0,0 +1,13 @@
1
+ === TODO (in order of priority)
2
+ * Add support for <tt>~/.gemrc</tt>
3
+ * Add support for configuring multiple gem servers.
4
+ * Remove any unnecessary “complex” code.
5
+ * Bring back to _one_ file, or at least less files if it doesn't harm
6
+ readability.
7
+
8
+ ==== Laurents wish list
9
+ * More commands:
10
+ * +uninstall+
11
+ * +search+
12
+ * +update+
13
+ * +list+
data/bin/zinflate ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # TODO: We should probably write a C version of this.
4
+
5
+ require File.expand_path('../../lib/microgem/utils', __FILE__)
6
+ require File.expand_path('../../lib/microgem/unpacker', __FILE__)
7
+
8
+ archive, out_file = ARGV[0], ARGV[1]
9
+
10
+ if archive && out_file
11
+ Gem::Micro::Unpacker.inflate_with_zlib(archive, out_file)
12
+ else
13
+ puts "This is a simple bin file which is able to inflate the .Z/.rz files returned from a gem server."
14
+ puts "This bin can be invoked from microgem with a Ruby which does support zlib."
15
+ puts ''
16
+ puts "Usage: zinflate /path/to/archive.Z /path/to/uncompressed_file"
17
+ end
data/bin//302/265gem ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if defined?(Gem)
4
+ # Hack to make sure we don't run with RubyGems loaded.
5
+ ENV['PRODUCTION'] = 'true'
6
+ exec "#{File.expand_path(__FILE__)} #{ARGV.join(' ')}"
7
+
8
+ else
9
+ # Run the normal operations.
10
+ $:.unshift File.expand_path('../../lib', __FILE__)
11
+ require 'microgem'
12
+
13
+ Gem::Micro.run(ARGV)
14
+ end
data/lib/microgem.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'microgem/yamlable'
2
+ require 'microgem/utils'
3
+
4
+ require 'microgem/bare_specification'
5
+ require 'microgem/bin_wrapper_emitter'
6
+ require 'microgem/config'
7
+ require 'microgem/dependency'
8
+ require 'microgem/downloader'
9
+ require 'microgem/installer'
10
+ require 'microgem/options_parser'
11
+ require 'microgem/requirement'
12
+ require 'microgem/source'
13
+ require 'microgem/specification'
14
+ require 'microgem/specification_emitter'
15
+ require 'microgem/unpacker'
16
+ require 'microgem/version'
17
+
18
+ module Gem
19
+ module Micro
20
+ class << self
21
+ include Utils
22
+
23
+ # The main +run+ method which is used by the µgem script to perform the
24
+ # actions specified by the user.
25
+ def run(arguments)
26
+ parser = OptionsParser.new
27
+ parser.parse(arguments)
28
+ Config.merge!(parser.options)
29
+
30
+ case parser.command
31
+ when 'install'
32
+ gem_spec = Source.gem_spec(parser.arguments.first, Gem::Version[:version => '0'])
33
+ gem_spec.install!
34
+
35
+ when 'sources'
36
+ case parser.arguments.first
37
+ when 'update'
38
+ Source.update!
39
+
40
+ end
41
+ else
42
+ puts parser.banner
43
+ end
44
+ end
45
+
46
+ # Returns an array of all installed gems their directory names,
47
+ # optionally limited to gems matching the given +name+.
48
+ def installed_gem_dirnames(name = nil)
49
+ directories = Dir.glob(File.join(Config.gems_path, '*')).sort
50
+ directories = directories.map { |dir| File.basename(dir) }
51
+
52
+ name.nil? ? directories : directories.select { |dirname| dirname =~ /^#{name}-[\d\.]+/ }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,40 @@
1
+ module Gem
2
+ module Micro
3
+ class BareSpecification
4
+ include Utils
5
+
6
+ def initialize(source, name, version)
7
+ @source, @name, @version = source, name, version
8
+ end
9
+
10
+ # Returns the url from where the gemspec should be downloaded.
11
+ def gem_spec_url
12
+ "http://#{@source.host}/quick/Marshal.4.8/#{@name}-#{@version}.gemspec.rz"
13
+ end
14
+
15
+ # Returns the full path to the gemspec file in the temporary work
16
+ # directory.
17
+ def gem_spec_work_file
18
+ File.join(tmpdir, "#{@name}-#{@version}.gemspec")
19
+ end
20
+
21
+ # Returns the full path to the archive containing the gemspec file in the
22
+ # temporary work directory.
23
+ def gem_spec_work_archive_file
24
+ "#{gem_spec_work_file}.rz"
25
+ end
26
+
27
+ # Retrieves the gem spec from gem_spec_url and loads the marshalled data.
28
+ #
29
+ # The returned Gem::Specification instance knows to which
30
+ # Gem::Micro::Source it belongs.
31
+ def gem_spec
32
+ Downloader.get(gem_spec_url, gem_spec_work_archive_file)
33
+ Unpacker.inflate(gem_spec_work_archive_file, gem_spec_work_file)
34
+ gem_spec = Marshal.load(File.read(gem_spec_work_file))
35
+ gem_spec.source = @source
36
+ gem_spec
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ require 'rbconfig'
2
+
3
+ module Gem
4
+ module Micro
5
+ class BinWrapperEmitter
6
+ # Initializes a new BinWrapperEmitter with a +gem_name+ and a +bin_name+.
7
+ def initialize(gem_name, bin_name)
8
+ @gem_name, @bin_name = gem_name, bin_name
9
+ end
10
+
11
+ # Returns the full path to where the bin wrapper script should be
12
+ # created. If Ruby was installed with a prefix or suffix it will be
13
+ # inflected and added to the name of the executable.
14
+ #
15
+ # If the prefix is, for instance, `mac' an executable like +macrake+ will
16
+ # be installed. With a suffix like `19' the name will be +rake19+.
17
+ def bin_wrapper_file
18
+ rbconfig('ruby_install_name').match /^(.*)ruby(.*)$/
19
+ File.join(Config.bin_dir, "#{$1}#{@bin_name}#{$2}")
20
+ end
21
+
22
+ # Creates the bin wrapper script in bin_wrapper_file.
23
+ def create_bin_wrapper!
24
+ Utils.log(:debug, "Creating bin wrapper `#{bin_wrapper_file}'")
25
+ File.open(bin_wrapper_file, 'w') { |f| f << to_ruby }
26
+ File.chmod(0755, bin_wrapper_file)
27
+ end
28
+
29
+ # Returns a string representation of the bin wrapper file.
30
+ def to_ruby
31
+ %{#!#{ File.join(rbconfig('bindir'), rbconfig('ruby_install_name')) }
32
+ #
33
+ # This file was generated by MicroGem (µgem).
34
+ #
35
+ # The application '#{@bin_name}' is installed as part of a gem, and
36
+ # this file is here to facilitate running it.
37
+
38
+ require 'rubygems'
39
+
40
+ version = "> 0"
41
+ if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
42
+ version = $1
43
+ ARGV.shift
44
+ end
45
+
46
+ gem '#{@gem_name}', version
47
+ load '#{@bin_name}'
48
+ }
49
+ end
50
+
51
+ private
52
+
53
+ def rbconfig(key)
54
+ ::Config::CONFIG[key]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,96 @@
1
+ require 'rbconfig'
2
+
3
+ module Gem
4
+ module Micro
5
+ class Config
6
+ class << self
7
+ include Utils
8
+
9
+ # Returns the full path to the Gem home directory.
10
+ def gem_home
11
+ @gem_home ||= ensure_dir(if ENV['PRODUCTION']
12
+ sitelibdir = ::Config::CONFIG['sitelibdir']
13
+ version = ::Config::CONFIG['ruby_version']
14
+ File.expand_path("../../Gems/#{version}", sitelibdir)
15
+ else
16
+ File.expand_path("../../../tmp/gem_home", __FILE__)
17
+ end)
18
+ end
19
+
20
+ # Returns the full path to the bin directory.
21
+ def bin_dir
22
+ @bin_dir ||= if ENV['PRODUCTION']
23
+ if macruby?
24
+ '/usr/local/bin'
25
+ elsif osx_default_ruby?
26
+ '/usr/bin'
27
+ else
28
+ ::Config::CONFIG['bindir']
29
+ end
30
+ else
31
+ ensure_dir(File.expand_path("../../../tmp/bin", __FILE__))
32
+ end
33
+ end
34
+
35
+ # Returns the full path to the directory where the installed gems are.
36
+ def gems_path
37
+ @gems_path ||= ensure_dir(File.join(gem_home, 'gems'))
38
+ end
39
+
40
+ # Returns the full path to the directory where the installed gem specs
41
+ # are.
42
+ def specifications_path
43
+ @specifications_path ||= ensure_dir(File.join(gem_home, 'specifications'))
44
+ end
45
+
46
+ # Returns the full path to the directory where the gems are cached.
47
+ def cache_path
48
+ @cache_path ||= ensure_dir(File.join(gem_home, 'cache'))
49
+ end
50
+
51
+ # Returns an array of source hosts from which to fetch gems.
52
+ def sources
53
+ %w{ gems.rubyforge.org gems.github.com }
54
+ end
55
+
56
+ attr_writer :log_level, :force, :simple_downloader, :simple_unpacker
57
+
58
+ # Returns the current log level, which is +:info+ or +:debug+.
59
+ def log_level
60
+ @log_level ||= :info
61
+ end
62
+
63
+ # Returns whether or not actions should be forced.
64
+ def force?
65
+ @force ||= false
66
+ end
67
+
68
+ # Returns whether or not to use +curl+ instead of Net::HTTP.
69
+ def simple_downloader?
70
+ @simple_downloader ||= false
71
+ end
72
+
73
+ # Returns whether or not to use the external +xinflate+ instead of
74
+ # Zlib::Inflate.inflate.
75
+ def simple_unpacker?
76
+ @simple_unpacker ||= false
77
+ end
78
+
79
+ # Merges the values from the +options+ hash.
80
+ def merge!(options)
81
+ options.each { |k,v| send("#{k}=", v) }
82
+ end
83
+
84
+ private
85
+
86
+ def osx_default_ruby?
87
+ defined? RUBY_FRAMEWORK_VERSION
88
+ end
89
+
90
+ def macruby?
91
+ defined? MACRUBY_VERSION
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,37 @@
1
+ module Gem
2
+ module Micro
3
+ class GemSpecMissingError < StandardError; end
4
+ end
5
+
6
+ class Dependency < Micro::YAMLable
7
+ attr_reader :name, :version, :version_requirements
8
+
9
+ # Returns whether or not any of the installed gems matches the requirements.
10
+ #
11
+ # For now we just compare against the last one which should be the highest.
12
+ def meets_requirements?
13
+ if dirname = Micro.installed_gem_dirnames(name).last
14
+ Gem::Version.from_gem_dirname(dirname) >= @version_requirements.version
15
+ end
16
+ end
17
+
18
+ # Returns the Gem::Specification instance for this dependency. If the
19
+ # required version is `0' then the latest version is used.
20
+ def gem_spec
21
+ if @gem_spec.nil?
22
+ unless @gem_spec = Micro::Source.gem_spec(@name, @version_requirements.version)
23
+ raise Micro::GemSpecMissingError, "Unable to locate Gem::Specification for Gem::Dependency `#{self}'"
24
+ end
25
+ end
26
+
27
+ @gem_spec
28
+ end
29
+
30
+ # Returns a ‘pretty’ string representation of the Dependency instance:
31
+ #
32
+ # dependency.to_s # => "rake >= 0.8.1"
33
+ def to_s
34
+ "#{name} #{@version_requirements}"
35
+ end
36
+ end
37
+ end