bundler 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Engine Yard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,156 @@
1
+ ## Bundler : A gem to bundle gems
2
+
3
+ Github: http://github.com/wycats/bundler
4
+ Mailing list: http://groups.google.com/group/ruby-bundler
5
+
6
+ ## Intro
7
+
8
+ Bundler is a tool that manages gem dependencies for your ruby application. It
9
+ takes a gem manifest file and is able to fetch, download, and install the gems
10
+ and all child dependencies specified in this manifest. It can manage any update
11
+ to the gem manifest file and update the bundled gems accordingly. It also lets
12
+ you run any ruby code in context of the bundled gem environment.
13
+
14
+ ## Disclaimer
15
+
16
+ This project is under rapid development. It is usable today, but there will be
17
+ many changes in the near future, including to the Gemfile DSL. We will bump up
18
+ versions with changes though. We greatly appreciate feedback.
19
+
20
+ ## Installation
21
+
22
+ Bundler has no dependencies. Just clone the git repository and install the gem
23
+ with the following rake task:
24
+
25
+ rake install
26
+
27
+ ## Usage
28
+
29
+ Bundler requires a gem manifest file to be created. This should be a file named
30
+ `Gemfile` located in the root directory of your application. After the manifest
31
+ has been created, in your shell, cd into your application's directory and run
32
+ `gem bundle`. This will start the bundling process.
33
+
34
+ ### Manifest file
35
+
36
+ This is where you specify all of your application's dependencies. By default
37
+ this should be in a file named `Gemfile` located in your application's root
38
+ directory. The following is an example of a potential `Gemfile`. For more
39
+ information, please refer to Bundler::ManifestBuilder.
40
+
41
+ # Specify a dependency on rails. When the bundler downloads gems,
42
+ # it will download rails as well as all of rails' dependencies (such as
43
+ # activerecord, actionpack, etc...)
44
+ #
45
+ # At least one dependency must be specified
46
+ gem "rails"
47
+
48
+ # Specify a dependency on rack v.1.0.0. The version is optional. If present,
49
+ # it can be specified the same way as with rubygems' #gem method.
50
+ gem "rack", "1.0.0"
51
+
52
+ # Specify a dependency rspec, but only activate that gem in the "testing"
53
+ # environment (read more about environments later). :except is also a valid
54
+ # option to specify environment restrictions.
55
+ gem "rspec", :only => :testing
56
+
57
+ # Add http://gems.github.com as a source that the bundler will use
58
+ # to find gems listed in the manifest. By default,
59
+ # http://gems.rubyforge.org is already added to the list.
60
+ #
61
+ # This is an optional setting.
62
+ source "http://gems.github.com"
63
+
64
+ # Specify where the bundled gems should be stashed. This directory will
65
+ # be a gem repository where all gems are downloaded to and installed to.
66
+ #
67
+ # This is an optional setting.
68
+ # The default is: vendor/gems
69
+ bundle_path "my/bundled/gems"
70
+
71
+ # Specify where gem executables should be copied to.
72
+ #
73
+ # This is an optional setting.
74
+ # The default is: bin
75
+ bin_path "my/executables"
76
+
77
+ # Specify that rubygems should be completely disabled. This means that it
78
+ # will be impossible to require it and that available gems will be
79
+ # limited exclusively to gems that have been bundled.
80
+ #
81
+ # The default is to automatically require rubygems. There is also a
82
+ # `disable_system_gems` option that will limit available rubygems to
83
+ # the ones that have been bundled.
84
+ disable_rubygems
85
+
86
+ ### Running Bundler
87
+
88
+ Once a manifest file has been created, the only thing that needs to be done
89
+ is to run the `gem bundle` command anywhere in your application. The script
90
+ will load the manifest file, resole all the dependencies, download all
91
+ needed gems, and install them into the specified directory.
92
+
93
+ Every time an update is made to the manifest file, run `gem bundle` again to
94
+ get the changes installed. This will only check the remote sources if your
95
+ currently installed gems do not satisfy the `Gemfile`. If you want to force
96
+ checking for updates on the remote sources, use the `--update` option.
97
+
98
+ ### Running your application
99
+
100
+ The easiest way to run your application is to start it with an executable
101
+ copied to the specified bin directory (by default, simply bin). For example,
102
+ if the application in question is a rack app, start it with `bin/rackup`.
103
+ This will automatically set the gem environment correctly.
104
+
105
+ Another way to run arbitrary ruby code in context of the bundled gems is to
106
+ run it with the `gem exec` command. For example:
107
+
108
+ gem exec ruby my_ruby_script.rb
109
+
110
+ Yet another way is to manually require the environment file first. This is
111
+ located in `[bundle_path]/environments/default.rb`. For example:
112
+
113
+ ruby -r vendor/gems/environments/default.rb my_ruby_script.rb
114
+
115
+ ### Using Bundler with Rails today
116
+
117
+ It should be possible to use Bundler with Rails today. Here are the steps
118
+ to follow.
119
+
120
+ * In your rails app, create a Gemfile and specify the gems that your
121
+ application depends on. Make sure to specify rails as well:
122
+
123
+ gem "rails", "2.1.2"
124
+ gem "will_paginate"
125
+
126
+ # Optionally, you can disable system gems all together and only
127
+ # use bundled gems.
128
+ disable_system_gems
129
+
130
+ * Run `gem bundle`
131
+
132
+ * You can now use rails if you prepend `gem exec` to every call to `script/*`
133
+ but that isn't fun.
134
+
135
+ * At the top of `config/boot.rb`, add the following line:
136
+
137
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'environments', 'default'))
138
+
139
+ In theory, this should be enough to get going.
140
+
141
+ ## To require rubygems or not
142
+
143
+ Ideally, no gem would assume the presence of rubygems at runtime. Rubygems provides
144
+ enough features so that this isn't necessary. However, there are a number of gems
145
+ that require specific rubygem features.
146
+
147
+ If the `disable_rubygems` option is used, Bundler will stub out the most common
148
+ of these features, but it is possible that things will not go as intended quite
149
+ yet. So, if you are brave, try your code without rubygems at runtime.
150
+
151
+ ## Reporting bugs
152
+
153
+ Please report all bugs on the github issue tracker for the project located
154
+ at:
155
+
156
+ http://github.com/wycats/bundler/issues/
@@ -0,0 +1,46 @@
1
+ require 'rubygems' unless ENV['NO_RUBYGEMS']
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = "bundler"
9
+ s.version = "0.3.0"
10
+ s.author = "Yehuda Katz"
11
+ s.email = "wycats@gmail.com"
12
+ s.homepage = "http://github.com/wycats/bundler"
13
+ s.description = s.summary = "An easy way to vendor gem dependencies"
14
+
15
+ s.platform = Gem::Platform::RUBY
16
+ s.has_rdoc = true
17
+ s.extra_rdoc_files = ["README.markdown", "LICENSE"]
18
+
19
+ s.require_path = 'lib'
20
+ s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("lib/**/*")
21
+ end
22
+
23
+ task :default => :spec
24
+
25
+ desc "Run specs"
26
+ Spec::Rake::SpecTask.new do |t|
27
+ t.spec_files = FileList['spec/**/*_spec.rb'] - FileList['spec/fixtures/**/*_spec.rb']
28
+ t.spec_opts = %w(-fs --color)
29
+ end
30
+
31
+
32
+ Rake::GemPackageTask.new(spec) do |pkg|
33
+ pkg.gem_spec = spec
34
+ end
35
+
36
+ desc "install the gem locally"
37
+ task :install => [:package] do
38
+ sh %{gem install pkg/#{spec.name}-#{spec.version}}
39
+ end
40
+
41
+ desc "create a gemspec file"
42
+ task :make_spec do
43
+ File.open("#{spec.name}.gemspec", "w") do |file|
44
+ file.puts spec.to_ruby
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ require 'pathname'
2
+ require 'logger'
3
+ require 'set'
4
+ require 'erb'
5
+ # Required elements of rubygems
6
+ require "rubygems/remote_fetcher"
7
+ require "rubygems/installer"
8
+
9
+ require "bundler/gem_bundle"
10
+ require "bundler/finder"
11
+ require "bundler/gem_ext"
12
+ require "bundler/resolver"
13
+ require "bundler/manifest_file"
14
+ require "bundler/manifest"
15
+ require "bundler/dependency"
16
+ require "bundler/runtime"
17
+ require "bundler/cli"
18
+ require "bundler/repository"
19
+
20
+ module Bundler
21
+ VERSION = "0.5.0"
22
+
23
+ class << self
24
+ attr_writer :logger
25
+
26
+ def logger
27
+ @logger ||= begin
28
+ logger = Logger.new(STDOUT, Logger::INFO)
29
+ logger.formatter = proc {|_,_,_,msg| "#{msg}\n" }
30
+ logger
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,43 @@
1
+ require "optparse"
2
+
3
+ module Bundler
4
+ class CLI
5
+ def self.run(command, options = {})
6
+ new(options).run(command)
7
+ end
8
+
9
+ def initialize(options)
10
+ @options = options
11
+ @manifest_file = Bundler::ManifestFile.load(@options[:manifest])
12
+ end
13
+
14
+ def bundle
15
+ @manifest_file.install(@options[:update])
16
+ end
17
+
18
+ def exec
19
+ @manifest_file.setup_environment
20
+ # w0t?
21
+ super(*@options[:args])
22
+ end
23
+
24
+ def run(command)
25
+
26
+ send(command)
27
+
28
+ rescue DefaultManifestNotFound => e
29
+ Bundler.logger.error "Could not find a Gemfile to use"
30
+ exit 2
31
+ rescue InvalidEnvironmentName => e
32
+ Bundler.logger.error "Gemfile error: #{e.message}"
33
+ exit
34
+ rescue InvalidRepository => e
35
+ Bundler.logger.error e.message
36
+ exit
37
+ rescue VersionConflict => e
38
+ Bundler.logger.error e.message
39
+ exit
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ class Gem::Commands::BundleCommand < Gem::Command
2
+
3
+ def initialize
4
+ super('bundle', 'Create a gem bundle based on your Gemfile', {:manifest => nil, :update => false})
5
+
6
+ add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options|
7
+ options[:manifest] = Pathname.new(manifest)
8
+ end
9
+
10
+ add_option('-u', '--update', "Force a remote check for newer gems") do
11
+ options[:update] = true
12
+ end
13
+ end
14
+
15
+ def usage
16
+ "#{program_name}"
17
+ end
18
+
19
+ def description # :nodoc:
20
+ <<-EOF
21
+ Bundle stuff
22
+ EOF
23
+ end
24
+
25
+ def execute
26
+ Bundler::CLI.run(:bundle, options)
27
+ end
28
+
29
+ end
@@ -0,0 +1,29 @@
1
+ class Gem::Commands::ExecCommand < Gem::Command
2
+
3
+ def initialize
4
+ super('exec', 'Run a command in context of a gem bundle', {:manifest => nil})
5
+
6
+ add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options|
7
+ options[:manifest] = Pathname.new(manifest)
8
+ end
9
+ end
10
+
11
+ def usage
12
+ "#{program_name} COMMAND"
13
+ end
14
+
15
+ def arguments # :nodoc:
16
+ "COMMAND command to run in context of the gem bundle"
17
+ end
18
+
19
+ def description # :nodoc:
20
+ <<-EOF.gsub(' ', '')
21
+ Run in context of a bundle
22
+ EOF
23
+ end
24
+
25
+ def execute
26
+ Bundler::CLI.run(:exec, options)
27
+ end
28
+
29
+ end
@@ -0,0 +1,45 @@
1
+ module Bundler
2
+ class InvalidEnvironmentName < StandardError; end
3
+
4
+ class Dependency
5
+ attr_reader :name, :version, :require_as, :only, :except
6
+
7
+ def initialize(name, options = {})
8
+ options.each do |k, v|
9
+ options[k.to_s] = v
10
+ end
11
+
12
+ @name = name
13
+ @version = options["version"] || ">= 0"
14
+ @require_as = Array(options["require_as"] || name)
15
+ @only = Array(options["only"]).map {|e| e.to_s } if options["only"]
16
+ @except = Array(options["except"]).map {|e| e.to_s } if options["except"]
17
+
18
+ if (@only && @only.include?("rubygems")) || (@except && @except.include?("rubygems"))
19
+ raise InvalidEnvironmentName, "'rubygems' is not a valid environment name"
20
+ end
21
+ end
22
+
23
+ def in?(environment)
24
+ environment = environment.to_s
25
+
26
+ return false unless !@only || @only.include?(environment)
27
+ return false if @except && @except.include?(environment)
28
+ true
29
+ end
30
+
31
+ def to_s
32
+ to_gem_dependency.to_s
33
+ end
34
+
35
+ def to_gem_dependency
36
+ @gem_dep ||= Gem::Dependency.new(name, version)
37
+ end
38
+
39
+ def ==(o)
40
+ [name, version, require_as, only, except] ==
41
+ [o.name, o.version, o.require_as, o.only, o.except]
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,89 @@
1
+ module Bundler
2
+ # Finder behaves like a rubygems source index in that it responds
3
+ # to #search. It also resolves a list of dependencies finding the
4
+ # best possible configuration of gems that satisifes all requirements
5
+ # without causing any gem activation errors.
6
+ class Finder
7
+
8
+ # Takes an array of gem sources and fetches the full index of
9
+ # gems from each one. It then combines the indexes together keeping
10
+ # track of the original source so that any resolved gem can be
11
+ # fetched from the correct source.
12
+ #
13
+ # ==== Parameters
14
+ # *sources<String>:: URI pointing to the gem repository
15
+ def initialize(*sources)
16
+ @results = {}
17
+ @index = Hash.new { |h,k| h[k] = {} }
18
+
19
+ sources.each { |source| fetch(source) }
20
+ end
21
+
22
+ # Figures out the best possible configuration of gems that satisfies
23
+ # the list of passed dependencies and any child dependencies without
24
+ # causing any gem activation errors.
25
+ #
26
+ # ==== Parameters
27
+ # *dependencies<Gem::Dependency>:: The list of dependencies to resolve
28
+ #
29
+ # ==== Returns
30
+ # <GemBundle>,nil:: If the list of dependencies can be resolved, a
31
+ # collection of gemspecs is returned. Otherwise, nil is returned.
32
+ def resolve(*dependencies)
33
+ Bundler.logger.info "Calculating dependencies..."
34
+
35
+ resolved = Resolver.resolve(dependencies, self)
36
+ resolved && GemBundle.new(resolved)
37
+ end
38
+
39
+ # Fetches the index from the remote source
40
+ #
41
+ # ==== Parameters
42
+ # source<String>:: URI pointing to the gem repository
43
+ #
44
+ # ==== Raises
45
+ # ArgumentError:: If the source is not a valid gem repository
46
+ def fetch(source)
47
+ Bundler.logger.info "Updating source: #{source}"
48
+
49
+ deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z")
50
+ inflated = Gem.inflate deflated
51
+
52
+ append(Marshal.load(inflated), source)
53
+ rescue Gem::RemoteFetcher::FetchError => e
54
+ raise ArgumentError, "#{source} is not a valid source: #{e.message}"
55
+ end
56
+
57
+ # Adds a new gem index linked to a gem source to the over all
58
+ # gem index that gets searched.
59
+ #
60
+ # ==== Parameters
61
+ # index<Gem::SourceIndex>:: The index to append to the list
62
+ # source<String>:: The original source
63
+ def append(index, source)
64
+ index.gems.values.each do |spec|
65
+ next unless Gem::Platform.match(spec.platform)
66
+ spec.source = source
67
+ @index[spec.name][spec.version] ||= spec
68
+ end
69
+ self
70
+ end
71
+
72
+ # Searches for a gem that matches the dependency
73
+ #
74
+ # ==== Parameters
75
+ # dependency<Gem::Dependency>:: The gem dependency to search for
76
+ #
77
+ # ==== Returns
78
+ # [Gem::Specification]:: A collection of gem specifications
79
+ # matching the search
80
+ def search(dependency)
81
+ @results[dependency.hash] ||= begin
82
+ possibilities = @index[dependency.name].values
83
+ possibilities.select do |spec|
84
+ dependency =~ spec
85
+ end.sort_by {|s| s.version }
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,16 @@
1
+ module Bundler
2
+ class GemBundle < Array
3
+ def download(directory)
4
+ FileUtils.mkdir_p(directory)
5
+
6
+ each do |spec|
7
+ unless directory.join("cache", "#{spec.full_name}.gem").file?
8
+ Bundler.logger.info "Downloading #{spec.full_name}.gem"
9
+ Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory)
10
+ end
11
+ end
12
+
13
+ self
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,19 @@
1
+ module Gem
2
+ class Installer
3
+ def app_script_text(bin_file_name)
4
+ path = @gem_home
5
+ template = File.read(File.join(File.dirname(__FILE__), "templates", "app_script.rb"))
6
+ erb = ERB.new(template)
7
+ erb.result(binding)
8
+ end
9
+ end
10
+
11
+ class Specification
12
+ attribute :source
13
+
14
+ def source=(source)
15
+ @source = source.is_a?(URI) ? source : URI.parse(source)
16
+ raise ArgumentError, "The source must be an absolute URI" unless @source.absolute?
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,136 @@
1
+ require "rubygems/source_index"
2
+
3
+ module Bundler
4
+ class VersionConflict < StandardError; end
5
+
6
+ class Manifest
7
+ attr_reader :sources, :dependencies, :path
8
+
9
+ def initialize(sources, dependencies, bindir, repository_path, rubygems, system_gems)
10
+ sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) }
11
+ @sources = sources
12
+ @dependencies = dependencies
13
+ @bindir = bindir
14
+ @repository = Repository.new(repository_path)
15
+ @rubygems = rubygems
16
+ @system_gems = system_gems
17
+ end
18
+
19
+ def install(update)
20
+ fetch(update)
21
+ @repository.install_cached_gems(:bin_dir => @bindir || @repository.path.join("bin"))
22
+ cleanup_removed_gems
23
+ create_environment_files(@repository.path.join("environments"))
24
+ Bundler.logger.info "Done."
25
+ end
26
+
27
+ def activate(environment = "default")
28
+ require @repository.path.join("environments", "#{environment}.rb")
29
+ end
30
+
31
+ def require_all
32
+ dependencies.each do |dep|
33
+ dep.require_as.each {|file| require file }
34
+ end
35
+ end
36
+
37
+ def gems_for(environment = nil)
38
+ deps = dependencies
39
+ deps = deps.select { |d| d.in?(environment) } if environment
40
+ deps = deps.map { |d| d.to_gem_dependency }
41
+ Resolver.resolve(deps, @repository.source_index)
42
+ end
43
+ alias gems gems_for
44
+
45
+ def environments
46
+ envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten
47
+ envs << "default"
48
+ end
49
+
50
+ private
51
+
52
+ def fetch(update)
53
+ return unless update || !all_gems_installed?
54
+
55
+ finder = Finder.new(*sources)
56
+ unless bundle = finder.resolve(*gem_dependencies)
57
+ gems = @dependencies.map {|d| " #{d.to_s}" }.join("\n")
58
+ raise VersionConflict, "No compatible versions could be found for:\n#{gems}"
59
+ end
60
+
61
+ bundle.download(@repository.path)
62
+ end
63
+
64
+ def gem_dependencies
65
+ @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency }
66
+ end
67
+
68
+ def all_gems_installed?
69
+ downloaded_gems = {}
70
+
71
+ Dir[@repository.path.join("cache", "*.gem")].each do |file|
72
+ file =~ /\/([^\/]+)-([\d\.]+)\.gem$/
73
+ name, version = $1, $2
74
+ downloaded_gems[name] = Gem::Version.new(version)
75
+ end
76
+
77
+ gem_dependencies.all? do |dep|
78
+ downloaded_gems[dep.name] &&
79
+ dep.version_requirements.satisfied_by?(downloaded_gems[dep.name])
80
+ end
81
+ end
82
+
83
+ def cleanup_removed_gems
84
+ glob = gems.map { |g| g.full_name }.join(',')
85
+ base = @repository.path.join("{cache,specifications,gems}")
86
+
87
+ (Dir[base.join("*")] - Dir[base.join("{#{glob}}{.gemspec,.gem,}")]).each do |file|
88
+ Bundler.logger.info "Deleting gem: #{File.basename(file, ".gem")}" if File.basename(file) =~ /\.gem$/
89
+ FileUtils.rm_rf(file)
90
+ end
91
+
92
+ glob = gems.map { |g| g.executables }.flatten.join(',')
93
+ (Dir[@bindir.join("*")] - Dir[@bindir.join("{#{glob}}")]).each do |file|
94
+ Bundler.logger.info "Deleting bin file: #{File.basename(file)}"
95
+ FileUtils.rm_rf(file)
96
+ end
97
+ end
98
+
99
+ def create_environment_files(path)
100
+ FileUtils.mkdir_p(path)
101
+ environments.each do |environment|
102
+ specs = gems_for(environment)
103
+ files = spec_files_for_specs(specs, path)
104
+ load_paths = load_paths_for_specs(specs)
105
+ create_environment_file(path, environment, files, load_paths)
106
+ end
107
+ end
108
+
109
+ def create_environment_file(path, environment, spec_files, load_paths)
110
+ File.open(path.join("#{environment}.rb"), "w") do |file|
111
+ template = File.read(File.join(File.dirname(__FILE__), "templates", "environment.rb"))
112
+ erb = ERB.new(template)
113
+ file.puts erb.result(binding)
114
+ end
115
+ end
116
+
117
+ def load_paths_for_specs(specs)
118
+ load_paths = []
119
+ specs.each do |spec|
120
+ load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir
121
+ spec.require_paths.each do |path|
122
+ load_paths << File.join(spec.full_gem_path, path)
123
+ end
124
+ end
125
+ load_paths
126
+ end
127
+
128
+ def spec_files_for_specs(specs, path)
129
+ files = {}
130
+ specs.each do |s|
131
+ files[s.name] = path.join("..", "specifications", "#{s.full_name}.gemspec").expand_path
132
+ end
133
+ files
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,75 @@
1
+ module Bundler
2
+ class DefaultManifestNotFound < StandardError; end
3
+
4
+ class ManifestFile
5
+ attr_reader :sources, :dependencies
6
+ attr_accessor :gem_path, :bindir, :rubygems, :system_gems
7
+
8
+ def self.load(filename = nil)
9
+ new(filename).load
10
+ end
11
+
12
+ def initialize(filename)
13
+ @filename = filename
14
+ @sources = %w(http://gems.rubyforge.org)
15
+ @dependencies = []
16
+ @rubygems = true
17
+ @system_gems = true
18
+ end
19
+
20
+ def load
21
+ manifest
22
+ self
23
+ end
24
+
25
+ def manifest
26
+ @manifest ||= load_manifest
27
+ end
28
+
29
+ def install(update = false)
30
+ manifest.install(update)
31
+ end
32
+
33
+ def setup_environment
34
+ unless @system_gems
35
+ ENV["GEM_HOME"] = @gem_path
36
+ ENV["GEM_PATH"] = @gem_path
37
+ end
38
+ ENV["PATH"] = "#{@bindir}:#{ENV["PATH"]}"
39
+ ENV["RUBYOPT"] = "-r#{@gem_path}/environments/default #{ENV["RUBYOPT"]}"
40
+ end
41
+
42
+ def load_manifest
43
+ ManifestBuilder.load(self, filename)
44
+ Manifest.new(sources, dependencies, bindir, gem_path, rubygems, system_gems)
45
+ end
46
+
47
+ def gem_path
48
+ @gem_path ||= root.join("vendor", "gems")
49
+ end
50
+
51
+ def bindir
52
+ @bindir ||= root.join("bin")
53
+ end
54
+
55
+ def root
56
+ filename.parent
57
+ end
58
+
59
+ def filename
60
+ @filename ||= find_manifest_file
61
+ end
62
+
63
+ def find_manifest_file
64
+ current = Pathname.new(Dir.pwd)
65
+
66
+ until current.root?
67
+ filename = current.join("Gemfile")
68
+ return filename if filename.exist?
69
+ current = current.parent
70
+ end
71
+
72
+ raise DefaultManifestNotFound
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,87 @@
1
+ module Bundler
2
+ class InvalidRepository < StandardError ; end
3
+
4
+ class Repository
5
+
6
+ attr_reader :path
7
+
8
+ def initialize(path)
9
+ @path = Pathname.new(path)
10
+ unless valid?
11
+ raise InvalidRepository, "'#{path}' is not a valid gem repository"
12
+ end
13
+ end
14
+
15
+ # Returns the source index for all gems installed in the
16
+ # repository
17
+ def source_index
18
+ Gem::SourceIndex.from_gems_in(@path.join("specifications"))
19
+ end
20
+
21
+ def valid?
22
+ (Dir[@path.join("*")] - Dir[@path.join("{cache,doc,gems,environments,specifications}")]).empty?
23
+ end
24
+
25
+ # Checks whether a gem is installed
26
+ def install_cached_gems(options = {})
27
+ cached_gems.each do |name, version|
28
+ unless installed?(name, version)
29
+ install_cached_gem(name, version, options)
30
+ end
31
+ end
32
+ end
33
+
34
+ def install_cached_gem(name, version, options = {})
35
+ cached_gem = cache_path.join("#{name}-#{version}.gem")
36
+ if cached_gem.file?
37
+ Bundler.logger.info "Installing #{name}-#{version}.gem"
38
+ installer = Gem::Installer.new(cached_gem.to_s, options.merge(
39
+ :install_dir => @path,
40
+ :ignore_dependencies => true,
41
+ :env_shebang => true,
42
+ :wrappers => true
43
+ ))
44
+ installer.install
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def cache_path
51
+ @path.join("cache")
52
+ end
53
+
54
+ def cache_files
55
+ Dir[cache_path.join("*.gem")]
56
+ end
57
+
58
+ def cached_gems
59
+ cache_files.map do |f|
60
+ full_name = File.basename(f).gsub(/\.gem$/, '')
61
+ full_name.split(/-(?=[\d.]+$)/)
62
+ end
63
+ end
64
+
65
+ def spec_path
66
+ @path.join("specifications")
67
+ end
68
+
69
+ def spec_files
70
+ Dir[spec_path.join("*.gemspec")]
71
+ end
72
+
73
+ def gem_path
74
+ @path.join("gems")
75
+ end
76
+
77
+ def gems
78
+ Dir[gem_path.join("*")]
79
+ end
80
+
81
+ def installed?(name, version)
82
+ spec_files.any? { |g| File.basename(g) == "#{name}-#{version}.gemspec" } &&
83
+ gems.any? { |g| File.basename(g) == "#{name}-#{version}" }
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,77 @@
1
+ # Extending Gem classes to add necessary tracking information
2
+ module Gem
3
+ class Dependency
4
+ def required_by
5
+ @required_by ||= []
6
+ end
7
+ end
8
+ class Specification
9
+ def required_by
10
+ @required_by ||= []
11
+ end
12
+ end
13
+ end
14
+
15
+ module Bundler
16
+
17
+ class Resolver
18
+
19
+ attr_reader :errors
20
+
21
+ def self.resolve(requirements, index = Gem.source_index)
22
+ result = catch(:success) do
23
+ resolver = new(index)
24
+ resolver.resolve(requirements, {})
25
+ nil
26
+ end
27
+ result && result.values
28
+ end
29
+
30
+ def initialize(index)
31
+ @errors = {}
32
+ @index = index
33
+ end
34
+
35
+ def resolve(reqs, activated)
36
+ throw :success, activated if reqs.empty?
37
+
38
+ reqs = reqs.sort_by do |req|
39
+ activated[req.name] ? 0 : @index.search(req).size
40
+ end
41
+
42
+ activated = activated.dup
43
+ current = reqs.shift
44
+
45
+ if existing = activated[current.name]
46
+ if current.version_requirements.satisfied_by?(existing.version)
47
+ @errors.delete(existing.name)
48
+ resolve(reqs, activated)
49
+ else
50
+ @errors[existing.name] = { :gem => existing, :requirement => current }
51
+ parent = current.required_by.last || existing.required_by.last
52
+ throw parent.name
53
+ end
54
+ else
55
+ @index.search(current).reverse_each do |spec|
56
+ resolve_requirement(spec, current, reqs.dup, activated.dup)
57
+ end
58
+ end
59
+ end
60
+
61
+ def resolve_requirement(spec, requirement, reqs, activated)
62
+ spec.required_by.replace requirement.required_by
63
+ activated[spec.name] = spec
64
+
65
+ spec.dependencies.each do |dep|
66
+ next if dep.type == :development
67
+ dep.required_by << requirement
68
+ reqs << dep
69
+ end
70
+
71
+ catch(requirement.name) do
72
+ resolve(reqs, activated)
73
+ end
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,59 @@
1
+ module Bundler
2
+ class ManifestFileNotFound < StandardError; end
3
+
4
+ class ManifestBuilder
5
+ def self.build(manifest_file, string)
6
+ builder = new(manifest_file)
7
+ builder.instance_eval(string)
8
+ builder
9
+ end
10
+
11
+ def self.load(manifest_file, filename)
12
+ unless File.exist?(filename)
13
+ raise ManifestFileNotFound, "#{filename.inspect} does not exist"
14
+ end
15
+ string = File.read(filename)
16
+ build(manifest_file, string)
17
+ end
18
+
19
+ def initialize(manifest_file)
20
+ @manifest_file = manifest_file
21
+ end
22
+
23
+ def bundle_path(path)
24
+ path = Pathname.new(path)
25
+ @manifest_file.gem_path = (path.relative? ?
26
+ @manifest_file.root.join(path) : path).expand_path
27
+ end
28
+
29
+ def bin_path(path)
30
+ path = Pathname.new(path)
31
+ @manifest_file.bindir = (path.relative? ?
32
+ @manifest_file.root.join(path) : path).expand_path
33
+ end
34
+
35
+ def disable_rubygems
36
+ @manifest_file.rubygems = false
37
+ end
38
+
39
+ def disable_system_gems
40
+ @manifest_file.system_gems = false
41
+ end
42
+
43
+ def source(source)
44
+ @manifest_file.sources << source
45
+ @manifest_file.sources.uniq!
46
+ end
47
+
48
+ def sources
49
+ @manifest_file.sources
50
+ end
51
+
52
+ def gem(name, *args)
53
+ options = args.last.is_a?(Hash) ? args.pop : {}
54
+ version = args.last
55
+
56
+ @manifest_file.dependencies << Dependency.new(name, options.merge(:version => version))
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ <%= shebang bin_file_name %>
2
+ require "<%= path.join("environments", "default") %>"
3
+ load "<%= path.join("gems", @spec.full_name, @spec.bindir, bin_file_name) %>"
@@ -0,0 +1,82 @@
1
+ <% unless @system_gems %>
2
+ ENV["GEM_HOME"] = "<%= @repository.path %>"
3
+ ENV["GEM_PATH"] = "<%= @repository.path %>"
4
+ <% end %>
5
+ ENV["PATH"] = "<%= @bindir %>:#{ENV["PATH"]}"
6
+ ENV["RUBYOPT"] = "-r#{__FILE__} #{ENV["RUBYOPT"]}"
7
+
8
+ <% load_paths.each do |load_path| %>
9
+ $LOAD_PATH.unshift "<%= load_path %>"
10
+ <% end %>
11
+
12
+ <% if @rubygems %>
13
+ require "rubygems"
14
+
15
+ module Bundler
16
+
17
+ @bundled_specs = {}
18
+ <% spec_files.each do |name, path| %>
19
+ @bundled_specs["<%= name %>"] = eval(File.read("<%= path %>"))
20
+ @bundled_specs["<%= name %>"].loaded_from = "<%= path %>"
21
+ <% end %>
22
+
23
+ def self.add_specs_to_loaded_specs
24
+ Gem.loaded_specs.merge! @bundled_specs
25
+ if Gem.respond_to?(:loaded_stacks)
26
+ @bundled_specs.keys.each { |name| Gem.loaded_stacks[name] = [] }
27
+ end
28
+ end
29
+
30
+ def self.add_specs_to_index
31
+ @bundled_specs.each do |name, spec|
32
+ Gem.source_index.add_spec spec
33
+ end
34
+ end
35
+
36
+ add_specs_to_loaded_specs
37
+ add_specs_to_index
38
+ end
39
+
40
+ module Gem
41
+ def source_index.refresh!
42
+ super
43
+ Bundler.add_specs_to_index
44
+ end
45
+ end
46
+
47
+ <% else %>
48
+
49
+ $" << "rubygems.rb"
50
+ module Kernel
51
+ def gem(*)
52
+ # Silently ignore calls to gem, since, in theory, everything
53
+ # is activated correctly already.
54
+ end
55
+ end
56
+
57
+ # Define all the Gem errors for gems that reference them.
58
+ module Gem
59
+ def self.ruby ; <%= Gem.ruby.inspect %> ; end
60
+ class LoadError < ::LoadError; end
61
+ class Exception < RuntimeError; end
62
+ class CommandLineError < Exception; end
63
+ class DependencyError < Exception; end
64
+ class DependencyRemovalException < Exception; end
65
+ class GemNotInHomeException < Exception ; end
66
+ class DocumentError < Exception; end
67
+ class EndOfYAMLException < Exception; end
68
+ class FilePermissionError < Exception; end
69
+ class FormatException < Exception; end
70
+ class GemNotFoundException < Exception; end
71
+ class InstallError < Exception; end
72
+ class InvalidSpecificationException < Exception; end
73
+ class OperationNotSupportedError < Exception; end
74
+ class RemoteError < Exception; end
75
+ class RemoteInstallationCancelled < Exception; end
76
+ class RemoteInstallationSkipped < Exception; end
77
+ class RemoteSourceException < Exception; end
78
+ class VerificationError < Exception; end
79
+ class SystemExitException < SystemExit; end
80
+ end
81
+
82
+ <% end %>
@@ -0,0 +1,5 @@
1
+ $:.delete File.expand_path(File.dirname(__FILE__))
2
+ load "rubygems.rb"
3
+ if defined?(Bundler) && Bundler.respond_to?(:rubygems_required)
4
+ Bundler.rubygems_required
5
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems/command_manager'
2
+ require 'bundler'
3
+ require 'bundler/commands/bundle_command'
4
+ require 'bundler/commands/exec_command'
5
+
6
+ Gem::CommandManager.instance.register_command :bundle
7
+ Gem::CommandManager.instance.register_command :exec
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bundler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Yehuda Katz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-29 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: An easy way to vendor gem dependencies
17
+ email: wycats@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ - LICENSE
25
+ files:
26
+ - LICENSE
27
+ - README.markdown
28
+ - Rakefile
29
+ - lib/bundler/cli.rb
30
+ - lib/bundler/commands/bundle_command.rb
31
+ - lib/bundler/commands/exec_command.rb
32
+ - lib/bundler/dependency.rb
33
+ - lib/bundler/finder.rb
34
+ - lib/bundler/gem_bundle.rb
35
+ - lib/bundler/gem_ext.rb
36
+ - lib/bundler/manifest.rb
37
+ - lib/bundler/manifest_file.rb
38
+ - lib/bundler/repository.rb
39
+ - lib/bundler/resolver.rb
40
+ - lib/bundler/runtime.rb
41
+ - lib/bundler/templates/app_script.rb
42
+ - lib/bundler/templates/environment.rb
43
+ - lib/bundler/templates/rubygems.rb
44
+ - lib/bundler.rb
45
+ - lib/rubygems_plugin.rb
46
+ has_rdoc: true
47
+ homepage: http://github.com/wycats/bundler
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.5
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: An easy way to vendor gem dependencies
74
+ test_files: []
75
+