From f416f9f0aef654fea94cae10027aca866d07c659 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Thu, 3 Sep 2009 09:31:04 -0700 Subject: Vendor the latest version of the bundler --- railties/lib/vendor/bundler/README.markdown | 162 +++++++++++++++++ railties/lib/vendor/bundler/Rakefile | 55 +++--- railties/lib/vendor/bundler/bin/gem_bundler | 40 ----- railties/lib/vendor/bundler/lib/bundler.rb | 28 ++- railties/lib/vendor/bundler/lib/bundler/cli.rb | 50 ++++-- .../bundler/lib/bundler/commands/bundle_command.rb | 31 ++++ .../bundler/lib/bundler/commands/exec_command.rb | 31 ++++ .../lib/vendor/bundler/lib/bundler/dependency.rb | 31 +++- railties/lib/vendor/bundler/lib/bundler/dsl.rb | 109 ++++++++++++ .../lib/vendor/bundler/lib/bundler/environment.rb | 111 ++++++++++++ railties/lib/vendor/bundler/lib/bundler/finder.rb | 67 ++++--- .../lib/vendor/bundler/lib/bundler/gem_bundle.rb | 18 +- railties/lib/vendor/bundler/lib/bundler/gem_ext.rb | 25 +++ .../bundler/lib/bundler/gem_specification.rb | 10 -- .../lib/vendor/bundler/lib/bundler/installer.rb | 44 ----- .../lib/vendor/bundler/lib/bundler/manifest.rb | 130 -------------- .../lib/vendor/bundler/lib/bundler/repository.rb | 151 ++++++++++++++++ .../lib/bundler/repository/directory_repository.rb | 46 +++++ .../lib/bundler/repository/gem_repository.rb | 108 ++++++++++++ .../lib/vendor/bundler/lib/bundler/resolver.rb | 194 +++++++++++++++++++-- .../bundler/lib/bundler/resolver/builders.rb | 61 ------- .../vendor/bundler/lib/bundler/resolver/engine.rb | 38 ---- .../vendor/bundler/lib/bundler/resolver/inspect.rb | 24 --- .../vendor/bundler/lib/bundler/resolver/search.rb | 71 -------- .../vendor/bundler/lib/bundler/resolver/stack.rb | 72 -------- .../vendor/bundler/lib/bundler/resolver/state.rb | 172 ------------------ railties/lib/vendor/bundler/lib/bundler/runtime.rb | 41 +---- railties/lib/vendor/bundler/lib/bundler/source.rb | 150 ++++++++++++++++ .../bundler/lib/bundler/templates/app_script.erb | 3 + .../bundler/lib/bundler/templates/environment.erb | 127 ++++++++++++++ 30 files changed, 1389 insertions(+), 811 deletions(-) create mode 100644 railties/lib/vendor/bundler/README.markdown delete mode 100755 railties/lib/vendor/bundler/bin/gem_bundler create mode 100644 railties/lib/vendor/bundler/lib/bundler/commands/bundle_command.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/commands/exec_command.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/dsl.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/environment.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/gem_ext.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/gem_specification.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/installer.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/manifest.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/repository.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/repository/directory_repository.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/repository/gem_repository.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/search.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb delete mode 100644 railties/lib/vendor/bundler/lib/bundler/resolver/state.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/source.rb create mode 100644 railties/lib/vendor/bundler/lib/bundler/templates/app_script.erb create mode 100644 railties/lib/vendor/bundler/lib/bundler/templates/environment.erb diff --git a/railties/lib/vendor/bundler/README.markdown b/railties/lib/vendor/bundler/README.markdown new file mode 100644 index 0000000000..26863e96f2 --- /dev/null +++ b/railties/lib/vendor/bundler/README.markdown @@ -0,0 +1,162 @@ +## Bundler : A gem to bundle gems + + Github: http://github.com/wycats/bundler + Mailing list: http://groups.google.com/group/ruby-bundler + IRC: #carlhuda on freenode + +## Intro + +Bundler is a tool that manages gem dependencies for your ruby application. It +takes a gem manifest file and is able to fetch, download, and install the gems +and all child dependencies specified in this manifest. It can manage any update +to the gem manifest file and update the bundled gems accordingly. It also lets +you run any ruby code in context of the bundled gem environment. + +## Disclaimer + +This project is under rapid development. It is usable today, but there will be +many changes in the near future, including to the Gemfile DSL. We will bump up +versions with changes though. We greatly appreciate feedback. + +## Installation + +Bundler has no dependencies. Just clone the git repository and install the gem +with the following rake task: + + rake install + +## Usage + +Bundler requires a gem manifest file to be created. This should be a file named +`Gemfile` located in the root directory of your application. After the manifest +has been created, in your shell, cd into your application's directory and run +`gem bundle`. This will start the bundling process. + +### Manifest file + +This is where you specify all of your application's dependencies. By default +this should be in a file named `Gemfile` located in your application's root +directory. The following is an example of a potential `Gemfile`. For more +information, please refer to Bundler::ManifestBuilder. + + # Specify a dependency on rails. When the bundler downloads gems, + # it will download rails as well as all of rails' dependencies (such as + # activerecord, actionpack, etc...) + # + # At least one dependency must be specified + gem "rails" + + # Specify a dependency on rack v.1.0.0. The version is optional. If present, + # it can be specified the same way as with rubygems' #gem method. + gem "rack", "1.0.0" + + # Specify a dependency rspec, but only activate that gem in the "testing" + # environment (read more about environments later). :except is also a valid + # option to specify environment restrictions. + gem "rspec", :only => :testing + + # Add http://gems.github.com as a source that the bundler will use + # to find gems listed in the manifest. By default, + # http://gems.rubyforge.org is already added to the list. + # + # This is an optional setting. + source "http://gems.github.com" + + # Specify where the bundled gems should be stashed. This directory will + # be a gem repository where all gems are downloaded to and installed to. + # + # This is an optional setting. + # The default is: vendor/gems + bundle_path "my/bundled/gems" + + # Specify where gem executables should be copied to. + # + # This is an optional setting. + # The default is: bin + bin_path "my/executables" + + # Specify that rubygems should be completely disabled. This means that it + # will be impossible to require it and that available gems will be + # limited exclusively to gems that have been bundled. + # + # The default is to automatically require rubygems. There is also a + # `disable_system_gems` option that will limit available rubygems to + # the ones that have been bundled. + disable_rubygems + +### Running Bundler + +Once a manifest file has been created, the only thing that needs to be done +is to run the `gem bundle` command anywhere in your application. The script +will load the manifest file, resole all the dependencies, download all +needed gems, and install them into the specified directory. + +Every time an update is made to the manifest file, run `gem bundle` again to +get the changes installed. This will only check the remote sources if your +currently installed gems do not satisfy the `Gemfile`. If you want to force +checking for updates on the remote sources, use the `--update` option. + +### Running your application + +The easiest way to run your application is to start it with an executable +copied to the specified bin directory (by default, simply bin). For example, +if the application in question is a rack app, start it with `bin/rackup`. +This will automatically set the gem environment correctly. + +Another way to run arbitrary ruby code in context of the bundled gems is to +run it with the `gem exec` command. For example: + + gem exec ruby my_ruby_script.rb + +Yet another way is to manually require the environment file first. This is +located in `[bundle_path]/environments/default.rb`. For example: + + ruby -r vendor/gems/environment.rb my_ruby_script.rb + +### Using Bundler with Rails today + +It should be possible to use Bundler with Rails today. Here are the steps +to follow. + +* In your rails app, create a Gemfile and specify the gems that your + application depends on. Make sure to specify rails as well: + + gem "rails", "2.1.2" + gem "will_paginate" + + # Optionally, you can disable system gems all together and only + # use bundled gems. + disable_system_gems + +* Run `gem bundle` + +* You can now use rails if you prepend `gem exec` to every call to `script/*` + but that isn't fun. + +* At the top of `config/boot.rb`, add the following line: + + require File.expand_path(File.join(File.dirname(__FILE__), '..', 'vendor', 'gems', 'environment')) + +In theory, this should be enough to get going. + +## To require rubygems or not + +Ideally, no gem would assume the presence of rubygems at runtime. Rubygems provides +enough features so that this isn't necessary. However, there are a number of gems +that require specific rubygem features. + +If the `disable_rubygems` option is used, Bundler will stub out the most common +of these features, but it is possible that things will not go as intended quite +yet. So, if you are brave, try your code without rubygems at runtime. + +## Known Issues + +* When a gem points to a git repository, the git repository will be cloned + every time Bundler does a gem dependency resolve. + +## Reporting bugs + +Please report all bugs on the github issue tracker for the project located +at: + + http://github.com/wycats/bundler/issues/ \ No newline at end of file diff --git a/railties/lib/vendor/bundler/Rakefile b/railties/lib/vendor/bundler/Rakefile index fd39fbff77..dc4c3d6d46 100644 --- a/railties/lib/vendor/bundler/Rakefile +++ b/railties/lib/vendor/bundler/Rakefile @@ -1,52 +1,57 @@ require 'rubygems' unless ENV['NO_RUBYGEMS'] -require 'rake/gempackagetask' require 'rubygems/specification' require 'date' -require 'spec/rake/spectask' spec = Gem::Specification.new do |s| - s.name = "bundler" - s.version = "0.0.1" - s.author = "Your Name" - s.email = "Your Email" - s.homepage = "http://example.com" - s.description = s.summary = "A gem that provides..." + s.name = "bundler" + s.version = "0.5.0.pre" + s.author = "Yehuda Katz" + s.email = "wycats@gmail.com" + s.homepage = "http://github.com/wycats/bundler" + s.description = s.summary = "An easy way to vendor gem dependencies" s.platform = Gem::Platform::RUBY s.has_rdoc = true - s.extra_rdoc_files = ["README", "LICENSE"] - s.summary = "" + s.extra_rdoc_files = ["README.markdown", "LICENSE"] - # Uncomment this to add a dependency - # s.add_dependency "foo" + s.required_rubygems_version = ">= 1.3.5" - s.bindir = "bin" - s.executables = %w( gem_bundler ) s.require_path = 'lib' - s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*") + s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("lib/**/*") end task :default => :spec -desc "Run specs" -Spec::Rake::SpecTask.new do |t| - t.spec_files = FileList['spec/**/*_spec.rb'] - t.spec_opts = %w(-fs --color) +begin + require 'spec/rake/spectask' +rescue LoadError + task(:spec) { $stderr.puts '`gem install rspec` to run specs' } +else + desc "Run specs" + Spec::Rake::SpecTask.new do |t| + t.spec_files = FileList['spec/**/*_spec.rb'] - FileList['spec/fixtures/**/*_spec.rb'] + t.spec_opts = %w(-fs --color) + end end - -Rake::GemPackageTask.new(spec) do |pkg| - pkg.gem_spec = spec +begin + require 'rake/gempackagetask' +rescue LoadError + task(:gem) { $stderr.puts '`gem install rake` to package gems' } +else + Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec + end end desc "install the gem locally" task :install => [:package] do - sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}} + sh %{gem install pkg/#{spec.name}-#{spec.version}} end desc "create a gemspec file" task :make_spec do - File.open("#{GEM}.gemspec", "w") do |file| + File.open("#{spec.name}.gemspec", "w") do |file| file.puts spec.to_ruby end -end \ No newline at end of file +end diff --git a/railties/lib/vendor/bundler/bin/gem_bundler b/railties/lib/vendor/bundler/bin/gem_bundler deleted file mode 100755 index 7a8771efee..0000000000 --- a/railties/lib/vendor/bundler/bin/gem_bundler +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env ruby -require "optparse" -require "bundler" - -options = {} - -parser = OptionParser.new do |op| - op.banner = "Usage: gem_bundler [OPTIONS] [PATH]" - - op.on("-m", "--manifest MANIFEST") do |manifest| - options[:manifest] = manifest - end - - op.on_tail("-h", "--help", "Show this message") do - puts op - exit - end -end -parser.parse! - -options[:path] = ARGV.shift - -unless options[:path] - puts parser - puts %( - [PATH] must be specified - ) - exit -end - -unless options[:manifest] && File.exist?(options[:manifest]) - puts parser - puts %( - MANIFEST must be a valid manifest file - ) - exit -end - - -Bundler.run(options) \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler.rb b/railties/lib/vendor/bundler/lib/bundler.rb index 4dcab20da9..1ede3517dd 100644 --- a/railties/lib/vendor/bundler/lib/bundler.rb +++ b/railties/lib/vendor/bundler/lib/bundler.rb @@ -1,24 +1,34 @@ +require 'pathname' require 'logger' require 'set' +require 'erb' # Required elements of rubygems require "rubygems/remote_fetcher" require "rubygems/installer" require "bundler/gem_bundle" -require "bundler/installer" +require "bundler/source" require "bundler/finder" -require "bundler/gem_specification" +require "bundler/gem_ext" require "bundler/resolver" -require "bundler/manifest" -require "bundler/dependency" -require "bundler/runtime" +require "bundler/environment" +require "bundler/dsl" require "bundler/cli" +require "bundler/repository" +require "bundler/dependency" module Bundler VERSION = "0.5.0" - def self.run(options = {}) - manifest = ManifestBuilder.load(options[:path], options[:manifest]) - manifest.install + class << self + attr_writer :logger + + def logger + @logger ||= begin + logger = Logger.new(STDOUT, Logger::INFO) + logger.formatter = proc {|_,_,_,msg| "#{msg}\n" } + logger + end + end end -end \ No newline at end of file +end diff --git a/railties/lib/vendor/bundler/lib/bundler/cli.rb b/railties/lib/vendor/bundler/lib/bundler/cli.rb index ed49ef88f0..df9181fbc4 100644 --- a/railties/lib/vendor/bundler/lib/bundler/cli.rb +++ b/railties/lib/vendor/bundler/lib/bundler/cli.rb @@ -1,24 +1,44 @@ -module Bundler - module CLI +require "optparse" - def default_manifest - current = Pathname.new(Dir.pwd) +module Bundler + class CLI + def self.run(command, options = {}) + new(options).run(command) + rescue DefaultManifestNotFound => e + Bundler.logger.error "Could not find a Gemfile to use" + exit 2 + rescue InvalidEnvironmentName => e + Bundler.logger.error "Gemfile error: #{e.message}" + exit + rescue InvalidRepository => e + Bundler.logger.error e.message + exit + rescue VersionConflict => e + Bundler.logger.error e.message + exit + rescue GemNotFound => e + Bundler.logger.error e.message + exit + end - begin - manifest = current.join("Gemfile") - return manifest.to_s if File.exist?(manifest) - current = current.parent - end until current.root? - nil + def initialize(options) + @options = options + @manifest = Bundler::Environment.load(@options[:manifest]) end - module_function :default_manifest + def bundle + @manifest.install(@options[:update]) + end - def default_path - Pathname.new(File.dirname(default_manifest)).join("vendor").join("gems").to_s + def exec + @manifest.setup_environment + # w0t? + super(*@options[:args]) end - module_function :default_path + def run(command) + send(command) + end end -end \ No newline at end of file +end diff --git a/railties/lib/vendor/bundler/lib/bundler/commands/bundle_command.rb b/railties/lib/vendor/bundler/lib/bundler/commands/bundle_command.rb new file mode 100644 index 0000000000..a1f9590f75 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/commands/bundle_command.rb @@ -0,0 +1,31 @@ +class Gem::Commands::BundleCommand < Gem::Command + + def initialize + super('bundle', 'Create a gem bundle based on your Gemfile', {:manifest => nil, :update => false}) + + add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options| + options[:manifest] = manifest + end + + add_option('-u', '--update', "Force a remote check for newer gems") do + options[:update] = true + end + end + + def usage + "#{program_name}" + end + + def description # :nodoc: + <<-EOF +Bundle stuff + EOF + end + + def execute + # Prevent the bundler from getting required unless it is actually being used + require 'bundler' + Bundler::CLI.run(:bundle, options) + end + +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/commands/exec_command.rb b/railties/lib/vendor/bundler/lib/bundler/commands/exec_command.rb new file mode 100644 index 0000000000..228aa60619 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/commands/exec_command.rb @@ -0,0 +1,31 @@ +class Gem::Commands::ExecCommand < Gem::Command + + def initialize + super('exec', 'Run a command in context of a gem bundle', {:manifest => nil}) + + add_option('-m', '--manifest MANIFEST', "Specify the path to the manifest file") do |manifest, options| + options[:manifest] = manifest + end + end + + def usage + "#{program_name} COMMAND" + end + + def arguments # :nodoc: + "COMMAND command to run in context of the gem bundle" + end + + def description # :nodoc: + <<-EOF.gsub(' ', '') + Run in context of a bundle + EOF + end + + def execute + # Prevent the bundler from getting required unless it is actually being used + require 'bundler' + Bundler::CLI.run(:exec, options) + end + +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/dependency.rb b/railties/lib/vendor/bundler/lib/bundler/dependency.rb index 739a7a117d..b627b58662 100644 --- a/railties/lib/vendor/bundler/lib/bundler/dependency.rb +++ b/railties/lib/vendor/bundler/lib/bundler/dependency.rb @@ -1,9 +1,10 @@ module Bundler - class Dependency + class InvalidEnvironmentName < StandardError; end + class Dependency attr_reader :name, :version, :require_as, :only, :except - def initialize(name, options = {}) + def initialize(name, options = {}, &block) options.each do |k, v| options[k.to_s] = v end @@ -11,8 +12,13 @@ module Bundler @name = name @version = options["version"] || ">= 0" @require_as = Array(options["require_as"] || name) - @only = Array(options["only"]).map {|e| e.to_s } if options["only"] - @except = Array(options["except"]).map {|e| e.to_s } if options["except"] + @only = options["only"] + @except = options["except"] + @block = block + + if (@only && @only.include?("rubygems")) || (@except && @except.include?("rubygems")) + raise InvalidEnvironmentName, "'rubygems' is not a valid environment name" + end end def in?(environment) @@ -27,9 +33,24 @@ module Bundler to_gem_dependency.to_s end + def require(environment) + return unless in?(environment) + + @require_as.each do |file| + super(file) + end + + @block.call if @block + end + def to_gem_dependency @gem_dep ||= Gem::Dependency.new(name, version) end + def ==(o) + [name, version, require_as, only, except] == + [o.name, o.version, o.require_as, o.only, o.except] + end + end -end \ No newline at end of file +end diff --git a/railties/lib/vendor/bundler/lib/bundler/dsl.rb b/railties/lib/vendor/bundler/lib/bundler/dsl.rb new file mode 100644 index 0000000000..d9a86ee1fd --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/dsl.rb @@ -0,0 +1,109 @@ +module Bundler + class ManifestFileNotFound < StandardError; end + + class Dsl + def initialize(environment) + @environment = environment + @sources = Hash.new { |h,k| h[k] = {} } + end + + def bundle_path(path) + path = Pathname.new(path) + @environment.gem_path = (path.relative? ? + @environment.root.join(path) : path).expand_path + end + + def bin_path(path) + path = Pathname.new(path) + @environment.bindir = (path.relative? ? + @environment.root.join(path) : path).expand_path + end + + def disable_rubygems + @environment.rubygems = false + end + + def disable_system_gems + @environment.system_gems = false + end + + def source(source) + source = GemSource.new(:uri => source) + unless @environment.sources.include?(source) + @environment.add_source(source) + end + end + + def only(env) + old, @only = @only, _combine_onlys(env) + yield + @only = old + end + + def except(env) + old, @except = @except, _combine_excepts(env) + yield + @except = old + end + + def clear_sources + @environment.clear_sources + end + + def gem(name, *args) + options = args.last.is_a?(Hash) ? args.pop : {} + version = args.last + + options[:only] = _combine_onlys(options[:only] || options["only"]) + options[:except] = _combine_excepts(options[:except] || options["except"]) + + dep = Dependency.new(name, options.merge(:version => version)) + + # OMG REFACTORZ. KTHX + if vendored_at = options[:vendored_at] + vendored_at = Pathname.new(vendored_at) + vendored_at = @environment.filename.dirname.join(vendored_at) if vendored_at.relative? + + @sources[:directory][vendored_at.to_s] ||= begin + source = DirectorySource.new( + :name => name, + :version => version, + :location => vendored_at + ) + @environment.add_priority_source(source) + true + end + elsif git = options[:git] + @sources[:git][git] ||= begin + source = GitSource.new( + :name => name, + :version => version, + :uri => git, + :ref => options[:commit] || options[:tag], + :branch => options[:branch] + ) + @environment.add_priority_source(source) + true + end + end + + @environment.dependencies << dep + end + + private + + def _combine_onlys(only) + return @only unless only + only = [only].flatten.compact.uniq.map { |o| o.to_s } + only &= @only if @only + only + end + + def _combine_excepts(except) + return @except unless except + except = [except].flatten.compact.uniq.map { |o| o.to_s } + except |= @except if @except + except + end + end +end diff --git a/railties/lib/vendor/bundler/lib/bundler/environment.rb b/railties/lib/vendor/bundler/lib/bundler/environment.rb new file mode 100644 index 0000000000..f07a9e2c6f --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/environment.rb @@ -0,0 +1,111 @@ +require "rubygems/source_index" + +module Bundler + class DefaultManifestNotFound < StandardError; end + + class Environment + attr_reader :filename, :dependencies + attr_accessor :rubygems, :system_gems, :gem_path, :bindir + + def self.load(gemfile = nil) + gemfile = gemfile ? Pathname.new(gemfile) : default_manifest_file + + unless gemfile.file? + raise ManifestFileNotFound, "#{filename.inspect} does not exist" + end + + new(gemfile) + end + + def self.default_manifest_file + current = Pathname.new(Dir.pwd) + + until current.root? + filename = current.join("Gemfile") + return filename if filename.exist? + current = current.parent + end + + raise DefaultManifestNotFound + end + + def initialize(filename) #, sources, dependencies, bindir, path, rubygems, system_gems) + @filename = filename + @default_sources = [GemSource.new(:uri => "http://gems.rubyforge.org")] + @sources = [] + @priority_sources = [] + @dependencies = [] + @rubygems = true + @system_gems = true + + # Evaluate the Gemfile + builder = Dsl.new(self) + builder.instance_eval(File.read(filename)) + end + + def install(update = false) + begin + tmp_path = filename.dirname.join(".tmp") + FileUtils.mkdir_p(tmp_path) + sources.each { |s| s.tmp_path = tmp_path } + repository.install(gem_dependencies, sources, + :rubygems => rubygems, + :system_gems => system_gems, + :manifest => filename, + :update => update + ) + ensure + FileUtils.rm_rf(tmp_path) + end + Bundler.logger.info "Done." + end + + def setup_environment + unless system_gems + ENV["GEM_HOME"] = gem_path + ENV["GEM_PATH"] = gem_path + end + ENV["PATH"] = "#{bindir}:#{ENV["PATH"]}" + ENV["RUBYOPT"] = "-r#{gem_path}/environment #{ENV["RUBYOPT"]}" + end + + def root + filename.parent + end + + def gem_path + @gem_path ||= root.join("vendor", "gems") + end + + def bindir + @bindir ||= root.join("bin") + end + + def sources + @priority_sources + @sources + @default_sources + end + + def add_source(source) + @sources << source + end + + def add_priority_source(source) + @priority_sources << source + end + + def clear_sources + @sources.clear + @default_sources.clear + end + + private + + def repository + @repository ||= Repository.new(gem_path, bindir) + end + + def gem_dependencies + @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency } + end + end +end diff --git a/railties/lib/vendor/bundler/lib/bundler/finder.rb b/railties/lib/vendor/bundler/lib/bundler/finder.rb index 43ff370ae4..b77ca65709 100644 --- a/railties/lib/vendor/bundler/lib/bundler/finder.rb +++ b/railties/lib/vendor/bundler/lib/bundler/finder.rb @@ -1,42 +1,51 @@ module Bundler + # Finder behaves like a rubygems source index in that it responds + # to #search. It also resolves a list of dependencies finding the + # best possible configuration of gems that satisifes all requirements + # without causing any gem activation errors. class Finder - def initialize(*sources) - @results = {} - @index = Hash.new { |h,k| h[k] = {} } - sources.each { |source| fetch(source) } + # Takes an array of gem sources and fetches the full index of + # gems from each one. It then combines the indexes together keeping + # track of the original source so that any resolved gem can be + # fetched from the correct source. + # + # ==== Parameters + # *sources:: URI pointing to the gem repository + def initialize(*sources) + @cache = {} + @index = {} + @sources = sources end - def resolve(*dependencies) - resolved = Resolver.resolve(dependencies, self) - resolved && GemBundle.new(resolved.all_specs) + # Searches for a gem that matches the dependency + # + # ==== Parameters + # dependency:: The gem dependency to search for + # + # ==== Returns + # [Gem::Specification]:: A collection of gem specifications + # matching the search + def search(dependency) + @cache[dependency.hash] ||= begin + find_by_name(dependency.name).select do |spec| + dependency =~ spec + end.sort_by {|s| s.version } + end end - def fetch(source) - deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z") - inflated = Gem.inflate deflated + private - append(Marshal.load(inflated), source) - rescue Gem::RemoteFetcher::FetchError => e - raise ArgumentError, "#{source} is not a valid source: #{e.message}" - end - - def append(index, source) - index.gems.values.each do |spec| - next unless Gem::Platform.match(spec.platform) - spec.source = source - @index[spec.name][spec.version] ||= spec + def find_by_name(name) + matches = @index[name] ||= begin + versions = {} + @sources.reverse_each do |source| + versions.merge! source.specs[name] || {} + end + versions end - self + matches.values end - def search(dependency) - @results[dependency.hash] ||= begin - possibilities = @index[dependency.name].values - possibilities.select do |spec| - dependency =~ spec - end.sort_by {|s| s.version } - end - end end end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb b/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb index b749720fd9..80d7710683 100644 --- a/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb +++ b/railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb @@ -1,22 +1,10 @@ module Bundler class GemBundle < Array - def download(directory) - FileUtils.mkdir_p(directory) - - current = Dir[File.join(directory, "cache", "*.gem*")] - - each do |spec| - cached = File.join(directory, "cache", "#{spec.full_name}.gem") - - unless File.file?(cached) - Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory) - end - - current.delete(cached) + def download(repository) + sort_by {|s| s.full_name.downcase }.each do |spec| + spec.source.download(spec, repository) end - current.each { |file| File.delete(file) } - self end end diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_ext.rb b/railties/lib/vendor/bundler/lib/bundler/gem_ext.rb new file mode 100644 index 0000000000..155ad04c7e --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/gem_ext.rb @@ -0,0 +1,25 @@ +module Gem + class Installer + def app_script_text(bin_file_name) + path = @gem_home + template = File.read(File.join(File.dirname(__FILE__), "templates", "app_script.erb")) + erb = ERB.new(template, nil, '-') + erb.result(binding) + end + end + + class Specification + attr_accessor :source + attr_accessor :location + + # Hack to fix github's strange marshal file + def specification_version + @specification_version && @specification_version.to_i + end + + alias full_gem_path_without_location full_gem_path + def full_gem_path + @location ? @location : full_gem_path_without_location + end + end +end diff --git a/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb b/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb deleted file mode 100644 index 680b61fd69..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/gem_specification.rb +++ /dev/null @@ -1,10 +0,0 @@ -module Gem - class Specification - attribute :source - - def source=(source) - @source = source.is_a?(URI) ? source : URI.parse(source) - raise ArgumentError, "The source must be an absolute URI" unless @source.absolute? - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/installer.rb b/railties/lib/vendor/bundler/lib/bundler/installer.rb deleted file mode 100644 index 581d9d3c8e..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/installer.rb +++ /dev/null @@ -1,44 +0,0 @@ -module Bundler - class Installer - def initialize(path) - if !File.directory?(path) - raise ArgumentError, "#{path} is not a directory" - elsif !File.directory?(File.join(path, "cache")) - raise ArgumentError, "#{path} is not a valid environment (it does not contain a cache directory)" - end - - @path = path - @gems = Dir[(File.join(path, "cache", "*.gem"))] - end - - def install(options = {}) - bin_dir = options[:bin_dir] ||= File.join(@path, "bin") - - specs = Dir[File.join(@path, "specifications", "*.gemspec")] - gems = Dir[File.join(@path, "gems", "*")] - - @gems.each do |gem| - name = File.basename(gem).gsub(/\.gem$/, '') - installed = specs.any? { |g| File.basename(g) == "#{name}.gemspec" } && - gems.any? { |g| File.basename(g) == name } - - unless installed - installer = Gem::Installer.new(gem, :install_dir => @path, - :ignore_dependencies => true, - :env_shebang => true, - :wrappers => true, - :bin_dir => bin_dir) - installer.install - end - - # remove this spec - specs.delete_if { |g| File.basename(g) == "#{name}.gemspec"} - gems.delete_if { |g| File.basename(g) == name } - end - - (specs + gems).each do |path| - FileUtils.rm_rf(path) - end - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/manifest.rb b/railties/lib/vendor/bundler/lib/bundler/manifest.rb deleted file mode 100644 index 847a41940d..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/manifest.rb +++ /dev/null @@ -1,130 +0,0 @@ -require "rubygems/source_index" -require "pathname" - -module Bundler - class VersionConflict < StandardError; end - - class Manifest - attr_reader :sources, :dependencies, :path - - def initialize(sources, dependencies, path) - sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) } - @sources, @dependencies, @path = sources, dependencies, Pathname.new(path) - end - - def fetch - return if all_gems_installed? - - finder = Finder.new(*sources) - unless bundle = finder.resolve(*gem_dependencies) - gems = @dependencies.map {|d| " #{d.to_s}" }.join("\n") - raise VersionConflict, "No compatible versions could be found for:\n#{gems}" - end - - bundle.download(@path) - end - - def install(options = {}) - fetch - installer = Installer.new(@path) - installer.install # options come here - create_load_paths_files(File.join(@path, "environments")) - create_fake_rubygems(File.join(@path, "environments")) - end - - def activate(environment = "default") - require File.join(@path, "environments", "#{environment}.rb") - end - - def require_all - dependencies.each do |dep| - dep.require_as.each {|file| require file } - end - end - - def gems_for(environment) - deps = dependencies.select { |d| d.in?(environment) } - deps.map! { |d| d.to_gem_dependency } - index = Gem::SourceIndex.from_gems_in(File.join(@path, "specifications")) - Resolver.resolve(deps, index).all_specs - end - - def environments - envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten - envs << "default" - end - - private - - def gem_dependencies - @gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency } - end - - def all_gems_installed? - gem_versions = {} - - Dir[File.join(@path, "cache", "*.gem")].each do |file| - file =~ /\/([^\/]+)-([\d\.]+)\.gem$/ - name, version = $1, $2 - gem_versions[name] = Gem::Version.new(version) - end - - gem_dependencies.all? do |dep| - gem_versions[dep.name] && - dep.version_requirements.satisfied_by?(gem_versions[dep.name]) - end - end - - def create_load_paths_files(path) - FileUtils.mkdir_p(path) - environments.each do |environment| - gem_specs = gems_for(environment) - File.open(File.join(path, "#{environment}.rb"), "w") do |file| - file.puts <<-RUBY_EVAL - module Bundler - def self.rubygems_required - #{create_gem_stubs(path, gem_specs)} - end - end - RUBY_EVAL - file.puts "$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))" - load_paths_for_specs(gem_specs).each do |load_path| - file.puts "$LOAD_PATH.unshift #{load_path.inspect}" - end - end - end - end - - def create_gem_stubs(path, gem_specs) - gem_specs.map do |spec| - path = File.expand_path(File.join(path, '..', 'specifications', "#{spec.full_name}.gemspec")) - %{ - Gem.loaded_specs["#{spec.name}"] = eval(File.read("#{path}")) - } - end.join("\n") - end - - def create_fake_rubygems(path) - File.open(File.join(path, "rubygems.rb"), "w") do |file| - file.puts <<-RUBY_EVAL - $:.delete File.expand_path(File.dirname(__FILE__)) - load "rubygems.rb" - if defined?(Bundler) && Bundler.respond_to?(:rubygems_required) - Bundler.rubygems_required - end - RUBY_EVAL - end - end - - def load_paths_for_specs(specs) - load_paths = [] - specs.each do |spec| - load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir - spec.require_paths.each do |path| - load_paths << File.join(spec.full_gem_path, path) - end - end - load_paths - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/repository.rb b/railties/lib/vendor/bundler/lib/bundler/repository.rb new file mode 100644 index 0000000000..1a1dc7497d --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/repository.rb @@ -0,0 +1,151 @@ +require "bundler/repository/gem_repository" +require "bundler/repository/directory_repository" + +module Bundler + class InvalidRepository < StandardError ; end + + class Repository + attr_reader :path + + def initialize(path, bindir) + FileUtils.mkdir_p(path) + + @path = Pathname.new(path) + @bindir = Pathname.new(bindir) + + @repos = { + :gem => Gems.new(@path, @bindir), + :directory => Directory.new(@path.join("dirs"), @bindir) + } + end + + def install(dependencies, sources, options = {}) + if options[:update] || !satisfies?(dependencies) + fetch(dependencies, sources) + expand(options) + else + # Remove any gems that are still around if the Gemfile changed without + # requiring new gems to be download (e.g. a line in the Gemfile was + # removed) + cleanup(Resolver.resolve(dependencies, [source_index])) + end + configure(options) + sync + end + + def gems + gems = [] + each_repo do |repo| + gems.concat repo.gems + end + gems + end + + def satisfies?(dependencies) + index = source_index + dependencies.all? { |dep| index.search(dep).size > 0 } + end + + def source_index + index = Gem::SourceIndex.new + + each_repo do |repo| + index.gems.merge!(repo.source_index.gems) + end + + index + end + + def add_spec(type, spec) + @repos[type].add_spec(spec) + end + + def download_path_for(type) + @repos[type].download_path_for + end + + private + + def cleanup(bundle) + each_repo do |repo| + repo.cleanup(bundle) + end + end + + def each_repo + @repos.each do |k, repo| + yield repo + end + end + + def fetch(dependencies, sources) + bundle = Resolver.resolve(dependencies, sources) + # Cleanup here to remove any gems that could cause problem in the expansion + # phase + # + # TODO: Try to avoid double cleanup + cleanup(bundle) + bundle.download(self) + end + + def sync + glob = gems.map { |g| g.executables }.flatten.join(',') + + (Dir[@bindir.join("*")] - Dir[@bindir.join("{#{glob}}")]).each do |file| + Bundler.logger.info "Deleting bin file: #{File.basename(file)}" + FileUtils.rm_rf(file) + end + end + + def expand(options) + each_repo do |repo| + repo.expand(options) + end + end + + def configure(options) + generate_environment(options) + end + + def generate_environment(options) + FileUtils.mkdir_p(path) + + specs = gems + load_paths = load_paths_for_specs(specs) + bindir = @bindir.relative_path_from(path).to_s + filename = options[:manifest].relative_path_from(path).to_s + spec_files = specs.inject({}) do |hash, spec| + relative = spec.loaded_from.relative_path_from(@path).to_s + hash.merge!(spec.name => relative) + end + + File.open(path.join("environment.rb"), "w") do |file| + template = File.read(File.join(File.dirname(__FILE__), "templates", "environment.erb")) + erb = ERB.new(template, nil, '-') + file.puts erb.result(binding) + end + end + + def load_paths_for_specs(specs) + load_paths = [] + specs.each do |spec| + gem_path = Pathname.new(spec.full_gem_path) + if spec.bindir + load_paths << gem_path.join(spec.bindir).relative_path_from(@path).to_s + end + spec.require_paths.each do |path| + load_paths << gem_path.join(path).relative_path_from(@path).to_s + end + end + load_paths + end + + def require_code(file, dep) + constraint = case + when dep.only then %{ if #{dep.only.inspect}.include?(env)} + when dep.except then %{ unless #{dep.except.inspect}.include?(env)} + end + "require #{file.inspect}#{constraint}" + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/repository/directory_repository.rb b/railties/lib/vendor/bundler/lib/bundler/repository/directory_repository.rb new file mode 100644 index 0000000000..e97dd38dd5 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/repository/directory_repository.rb @@ -0,0 +1,46 @@ +module Bundler + class Repository + class Directory + attr_reader :path, :bindir + + def initialize(path, bindir) + @path = path + @bindir = bindir + + FileUtils.mkdir_p(path.to_s) + end + + def source_index + index = Gem::SourceIndex.from_gems_in(@path.join("specifications")) + index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") } + index + end + + def gems + source_index.gems.values + end + + def add_spec(spec) + destination = path.join('specifications') + destination.mkdir unless destination.exist? + + File.open(destination.join("#{spec.full_name}.gemspec"), 'w') do |f| + f.puts spec.to_ruby + end + end + + def download_path_for + @path.join("dirs") + end + + # Checks whether a gem is installed + def expand(options) + # raise NotImplementedError + end + + def cleanup(gems) + # raise NotImplementedError + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/repository/gem_repository.rb b/railties/lib/vendor/bundler/lib/bundler/repository/gem_repository.rb new file mode 100644 index 0000000000..90de49d83d --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/repository/gem_repository.rb @@ -0,0 +1,108 @@ +module Bundler + class Repository + class Gems + attr_reader :path, :bindir + + def initialize(path, bindir) + @path = path + @bindir = bindir + end + + # Returns the source index for all gems installed in the + # repository + def source_index + index = Gem::SourceIndex.from_gems_in(@path.join("specifications")) + index.each { |n, spec| spec.loaded_from = @path.join("specifications", "#{spec.full_name}.gemspec") } + index + end + + def gems + source_index.gems.values + end + + # Checks whether a gem is installed + def expand(options) + cached_gems.each do |name, version| + unless installed?(name, version) + install_cached_gem(name, version, options) + end + end + end + + def cleanup(gems) + glob = gems.map { |g| g.full_name }.join(',') + base = path.join("{cache,specifications,gems}") + + (Dir[base.join("*")] - Dir[base.join("{#{glob}}{.gemspec,.gem,}")]).each do |file| + if File.basename(file) =~ /\.gem$/ + name = File.basename(file, '.gem') + Bundler.logger.info "Deleting gem: #{name}" + end + FileUtils.rm_rf(file) + end + end + + def add_spec(spec) + raise NotImplementedError + end + + def download_path_for + path + end + + private + + def cache_path + @path.join("cache") + end + + def cache_files + Dir[cache_path.join("*.gem")] + end + + def cached_gems + cache_files.map do |f| + full_name = File.basename(f).gsub(/\.gem$/, '') + full_name.split(/-(?=[^-]+$)/) + end + end + + def spec_path + @path.join("specifications") + end + + def spec_files + Dir[spec_path.join("*.gemspec")] + end + + def gem_path + @path.join("gems") + end + + def gem_paths + Dir[gem_path.join("*")] + end + + def installed?(name, version) + spec_files.any? { |g| File.basename(g) == "#{name}-#{version}.gemspec" } && + gem_paths.any? { |g| File.basename(g) == "#{name}-#{version}" } + end + + def install_cached_gem(name, version, options = {}) + cached_gem = cache_path.join("#{name}-#{version}.gem") + # TODO: Add a warning if cached_gem is not a file + if cached_gem.file? + Bundler.logger.info "Installing #{name}-#{version}.gem" + installer = Gem::Installer.new(cached_gem.to_s, options.merge( + :install_dir => @path, + :ignore_dependencies => true, + :env_shebang => true, + :wrappers => true, + :bin_dir => @bindir + )) + installer.install + end + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver.rb b/railties/lib/vendor/bundler/lib/bundler/resolver.rb index 1ec89e53c4..2a6a6371c2 100644 --- a/railties/lib/vendor/bundler/lib/bundler/resolver.rb +++ b/railties/lib/vendor/bundler/lib/bundler/resolver.rb @@ -1,19 +1,189 @@ -require 'bundler/resolver/inspect' -require 'bundler/resolver/search' -require 'bundler/resolver/engine' -require 'bundler/resolver/stack' -require 'bundler/resolver/state' +# This is the latest iteration of the gem dependency resolving algorithm. As of now, +# it can resolve (as a success of failure) any set of gem dependencies we throw at it +# in a reasonable amount of time. The most iterations I've seen it take is about 150. +# The actual implementation of the algorithm is not as good as it could be yet, but that +# can come later. + +# Extending Gem classes to add necessary tracking information +module Gem + class Dependency + def required_by + @required_by ||= [] + end + end + class Specification + def required_by + @required_by ||= [] + end + end +end module Bundler - module Resolver - def self.resolve(deps, source_index = Gem.source_index, logger = nil) - unless logger - logger = Logger.new($stderr) - logger.datetime_format = "" - logger.level = ENV["GEM_RESOLVER_DEBUG"] ? Logger::DEBUG : Logger::ERROR + class GemNotFound < StandardError; end + class VersionConflict < StandardError; end + + class Resolver + + attr_reader :errors + + # Figures out the best possible configuration of gems that satisfies + # the list of passed dependencies and any child dependencies without + # causing any gem activation errors. + # + # ==== Parameters + # *dependencies:: The list of dependencies to resolve + # + # ==== Returns + # ,nil:: If the list of dependencies can be resolved, a + # collection of gemspecs is returned. Otherwise, nil is returned. + def self.resolve(requirements, sources) + Bundler.logger.info "Calculating dependencies..." + + resolver = new(sources) + result = catch(:success) do + resolver.resolve(requirements, {}) + output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))| + o << " Conflict on: #{conflict.inspect}:\n" + o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n" + o << " * #{requirement} required by #{requirement.required_by.first}\n" + o << " All possible versions of origin requirements conflict." + end + raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{output}" + nil + end + result && GemBundle.new(result.values) + end + + def initialize(sources) + @errors = {} + @stack = [] + @specs = Hash.new { |h,k| h[k] = {} } + @cache = {} + @index = {} + + sources.reverse_each do |source| + source.gems.values.each do |spec| + # TMP HAX FOR OPTZ + spec.source = source + next unless Gem::Platform.match(spec.platform) + @specs[spec.name][spec.version] = spec + end + end + end + + def resolve(reqs, activated) + # If the requirements are empty, then we are in a success state. Aka, all + # gem dependencies have been resolved. + throw :success, activated if reqs.empty? + + # Sort requirements so that the ones that are easiest to resolve are first. + # Easiest to resolve is defined by: Is this gem already activated? Otherwise, + # check the number of child dependencies this requirement has. + reqs = reqs.sort_by do |req| + activated[req.name] ? 0 : search(req).size + end + + activated = activated.dup + # Pull off the first requirement so that we can resolve it + current = reqs.shift + + # Check if the gem has already been activated, if it has, we will make sure + # that the currently activated gem satisfies the requirement. + if existing = activated[current.name] + if current.version_requirements.satisfied_by?(existing.version) + @errors.delete(existing.name) + # Since the current requirement is satisfied, we can continue resolving + # the remaining requirements. + resolve(reqs, activated) + else + @errors[existing.name] = [existing, current] + # Since the current requirement conflicts with an activated gem, we need + # to backtrack to the current requirement's parent and try another version + # of it (maybe the current requirement won't be present anymore). If the + # current requirement is a root level requirement, we need to jump back to + # where the conflicting gem was activated. + parent = current.required_by.last || existing.required_by.last + # We track the spot where the current gem was activated because we need + # to keep a list of every spot a failure happened. + throw parent.name, existing.required_by.last.name + end + else + # There are no activated gems for the current requirement, so we are going + # to find all gems that match the current requirement and try them in decending + # order. We also need to keep a set of all conflicts that happen while trying + # this gem. This is so that if no versions work, we can figure out the best + # place to backtrack to. + conflicts = Set.new + + # Fetch all gem versions matching the requirement + # + # TODO: Warn / error when no matching versions are found. + matching_versions = search(current) + + if matching_versions.empty? + if current.required_by.empty? + raise GemNotFound, "Could not find gem '#{current}' in any of the sources" + end + Bundler.logger.warn "Could not find gem '#{current}' (required by '#{current.required_by.last}') in any of the sources" + end + + matching_versions.reverse_each do |spec| + conflict = resolve_requirement(spec, current, reqs.dup, activated.dup) + conflicts << conflict if conflict + end + # If the current requirement is a root level gem and we have conflicts, we + # can figure out the best spot to backtrack to. + if current.required_by.empty? && !conflicts.empty? + # Check the current "catch" stack for the first one that is included in the + # conflicts set. That is where the parent of the conflicting gem was required. + # By jumping back to this spot, we can try other version of the parent of + # the conflicting gem, hopefully finding a combination that activates correctly. + @stack.reverse_each do |savepoint| + if conflicts.include?(savepoint) + throw savepoint + end + end + end + end + end + + def resolve_requirement(spec, requirement, reqs, activated) + # We are going to try activating the spec. We need to keep track of stack of + # requirements that got us to the point of activating this gem. + spec.required_by.replace requirement.required_by + spec.required_by << requirement + + activated[spec.name] = spec + + # Now, we have to loop through all child dependencies and add them to our + # array of requirements. + spec.dependencies.each do |dep| + next if dep.type == :development + dep.required_by << requirement + reqs << dep end - Engine.resolve(deps, source_index, logger) + # We create a savepoint and mark it by the name of the requirement that caused + # the gem to be activated. If the activated gem ever conflicts, we are able to + # jump back to this point and try another version of the gem. + length = @stack.length + @stack << requirement.name + retval = catch(requirement.name) do + resolve(reqs, activated) + end + # Since we're doing a lot of throw / catches. A push does not necessarily match + # up to a pop. So, we simply slice the stack back to what it was before the catch + # block. + @stack.slice!(length..-1) + retval + end + + def search(dependency) + @cache[dependency.hash] ||= begin + @specs[dependency.name].values.select do |spec| + dependency =~ spec + end.sort_by {|s| s.version } + end end end end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb deleted file mode 100644 index 2b7b48211c..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb +++ /dev/null @@ -1,61 +0,0 @@ -module Bundler - module Resolver - module Builders - def build_index(&block) - index = Gem::SourceIndex.new - IndexBuilder.run(index, &block) if block_given? - index - end - - def build_spec(name, version, &block) - spec = Gem::Specification.new - spec.instance_variable_set(:@name, name) - spec.instance_variable_set(:@version, Gem::Version.new(version)) - DepBuilder.run(spec, &block) if block_given? - spec - end - - def build_dep(name, requirements, type = :runtime) - Gem::Dependency.new(name, requirements, type) - end - - class IndexBuilder - include Builders - - def self.run(index, &block) - new(index).run(&block) - end - - def initialize(index) - @index = index - end - - def run(&block) - instance_eval(&block) - end - - def add_spec(*args, &block) - @index.add_spec(build_spec(*args, &block)) - end - end - - class DepBuilder - def self.run(spec, &block) - new(spec).run(&block) - end - - def initialize(spec) - @spec = spec - end - - def run(&block) - instance_eval(&block) - end - - def runtime(name, requirements) - @spec.add_runtime_dependency(name, requirements) - end - end - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb deleted file mode 100644 index 475ba516ff..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/engine.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Bundler - module Resolver - class ClosedSet < Set - end - - class Engine - include Search, Inspect - - def self.resolve(deps, source_index, logger) - new(deps, source_index, logger).resolve - end - - def initialize(deps, source_index, logger) - @deps, @source_index, @logger = deps, source_index, logger - logger.debug "searching for #{gem_resolver_inspect(@deps)}" - end - attr_reader :deps, :source_index, :logger, :solution - - def resolve - state = State.initial(self, [], Stack.new, Stack.new([[[], @deps.dup]])) - if solution = search(state) - logger.info "got the solution with #{solution.all_specs.size} specs" - solution.dump(Logger::INFO) - solution - end - end - - def open - @open ||= [] - end - - def closed - @closed ||= ClosedSet.new - end - end - end - -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb deleted file mode 100644 index 59640aa5f3..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/inspect.rb +++ /dev/null @@ -1,24 +0,0 @@ -module Bundler - module Resolver - module Inspect - def gem_resolver_inspect(o) - case o - when Gem::Specification - "#" - when Array - '[' + o.map {|x| gem_resolver_inspect(x)}.join(", ") + ']' - when Set - gem_resolver_inspect(o.to_a) - when Hash - '{' + o.map {|k,v| "#{gem_resolver_inspect(k)} => #{gem_resolver_inspect(v)}"}.join(", ") + '}' - when Stack - o.gem_resolver_inspect - else - o.inspect - end - end - - module_function :gem_resolver_inspect - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb deleted file mode 100644 index 34102ff04c..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/search.rb +++ /dev/null @@ -1,71 +0,0 @@ -module Bundler - module Resolver - module Search - def search(initial, max_depth = (1.0 / 0.0)) - if initial.goal_met? - return initial - end - - open << initial - - while open.any? - current = open.pop - closed << current - - new = [] - current.each_possibility do |attempt| - unless closed.include?(attempt) - if attempt.goal_met? - return attempt - elsif attempt.depth < max_depth - new << attempt - end - end - end - new.reverse.each do |state| - open << state - end - end - - nil - end - - def open - raise "implement #open in #{self.class}" - end - - def closed - raise "implement #closed in #{self.class}" - end - - module Node - def self.included(base) - base.extend(ClassMethods) - end - - module ClassMethods - def initial(*data) - new(0, *data) - end - end - - def initialize(depth) - @depth = depth - end - attr_reader :depth - - def child(*data) - self.class.new(@depth + 1, *data) - end - - def each_possibility - raise "implement #each_possibility on #{self.class}" - end - - def goal_met? - raise "implement #goal_met? on #{self.class}" - end - end - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb deleted file mode 100644 index 6e1ac67e1f..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/stack.rb +++ /dev/null @@ -1,72 +0,0 @@ -module Bundler - module Resolver - class Stack - def initialize(initial = []) - @data = [] - initial.each do |(path,value)| - self[path] = value - end - end - - def last - @data.last - end - - def []=(path, value) - raise ArgumentError, "#{path.inspect} already has a value" if key?(path) - @data << [path.dup, value] - end - - def [](path) - if key?(path) - _, value = @data.find do |(k,v)| - k == path - end - value - else - raise "No value for #{path.inspect}" - end - end - - def key?(path) - @data.any? do |(k,v)| - k == path - end - end - - def each - @data.each do |(k,v)| - yield k, v - end - end - - def map - @data.map do |(k,v)| - yield k, v - end - end - - def each_value - @data.each do |(k,v)| - yield v - end - end - - def dup - self.class.new(@data.dup) - end - - def to_s - @data.to_s - end - - def inspect - @data.inspect - end - - def gem_resolver_inspect - Inspect.gem_resolver_inspect(@data) - end - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb b/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb deleted file mode 100644 index f13ecbbee7..0000000000 --- a/railties/lib/vendor/bundler/lib/bundler/resolver/state.rb +++ /dev/null @@ -1,172 +0,0 @@ -module Bundler - module Resolver - class State - include Search::Node, Inspect - - def initialize(depth, engine, path, spec_stack, dep_stack) - super(depth) - @engine, @path, @spec_stack, @dep_stack = engine, path, spec_stack, dep_stack - end - attr_reader :path - - def logger - @engine.logger - end - - def goal_met? - logger.info "checking if goal is met" - dump - no_duplicates? - all_deps.all? do |dep| - dependency_satisfied?(dep) - end - end - - def no_duplicates? - names = [] - all_specs.each do |s| - if names.include?(s.name) - raise "somehow got duplicates for #{s.name}" - end - names << s.name - end - end - - def dependency_satisfied?(dep) - all_specs.any? do |spec| - spec.satisfies_requirement?(dep) - end - end - - def each_possibility(&block) - index, dep = remaining_deps.first - if dep - logger.warn "working on #{dep} for #{spec_name}" - handle_dep(index, dep, &block) - else - logger.warn "no dependencies left for #{spec_name}" - jump_to_parent(&block) - end - end - - def handle_dep(index, dep) - specs = @engine.source_index.search(dep) - - specs.reverse.each do |s| - logger.info "attempting with spec: #{s.full_name}" - new_path = @path + [index] - new_spec_stack = @spec_stack.dup - new_dep_stack = @dep_stack.dup - - new_spec_stack[new_path] = s - new_dep_stack[new_path] = s.runtime_dependencies.sort_by do |dep| - @engine.source_index.search(dep).size - end - yield child(@engine, new_path, new_spec_stack, new_dep_stack) - end - end - - def jump_to_parent - if @path.empty? - dump - logger.warn "at the end" - return - end - - logger.info "jumping to parent for #{spec_name}" - new_path = @path[0..-2] - new_spec_stack = @spec_stack.dup - new_dep_stack = @dep_stack.dup - - yield child(@engine, new_path, new_spec_stack, new_dep_stack) - end - - def remaining_deps - remaining_deps_for(@path) - end - - def remaining_deps_for(path) - no_duplicates? - remaining = [] - @dep_stack[path].each_with_index do |dep,i| - remaining << [i, dep] unless all_specs.find {|s| s.name == dep.name} - end - remaining - end - - def deps - @dep_stack[@path] - end - - def spec - @spec_stack[@path] - end - - def spec_name - @path.empty? ? "" : spec.full_name - end - - def all_deps - all_deps = Set.new - @dep_stack.each_value do |deps| - all_deps.merge(deps) - end - all_deps.to_a - end - - def all_specs - @spec_stack.map do |path,spec| - spec - end - end - - def dump(level = Logger::DEBUG) - logger.add level, "v" * 80 - logger.add level, "path: #{@path.inspect}" - logger.add level, "deps: (#{deps.size})" - deps.map do |dep| - logger.add level, gem_resolver_inspect(dep) - end - logger.add level, "remaining_deps: (#{remaining_deps.size})" - remaining_deps.each do |dep| - logger.add level, gem_resolver_inspect(dep) - end - logger.add level, "dep_stack: " - @dep_stack.each do |path,deps| - logger.add level, "#{path.inspect} (#{deps.size})" - deps.each do |dep| - logger.add level, "-> #{gem_resolver_inspect(dep)}" - end - end - logger.add level, "spec_stack: " - @spec_stack.each do |path,spec| - logger.add level, "#{path.inspect}: #{gem_resolver_inspect(spec)}" - end - logger.add level, "^" * 80 - end - - def to_dot - io = StringIO.new - io.puts 'digraph deps {' - io.puts ' fontname = "Courier";' - io.puts ' mincross = 4.0;' - io.puts ' ratio = "auto";' - dump_to_dot(io, "", []) - io.puts '}' - io.string - end - - def dump_to_dot(io, name, path) - @dep_stack[path].each_with_index do |dep,i| - new_path = path + [i] - spec_name = all_specs.find {|x| x.name == dep.name}.full_name - io.puts ' "%s" -> "%s";' % [name, dep.to_s] - io.puts ' "%s" -> "%s";' % [dep.to_s, spec_name] - if @spec_stack.key?(new_path) - dump_to_dot(io, spec_name, new_path) - end - end - end - end - end -end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/runtime.rb b/railties/lib/vendor/bundler/lib/bundler/runtime.rb index 2376deef28..27e0254966 100644 --- a/railties/lib/vendor/bundler/lib/bundler/runtime.rb +++ b/railties/lib/vendor/bundler/lib/bundler/runtime.rb @@ -1,39 +1,2 @@ -module Bundler - class ManifestBuilder - - attr_reader :sources - - def self.build(path, string) - builder = new(path) - builder.instance_eval(string) - builder.to_manifest - end - - def self.load(path, file) - string = File.read(file) - build(path, string) - end - - def initialize(path) - @path = path - @sources = %w(http://gems.rubyforge.org) - @dependencies = [] - end - - def to_manifest - Manifest.new(@sources, @dependencies, @path) - end - - def source(source) - @sources << source - end - - def gem(name, *args) - options = args.last.is_a?(Hash) ? args.pop : {} - version = args.last - - @dependencies << Dependency.new(name, options.merge(:version => version)) - end - - end -end \ No newline at end of file +require File.join(File.dirname(__FILE__), "runtime", "dsl") +require File.join(File.dirname(__FILE__), "runtime", "dependency") \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/source.rb b/railties/lib/vendor/bundler/lib/bundler/source.rb new file mode 100644 index 0000000000..37828ca316 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/source.rb @@ -0,0 +1,150 @@ +module Bundler + # Represents a source of rubygems. Initially, this is only gem repositories, but + # eventually, this will be git, svn, HTTP + class Source + attr_accessor :tmp_path + end + + class GemSource < Source + attr_reader :uri + + def initialize(options) + @uri = options[:uri] + @uri = URI.parse(@uri) unless @uri.is_a?(URI) + raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute? + end + + def gems + @specs ||= fetch_specs + end + + def ==(other) + uri == other.uri + end + + def to_s + @uri.to_s + end + + class RubygemsRetardation < StandardError; end + + def download(spec, repository) + Bundler.logger.info "Downloading #{spec.full_name}.gem" + + destination = repository.download_path_for(:gem) + + unless destination.writable? + raise RubygemsRetardation + end + + Gem::RemoteFetcher.fetcher.download(spec, uri, repository.download_path_for(:gem)) + end + + private + + def fetch_specs + Bundler.logger.info "Updating source: #{to_s}" + + deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/Marshal.4.8.Z") + inflated = Gem.inflate deflated + + index = Marshal.load(inflated) + index.gems + rescue Gem::RemoteFetcher::FetchError => e + raise ArgumentError, "#{to_s} is not a valid source: #{e.message}" + end + end + + class DirectorySource < Source + def initialize(options) + @name = options[:name] + @version = options[:version] + @location = options[:location] + @require_paths = options[:require_paths] || %w(lib) + end + + def gems + @gems ||= begin + specs = {} + + # Find any gemspec files in the directory and load those specs + Dir[@location.join('**', '*.gemspec')].each do |file| + path = Pathname.new(file).relative_path_from(@location).dirname + spec = eval(File.read(file)) + spec.require_paths.map! { |p| path.join(p) } + specs[spec.full_name] = spec + end + + # If a gemspec for the dependency was not found, add it to the list + if specs.keys.grep(/^#{Regexp.escape(@name)}/).empty? + case + when @version.nil? + raise ArgumentError, "If you use :at, you must specify the gem" \ + "and version you wish to stand in for" + when !Gem::Version.correct?(@version) + raise ArgumentError, "If you use :at, you must specify a gem and" \ + "version. You specified #{@version} for the version" + end + + default = Gem::Specification.new do |s| + s.name = @name + s.version = Gem::Version.new(@version) if @version + end + specs[default.full_name] = default + end + + specs + end + end + + def ==(other) + # TMP HAX + other.is_a?(DirectorySource) + end + + def to_s + "#{@name} (#{@version}) Located at: '#{@location}'" + end + + def download(spec, repository) + spec.require_paths.map! { |p| File.join(@location, p) } + repository.add_spec(:directory, spec) + end + end + + class GitSource < DirectorySource + def initialize(options) + super + @uri = options[:uri] + @ref = options[:ref] + @branch = options[:branch] + end + + def gems + FileUtils.mkdir_p(tmp_path.join("gitz")) + + # TMP HAX to get the *.gemspec reading to work + @location = tmp_path.join("gitz", @name) + + Bundler.logger.info "Cloning git repository at: #{@uri}" + `git clone #{@uri} #{@location} --no-hardlinks` + + if @ref + Dir.chdir(@location) { `git checkout #{@ref}` } + elsif @branch && @branch != "master" + Dir.chdir(@location) { `git checkout origin/#{@branch}` } + end + super + end + + def download(spec, repository) + dest = repository.download_path_for(:directory).join(@name) + spec.require_paths.map! { |p| File.join(dest, p) } + repository.add_spec(:directory, spec) + if spec.name == @name + FileUtils.mkdir_p(dest.dirname) + FileUtils.mv(tmp_path.join("gitz", spec.name), dest) + end + end + end +end \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/templates/app_script.erb b/railties/lib/vendor/bundler/lib/bundler/templates/app_script.erb new file mode 100644 index 0000000000..3e47a53ca8 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/templates/app_script.erb @@ -0,0 +1,3 @@ +<%= shebang bin_file_name %> +require File.join(File.dirname(__FILE__), "<%= path.join("environment").relative_path_from(Pathname.new(bin_dir)) %>") +load File.join(File.dirname(__FILE__), "<%= path.join("gems", @spec.full_name, @spec.bindir, bin_file_name).relative_path_from(Pathname.new(bin_dir)) %>") \ No newline at end of file diff --git a/railties/lib/vendor/bundler/lib/bundler/templates/environment.erb b/railties/lib/vendor/bundler/lib/bundler/templates/environment.erb new file mode 100644 index 0000000000..21f3de8854 --- /dev/null +++ b/railties/lib/vendor/bundler/lib/bundler/templates/environment.erb @@ -0,0 +1,127 @@ +# DO NOT MODIFY THIS FILE +module Bundler + dir = File.dirname(__FILE__) + +<% unless options[:system_gems] -%> + ENV["GEM_HOME"] = dir + ENV["GEM_PATH"] = dir +<% end -%> + ENV["PATH"] = "#{dir}/<%= bindir %>:#{ENV["PATH"]}" + ENV["RUBYOPT"] = "-r#{__FILE__} #{ENV["RUBYOPT"]}" + +<% load_paths.each do |load_path| -%> + $LOAD_PATH.unshift File.expand_path("#{dir}/<%= load_path %>") +<% end -%> + + @gemfile = "#{dir}/<%= filename %>" + +<% if options[:rubygems] -%> + require "rubygems" + + @bundled_specs = {} +<% spec_files.each do |name, path| -%> + @bundled_specs["<%= name %>"] = eval(File.read("#{dir}/<%= path %>")) + @bundled_specs["<%= name %>"].loaded_from = "#{dir}/<%= path %>" +<% end -%> + + def self.add_specs_to_loaded_specs + Gem.loaded_specs.merge! @bundled_specs + end + + def self.add_specs_to_index + @bundled_specs.each do |name, spec| + Gem.source_index.add_spec spec + end + end + + add_specs_to_loaded_specs + add_specs_to_index +<% end -%> + + def self.require_env(env = nil) + context = Class.new do + def initialize(env) @env = env && env.to_s ; end + def method_missing(*) ; end + def only(env) + old, @only = @only, _combine_onlys(env) + yield + @only = old + end + def except(env) + old, @except = @except, _combine_excepts(env) + yield + @except = old + end + def gem(name, *args) + opt = args.last || {} + only = _combine_onlys(opt[:only] || opt["only"]) + except = _combine_excepts(opt[:except] || opt["except"]) + files = opt[:require_as] || opt["require_as"] || name + + return unless !only || only.any? {|e| e == @env } + return if except && except.any? {|e| e == @env } + + files.each { |f| require f } + yield if block_given? + true + end + private + def _combine_onlys(only) + return @only unless only + only = [only].flatten.compact.uniq.map { |o| o.to_s } + only &= @only if @only + only + end + def _combine_excepts(except) + return @except unless except + except = [except].flatten.compact.uniq.map { |o| o.to_s } + except |= @except if @except + except + end + end + context.new(env && env.to_s).instance_eval(File.read(@gemfile)) + end +end + +<% if options[:rubygems] -%> +module Gem + def source_index.refresh! + super + Bundler.add_specs_to_index + end +end +<% else -%> +$" << "rubygems.rb" + +module Kernel + def gem(*) + # Silently ignore calls to gem, since, in theory, everything + # is activated correctly already. + end +end + +# Define all the Gem errors for gems that reference them. +module Gem + def self.ruby ; <%= Gem.ruby.inspect %> ; end + class LoadError < ::LoadError; end + class Exception < RuntimeError; end + class CommandLineError < Exception; end + class DependencyError < Exception; end + class DependencyRemovalException < Exception; end + class GemNotInHomeException < Exception ; end + class DocumentError < Exception; end + class EndOfYAMLException < Exception; end + class FilePermissionError < Exception; end + class FormatException < Exception; end + class GemNotFoundException < Exception; end + class InstallError < Exception; end + class InvalidSpecificationException < Exception; end + class OperationNotSupportedError < Exception; end + class RemoteError < Exception; end + class RemoteInstallationCancelled < Exception; end + class RemoteInstallationSkipped < Exception; end + class RemoteSourceException < Exception; end + class VerificationError < Exception; end + class SystemExitException < SystemExit; end +end +<% end -%> \ No newline at end of file -- cgit v1.2.3