original version

This commit is contained in:
BOHUNG 2019-10-06 23:45:34 +08:00
commit 6cfe2d1bf4
275 changed files with 36951 additions and 0 deletions

25
bundler-1.7.2/.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# Please do not submit patches for including directives to ignore IDE/editor
# generated files. Use a global gitignore as described in
# https://help.github.com/articles/ignoring-files and find useful gitignore
# samples at https://github.com/github/gitignore
# system crap
.DS_Store
.*.swp
# files created by running the specs
tmp/
# built gems
pkg/
*.gem
# rubinius bytecode
*.rbc
.rbx/
# output from ronn
lib/bundler/man/
# output from ci_reporter
spec/reports/

2
bundler-1.7.2/.rspec Normal file
View File

@ -0,0 +1,2 @@
--format documentation
--color

96
bundler-1.7.2/.travis.yml Normal file
View File

@ -0,0 +1,96 @@
language: ruby
script: rake spec:travis
before_script: travis_retry rake spec:travis:deps
branches:
only:
- master
- 1-7-stable
- 1-6-stable
- 1-5-stable
- 1-3-stable
- 1-2-stable
- 1-1-stable
- 1-0-stable
notifications:
email:
# andre
- secure: "bCcvqJT7YrBawtkXXwHhT+jOFth7r2Qv/30PkkbhQxk6Jb3xambjCOJ3U6vJ\ngYmiL50exi5lUp3oc3SEbHN5t2CrZqOZDQ6o7P8EAmB5c0oH2RrYaFOkI5Gt\nul/jGH/96A9sj0aMwG7JfdMSfhqj1DUKAm2PnnbXPL853VfmT24="
# terence
- secure: "MQ8eA5Jb8YzEpAo58DRGfVJklAPcEbAulpBZnTxp0am6ldneDtJHbQk21w6R\nj5GsDHlzr/lMp/GHIimtUZ7rLohfND8fj/W7fs1Dkd4eN02/ERt98x3pHlqv\nvZgSnZ39uVYv+OcphraE24QaRaGWLhWZAMYQTVe/Yz50NyG8g1U="
campfire:
on_success: change
on_failure: always
rooms:
# Bundler Ops
secure: MNTSGIySYwHia5gIgRiZxXtPMPDIP9KmzQk7Kq2ZoVvP3mIk8W1TMkvcyFkEf6uCasyVZZixzUBfY+E0BlHAz1ycQpTh1jvSpuIpEVYW48ShJldJ+8W8xfzafyOHii3z7VrDaomEffmMDdmHRsbQAfekMjdR4bTpXtT9V+wOXlg=
rvm:
- 2.1.1
- 2.0.0
- 1.9.3
- 1.8.7
# Rubygems versions MUST be available as rake tasks
# see Rakefile:66 for the list of possible RGV values
env:
# We need to know if changes to rubygems will break bundler on release
- RGV=master
# Test the latest rubygems release with all of our supported rubies
- RGV=v2.2.2
matrix:
include:
# Ruby 2.0.0, Rubygems 2.0 and up
- rvm: 2.0.0
env: RGV=v2.1.11
- rvm: 2.0.0
env: RGV=v2.0.14
# Ruby 1.9.3, Rubygems 1.5.3 and up
- rvm: 1.9.3
env: RGV=v2.1.11
- rvm: 1.9.3
env: RGV=v2.0.14
- rvm: 1.9.3
env: RGV=v1.8.29
- rvm: 1.9.3
env: RGV=v1.7.2
- rvm: 1.9.3
env: RGV=v1.6.2
- rvm: 1.9.3
env: RGV=v1.5.3
# Ruby 1.8.7, Rubygems 1.3.6 and up
- rvm: 1.8.7
env: RGV=v2.1.11
- rvm: 1.8.7
env: RGV=v2.0.14
- rvm: 1.8.7
env: RGV=v1.8.29
- rvm: 1.8.7
env: RGV=v1.7.2
- rvm: 1.8.7
env: RGV=v1.6.2
- rvm: 1.8.7
env: RGV=v1.5.3
- rvm: 1.8.7
env: RGV=v1.4.2
- rvm: 1.8.7
env: RGV=v1.3.7
- rvm: 1.8.7
env: RGV=v1.3.6
# ALLOWED FAILURES
# Ruby 1.9.2 sanity check
# (but it's just too slow and sometimes goes over the Travis limit)
- rvm: 1.9.2
env: RGV=v2.2.2
# Ruby-head (we want to know how we're doing, but not fail the build)
- rvm: ruby-head
env: RGV=master
# JRuby, the latest (not maintained, but good to know)
- rvm: jruby
env: RGV=v2.2.2
# Rubinius, the latest (not maintained, but good to know)
- rvm: rbx
env: RGV=v2.2.2
allow_failures:
- rvm: ruby-head
- rvm: 1.9.2
- rvm: jruby
- rvm: rbx

1680
bundler-1.7.2/CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
# Contributing
Bundler welcomes contributions from *everyone*. While contributing, please follow the project [code of conduct](http://bundler.io/conduct.html), so that everyone can be included.
Here are some ways you can contribute:
- by using prerelease versions
- by reporting bugs
- by suggesting new features
- by writing or editing documentation
- by closing issues
- by reviewing patches
- by refactoring code
- by writing code (no patch is too small! fix typos or bad whitespace)
If you'd like to help make Bundler better, you totally rock! Please check out the [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md) file for an introduction to the project, guidelines for contributing, and details about what would be helpful.
Thanks for helping us make Bundler better.
# Troubleshooting
If you're having a problem, please see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md) for troubleshooting steps and a guide for how to submit a ticket that will help us solve the problem you are having as quickly as possible.
# Requesting Features
Head on over to the [Bundler features](https://github.com/bundler/bundler-features) project, or any of the lists or channels listed below. Feature-wise we consider Bundler stable, so the core team does not tend to work on feature suggestions. Pull requests welcome though!
# Discussing Bundler
If you'd like to discuss features, ask questions, or just engage in general Bundler-focused discussion, please see the [#bundler](irc://irc.freenode.net/#bundler) IRC channel on Freenode, and the [Bundler mailing list](http://groups.google.com/group/ruby-bundler) on Google Groups.

View File

@ -0,0 +1,117 @@
Great to have you here! Here are a few ways you can help out with [Bundler](http://github.com/bundler/bundler).
# Where should I start?
You can start learning about Bundler by reading [the documentation](http://bundler.io). If you want, you can also read a (lengthy) explanation of [why Bundler exists and what it does](http://bundler.io/v1.5/rationale.html). You can also check out discussions about Bundler on the [Bundler mailing list](https://groups.google.com/group/ruby-bundler) and in the [Bundler IRC channel](http://webchat.freenode.net/?channels=%23bundler), which is #bundler on Freenode.
## Your first commits
If youre interested in contributing to Bundler, thats awesome! Wed love your help.
If you have any questions after reading this page, please feel free to contact either [@indirect](http://github.com/indirect) or [@hone](http://github.com/hone). They are both happy to provide help working through your first bugfix or thinking through the problem youre trying to resolve.
## Tackle some small problems
We track [small
bugs](https://github.com/bundler/bundler/issues?labels=small&state=open) and [small features](https://github.com/bundler/bundler-features/issues?labels=small&state=open) so that anyone who wants to help can start with something that's not too overwhelming. If nothing on those lists looks good, though, just talk to us.
# Development setup
Bundler doesn't use a Gemfile to list development dependencies, because when we tried it we couldn't tell if we were awake or it was just another level of dreams. To work on Bundler, you'll probably want to do a couple of things.
1. Install Bundler's development dependencies
$ rake spec:deps
2. Run the test suite, to make sure things are working
$ rake spec
3. Set up a shell alias to run Bundler from your clone, e.g. a Bash alias:
$ alias dbundle='ruby -I /path/to/bundler/lib /path/to/bundler/bin/bundle'
With that set up, you can test changes you've made to Bundler by running `dbundle`, without interfering with the regular `bundle` command.
# Bug triage
Triage is the work of processing tickets that have been opened into actionable issues, feature requests, or bug reports. That includes verifying bugs, categorizing the ticket, and ensuring there's enough information to reproduce the bug for anyone who wants to try to fix it.
We've created an [issues guide](https://github.com/bundler/bundler/blob/master/ISSUES.md) to walk Bundler users through the process of troubleshooting issues and reporting bugs.
If you'd like to help, awesome! You can [report a new bug](https://github.com/bundler/bundler/issues/new) or browse our [existing open tickets](https://github.com/bundler/bundler/issues).
Not every ticket will point to a bug in Bundler's code, but open tickets usually mean that there is something we could improve to help that user. Sometimes that means writing additional documentation, sometimes that means making error messages clearer, and sometimes that means explaining to a user that they need to install git to use git gems.
When you're looking at a ticket, here are the main questions to ask:
* Can I reproduce this bug myself?
* Are the steps to reproduce clearly stated in the ticket?
* Which versions of Bundler (1.1.x, 1.2.x, git, etc.) manifest this bug?
* Which operating systems (OS X, Windows, Ubuntu, CentOS, etc.) manifest this bug?
* Which rubies (MRI, JRuby, Rubinius, etc.) and which versions (1.8.7, 1.9.3, etc.) have this bug?
If you can't reproduce an issue, chances are good that the bug has been fixed (hurrah!). That's a good time to post to the ticket explaining what you did and how it worked.
If you can reproduce an issue, you're well on your way to fixing it. :) Fixing issues is similar to adding new features:
1. Discuss the fix on the existing issue. Coordinating with everyone else saves duplicate work and serves as a great way to get suggestions and ideas if you need any.
2. Base your commits on the correct branch. Bugfixes for 1.x versions of Bundler should be based on the matching 1-x-stable branch.
3. Commit the code and at least one test covering your changes to a named branch in your fork.
4. Put a line in the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md) summarizing your changes under the next release under the “Bugfixes” heading.
5. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your bugfix branch.
Finally, the ticket may be a duplicate of another older ticket. If you notice a ticket is a duplicate, simply comment on the ticket noting the original tickets number. For example, you could say “This is a duplicate of issue #42, and can be closed”.
# Adding new features
If you would like to add a new feature to Bundler, please follow these steps:
1. [Create an issue](https://github.com/bundler/bundler-features/issues/new) to discuss your feature.
2. Base your commits on the master branch, since we follow [SemVer](http://semver.org) and don't add new features to old releases.
3. Commit the code and at least one test covering your changes to a feature branch in your fork.
4. Put a line in the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md) summarizing your changes under the next release under the "Features" heading.
5. Send us a [pull request](https://help.github.com/articles/using-pull-requests) from your feature branch.
If you don't hear back immediately, dont get discouraged! We all have day jobs, but we respond to most tickets within a day or two.
# Beta testing
Early releases require heavy testing, especially across various system setups. We :heart: testers, and are big fans of anyone who can run `gem install bundler --pre` and try out upcoming releases in their development and staging environments.
There may not always be prereleases or beta versions of Bundler. That said, you are always welcome to try checking out master and building a gem yourself if you want to try out the latest changes.
# Translations
We don't currently have any translations, but please reach out to us if you would like to help get this going.
# Documentation
Code needs explanation, and sometimes those who know the code well have trouble explaining it to someone just getting into it. Because of that, we welcome documentation suggestions and patches from everyone, especially if they are brand new to using Bundler.
Bundler has two main sources of documentation: the built-in help (including usage information and man pages) and the [Bundler documentation site](http://bundler.io).
If youd like to submit a patch to the man pages, follow the steps for adding a feature above. All of the man pages are located in the `man` directory. Just use the “Documentation” heading when you describe what you did in the changelog.
If you have a suggestion or proposed change for [bundler.io](http://bundler.io), please open an issue or send a pull request to the [bundler-site](https://github.com/bundler/bundler-site) repository.
# Community
Community is an important part of all we do. If youd like to be part of the Bundler community, you can jump right in and start helping make Bundler better for everyone who uses it.
It would be tremendously helpful to have more people answering questions about Bundler (and often simply about Rubygems or Ruby itself) in our [issue tracker](https://github.com/bundler/bundler/issues) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/bundler).
Additional documentation and explanation is always helpful, too. If you have any suggestions for the Bundler website [bundler.io](http://bundler.io), we would absolutely love it if you opened an issue or pull request on the [bundler-site](https://github.com/bundler/bundler-site) repository.
Finally, sharing your experiences and discoveries by writing them up is a valuable way to help others who have similar problems or experiences in the future. You can write a blog post, create an example and commit it to Github, take screenshots, or make videos.
Examples of how Bundler is used help everyone, and weve discovered that people already use it in ways that we never imagined when we were writing it. If youre still not sure what to write about, there are also several projects doing interesting things based on Bundler. They could probably use publicity too.
If you let someone on the core team know you wrote about Bundler, we will add your post to the list of Bundler resources on the Github project wiki.

96
bundler-1.7.2/ISSUES.md Normal file
View File

@ -0,0 +1,96 @@
# Bundler Issues
So! You're having problems with Bundler. This file is here to help. If you're running into an error, try reading the rest of this file for help. If you can't figure out how to solve your problem, there are also instructions on how to report a bug.
**Please use the [Bundler
Features](https://github.com/bundler/bundler-features) repo to suggest and
discuss features. The bundler issue tracker is only for bugs.**
## Documentation
Instructions for common Bundler uses can be found on the [Bundler documentation site](http://bundler.io/).
Detailed information about each Bundler command, including help with common problems, can be found in the [Bundler man pages](http://bundler.io/v1.3/man/bundle.1.html).
## Troubleshooting
### Heroku errors
Please open a ticket with Heroku if you're having trouble deploying. They have a professional support team who can help you resolve Heroku issues far better than the Bundler team can. If the problem that you are having turns out to be a bug in Bundler itself, Heroku support can get the exact details to us.
### Other problems
First, figure out exactly what it is that you're trying to do. Then, go to the [Bundler documentation website](http://bundler.io) and see if we have instructions on how to do that.
Second, check [the compatibility
list](http://bundler.io/compatibility.html), and make sure that the version of Bundler that you are
using works with the versions of Ruby and Rubygems that you are using.
If the instructions don't work, or you can't find any instructions, you can try these troubleshooting steps:
# remove user-specific gems and git repos
rm -rf ~/.bundle/ ~/.gem/bundler/ ~/.gems/cache/bundler/
# remove system-wide git repos and git checkouts
rm -rf $GEM_HOME/bundler/ $GEM_HOME/cache/bundler/
# remove project-specific settings
rm -rf .bundle/
# remove project-specific cached gems and repos
rm -rf vendor/cache/
# remove the saved resolve of the Gemfile
rm -rf Gemfile.lock
# uninstall the rubygems-bundler and open_gem gems
rvm gemset use global # if using rvm
gem uninstall rubygems-bundler open_gem
# try to install one more time
bundle install
## Reporting unresolved problems
Hopefully the troubleshooting steps above resolved your problem. If things still aren't working the way you expect them to, please let us know so that we can diagnose and hopefully fix the problem you're having.
**The best way to report a bug is by providing a reproduction script.** See these examples:
* [Git environment variables causing install to fail.](https://gist.github.com/xaviershay/6207550)
* [Multiple gems in a repository cannot be updated independently.](https://gist.github.com/xaviershay/6295889)
A half working script with comments for the parts you were unable to automate is still appreciated.
If you are unable to do that, please include the following information in your report:
- What you're trying to accomplish
- The command you ran
- What you expected to happen
- What actually happened
- The exception backtrace(s), if any
- Everything output by running `bundle env`
If your version of Bundler does not have the `bundle env` command, then please include:
- Your Gemfile
- Your Gemfile.lock
- Your Bundler configuration settings (run `bundle config`)
- What version of bundler you are using (run `bundle -v`)
- What version of Ruby you are using (run `ruby -v`)
- What version of Rubygems you are using (run `gem -v`)
- Whether you are using RVM, and if so what version (run `rvm -v`)
- Whether you have the `rubygems-bundler` gem, which can break gem executables (run `gem list rubygems-bundler`)
- Whether you have the `open_gem` gem, which can cause rake activation conflicts (run `gem list open_gem`)
If you are using Rails 2.3, please also include:
- Your boot.rb file
- Your preinitializer.rb file
- Your environment.rb file
If you have either `rubygems-bundler` or `open_gem` installed, please try removing them and then following the troubleshooting steps above before opening a new ticket.
[Create a gist](https://gist.github.com) containing all of that information, then visit the [Bundler issue tracker](https://github.com/bundler/bundler/issues) and [create a ticket](https://github.com/bundler/bundler/issues/new) describing your problem and linking to your gist.
Thanks for reporting issues and helping make Bundler better!

23
bundler-1.7.2/LICENSE.md Normal file
View File

@ -0,0 +1,23 @@
Portions copyright (c) 2010 Andre Arko
Portions copyright (c) 2009 Engine Yard
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

40
bundler-1.7.2/README.md Normal file
View File

@ -0,0 +1,40 @@
[![Code Climate](https://img.shields.io/codeclimate/github/bundler/bundler.svg)](https://codeclimate.com/github/bundler/bundler)
[![Build Status](https://img.shields.io/travis/bundler/bundler/master.svg)](https://travis-ci.org/bundler/bundler)
[![Version ](https://img.shields.io/gem/v/bundler.svg)](https://rubygems.org/gems/bundler)
# Bundler: a gem to bundle gems
Bundler keeps ruby applications running the same code on every machine.
It does this by managing the gems that the application depends on. Given a list of gems, it can automatically download and install those gems, as well as any other gems needed by the gems that are listed. Before installing gems, it checks the versions of every gem to make sure that they are compatible, and can all be loaded at the same time. After the gems have been installed, Bundler can help you update some or all of them when new versions become available. Finally, it records the exact versions that have been installed, so that others can install the exact same gems.
### Installation and usage
```
gem install bundler
bundle init
echo "gem 'rails'" >> Gemfile
bundle install
bundle exec rails new myapp
```
See [bundler.io](http://bundler.io) for the full documentation.
### Troubleshooting
For help with common problems, see [ISSUES](https://github.com/bundler/bundler/blob/master/ISSUES.md).
### Contributing
If you'd like to contribute to Bundler, that's awesome, and we <3 you. There's a guide to contributing to Bundler (both code and general help) over in [DEVELOPMENT](https://github.com/bundler/bundler/blob/master/DEVELOPMENT.md)
The `master` branch contains our current progress towards version 1.5. Versions 1.0-1.3 each have their own stable branches. Please submit bugfixes as pull requests to the stable branch for the version you would like to fix.
### Core Team
The Bundler core team consists of André Arko ([@indirect](http://github.com/indirect)), Terence Lee ([@hone](http://github.com/hone)), and Jessica Lynn Suttles ([@jlsuttles](http://github.com/jlsuttles)), with support and advice from original Bundler author Yehuda Katz ([@wycats](http://github.com/wycats)).
### Other questions
To see what has changed in recent versions of Bundler, see the [CHANGELOG](https://github.com/bundler/bundler/blob/master/CHANGELOG.md).
Feel free to chat with the Bundler core team (and many other users) on IRC in the [#bundler](irc://irc.freenode.net/bundler) channel on Freenode, or via email on the [Bundler mailing list](http://groups.google.com/group/ruby-bundler).

256
bundler-1.7.2/Rakefile Normal file
View File

@ -0,0 +1,256 @@
# -*- encoding: utf-8 -*-
$:.unshift File.expand_path("../lib", __FILE__)
require 'rubygems'
require 'shellwords'
require 'benchmark'
RUBYGEMS_REPO = File.expand_path("tmp/rubygems")
def safe_task(&block)
yield
true
rescue
false
end
# Benchmark task execution
module Rake
class Task
alias_method :real_invoke, :invoke
def invoke(*args)
time = Benchmark.measure(@name) do
real_invoke(*args)
end
puts "#{@name} ran for #{time}"
end
end
end
namespace :spec do
desc "Ensure spec dependencies are installed"
task :deps do
spec = Gem::Specification.load("bundler.gemspec")
deps = Hash[spec.development_dependencies.map do |d|
[d.name, d.requirement.to_s]
end]
# JRuby can't build ronn or rdiscount, so we skip that
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
deps.delete("ronn")
deps.delete("rdiscount")
end
deps.each do |name, version|
sh "#{Gem.ruby} -S gem list -i '^#{name}$' -v '#{version}' || " \
"#{Gem.ruby} -S gem install #{name} -v '#{version}' --no-ri --no-rdoc"
end
# Download and install gems used inside tests
$LOAD_PATH.unshift("./spec")
require 'support/rubygems_ext'
Spec::Rubygems.setup
end
namespace :travis do
task :deps do
# Give the travis user a name so that git won't fatally error
system "sudo sed -i 's/1000::/1000:Travis:/g' /etc/passwd"
# Strip secure_path so that RVM paths transmit through sudo -E
system "sudo sed -i '/secure_path/d' /etc/sudoers"
# Install groff for the ronn gem
sh "sudo apt-get install groff -y"
if RUBY_VERSION < '1.9'
# Downgrade Rubygems on 1.8 so Ronn can be required
# https://github.com/rubygems/rubygems/issues/784
sh "gem update --system 2.1.11"
else
# Downgrade Rubygems so RSpec 3 can be instaled
# https://github.com/rubygems/rubygems/issues/813
sh "gem update --system 2.2.0"
end
# Install the other gem deps, etc.
Rake::Task["spec:deps"].invoke
end
end
end
begin
require 'rspec/core/rake_task'
desc "Run specs"
RSpec::Core::RakeTask.new do |t|
t.rspec_opts = %w(--format documentation --color)
t.ruby_opts = %w(-w)
end
task :spec => "man:build"
namespace :spec do
task :clean do
rm_rf 'tmp'
end
desc "Run the real-world spec suite (requires internet)"
task :realworld => ["set_realworld", "spec"]
task :set_realworld do
ENV['BUNDLER_REALWORLD_TESTS'] = '1'
end
desc "Run the spec suite with the sudo tests"
task :sudo => ["set_sudo", "spec", "clean_sudo"]
task :set_sudo do
ENV['BUNDLER_SUDO_TESTS'] = '1'
end
task :clean_sudo do
puts "Cleaning up sudo test files..."
system "sudo rm -rf #{File.expand_path('../tmp/sudo_gem_home', __FILE__)}"
end
# Rubygems specs by version
namespace :rubygems do
rubyopt = ENV["RUBYOPT"]
# When editing this list, also edit .travis.yml!
branches = %w(master 2.2)
releases = %w(v1.3.6 v1.3.7 v1.4.2 v1.5.3 v1.6.2 v1.7.2 v1.8.29 v2.0.14 v2.1.11 v2.2.2)
(branches + releases).each do |rg|
desc "Run specs with Rubygems #{rg}"
RSpec::Core::RakeTask.new(rg) do |t|
t.rspec_opts = %w(--format documentation --color)
t.ruby_opts = %w(-w)
end
# Create tasks like spec:rubygems:v1.8.3:sudo to run the sudo specs
namespace rg do
task :sudo => ["set_sudo", rg, "clean_sudo"]
task :realworld => ["set_realworld", rg]
end
task "clone_rubygems_#{rg}" do
unless File.directory?("tmp/rubygems")
system("git clone https://github.com/rubygems/rubygems.git tmp/rubygems")
end
hash = nil
Dir.chdir(RUBYGEMS_REPO) do
system("git remote update")
if rg == "master"
system("git checkout origin/master")
else
system("git checkout #{rg}") || raise("Unknown Rubygems ref #{rg}")
end
hash = `git rev-parse HEAD`.chomp
end
puts "Checked out rubygems '#{rg}' at #{hash}"
ENV["RUBYOPT"] = "-I#{File.expand_path("tmp/rubygems/lib")} #{rubyopt}"
puts "RUBYOPT=#{ENV['RUBYOPT']}"
end
task rg => ["man:build", "clone_rubygems_#{rg}"]
task "rubygems:all" => rg
end
desc "Run specs under a Rubygems checkout (set RG=path)"
RSpec::Core::RakeTask.new("co") do |t|
t.rspec_opts = %w(--format documentation --color)
t.ruby_opts = %w(-w)
end
task "setup_co" do
ENV["RUBYOPT"] = "-I#{File.expand_path ENV['RG']} #{rubyopt}"
end
task "co" => "setup_co"
task "rubygems:all" => "co"
end
desc "Run the tests on Travis CI against a rubygem version (using ENV['RGV'])"
task :travis do
rg = ENV['RGV'] || raise("Rubygems version is required on Travis!")
puts "\n\e[1;33m[Travis CI] Running bundler specs against rubygems #{rg}\e[m\n\n"
specs = safe_task { Rake::Task["spec:rubygems:#{rg}"].invoke }
Rake::Task["spec:rubygems:#{rg}"].reenable
puts "\n\e[1;33m[Travis CI] Running bundler sudo specs against rubygems #{rg}\e[m\n\n"
sudos = system("sudo -E rake spec:rubygems:#{rg}:sudo")
# clean up by chowning the newly root-owned tmp directory back to the travis user
system("sudo chown -R #{ENV['USER']} #{File.join(File.dirname(__FILE__), 'tmp')}")
Rake::Task["spec:rubygems:#{rg}"].reenable
puts "\n\e[1;33m[Travis CI] Running bundler real world specs against rubygems #{rg}\e[m\n\n"
realworld = safe_task { Rake::Task["spec:rubygems:#{rg}:realworld"].invoke }
{"specs" => specs, "sudo" => sudos, "realworld" => realworld}.each do |name, passed|
if passed
puts "\e[0;32m[Travis CI] #{name} passed\e[m"
else
puts "\e[0;31m[Travis CI] #{name} failed\e[m"
end
end
unless specs && sudos && realworld
fail "Spec run failed, please review the log for more information"
end
end
end
rescue LoadError
task :spec do
abort "Run `rake spec:deps` to be able to run the specs"
end
end
begin
require 'ronn'
namespace :man do
directory "lib/bundler/man"
Dir["man/*.ronn"].each do |ronn|
basename = File.basename(ronn, ".ronn")
roff = "lib/bundler/man/#{basename}"
file roff => ["lib/bundler/man", ronn] do
sh "#{Gem.ruby} -S ronn --roff --pipe #{ronn} > #{roff}"
end
file "#{roff}.txt" => roff do
sh "groff -Wall -mtty-char -mandoc -Tascii #{roff} | col -b > #{roff}.txt"
end
task :build_all_pages => "#{roff}.txt"
end
desc "Build the man pages"
task :build => "man:build_all_pages"
desc "Clean up from the built man pages"
task :clean do
rm_rf "lib/bundler/man"
end
end
rescue LoadError
namespace :man do
task(:build) { warn "Install the ronn gem to be able to release!" }
task(:clean) { warn "Install the ronn gem to be able to release!" }
end
end
desc "Update vendored SSL certs to match the certs vendored by Rubygems"
task :update_certs => "spec:rubygems:clone_rubygems_master" do
require 'bundler/ssl_certs/certificate_manager'
Bundler::SSLCerts::CertificateManager.update_from!(RUBYGEMS_REPO)
end
require 'bundler/gem_tasks'
task :build => ["man:clean", "man:build"]
task :release => ["man:clean", "man:build"]
task :default => :spec

103
bundler-1.7.2/UPGRADING.md Normal file
View File

@ -0,0 +1,103 @@
## Bundler 0.9 to 1.0 and above
Upgrading from Bundler 0.9 to 1.0 is relatively painless. The
Gemfile API is the same, so your old Gemfiles should continue
to work.
The "env" file that 0.9 created at `.bundle/environment.rb` has been
removed. As a side effect of this, Passenger will only find your
bundled gems if you install with `bundle install --deployment`.
Alternatively, you can tell Passenger where you gems are installed,
[something like this](http://andre.arko.net/2010/08/16/using-passengerpane-with-gem_home-set/).
The `bundle lock` command is no longer needed, as the
Gemfile.lock file is now automatically generated by `bundle install`.
If you have not yet done so, add your Gemfile.lock to source control
and check it in.
Running `bundle install` no longer updates the versions of your gems.
If you need to update just one gem, run `bundle update GEMNAME`. To
update all gems to the newest versions possible, run `bundle update`.
Bundler now supports multiple platforms, using a block syntax to
declare platform-specific gems:
platform :jruby do
gem "jruby-maven-plugins"
end
Deploying using Bundler is even easier than it was before, as Bundler
now includes a Capistrano recipe. Simply add this line to the top of
your deploy.rb file to run Bundler automatically as part of deploying:
require 'bundler/capistrano'
For more details on deploying using bundler, see the documentation
for the bundler cap task, and the [documentation on deploying](http://bundler.io/deploying.html).
## Bundler 0.8 to 0.9 and above
Upgrading to Bundler 0.9 from Bundler 0.8 requires upgrading several
API calls in your Gemfile, and some workarounds if you are using Rails 2.3.
### Gemfile Removals
Bundler 0.9 removes the following Bundler 0.8 Gemfile APIs:
1. `disable_system_gems`: This is now the default (and only) option
for bundler. Bundler uses the system gems you have specified
in the Gemfile, and only the system gems you have specified
(and their dependencies)
2. `disable_rubygems`: This is no longer supported. We are looking
into ways to get the fastest performance out of each supported
scenario, and we will make speed the default where possible.
3. `clear_sources`: Bundler now defaults to an empty source
list. If you want to include Rubygems, you can add the source
via source "http://gemcutter.org". If you use bundle init, this
source will be automatically added for you in the generated
Gemfile
4. `bundle_path`: You can specify this setting when installing
via `bundle install /path/to/bundle`. Bundler will remember
where you installed the dependencies to on a particular
machine for future installs, loads, setups, etc.
5. `bin_path`: Bundler no longer generates executables in the root
of your app. You should use `bundle exec` to execute executables
in the current context.
### Gemfile Changes
Bundler 0.9 changes the following Bundler 0.8 Gemfile APIs:
1. Bundler 0.8 supported :only and :except as APIs for describing
groups of gems. Bundler 0.9 supports a single `group` method,
which you can use to group gems together. See the above "Group"
section for more information.
This means that `gem "foo", :only => :production` becomes
`gem "foo", :group => :production`, and
`only :production { gem "foo" }` becomes
`group :production { gem "foo" }`
The short version is: group your gems together logically, and
use the available commands to make use of the groups you've
created.
2. `:require_as` becomes `:require`
3. `:vendored_at` is fully removed; you should use `:path`
### API Changes
1. `Bundler.require_env(:environment)` becomes
`Bundler.require(:multiple, :groups)`. You must
now specify the default group (the default group is the
group made up of the gems not assigned to any group)
explicitly. So `Bundler.require_env(:test)` becomes
`Bundler.require(:default, :test)`
2. `require 'vendor/gems/environment'`: In unlocked
mode, where using system gems, this becomes
`Bundler.setup(:multiple, :groups)`. If you don't
specify any groups, this puts all groups on the load
path. In locked mode, it becomes `require '.bundle/environment'`

21
bundler-1.7.2/bin/bundle Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env ruby
# Exit cleanly from an early interrupt
Signal.trap("INT") { exit 1 }
require 'bundler'
# Check if an older version of bundler is installed
$LOAD_PATH.each do |path|
if path =~ %r'/bundler-0.(\d+)' && $1.to_i < 9
err = "Looks like you have a version of bundler that's older than 0.9.\n"
err << "Please remove your old versions.\n"
err << "An easy way to do this is by running `gem cleanup bundler`."
abort(err)
end
end
require 'bundler/friendly_errors'
Bundler.with_friendly_errors do
require 'bundler/cli'
Bundler::CLI.start(ARGV, :debug => true)
end

View File

@ -0,0 +1,56 @@
#!/usr/bin/env ruby
Signal.trap("INT") { exit 1 }
require 'bundler/ruby_version'
require 'bundler/ruby_dsl'
require 'bundler/shared_helpers'
module Bundler
class GemfileError < RuntimeError; end
class Dsl
include RubyDsl
attr_accessor :ruby_version
def initialize
@ruby_version = nil
end
def eval_gemfile(gemfile, contents = nil)
contents ||= File.open(gemfile, "rb") { |f| f.read }
instance_eval(contents, gemfile.to_s, 1)
rescue SyntaxError => e
bt = e.message.split("\n")[1..-1]
raise GemfileError, ["Gemfile syntax error:", *bt].join("\n")
rescue ScriptError, RegexpError, NameError, ArgumentError => e
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
STDERR.puts e.backtrace.join("\n ")
raise GemfileError, "There was an error in your Gemfile," \
" and Bundler cannot continue."
end
def source(source, options = {})
end
def gem(name, *args)
end
def group(*args, &blk)
end
end
end
dsl = Bundler::Dsl.new
begin
dsl.eval_gemfile(Bundler::SharedHelpers.default_gemfile)
ruby_version = dsl.ruby_version
if ruby_version
puts ruby_version
else
puts "No ruby version specified"
end
rescue Bundler::GemfileError => e
puts e.message
exit(-1)
end

21
bundler-1.7.2/bin/bundler Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env ruby
# Exit cleanly from an early interrupt
Signal.trap("INT") { exit 1 }
require 'bundler'
# Check if an older version of bundler is installed
$LOAD_PATH.each do |path|
if path =~ %r'/bundler-0.(\d+)' && $1.to_i < 9
err = "Looks like you have a version of bundler that's older than 0.9.\n"
err << "Please remove your old versions.\n"
err << "An easy way to do this is by running `gem cleanup bundler`."
abort(err)
end
end
require 'bundler/friendly_errors'
Bundler.with_friendly_errors do
require 'bundler/cli'
Bundler::CLI.start(ARGV, :debug => true)
end

View File

@ -0,0 +1,29 @@
# coding: utf-8
lib = File.expand_path('../lib/', __FILE__)
$:.unshift lib unless $:.include?(lib)
require 'bundler/version'
Gem::Specification.new do |spec|
spec.name = 'bundler'
spec.version = Bundler::VERSION
spec.licenses = ['MIT']
spec.authors = ["André Arko", "Terence Lee", "Carl Lerche", "Yehuda Katz"]
spec.email = ["andre@arko.net"]
spec.homepage = "http://bundler.io"
spec.summary = %q{The best way to manage your application's dependencies}
spec.description = %q{Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably}
spec.required_ruby_version = '>= 1.8.7'
spec.required_rubygems_version = '>= 1.3.6'
spec.add_development_dependency 'rdiscount', '~> 1.6'
spec.add_development_dependency 'ronn', '~> 0.7.3'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.files = `git ls-files -z`.split("\x0")
spec.files += Dir.glob('lib/bundler/man/**/*') # man/ is ignored by git
spec.test_files = spec.files.grep(%r{^spec/})
spec.executables = %w(bundle bundler)
spec.require_paths = ["lib"]
end

View File

@ -0,0 +1,428 @@
require 'fileutils'
require 'pathname'
require 'rbconfig'
require 'bundler/gem_path_manipulation'
require 'bundler/rubygems_ext'
require 'bundler/rubygems_integration'
require 'bundler/version'
require 'bundler/constants'
require 'bundler/current_ruby'
module Bundler
preserve_gem_path
ORIGINAL_ENV = ENV.to_hash
autoload :Definition, 'bundler/definition'
autoload :Dependency, 'bundler/dependency'
autoload :DepProxy, 'bundler/dep_proxy'
autoload :Deprecate, 'bundler/deprecate'
autoload :Dsl, 'bundler/dsl'
autoload :EndpointSpecification, 'bundler/endpoint_specification'
autoload :Environment, 'bundler/environment'
autoload :Env, 'bundler/env'
autoload :Fetcher, 'bundler/fetcher'
autoload :GemHelper, 'bundler/gem_helper'
autoload :GemHelpers, 'bundler/gem_helpers'
autoload :GemInstaller, 'bundler/gem_installer'
autoload :Graph, 'bundler/graph'
autoload :Index, 'bundler/index'
autoload :Installer, 'bundler/installer'
autoload :Injector, 'bundler/injector'
autoload :LazySpecification, 'bundler/lazy_specification'
autoload :LockfileParser, 'bundler/lockfile_parser'
autoload :MatchPlatform, 'bundler/match_platform'
autoload :RemoteSpecification, 'bundler/remote_specification'
autoload :Resolver, 'bundler/resolver'
autoload :Retry, 'bundler/retry'
autoload :RubyVersion, 'bundler/ruby_version'
autoload :RubyDsl, 'bundler/ruby_dsl'
autoload :Runtime, 'bundler/runtime'
autoload :Settings, 'bundler/settings'
autoload :SharedHelpers, 'bundler/shared_helpers'
autoload :SpecSet, 'bundler/spec_set'
autoload :Source, 'bundler/source'
autoload :SourceList, 'bundler/source_list'
autoload :Specification, 'bundler/shared_helpers'
autoload :SystemRubyVersion, 'bundler/ruby_version'
autoload :UI, 'bundler/ui'
class BundlerError < StandardError
def self.status_code(code)
define_method(:status_code) { code }
end
end
class GemfileNotFound < BundlerError; status_code(10) ; end
class GemNotFound < BundlerError; status_code(7) ; end
class GemfileError < BundlerError; status_code(4) ; end
class InstallError < BundlerError; status_code(5) ; end
class InstallHookError < BundlerError; status_code(8) ; end
class PathError < BundlerError; status_code(13) ; end
class GitError < BundlerError; status_code(11) ; end
class DeprecatedError < BundlerError; status_code(12) ; end
class GemspecError < BundlerError; status_code(14) ; end
class InvalidOption < BundlerError; status_code(15) ; end
class ProductionError < BundlerError; status_code(16) ; end
class HTTPError < BundlerError; status_code(17) ; end
class RubyVersionMismatch < BundlerError; status_code(18) ; end
class SecurityError < BundlerError; status_code(19) ; end
class LockfileError < BundlerError; status_code(20) ; end
class CyclicDependencyError < BundlerError; status_code(21) ; end
class GemfileLockNotFound < BundlerError; status_code(22) ; end
# Internal errors, should be rescued
class VersionConflict < BundlerError
attr_reader :conflicts
def initialize(conflicts, msg = nil)
super(msg)
@conflicts = conflicts
end
status_code(6)
end
class MarshalError < StandardError; end
class << self
attr_writer :ui, :bundle_path
def configure
@configured ||= configure_gem_home_and_path
end
def ui
@ui ||= UI::Silent.new
end
# Returns absolute path of where gems are installed on the filesystem.
def bundle_path
@bundle_path ||= Pathname.new(settings.path).expand_path(root)
end
# Returns absolute location of where binstubs are installed to.
def bin_path
@bin_path ||= begin
path = settings[:bin] || "bin"
path = Pathname.new(path).expand_path(root).expand_path
FileUtils.mkdir_p(path)
path
end
end
def setup(*groups)
# Just return if all groups are already loaded
return @setup if defined?(@setup)
definition.validate_ruby!
if groups.empty?
# Load all groups, but only once
@setup = load.setup
else
@completed_groups ||= []
# Figure out which groups haven't been loaded yet
unloaded = groups - @completed_groups
# Record groups that are now loaded
@completed_groups = groups
unloaded.any? ? load.setup(*groups) : load
end
end
def require(*groups)
setup(*groups).require(*groups)
end
def load
@load ||= Runtime.new(root, definition)
end
def environment
Bundler::Environment.new(root, definition)
end
# Returns an instance of Bundler::Definition for given Gemfile and lockfile
#
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @return [Bundler::Definition]
def definition(unlock = nil)
@definition = nil if unlock
@definition ||= begin
configure
upgrade_lockfile
Definition.build(default_gemfile, default_lockfile, unlock)
end
end
def locked_gems
return @locked_gems if defined?(@locked_gems)
if Bundler.default_lockfile.exist?
lock = Bundler.read_file(Bundler.default_lockfile)
@locked_gems = LockfileParser.new(lock)
else
@locked_gems = nil
end
end
def ruby_scope
"#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}"
end
def user_bundle_path
Pathname.new(Bundler.rubygems.user_home).join(".bundler")
end
def home
bundle_path.join("bundler")
end
def install_path
home.join("gems")
end
def specs_path
bundle_path.join("specifications")
end
def cache
bundle_path.join("cache/bundler")
end
def root
@root ||= default_gemfile.dirname.expand_path
end
def app_config_path
ENV['BUNDLE_APP_CONFIG'] ?
Pathname.new(ENV['BUNDLE_APP_CONFIG']).expand_path(root) :
root.join('.bundle')
end
def app_cache(custom_path = nil)
path = custom_path || root
path.join("vendor/cache")
end
def tmp(name = Process.pid.to_s)
@tmp ||= Pathname.new Dir.mktmpdir("bundler")
@tmp.join(name)
end
def settings
@settings ||= begin
Settings.new(app_config_path)
rescue GemfileNotFound
Settings.new
end
end
def with_original_env
bundled_env = ENV.to_hash
ENV.replace(ORIGINAL_ENV)
yield
ensure
ENV.replace(bundled_env.to_hash)
end
def with_clean_env
with_original_env do
ENV['MANPATH'] = ENV['BUNDLE_ORIG_MANPATH']
ENV.delete_if { |k,_| k[0,7] == 'BUNDLE_' }
if ENV.has_key? 'RUBYOPT'
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub '-rbundler/setup', ''
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub "-I#{File.expand_path('..', __FILE__)}", ''
end
yield
end
end
def clean_system(*args)
with_clean_env { Kernel.system(*args) }
end
def clean_exec(*args)
with_clean_env { Kernel.exec(*args) }
end
def default_gemfile
SharedHelpers.default_gemfile
end
def default_lockfile
SharedHelpers.default_lockfile
end
def system_bindir
# Gem.bindir doesn't always return the location that Rubygems will install
# system binaries. If you put '-n foo' in your .gemrc, Rubygems will
# install binstubs there instead. Unfortunately, Rubygems doesn't expose
# that directory at all, so rather than parse .gemrc ourselves, we allow
# the directory to be set as well, via `bundle config bindir foo`.
Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir
end
def requires_sudo?
return @requires_sudo if defined?(@requires_sudo_ran)
if settings.allow_sudo?
sudo_present = which "sudo"
end
if sudo_present
# the bundle path and subdirectories need to be writable for Rubygems
# to be able to unpack and install gems without exploding
path = bundle_path
path = path.parent until path.exist?
# bins are written to a different location on OS X
bin_dir = Pathname.new(Bundler.system_bindir)
bin_dir = bin_dir.parent until bin_dir.exist?
# if any directory is not writable, we need sudo
files = [path, bin_dir] | Dir[path.join('build_info/*').to_s] | Dir[path.join('*').to_s]
sudo_needed = files.any?{|f| !File.writable?(f) }
end
@requires_sudo_ran = true
@requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed
end
def mkdir_p(path)
if requires_sudo?
sudo "mkdir -p '#{path}'" unless File.exist?(path)
else
FileUtils.mkdir_p(path)
end
end
def which(executable)
if File.file?(executable) && File.executable?(executable)
executable
elsif ENV['PATH']
path = ENV['PATH'].split(File::PATH_SEPARATOR).find do |p|
File.executable?(File.join(p, executable))
end
path && File.expand_path(executable, path)
end
end
def sudo(str)
prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, '').strip + " "
Your user account isn't allowed to install to the system Rubygems.
You can cancel this installation and run:
bundle install --path vendor/bundle
to install the gems into ./vendor/bundle/, or you can enter your password
and install the bundled gems to Rubygems using sudo.
Password:
PROMPT
`sudo -p "#{prompt}" #{str}`
end
def read_file(file)
File.open(file, "rb") { |f| f.read }
end
def load_marshal(data)
Marshal.load(data)
rescue => e
raise MarshalError, "#{e.class}: #{e.message}"
end
def load_gemspec(file)
@gemspec_cache ||= {}
key = File.expand_path(file)
spec = ( @gemspec_cache[key] ||= load_gemspec_uncached(file) )
# Protect against caching side-effected gemspecs by returning a
# new instance each time.
spec.dup if spec
end
def load_gemspec_uncached(file)
path = Pathname.new(file)
# Eval the gemspec from its parent directory, because some gemspecs
# depend on "./" relative paths.
SharedHelpers.chdir(path.dirname.to_s) do
contents = path.read
if contents[0..2] == "---" # YAML header
eval_yaml_gemspec(path, contents)
else
eval_gemspec(path, contents)
end
end
end
def clear_gemspec_cache
@gemspec_cache = {}
end
def git_present?
return @git_present if defined?(@git_present)
@git_present = Bundler.which("git") || Bundler.which("git.exe")
end
def ruby_version
@ruby_version ||= SystemRubyVersion.new
end
private
def eval_yaml_gemspec(path, contents)
# If the YAML is invalid, Syck raises an ArgumentError, and Psych
# raises a Psych::SyntaxError. See psyched_yaml.rb for more info.
Gem::Specification.from_yaml(contents)
rescue YamlSyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception
eval_gemspec(path, contents)
end
def eval_gemspec(path, contents)
eval(contents, TOPLEVEL_BINDING, path.expand_path.to_s)
rescue ScriptError, StandardError => e
original_line = e.backtrace.find { |line| line.include?(path.to_s) }
msg = "There was a #{e.class} while loading #{path.basename}: \n#{e.message}"
msg << " from\n #{original_line}" if original_line
msg << "\n"
if e.is_a?(LoadError) && RUBY_VERSION >= "1.9"
msg << "\nDoes it try to require a relative path? That's been removed in Ruby 1.9."
end
raise GemspecError, msg
end
def configure_gem_home_and_path
blank_home = ENV['GEM_HOME'].nil? || ENV['GEM_HOME'].empty?
if settings[:disable_shared_gems]
ENV['GEM_PATH'] = ''
elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s
possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path]
paths = possibles.flatten.compact.uniq.reject { |p| p.empty? }
ENV["GEM_PATH"] = paths.join(File::PATH_SEPARATOR)
end
configure_gem_home
bundle_path
end
def configure_gem_home
# TODO: This mkdir_p is only needed for JRuby <= 1.5 and should go away (GH #602)
FileUtils.mkdir_p bundle_path.to_s rescue nil
ENV['GEM_HOME'] = File.expand_path(bundle_path, root)
Bundler.rubygems.clear_paths
end
def upgrade_lockfile
lockfile = default_lockfile
if lockfile.exist? && lockfile.read(3) == "---"
Bundler.ui.warn "Detected Gemfile.lock generated by 0.9, deleting..."
lockfile.rmtree
end
end
end
end

View File

@ -0,0 +1,16 @@
# Capistrano task for Bundler.
#
# Just add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and
# Bundler will be activated after each new deployment.
require 'bundler/deployment'
require 'capistrano/version'
if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0")
raise "For Capistrano 3.x integration, please use http://github.com/capistrano/bundler"
end
Capistrano::Configuration.instance(:must_exist).load do
before "deploy:finalize_update", "bundle:install"
Bundler::Deployment.define_task(self, :task, :except => { :no_release => true })
set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" }
end

View File

@ -0,0 +1,372 @@
require 'bundler'
require 'bundler/vendored_thor'
module Bundler
class CLI < Thor
include Thor::Actions
def self.start(*)
super
rescue Exception => e
Bundler.ui = UI::Shell.new
raise e
end
def initialize(*)
super
ENV['BUNDLE_GEMFILE'] = File.expand_path(options[:gemfile]) if options[:gemfile]
Bundler::Retry.attempts = options[:retry] || Bundler.settings[:retry] || Bundler::Retry::DEFAULT_ATTEMPTS
Bundler.rubygems.ui = UI::RGProxy.new(Bundler.ui)
rescue UnknownArgumentError => e
raise InvalidOption, e.message
ensure
self.options ||= {}
Bundler.ui = UI::Shell.new(options)
Bundler.ui.level = "debug" if options["verbose"]
end
check_unknown_options!(:except => [:config, :exec])
stop_on_unknown_option! :exec
default_task :install
class_option "no-color", :type => :boolean, :banner => "Disable colorization in output"
class_option "verbose", :type => :boolean, :banner => "Enable verbose output mode", :aliases => "-V"
class_option "retry", :type => :numeric, :aliases => "-r", :banner =>
"Specify the number of times you wish to attempt network commands"
def help(cli = nil)
case cli
when "gemfile" then command = "gemfile.5"
when nil then command = "bundle"
else command = "bundle-#{cli}"
end
manpages = %w(
bundle
bundle-config
bundle-exec
bundle-install
bundle-package
bundle-update
bundle-platform
gemfile.5)
if manpages.include?(command)
root = File.expand_path("../man", __FILE__)
if Bundler.which("man") && root !~ %r{^file:/.+!/META-INF/jruby.home/.+}
Kernel.exec "man #{root}/#{command}"
else
puts File.read("#{root}/#{command}.txt")
end
else
super
end
end
desc "init [OPTIONS]", "Generates a Gemfile into the current working directory"
long_desc <<-D
Init generates a default Gemfile in the current working directory. When adding a
Gemfile to a gem with a gemspec, the --gemspec option will automatically add each
dependency listed in the gemspec file to the newly created Gemfile.
D
method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile"
def init
require 'bundler/cli/init'
Init.new(options.dup).run
end
desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems"
long_desc <<-D
Check searches the local machine for each of the gems requested in the Gemfile. If
all gems are found, Bundler prints a success message and exits with a status of 0.
If not, the first missing gem is listed and Bundler exits status 1.
D
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"Lock the Gemfile"
def check
require 'bundler/cli/check'
Check.new(options).run
end
desc "install [OPTIONS]", "Install the current environment to the system"
long_desc <<-D
Install will install all of the gems in the current bundle, making them available
for use. In a freshly checked out repository, this command will give you the same
gem versions as the last person who updated the Gemfile and ran `bundle update`.
Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed
into the [DIR] directory rather than into system gems.
If the bundle has already been installed, bundler will tell you so and then exit.
D
method_option "without", :type => :array, :banner =>
"Exclude gems that are part of the specified named group."
method_option "gemfile", :type => :string, :banner =>
"Use the specified gemfile instead of Gemfile"
method_option "no-prune", :type => :boolean, :banner =>
"Don't remove stale gems from the cache."
method_option "no-cache", :type => :boolean, :banner =>
"Don't update the existing gem cache."
method_option "quiet", :type => :boolean, :banner =>
"Only output warnings and errors."
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "binstubs", :type => :string, :lazy_default => "bin", :banner =>
"Generate bin stubs for bundled gems to ./bin"
method_option "shebang", :type => :string, :banner =>
"Specify a different shebang executable name than the default (usually 'ruby')"
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
method_option "system", :type => :boolean, :banner =>
"Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application"
method_option "frozen", :type => :boolean, :banner =>
"Do not allow the Gemfile.lock to be updated after this install"
method_option "deployment", :type => :boolean, :banner =>
"Install using defaults tuned for deployment environments"
method_option "standalone", :type => :array, :lazy_default => [], :banner =>
"Make a bundle that can work without the Bundler runtime"
method_option "full-index", :type => :boolean, :banner =>
"Use the rubygems modern index instead of the API endpoint"
method_option "clean", :type => :boolean, :banner =>
"Run bundle clean automatically after install"
method_option "trust-policy", :alias => "P", :type => :string, :banner =>
"Gem trust policy (like gem install -P). Must be one of " +
Bundler.rubygems.security_policy_keys.join('|')
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
"Specify the number of jobs to run in parallel"
def install
require 'bundler/cli/install'
Install.new(options.dup).run
end
desc "update [OPTIONS]", "update the current environment"
long_desc <<-D
Update will install the newest versions of the gems listed in the Gemfile. Use
update when you have changed the Gemfile, or if you want to get the newest
possible versions of the gems in the bundle.
D
method_option "source", :type => :array, :banner => "Update a specific source (and all gems associated with it)"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "quiet", :type => :boolean, :banner =>
"Only output warnings and errors."
method_option "full-index", :type => :boolean, :banner =>
"Use the rubygems modern index instead of the API endpoint"
method_option "jobs", :aliases => "-j", :type => :numeric, :banner =>
"Specify the number of jobs to run in parallel"
method_option "group", :aliases => "-g", :type => :array, :banner =>
"Update a specific group"
def update(*gems)
require 'bundler/cli/update'
Update.new(options, gems).run
end
desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem"
long_desc <<-D
Show lists the names and versions of all gems that are required by your Gemfile.
Calling show with [GEM] will list the exact location of that gem on your machine.
D
method_option "paths", :type => :boolean,
:banner => "List the paths of all gems that are required by your Gemfile."
def show(gem_name = nil)
require 'bundler/cli/show'
Show.new(options, gem_name).run
end
map %w(list) => "show"
desc "binstubs GEM [OPTIONS]", "install the binstubs of the listed gem"
long_desc <<-D
Generate binstubs for executables in [GEM]. Binstubs are put into bin,
or the --binstubs directory if one has been set.
D
method_option "path", :type => :string, :lazy_default => "bin", :banner =>
"binstub destination directory (default bin)"
method_option "force", :type => :boolean, :default => false, :banner =>
"overwrite existing binstubs if they exist"
def binstubs(*gems)
require 'bundler/cli/binstubs'
Binstubs.new(options, gems).run
end
desc "outdated GEM [OPTIONS]", "list installed gems with newer versions available"
long_desc <<-D
Outdated lists the names and versions of gems that have a newer version available
in the given source. Calling outdated with [GEM [GEM]] will only check for newer
versions of the given gems. Prerelease gems are ignored by default. If your gems
are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1.
D
method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems"
method_option "source", :type => :array, :banner => "Check against a specific source"
method_option "local", :type => :boolean, :banner =>
"Do not attempt to fetch gems remotely and use the gem cache instead"
method_option "strict", :type => :boolean, :banner =>
"Only list newer versions allowed by your Gemfile requirements"
def outdated(*gems)
require 'bundler/cli/outdated'
Outdated.new(options, gems).run
end
desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
def cache
require 'bundler/cli/cache'
Cache.new(options).run
end
desc "package [OPTIONS]", "Locks and then caches all of the gems into vendor/cache"
method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache."
method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)."
method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors."
method_option "path", :type => :string, :banner =>
"Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine"
method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile"
long_desc <<-D
The package command will copy the .gem files for every gem in the bundle into the
directory ./vendor/cache. If you then check that directory into your source
control repository, others who check out your source will be able to install the
bundle without having to download any additional gems.
D
def package
require 'bundler/cli/package'
Package.new(options).run
end
map %w(pack) => :package
desc "exec [OPTIONS]", "Run the command in context of the bundle"
method_option :keep_file_descriptors, :type => :boolean, :default => false
long_desc <<-D
Exec runs a command, providing it access to the gems in the bundle. While using
bundle exec you can require and call the bundled gems as if they were installed
into the system wide Rubygems repository.
D
def exec(*args)
require 'bundler/cli/exec'
Exec.new(options, args).run
end
desc "config NAME [VALUE]", "retrieve or set a configuration value"
long_desc <<-D
Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the
existing value with the newly provided one.
By default, setting a configuration value sets it for all projects
on the machine.
If a global setting is superceded by local configuration, this command
will show the current value, as well as any superceded values and
where they were specified.
D
def config(*args)
require 'bundler/cli/config'
Config.new(options, args, self).run
end
desc "open GEM", "Opens the source directory of the given bundled gem"
def open(name)
require 'bundler/cli/open'
Open.new(options, name).run
end
CONSOLES = {
'pry' => :Pry,
'ripl' => :Ripl,
'irb' => :IRB,
}
desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded"
def console(group = nil)
require 'bundler/cli/console'
Console.new(options, group, CONSOLES).run
end
desc "version", "Prints the bundler's version information"
def version
Bundler.ui.info "Bundler version #{Bundler::VERSION}"
end
map %w(-v --version) => :version
desc "licenses", "Prints the license of all gems in the bundle"
def licenses
Bundler.load.specs.sort_by { |s| s.license.to_s }.reverse.each do |s|
gem_name = s.name
license = s.license || s.licenses
if license.empty?
Bundler.ui.warn "#{gem_name}: Unknown"
else
Bundler.ui.info "#{gem_name}: #{license}"
end
end
end
desc 'viz [OPTIONS]', "Generates a visual dependency graph"
long_desc <<-D
Viz generates a PNG file of the current Gemfile as a dependency graph.
Viz requires the ruby-graphviz gem (and its dependencies).
The associated gems must also be installed via 'bundle install'.
D
method_option :file, :type => :string, :default => 'gem_graph', :aliases => '-f', :banner => "The name to use for the generated file. see format option"
method_option :version, :type => :boolean, :default => false, :aliases => '-v', :banner => "Set to show each gem version."
method_option :requirements, :type => :boolean, :default => false, :aliases => '-r', :banner => "Set to show the version of each required dependency."
method_option :format, :type => :string, :default => "png", :aliases => '-F', :banner => "This is output format option. Supported format is png, jpg, svg, dot ..."
def viz
require 'bundler/cli/viz'
Viz.new(options).run
end
desc "gem GEM [OPTIONS]", "Creates a skeleton for creating a rubygem"
method_option :bin, :type => :boolean, :default => false, :aliases => '-b', :banner => "Generate a binary for your library."
method_option :test, :type => :string, :lazy_default => 'rspec', :aliases => '-t', :banner => "Generate a test directory for your library: 'rspec' is the default, but 'minitest' is also supported."
method_option :edit, :type => :string, :aliases => "-e",
:lazy_default => [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? },
:required => false, :banner => "/path/to/your/editor",
:desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)"
method_option :ext, :type => :boolean, :detailt => false, :banner => "Generate the boilerplate for C extension code"
def gem(name)
require 'bundler/cli/gem'
Gem.new(options, name, self).run
end
def self.source_root
File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
end
desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory"
method_option "dry-run", :type => :boolean, :default => false, :banner =>
"only print out changes, do not actually clean gems"
method_option "force", :type => :boolean, :default => false, :banner =>
"forces clean even if --path is not set"
def clean
require 'bundler/cli/clean'
Clean.new(options.dup).run
end
desc "platform [OPTIONS]", "Displays platform compatibility information"
method_option "ruby", :type => :boolean, :default => false, :banner =>
"only display ruby related platform information"
def platform
require 'bundler/cli/platform'
Platform.new(options).run
end
desc "inject GEM VERSION ...", "Add the named gem(s), with version requirements, to the resolved Gemfile"
def inject(name, version, *gems)
require 'bundler/cli/inject'
Inject.new(options, name, version, gems).run
end
desc "env", "Print information about the environment Bundler is running under"
def env
Env.new.write($stdout)
end
end
end

View File

@ -0,0 +1,38 @@
require "bundler/cli/common"
module Bundler
class CLI::Binstubs
attr_reader :options, :gems
def initialize(options, gems)
@options = options
@gems = gems
end
def run
Bundler.definition.validate_ruby!
Bundler.settings[:bin] = options["path"] if options["path"]
Bundler.settings[:bin] = nil if options["path"] && options["path"].empty?
installer = Installer.new(Bundler.root, Bundler.definition)
if gems.empty?
Bundler.ui.error "`bundle binstubs` needs at least one gem to run."
exit 1
end
gems.each do |gem_name|
spec = installer.specs.find{|s| s.name == gem_name }
unless spec
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(
gem_name, Bundler.definition.specs)
end
if spec.name == "bundler"
Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems."
else
installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true)
end
end
end
end
end

View File

@ -0,0 +1,34 @@
module Bundler
class CLI::Cache
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.definition.validate_ruby!
Bundler.definition.resolve_with_cache!
setup_cache_all
Bundler.load.cache
Bundler.settings[:no_prune] = true if options["no-prune"]
Bundler.load.lock
rescue GemNotFound => e
Bundler.ui.error(e.message)
Bundler.ui.warn "Run `bundle install` to install missing gems."
exit 1
end
private
def setup_cache_all
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
"to package them as well, please pass the --all flag. This will be the default " \
"on Bundler 2.0."
end
end
end
end

View File

@ -0,0 +1,35 @@
module Bundler
class CLI::Check
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
begin
definition = Bundler.definition
definition.validate_ruby!
not_installed = definition.missing_specs
rescue GemNotFound, VersionConflict
Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies."
Bundler.ui.warn "Install missing gems with `bundle install`."
exit 1
end
if not_installed.any?
Bundler.ui.error "The following gems are missing"
not_installed.each { |s| Bundler.ui.error " * #{s.name} (#{s.version})" }
Bundler.ui.warn "Install missing gems with `bundle install`"
exit 1
elsif !Bundler.default_lockfile.exist? && Bundler.settings[:frozen]
Bundler.ui.error "This bundle has been frozen, but there is no Gemfile.lock present"
exit 1
else
Bundler.load.lock unless options[:"dry-run"]
Bundler.ui.info "The Gemfile's dependencies are satisfied"
end
end
end
end

View File

@ -0,0 +1,19 @@
module Bundler
class CLI::Clean
attr_reader :options
def initialize(options)
@options = options
end
def run
if Bundler.settings[:path] || options[:force]
Bundler.load.clean(options[:"dry-run"])
else
Bundler.ui.error "Can only use bundle clean when --path is set or --force is set"
exit 1
end
end
end
end

View File

@ -0,0 +1,54 @@
module Bundler
module CLI::Common
def self.without_groups_message
groups = Bundler.settings.without
group_list = [groups[0...-1].join(", "), groups[-1..-1]].
reject{|s| s.to_s.empty? }.join(" and ")
group_str = (groups.size == 1) ? "group" : "groups"
"Gems in the #{group_str} #{group_list} were not installed."
end
def self.select_spec(name, regex_match = nil)
specs = []
regexp = Regexp.new(name) if regex_match
Bundler.definition.specs.each do |spec|
return spec if spec.name == name
specs << spec if regexp && spec.name =~ regexp
end
case specs.count
when 0
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
when 1
specs.first
else
ask_for_spec_from(specs)
end
end
def self.ask_for_spec_from(specs)
if !$stdout.tty? && ENV['BUNDLE_SPEC_RUN'].nil?
raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies)
end
specs.each_with_index do |spec, index|
Bundler.ui.info "#{index.succ} : #{spec.name}", true
end
Bundler.ui.info '0 : - exit -', true
num = Bundler.ui.ask('> ').to_i
num > 0 ? specs[num - 1] : nil
end
def self.gem_not_found_message(missing_gem_name, alternatives)
require 'bundler/similarity_detector'
message = "Could not find gem '#{missing_gem_name}'."
alternate_names = alternatives.map { |a| a.respond_to?(:name) ? a.name : a }
suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name)
message += "\nDid you mean #{suggestions}?" if suggestions
message
end
end
end

View File

@ -0,0 +1,84 @@
module Bundler
class CLI::Config
attr_reader :options, :thor
attr_accessor :args
def initialize(options, args, thor)
@options = options
@args = args
@thor = thor
end
def run
peek = args.shift
if peek && peek =~ /^\-\-/
name, scope = args.shift, $'
else
name, scope = peek, "global"
end
unless name
Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n"
Bundler.settings.all.each do |setting|
Bundler.ui.confirm "#{setting}"
thor.with_padding do
Bundler.settings.pretty_values_for(setting).each do |line|
Bundler.ui.info line
end
end
Bundler.ui.confirm ""
end
return
end
case scope
when "delete"
Bundler.settings.set_local(name, nil)
Bundler.settings.set_global(name, nil)
when "local", "global"
if args.empty?
Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used"
thor.with_padding do
Bundler.settings.pretty_values_for(name).each { |line| Bundler.ui.info line }
end
return
end
locations = Bundler.settings.locations(name)
if scope == "global"
if local = locations[:local]
Bundler.ui.info "Your application has set #{name} to #{local.inspect}. This will override the " \
"global value you are currently setting"
end
if env = locations[:env]
Bundler.ui.info "You have a bundler environment variable for #{name} set to #{env.inspect}. " \
"This will take precedence over the global value you are setting"
end
if global = locations[:global]
Bundler.ui.info "You are replacing the current global value of #{name}, which is currently #{global.inspect}"
end
end
if scope == "local" && local = locations[:local]
Bundler.ui.info "You are replacing the current local value of #{name}, which is currently #{local.inspect}"
end
if name.match(/\Alocal\./)
pathname = Pathname.new(args.join(" "))
self.args = [pathname.expand_path.to_s] if pathname.directory?
end
Bundler.settings.send("set_#{scope}", name, args.join(" "))
else
Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global."
exit 1
end
end
end
end

View File

@ -0,0 +1,42 @@
module Bundler
class CLI::Console
attr_reader :options, :group, :consoles
def initialize(options, group, consoles)
@options = options
@group = group
@consoles = consoles
end
def run
group ? Bundler.require(:default, *(group.split.map! {|g| g.to_sym })) : Bundler.require
ARGV.clear
preferred = Bundler.settings[:console] || 'irb'
# See if console is available
begin
require preferred || true
rescue LoadError
# Is it in Gemfile?
Bundler.ui.error "Could not load the #{preferred} console"
Bundler.ui.info "Falling back on IRB..."
require 'irb'
preferred = 'irb'
end
constant = consoles[preferred]
console = begin
Object.const_get(constant)
rescue NameError => e
Bundler.ui.error e.inspect
Bundler.ui.error "Could not load the #{constant} console"
return
end
console.start
end
end
end

View File

@ -0,0 +1,37 @@
module Bundler
class CLI::Exec
attr_reader :options, :args
def initialize(options, args)
@options = options
@args = args
end
def run
Bundler.definition.validate_ruby!
Bundler.load.setup_environment
begin
if RUBY_VERSION >= "2.0"
@args << { :close_others => !options.keep_file_descriptors? }
elsif options.keep_file_descriptors?
Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec."
end
# Run
Kernel.exec(*args)
rescue Errno::EACCES
Bundler.ui.error "bundler: not executable: #{args.first}"
exit 126
rescue Errno::ENOENT
Bundler.ui.error "bundler: command not found: #{args.first}"
Bundler.ui.warn "Install missing gem executables with `bundle install`"
exit 127
rescue ArgumentError
Bundler.ui.error "bundler: exec needs a command to run"
exit 128
end
end
end
end

View File

@ -0,0 +1,77 @@
module Bundler
class CLI::Gem
attr_reader :options, :gem_name, :thor
def initialize(options, gem_name, thor)
@options = options
@gem_name = gem_name
@thor = thor
end
def run
if options[:ext] && gem_name.index('-')
Bundler.ui.error "You have specified a gem name which does not conform to the \n" \
"naming guidelines for C extensions. For more information, \n" \
"see the 'Extension Naming' section at the following URL:\n" \
"http://guides.rubygems.org/gems-with-extensions/\n"
exit 1
end
name = gem_name.chomp("/") # remove trailing slash if present
underscored_name = name.tr('-', '_')
namespaced_path = name.tr('-', '/')
target = File.join(Dir.pwd, name)
constant_name = name.split('_').map{|p| p[0..0].upcase + p[1..-1] }.join
constant_name = constant_name.split('-').map{|q| q[0..0].upcase + q[1..-1] }.join('::') if constant_name =~ /-/
constant_array = constant_name.split('::')
git_user_name = `git config user.name`.chomp
git_user_email = `git config user.email`.chomp
opts = {
:name => name,
:underscored_name => underscored_name,
:namespaced_path => namespaced_path,
:constant_name => constant_name,
:constant_array => constant_array,
:author => git_user_name.empty? ? "TODO: Write your name" : git_user_name,
:email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email,
:test => options[:test],
:ext => options[:ext]
}
gemspec_dest = File.join(target, "#{name}.gemspec")
thor.template(File.join("newgem/Gemfile.tt"), File.join(target, "Gemfile"), opts)
thor.template(File.join("newgem/Rakefile.tt"), File.join(target, "Rakefile"), opts)
thor.template(File.join("newgem/LICENSE.txt.tt"), File.join(target, "LICENSE.txt"), opts)
thor.template(File.join("newgem/README.md.tt"), File.join(target, "README.md"), opts)
thor.template(File.join("newgem/gitignore.tt"), File.join(target, ".gitignore"), opts)
thor.template(File.join("newgem/newgem.gemspec.tt"), gemspec_dest, opts)
thor.template(File.join("newgem/lib/newgem.rb.tt"), File.join(target, "lib/#{namespaced_path}.rb"), opts)
thor.template(File.join("newgem/lib/newgem/version.rb.tt"), File.join(target, "lib/#{namespaced_path}/version.rb"), opts)
if options[:bin]
thor.template(File.join("newgem/bin/newgem.tt"), File.join(target, 'bin', name), opts)
end
case options[:test]
when 'rspec'
thor.template(File.join("newgem/rspec.tt"), File.join(target, ".rspec"), opts)
thor.template(File.join("newgem/spec/spec_helper.rb.tt"), File.join(target, "spec/spec_helper.rb"), opts)
thor.template(File.join("newgem/spec/newgem_spec.rb.tt"), File.join(target, "spec/#{namespaced_path}_spec.rb"), opts)
when 'minitest'
thor.template(File.join("newgem/test/minitest_helper.rb.tt"), File.join(target, "test/minitest_helper.rb"), opts)
thor.template(File.join("newgem/test/test_newgem.rb.tt"), File.join(target, "test/test_#{namespaced_path}.rb"), opts)
end
if options[:test]
thor.template(File.join("newgem/.travis.yml.tt"), File.join(target, ".travis.yml"), opts)
end
if options[:ext]
thor.template(File.join("newgem/ext/newgem/extconf.rb.tt"), File.join(target, "ext/#{name}/extconf.rb"), opts)
thor.template(File.join("newgem/ext/newgem/newgem.h.tt"), File.join(target, "ext/#{name}/#{underscored_name}.h"), opts)
thor.template(File.join("newgem/ext/newgem/newgem.c.tt"), File.join(target, "ext/#{name}/#{underscored_name}.c"), opts)
end
Bundler.ui.info "Initializing git repo in #{target}"
Dir.chdir(target) { `git init`; `git add .` }
if options[:edit]
thor.run("#{options["edit"]} \"#{gemspec_dest}\"") # Open gemspec in editor
end
end
end
end

View File

@ -0,0 +1,33 @@
module Bundler
class CLI::Init
attr_reader :options
def initialize(options)
@options = options
end
def run
if File.exist?("Gemfile")
Bundler.ui.error "Gemfile already exists at #{Dir.pwd}/Gemfile"
exit 1
end
if options[:gemspec]
gemspec = File.expand_path(options[:gemspec])
unless File.exist?(gemspec)
Bundler.ui.error "Gem specification #{gemspec} doesn't exist"
exit 1
end
spec = Gem::Specification.load(gemspec)
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
File.open('Gemfile', 'wb') do |file|
file << "# Generated from #{gemspec}\n"
file << spec.to_gemfile
end
else
puts "Writing new Gemfile to #{Dir.pwd}/Gemfile"
FileUtils.cp(File.expand_path('../../templates/Gemfile', __FILE__), 'Gemfile')
end
end
end
end

View File

@ -0,0 +1,33 @@
module Bundler
class CLI::Inject
attr_reader :options, :name, :version, :gems
def initialize(options, name, version, gems)
@options = options
@name = name
@version = version
@gems = gems
end
def run
# The required arguments allow Thor to give useful feedback when the arguments
# are incorrect. This adds those first two arguments onto the list as a whole.
gems.unshift(version).unshift(name)
# Build an array of Dependency objects out of the arguments
deps = []
gems.each_slice(2) do |gem_name, gem_version|
deps << Bundler::Dependency.new(gem_name, gem_version)
end
added = Injector.inject(deps)
if added.any?
Bundler.ui.confirm "Added to Gemfile:"
Bundler.ui.confirm added.map{ |g| " #{g}" }.join("\n")
else
Bundler.ui.confirm "All injected gems were already present in the Gemfile"
end
end
end
end

View File

@ -0,0 +1,141 @@
module Bundler
class CLI::Install
attr_reader :options
def initialize(options)
@options = options.dup
end
def run
warn_if_root
if options[:without]
options[:without] = options[:without].map{|g| g.tr(' ', ':') }
end
ENV['RB_USER_INSTALL'] = '1' if Bundler::FREEBSD
# Just disable color in deployment mode
Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment]
if (options[:path] || options[:deployment]) && options[:system]
Bundler.ui.error "You have specified both a path to install your gems to, \n" \
"as well as --system. Please choose."
exit 1
end
if (options["trust-policy"])
unless (Bundler.rubygems.security_policies.keys.include?(options["trust-policy"]))
Bundler.ui.error "Rubygems doesn't know about trust policy '#{options["trust-policy"]}'. " \
"The known policies are: #{Bundler.rubygems.security_policies.keys.join(', ')}."
exit 1
end
Bundler.settings["trust-policy"] = options["trust-policy"]
else
Bundler.settings["trust-policy"] = nil if Bundler.settings["trust-policy"]
end
if options[:deployment] || options[:frozen]
unless Bundler.default_lockfile.exist?
flag = options[:deployment] ? '--deployment' : '--frozen'
raise ProductionError, "The #{flag} flag requires a Gemfile.lock. Please make " \
"sure you have checked your Gemfile.lock into version control " \
"before deploying."
end
if Bundler.root.join("vendor/cache").exist?
options[:local] = true
end
Bundler.settings[:frozen] = '1'
end
# When install is called with --no-deployment, disable deployment mode
if options[:deployment] == false
Bundler.settings.delete(:frozen)
options[:system] = true
end
Bundler.settings[:path] = nil if options[:system]
Bundler.settings[:path] = "vendor/bundle" if options[:deployment]
Bundler.settings[:path] = options["path"] if options["path"]
Bundler.settings[:path] ||= "bundle" if options["standalone"]
Bundler.settings[:bin] = options["binstubs"] if options["binstubs"]
Bundler.settings[:bin] = nil if options["binstubs"] && options["binstubs"].empty?
Bundler.settings[:shebang] = options["shebang"] if options["shebang"]
Bundler.settings[:jobs] = options["jobs"] if options["jobs"]
Bundler.settings[:no_prune] = true if options["no-prune"]
Bundler.settings[:clean] = options["clean"] if options["clean"]
Bundler.settings.without = options[:without]
Bundler.ui.level = "warn" if options[:quiet]
Bundler::Fetcher.disable_endpoint = options["full-index"]
Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? '1' : nil
# rubygems plugins sometimes hook into the gem install process
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
definition = Bundler.definition
definition.validate_ruby!
Installer.install(Bundler.root, definition, options)
Bundler.load.cache if Bundler.root.join("vendor/cache").exist? && !options["no-cache"] && !Bundler.settings[:frozen]
if Bundler.settings[:path]
absolute_path = File.expand_path(Bundler.settings[:path])
relative_path = absolute_path.sub(File.expand_path('.'), '.')
Bundler.ui.confirm "Your bundle is complete!"
without_groups_messages
Bundler.ui.confirm "It was installed into #{relative_path}"
else
Bundler.ui.confirm "Your bundle is complete!"
without_groups_messages
Bundler.ui.confirm "Use `bundle show [gemname]` to see where a bundled gem is installed."
end
Installer.post_install_messages.to_a.each do |name, msg|
Bundler.ui.confirm "Post-install message from #{name}:"
Bundler.ui.info msg
end
Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris|
Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources."
Bundler.ui.error "Installed from: #{installed_from_uri}"
Bundler.ui.error "Also found in:"
also_found_in_uris.each { |uri| Bundler.ui.error " * #{uri}" }
Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source."
Bundler.ui.error "For example:"
Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'"
end
if Bundler.settings[:clean] && Bundler.settings[:path]
require "bundler/cli/clean"
Bundler::CLI::Clean.new(options).run
end
rescue GemNotFound, VersionConflict => e
if options[:local] && Bundler.app_cache.exist?
Bundler.ui.warn "Some gems seem to be missing from your vendor/cache directory."
end
unless Bundler.definition.has_rubygems_remotes?
Bundler.ui.warn <<-WARN, :wrap => true
Your Gemfile has no gem server sources. If you need gems that are \
not already on your machine, add a line like this to your Gemfile:
source 'https://rubygems.org'
WARN
end
raise e
end
private
def warn_if_root
return if Bundler::WINDOWS || !Process.uid.zero?
Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
"if it is needed, and installing your bundle as root will break this " \
"application for all non-root users on this machine.", :wrap => true
end
def without_groups_messages
if Bundler.settings.without.any?
require "bundler/cli/common"
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
end
end
end
end

View File

@ -0,0 +1,23 @@
require 'bundler/cli/common'
require 'shellwords'
module Bundler
class CLI::Open
attr_reader :options, :name
def initialize(options, name)
@options = options
@name = name
end
def run
editor = [ENV['BUNDLER_EDITOR'], ENV['VISUAL'], ENV['EDITOR']].find{|e| !e.nil? && !e.empty? }
return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor
path = Bundler::CLI::Common.select_spec(name, :regex_match).full_gem_path
Dir.chdir(path) do
command = Shellwords.split(editor) + [path]
system(*command) || Bundler.ui.info("Could not run '#{command.join(' ')}'")
end
end
end
end

View File

@ -0,0 +1,80 @@
require 'bundler/cli/common'
module Bundler
class CLI::Outdated
attr_reader :options, :gems
def initialize(options, gems)
@options = options
@gems = gems
end
def run
sources = Array(options[:source])
gems.each do |gem_name|
Bundler::CLI::Common.select_spec(gem_name)
end
Bundler.definition.validate_ruby!
current_specs = Bundler.ui.silence { Bundler.load.specs }
current_dependencies = {}
Bundler.ui.silence { Bundler.load.dependencies.each { |dep| current_dependencies[dep.name] = dep } }
if gems.empty? && sources.empty?
# We're doing a full update
definition = Bundler.definition(true)
else
definition = Bundler.definition(:gems => gems, :sources => sources)
end
options["local"] ? definition.resolve_with_cache! : definition.resolve_remotely!
Bundler.ui.info ""
out_count = 0
# Loop through the current specs
gemfile_specs, dependency_specs = current_specs.partition { |spec| current_dependencies.has_key? spec.name }
[gemfile_specs.sort_by(&:name), dependency_specs.sort_by(&:name)].flatten.each do |current_spec|
next if !gems.empty? && !gems.include?(current_spec.name)
dependency = current_dependencies[current_spec.name]
if options["strict"]
active_spec = definition.specs.detect { |spec| spec.name == current_spec.name }
else
active_spec = definition.index[current_spec.name].sort_by { |b| b.version }
if !current_spec.version.prerelease? && !options[:pre] && active_spec.size > 1
active_spec = active_spec.delete_if { |b| b.respond_to?(:version) && b.version.prerelease? }
end
active_spec = active_spec.last
end
next if active_spec.nil?
gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version)
git_outdated = current_spec.git_version != active_spec.git_version
if gem_outdated || git_outdated
if out_count == 0
if options["pre"]
Bundler.ui.info "Outdated gems included in the bundle (including pre-releases):"
else
Bundler.ui.info "Outdated gems included in the bundle:"
end
end
spec_version = "#{active_spec.version}#{active_spec.git_version}"
current_version = "#{current_spec.version}#{current_spec.git_version}"
dependency_version = %|Gemfile specifies "#{dependency.requirement}"| if dependency && dependency.specific?
Bundler.ui.info " * #{active_spec.name} (#{spec_version} > #{current_version}) #{dependency_version}".rstrip
out_count += 1
end
Bundler.ui.debug "from #{active_spec.loaded_from}"
end
if out_count.zero?
Bundler.ui.info "Your bundle is up to date!\n"
else
exit 1
end
end
end
end

View File

@ -0,0 +1,36 @@
module Bundler
class CLI::Package
attr_reader :options
def initialize(options)
@options = options
end
def run
Bundler.ui.level = "warn" if options[:quiet]
Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path]
setup_cache_all
install
# TODO: move cache contents here now that all bundles are locked
custom_path = Pathname.new(options[:path]) if options[:path]
Bundler.load.cache(custom_path)
end
private
def install
require 'bundler/cli/install'
Bundler::CLI::Install.new(options.dup).run
end
def setup_cache_all
Bundler.settings[:cache_all] = options[:all] if options.key?("all")
if Bundler.definition.has_local_dependencies? && !Bundler.settings[:cache_all]
Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \
"to package them as well, please pass the --all flag. This will be the default " \
"on Bundler 2.0."
end
end
end
end

View File

@ -0,0 +1,43 @@
module Bundler
class CLI::Platform
attr_reader :options
def initialize(options)
@options = options
end
def run
platforms, ruby_version = Bundler.ui.silence do
[ Bundler.definition.platforms.map {|p| "* #{p}" },
Bundler.definition.ruby_version ]
end
output = []
if options[:ruby]
if ruby_version
output << ruby_version
else
output << "No ruby version specified"
end
else
output << "Your platform is: #{RUBY_PLATFORM}"
output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}"
if ruby_version
output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}"
begin
Bundler.definition.validate_ruby!
output << "Your current platform satisfies the Ruby version requirement."
rescue RubyVersionMismatch => e
output << e.message
end
else
output << "Your Gemfile does not specify a Ruby version requirement."
end
end
Bundler.ui.info output.join("\n\n")
end
end
end

View File

@ -0,0 +1,48 @@
require 'bundler/cli/common'
module Bundler
class CLI::Show
attr_reader :options, :gem_name
def initialize(options, gem_name)
@options = options
@gem_name = gem_name
end
def run
Bundler.ui.silence do
Bundler.definition.validate_ruby!
Bundler.load.lock
end
if gem_name
if gem_name == "bundler"
path = File.expand_path("../../../..", __FILE__)
else
spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match)
return unless spec
path = spec.full_gem_path
if !File.directory?(path)
Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:"
end
end
return Bundler.ui.info(path)
end
if options[:paths]
Bundler.load.specs.sort_by { |s| s.name }.map do |s|
Bundler.ui.info s.full_gem_path
end
else
Bundler.ui.info "Gems included by the bundle:"
Bundler.load.specs.sort_by { |s| s.name }.each do |s|
desc = " * #{s.name} (#{s.version}#{s.git_version})"
if @options[:verbose]
Bundler.ui.info "#{desc} - #{s.summary || 'No description available.'}"
else
Bundler.ui.info desc
end
end
end
end
end
end

View File

@ -0,0 +1,73 @@
module Bundler
class CLI::Update
attr_reader :options, :gems
def initialize(options, gems)
@options = options
@gems = gems
end
def run
sources = Array(options[:source])
groups = Array(options[:group]).map(&:to_sym)
Bundler.ui.level = "warn" if options[:quiet]
if gems.empty? && sources.empty? && groups.empty?
# We're doing a full update
Bundler.definition(true)
else
unless Bundler.default_lockfile.exist?
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
"Run `bundle install` to update and install the bundled gems."
end
# cycle through the requested gems, just to make sure they exist
names = Bundler.locked_gems.specs.map{ |s| s.name }
gems.each do |g|
next if names.include?(g)
require "bundler/cli/common"
raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(g, names)
end
if groups.any?
specs = Bundler.definition.specs_for groups
sources.concat(specs.map(&:name))
end
Bundler.definition(:gems => gems, :sources => sources)
end
Bundler::Fetcher.disable_endpoint = options["full-index"]
opts = options.dup
opts["update"] = true
opts["local"] = options[:local]
Bundler.settings[:jobs] = opts["jobs"] if opts["jobs"]
# rubygems plugins sometimes hook into the gem install process
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
Bundler.definition.validate_ruby!
Installer.install Bundler.root, Bundler.definition, opts
Bundler.load.cache if Bundler.root.join("vendor/cache").exist?
if Bundler.settings[:clean] && Bundler.settings[:path]
require "bundler/cli/clean"
Bundler::CLI::Clean.new(options).run
end
Bundler.ui.confirm "Your bundle is updated!"
without_groups_messages
end
private
def without_groups_messages
if Bundler.settings.without.any?
require "bundler/cli/common"
Bundler.ui.confirm Bundler::CLI::Common.without_groups_message
end
end
end
end

View File

@ -0,0 +1,27 @@
module Bundler
class CLI::Viz
attr_reader :options, :gem_name
def initialize(options)
@options = options
end
def run
require 'graphviz'
output_file = File.expand_path(options[:file])
graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format])
graph.viz
rescue LoadError => e
Bundler.ui.error e.inspect
Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
Bundler.ui.warn "`gem install ruby-graphviz`"
rescue StandardError => e
if e.message =~ /GraphViz not installed or dot not in PATH/
Bundler.ui.error e.message
Bundler.ui.warn "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`."
else
raise
end
end
end
end

View File

@ -0,0 +1,5 @@
module Bundler
WINDOWS = RbConfig::CONFIG["host_os"] =~ %r!(msdos|mswin|djgpp|mingw)!
FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/
NULL = WINDOWS ? "NUL" : "/dev/null"
end

View File

@ -0,0 +1,123 @@
module Bundler
# Returns current version of Ruby
#
# @return [CurrentRuby] Current version of Ruby
def self.current_ruby
@current_ruby ||= CurrentRuby.new
end
class CurrentRuby
def on_18?
RUBY_VERSION =~ /^1\.8/
end
def on_19?
RUBY_VERSION =~ /^1\.9/
end
def on_20?
RUBY_VERSION =~ /^2\.0/
end
def on_21?
RUBY_VERSION =~ /^2\.1/
end
def ruby?
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
end
def ruby_18?
ruby? && on_18?
end
def ruby_19?
ruby? && on_19?
end
def ruby_20?
ruby? && on_20?
end
def ruby_21?
ruby? && on_21?
end
def mri?
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
end
def mri_18?
mri? && on_18?
end
def mri_19?
mri? && on_19?
end
def mri_20?
mri? && on_20?
end
def mri_21?
mri? && on_21?
end
def rbx?
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
end
def jruby?
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
end
def jruby_18?
jruby? && on_18?
end
def jruby_19?
jruby? && on_19?
end
def maglev?
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
end
def mswin?
Bundler::WINDOWS
end
def mingw?
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != 'x64'
end
def mingw_18?
mingw? && on_18?
end
def mingw_19?
mingw? && on_19?
end
def mingw_20?
mingw? && on_20?
end
def mingw_21?
mingw? && on_21?
end
def x64_mingw?
Bundler::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu == 'x64'
end
def x64_mingw_20?
x64_mingw? && on_20?
end
def x64_mingw_21?
x64_mingw? && on_21?
end
end
end

View File

@ -0,0 +1,610 @@
require "digest/sha1"
require "set"
module Bundler
class Definition
include GemHelpers
attr_reader :dependencies, :platforms, :ruby_version, :locked_deps
# Given a gemfile and lockfile creates a Bundler definition
#
# @param gemfile [Pathname] Path to Gemfile
# @param lockfile [Pathname,nil] Path to Gemfile.lock
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @return [Bundler::Definition]
def self.build(gemfile, lockfile, unlock)
unlock ||= {}
gemfile = Pathname.new(gemfile).expand_path
unless gemfile.file?
raise GemfileNotFound, "#{gemfile} not found"
end
Dsl.evaluate(gemfile, lockfile, unlock)
end
#
# How does the new system work?
#
# * Load information from Gemfile and Lockfile
# * Invalidate stale locked specs
# * All specs from stale source are stale
# * All specs that are reachable only through a stale
# dependency are stale.
# * If all fresh dependencies are satisfied by the locked
# specs, then we can try to resolve locally.
#
# @param lockfile [Pathname] Path to Gemfile.lock
# @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile
# @param sources [Bundler::SourceList]
# @param unlock [Hash, Boolean, nil] Gems that have been requested
# to be updated or true if all gems should be updated
# @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
@unlocking = unlock == true || !unlock.empty?
@dependencies, @sources, @unlock = dependencies, sources, unlock
@remote = false
@specs = nil
@lockfile_contents = ""
@ruby_version = ruby_version
if lockfile && File.exist?(lockfile)
@lockfile_contents = Bundler.read_file(lockfile)
locked = LockfileParser.new(@lockfile_contents)
@platforms = locked.platforms
if unlock != true
@locked_deps = locked.dependencies
@locked_specs = SpecSet.new(locked.specs)
@locked_sources = locked.sources
else
@unlock = {}
@locked_deps = []
@locked_specs = SpecSet.new([])
@locked_sources = []
end
else
@unlock = {}
@platforms = []
@locked_deps = []
@locked_specs = SpecSet.new([])
@locked_sources = []
end
@unlock[:gems] ||= []
@unlock[:sources] ||= []
current_platform = Bundler.rubygems.platforms.map { |p| generic(p) }.compact.last
@new_platform = !@platforms.include?(current_platform)
@platforms |= [current_platform]
@path_changes = converge_paths
eager_unlock = expand_dependencies(@unlock[:gems])
@unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name }
@source_changes = converge_sources
@dependency_changes = converge_dependencies
@local_changes = converge_locals
fixup_dependency_types!
end
def fixup_dependency_types!
# XXX This is a temporary workaround for a bug when using rubygems 1.8.15
# where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
# doesn't carry a notion of the dependency type, if you use
# add_development_dependency in a gemspec that's loaded with the gemspec
# directive, the lockfile dependencies and resolved dependencies end up
# with a mismatch on #type.
# Test coverage to catch a regression on this is in gemspec_spec.rb
@dependencies.each do |d|
if ld = @locked_deps.find { |l| l.name == d.name }
ld.instance_variable_set(:@type, d.type)
end
end
end
def resolve_with_cache!
raise "Specs already loaded" if @specs
sources.cached!
specs
end
def resolve_remotely!
raise "Specs already loaded" if @specs
@remote = true
sources.remote!
specs
end
# For given dependency list returns a SpecSet with Gemspec of all the required
# dependencies.
# 1. The method first resolves the dependencies specified in Gemfile
# 2. After that it tries and fetches gemspec of resolved dependencies
#
# @return [Bundler::SpecSet]
def specs
@specs ||= begin
specs = resolve.materialize(requested_dependencies)
unless specs["bundler"].any?
local = Bundler.settings[:frozen] ? rubygems_index : index
bundler = local.search(Gem::Dependency.new('bundler', VERSION)).last
specs["bundler"] = bundler if bundler
end
specs
end
end
def new_specs
specs - @locked_specs
end
def removed_specs
@locked_specs - specs
end
def new_platform?
@new_platform
end
def missing_specs
missing = []
resolve.materialize(requested_dependencies, missing)
missing
end
def requested_specs
@requested_specs ||= begin
groups = self.groups - Bundler.settings.without
groups.map! { |g| g.to_sym }
specs_for(groups)
end
end
def current_dependencies
dependencies.reject { |d| !d.should_include? }
end
def specs_for(groups)
deps = dependencies.select { |d| (d.groups & groups).any? }
deps.delete_if { |d| !d.should_include? }
specs.for(expand_dependencies(deps))
end
# Resolve all the dependencies specified in Gemfile. It ensures that
# dependencies that have been already resolved via locked file and are fresh
# are reused when resolving dependencies
#
# @return [SpecSet] resolved dependencies
def resolve
@resolve ||= begin
if Bundler.settings[:frozen] || (!@unlocking && nothing_changed?)
@locked_specs
else
last_resolve = converge_locked_specs
# Run a resolve against the locally available gems
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve)
end
end
end
def index
@index ||= Index.build do |idx|
dependency_names = @dependencies.dup || []
dependency_names.map! {|d| d.name }
sources.all_sources.each do |s|
s.dependency_names = dependency_names
idx.add_source s.specs
dependency_names.push(*s.unmet_deps).uniq!
end
end
end
# used when frozen is enabled so we can find the bundler
# spec, even if (say) a git gem is not checked out.
def rubygems_index
@rubygems_index ||= Index.build do |idx|
sources.rubygems_sources.each do |rubygems|
idx.add_source rubygems.specs
end
end
end
def has_rubygems_remotes?
sources.rubygems_sources.any? {|s| s.remotes.any? }
end
def has_local_dependencies?
!sources.path_sources.empty? || !sources.git_sources.empty?
end
def spec_git_paths
sources.git_sources.map {|s| s.path.to_s }
end
def groups
dependencies.map { |d| d.groups }.flatten.uniq
end
def lock(file)
contents = to_lock
# Convert to \r\n if the existing lock has them
# i.e., Windows with `git config core.autocrlf=true`
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
return if @lockfile_contents == contents
if Bundler.settings[:frozen]
Bundler.ui.error "Cannot write a changed lockfile while frozen."
return
end
File.open(file, 'wb'){|f| f.puts(contents) }
rescue Errno::EACCES
raise Bundler::InstallError,
"There was an error while trying to write to Gemfile.lock. It is likely that \n" \
"you need to allow write permissions for the file at path: \n" \
"#{File.expand_path(file)}"
end
def to_lock
out = ""
sources.lock_sources.each do |source|
# Add the source header
out << source.to_lock
# Find all specs for this source
resolve.
select { |s| source.can_lock?(s) }.
# This needs to be sorted by full name so that
# gems with the same name, but different platform
# are ordered consistently
sort_by { |s| s.full_name }.
each do |spec|
next if spec.name == 'bundler'
out << spec.to_lock
end
out << "\n"
end
out << "PLATFORMS\n"
platforms.map { |p| p.to_s }.sort.each do |p|
out << " #{p}\n"
end
out << "\n"
out << "DEPENDENCIES\n"
handled = []
dependencies.
sort_by { |d| d.to_s }.
each do |dep|
next if handled.include?(dep.name)
out << dep.to_lock
handled << dep.name
end
out
end
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
changes = false
msg = "You are trying to install in deployment mode after changing\n" \
"your Gemfile. Run `bundle install` elsewhere and add the\n" \
"updated Gemfile.lock to version control."
unless explicit_flag
msg += "\n\nIf this is a development machine, remove the Gemfile " \
"freeze \nby running `bundle install --no-deployment`."
end
added = []
deleted = []
changed = []
gemfile_sources = sources.all_sources
if @locked_sources != gemfile_sources
new_sources = gemfile_sources - @locked_sources
deleted_sources = @locked_sources - gemfile_sources
if new_sources.any?
added.concat new_sources.map { |source| "* source: #{source}" }
end
if deleted_sources.any?
deleted.concat deleted_sources.map { |source| "* source: #{source}" }
end
changes = true
end
both_sources = Hash.new { |h,k| h[k] = ["no specified source", "no specified source"] }
@dependencies.each { |d| both_sources[d.name][0] = d.source if d.source }
@locked_deps.each { |d| both_sources[d.name][1] = d.source if d.source }
both_sources.delete_if { |k,v| v[0] == v[1] }
if @dependencies != @locked_deps
new_deps = @dependencies - @locked_deps
deleted_deps = @locked_deps - @dependencies
if new_deps.any?
added.concat new_deps.map { |d| "* #{pretty_dep(d)}" }
end
if deleted_deps.any?
deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" }
end
both_sources.each do |name, sources|
changed << "* #{name} from `#{sources[0]}` to `#{sources[1]}`"
end
changes = true
end
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
msg << "\n"
raise ProductionError, msg if added.any? || deleted.any? || changed.any?
end
def validate_ruby!
return unless ruby_version
if diff = ruby_version.diff(Bundler.ruby_version)
problem, expected, actual = diff
msg = case problem
when :engine
"Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
when :version
"Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
when :engine_version
"Your #{Bundler.ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
when :patchlevel
if !expected.is_a?(String)
"The Ruby patchlevel in your Gemfile must be a string"
else
"Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
end
end
raise RubyVersionMismatch, msg
end
end
private
attr_reader :sources
def nothing_changed?
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
end
def pretty_dep(dep, source = false)
msg = "#{dep.name}"
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
msg << " from the `#{dep.source}` source" if source && dep.source
msg
end
# Check if the specs of the given source changed
# according to the locked source. A block should be
# in order to specify how the locked version of
# the source should be found.
def specs_changed?(source, &block)
locked = @locked_sources.find(&block)
if locked
unlocking = @locked_specs.any? do |locked_spec|
locked_spec.source != locked
end
end
!locked || unlocking || source.specs != locked.specs
end
# Get all locals and override their matching sources.
# Return true if any of the locals changed (for example,
# they point to a new revision) or depend on new specs.
def converge_locals
locals = []
Bundler.settings.local_overrides.map do |k,v|
spec = @dependencies.find { |s| s.name == k }
source = spec && spec.source
if source && source.respond_to?(:local_override!)
source.unlock! if @unlock[:gems].include?(spec.name)
locals << [ source, source.local_override!(v) ]
end
end
locals.any? do |source, changed|
changed || specs_changed?(source) { |o| source.class == o.class && source.uri == o.uri }
end
end
def converge_paths
sources.path_sources.any? do |source|
specs_changed?(source) do |ls|
ls.class == source.class && ls.path == source.path
end
end
end
def converge_sources
changes = false
# Get the Rubygems sources from the Gemfile.lock
locked_gem_sources = @locked_sources.select { |s| s.kind_of?(Source::Rubygems) }
# Get the Rubygems sources from the Gemfile
actual_gem_sources = @sources.rubygems_sources
# If there is a Rubygems source in both
unless locked_gem_sources.empty? && actual_gem_sources.empty?
actual_remotes = actual_gem_sources.map(&:remotes).flatten.uniq
locked_gem_sources.each do |locked_gem|
# Merge the remotes from the Gemfile into the Gemfile.lock
changes = changes | locked_gem.replace_remotes(actual_remotes)
end
end
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
# source in the Gemfile.lock, use the one from the Gemfile.
sources.replace_sources!(@locked_sources)
gemfile_sources = sources.all_sources
changes = changes | (Set.new(gemfile_sources) != Set.new(@locked_sources))
gemfile_sources.each do |source|
# If the source is unlockable and the current command allows an unlock of
# the source (for example, you are doing a `bundle update <foo>` of a git-pinned
# gem), unlock it. For git sources, this means to unlock the revision, which
# will cause the `ref` used to be the most recent for the branch (or master) if
# an explicit `ref` is not used.
if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
source.unlock!
changes = true
end
end
changes
end
def converge_dependencies
(@dependencies + @locked_deps).each do |dep|
if dep.source
dep.source = sources.get(dep.source)
end
end
Set.new(@dependencies) != Set.new(@locked_deps)
end
# Remove elements from the locked specs that are expired. This will most
# commonly happen if the Gemfile has changed since the lockfile was last
# generated
def converge_locked_specs
deps = []
# Build a list of dependencies that are the same in the Gemfile
# and Gemfile.lock. If the Gemfile modified a dependency, but
# the gem in the Gemfile.lock still satisfies it, this is fine
# too.
locked_deps_hash = @locked_deps.inject({}) { |hsh, dep| hsh[dep] = dep; hsh }
@dependencies.each do |dep|
locked_dep = locked_deps_hash[dep]
if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
deps << dep
elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
@locked_specs.each do |s|
@unlock[:gems] << s.name if s.source == dep.source
end
dep.source.unlock! if dep.source.respond_to?(:unlock!)
dep.source.specs.each { |s| @unlock[:gems] << s.name }
end
end
converged = []
@locked_specs.each do |s|
s.source = sources.get(s.source)
# Don't add a spec to the list if its source is expired. For example,
# if you change a Git gem to Rubygems.
next if s.source.nil? || @unlock[:sources].include?(s.name)
# If the spec is from a path source and it doesn't exist anymore
# then we just unlock it.
# Path sources have special logic
if s.source.instance_of?(Source::Path)
other = s.source.specs[s].first
# If the spec is no longer in the path source, unlock it. This
# commonly happens if the version changed in the gemspec
next unless other
deps2 = other.dependencies.select { |d| d.type != :development }
# If the dependencies of the path source have changed, unlock it
next unless s.dependencies.sort == deps2.sort
end
converged << s
end
resolve = SpecSet.new(converged)
resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems])
diff = @locked_specs.to_a - resolve.to_a
# Now, we unlock any sources that do not have anymore gems pinned to it
sources.all_sources.each do |source|
next unless source.respond_to?(:unlock!)
unless resolve.any? { |s| s.source == source }
source.unlock! if !diff.empty? && diff.any? { |s| s.source == source }
end
end
resolve
end
def in_locked_deps?(dep, d)
d && dep.source == d.source
end
def satisfies_locked_spec?(dep)
@locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source == dep.source) }
end
def expanded_dependencies
@expanded_dependencies ||= expand_dependencies(dependencies, @remote)
end
def expand_dependencies(dependencies, remote = false)
deps = []
dependencies.each do |dep|
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
next unless remote || dep.current_platform?
dep.gem_platforms(@platforms).each do |p|
deps << DepProxy.new(dep, p) if remote || p == generic(Gem::Platform.local)
end
end
deps
end
def requested_dependencies
groups = self.groups - Bundler.settings.without
groups.map! { |g| g.to_sym }
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
end
def source_requirements
# Load all specs from remote sources
index
# Record the specs available in each gem's source, so that those
# specs will be available later when the resolver knows where to
# look for that gemspec (or its dependencies)
source_requirements = {}
dependencies.each do |dep|
next unless dep.source
source_requirements[dep.name] = dep.source.specs
end
source_requirements
end
end
end

View File

@ -0,0 +1,43 @@
module Bundler
class DepProxy
attr_reader :required_by, :__platform, :dep
def initialize(dep, platform)
@dep, @__platform, @required_by = dep, platform, []
end
def hash
@hash ||= dep.hash
end
def ==(o)
dep == o.dep && __platform == o.__platform
end
alias eql? ==
def type
@dep.type
end
def name
@dep.name
end
def requirement
@dep.requirement
end
def to_s
"#{name} (#{requirement}) #{__platform}"
end
private
def method_missing(*args)
@dep.send(*args)
end
end
end

View File

@ -0,0 +1,99 @@
require 'rubygems/dependency'
require 'bundler/shared_helpers'
require 'bundler/rubygems_ext'
module Bundler
class Dependency < Gem::Dependency
attr_reader :autorequire
attr_reader :groups
attr_reader :platforms
PLATFORM_MAP = {
:ruby => Gem::Platform::RUBY,
:ruby_18 => Gem::Platform::RUBY,
:ruby_19 => Gem::Platform::RUBY,
:ruby_20 => Gem::Platform::RUBY,
:ruby_21 => Gem::Platform::RUBY,
:mri => Gem::Platform::RUBY,
:mri_18 => Gem::Platform::RUBY,
:mri_19 => Gem::Platform::RUBY,
:mri_20 => Gem::Platform::RUBY,
:mri_21 => Gem::Platform::RUBY,
:rbx => Gem::Platform::RUBY,
:jruby => Gem::Platform::JAVA,
:jruby_18 => Gem::Platform::JAVA,
:jruby_19 => Gem::Platform::JAVA,
:mswin => Gem::Platform::MSWIN,
:mingw => Gem::Platform::MINGW,
:mingw_18 => Gem::Platform::MINGW,
:mingw_19 => Gem::Platform::MINGW,
:mingw_20 => Gem::Platform::MINGW,
:mingw_21 => Gem::Platform::MINGW,
:x64_mingw => Gem::Platform::X64_MINGW,
:x64_mingw_20 => Gem::Platform::X64_MINGW,
:x64_mingw_21 => Gem::Platform::X64_MINGW
}.freeze
def initialize(name, version, options = {}, &blk)
type = options["type"] || :runtime
super(name, version, type)
@autorequire = nil
@groups = Array(options["group"] || :default).map { |g| g.to_sym }
@source = options["source"]
@platforms = Array(options["platforms"])
@env = options["env"]
if options.key?('require')
@autorequire = Array(options['require'] || [])
end
end
def gem_platforms(valid_platforms)
return valid_platforms if @platforms.empty?
platforms = []
@platforms.each do |p|
platform = PLATFORM_MAP[p]
next unless valid_platforms.include?(platform)
platforms |= [platform]
end
platforms
end
def should_include?
current_env? && current_platform?
end
def current_env?
return true unless @env
if @env.is_a?(Hash)
@env.all? do |key, val|
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
end
else
ENV[@env.to_s]
end
end
def current_platform?
return true if @platforms.empty?
@platforms.any? { |p|
Bundler.current_ruby.send("#{p}?")
}
end
def to_lock
out = super
out << '!' if source
out << "\n"
end
def specific?
super
rescue NoMethodError
requirement != ">= 0"
end
end
end

View File

@ -0,0 +1,59 @@
module Bundler
class Deployment
def self.define_task(context, task_method = :task, opts = {})
if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
context_name = "capistrano"
role_default = "{:except => {:no_release => true}}"
error_type = ::Capistrano::CommandError
else
context_name = "vlad"
role_default = "[:app]"
error_type = ::Rake::CommandFailedError
end
roles = context.fetch(:bundle_roles, false)
opts[:roles] = roles if roles
context.send :namespace, :bundle do
send :desc, <<-DESC
Install the current Bundler environment. By default, gems will be \
installed to the shared/bundle path. Gems in the development and \
test group will not be installed. The install command is executed \
with the --deployment and --quiet flags. If the bundle cmd cannot \
be found then you can override the bundle_cmd variable to specify \
which one it should use. The base path to the app is fetched from \
the :latest_release variable. Set it for custom deploy layouts.
You can override any of these defaults by setting the variables shown below.
N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \
in your deploy.rb file.
set :bundle_gemfile, "Gemfile"
set :bundle_dir, File.join(fetch(:shared_path), 'bundle')
set :bundle_flags, "--deployment --quiet"
set :bundle_without, [:development, :test]
set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle"
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
DESC
send task_method, :install, opts do
bundle_cmd = context.fetch(:bundle_cmd, "bundle")
bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'bundle'))
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
app_path = context.fetch(:latest_release)
if app_path.to_s.empty?
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
end
args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
args << bundle_flags.to_s
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
end
end
end
end
end

View File

@ -0,0 +1,15 @@
module Bundler
if defined? ::Deprecate
Deprecate = ::Deprecate
elsif defined? Gem::Deprecate
Deprecate = Gem::Deprecate
else
class Deprecate; end
end
unless Deprecate.respond_to?(:skip_during)
def Deprecate.skip_during; yield; end
end
end

View File

@ -0,0 +1,294 @@
require 'bundler/dependency'
require 'bundler/ruby_dsl'
module Bundler
class Dsl
include RubyDsl
def self.evaluate(gemfile, lockfile, unlock)
builder = new
builder.eval_gemfile(gemfile)
builder.to_definition(lockfile, unlock)
end
VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze
attr_accessor :dependencies
def initialize
@source = nil
@sources = SourceList.new
@git_sources = {}
@dependencies = []
@groups = []
@platforms = []
@env = nil
@ruby_version = nil
add_github_sources
end
def eval_gemfile(gemfile, contents = nil)
contents ||= Bundler.read_file(gemfile.to_s)
instance_eval(contents, gemfile.to_s, 1)
rescue SyntaxError => e
syntax_msg = e.message.gsub("#{gemfile.to_s}:", 'on line ')
raise GemfileError, "Gemfile syntax error #{syntax_msg}"
rescue ScriptError, RegexpError, NameError, ArgumentError => e
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
Bundler.ui.warn e.backtrace.join("\n ")
raise GemfileError, "There was an error in your Gemfile," \
" and Bundler cannot continue."
end
def gemspec(opts = nil)
path = opts && opts[:path] || '.'
name = opts && opts[:name] || '{,*}'
development_group = opts && opts[:development_group] || :development
expanded_path = File.expand_path(path, Bundler.default_gemfile.dirname)
gemspecs = Dir[File.join(expanded_path, "#{name}.gemspec")]
case gemspecs.size
when 1
spec = Bundler.load_gemspec(gemspecs.first)
raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec
gem spec.name, :path => path
group(development_group) do
spec.development_dependencies.each do |dep|
gem dep.name, *(dep.requirement.as_list + [:type => :development])
end
end
when 0
raise InvalidOption, "There are no gemspecs at #{expanded_path}."
else
raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. Please use the :name option to specify which one."
end
end
def gem(name, *args)
options = args.last.is_a?(Hash) ? args.pop.dup : {}
version = args || [">= 0"]
normalize_options(name, version, options)
dep = Dependency.new(name, version, options)
# if there's already a dependency with this name we try to prefer one
if current = @dependencies.find { |d| d.name == dep.name }
if current.requirement != dep.requirement
if current.type == :development
@dependencies.delete current
elsif dep.type == :development
return
else
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})"
end
else
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
"You should probably keep only one of them.\n" \
"While it's not a problem now, it could cause errors if you change the version of just one of them later."
end
if current.source != dep.source
if current.type == :development
@dependencies.delete current
elsif dep.type == :development
return
else
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
"#{current.source || 'an unspecified source'} and #{dep.source}\n"
end
end
end
@dependencies << dep
end
def source(source, &blk)
source = normalize_source(source)
if block_given?
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
else
@sources.add_rubygems_remote(source)
end
end
def git_source(name, &block)
unless block_given?
raise InvalidOption, "You need to pass a block to #git_source"
end
if valid_keys.include?(name.to_s)
raise InvalidOption, "You cannot use #{name} as a git source. It " \
"is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
end
@git_sources[name.to_s] = block
end
def path(path, options = {}, &blk)
with_source(@sources.add_path_source(normalize_hash(options).merge("path" => Pathname.new(path))), &blk)
end
def git(uri, options = {}, &blk)
unless block_given?
msg = "You can no longer specify a git source by itself. Instead, \n" \
"either use the :git option on a gem, or specify the gems that \n" \
"bundler should find in the git source by passing a block to \n" \
"the git method, like: \n\n" \
" git 'git://github.com/rails/rails.git' do\n" \
" gem 'rails'\n" \
" end"
raise DeprecatedError, msg
end
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
end
def to_definition(lockfile, unlock)
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
end
def group(*args, &blk)
@groups.concat args
yield
ensure
args.each { @groups.pop }
end
def platforms(*platforms)
@platforms.concat platforms
yield
ensure
platforms.each { @platforms.pop }
end
alias_method :platform, :platforms
def env(name)
@env, old = name, @env
yield
ensure
@env = old
end
def method_missing(name, *args)
location = caller[0].split(':')[0..1].join(':')
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
" from #{location}"
end
private
def add_github_sources
git_source(:github) do |repo_name|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
"git://github.com/#{repo_name}.git"
end
git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" }
end
def with_source(source)
if block_given?
@source = source
yield
end
source
ensure
@source = nil
end
def normalize_hash(opts)
opts.keys.each do |k|
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
end
opts
end
def valid_keys
@valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
end
def normalize_options(name, version, opts)
if name.is_a?(Symbol)
raise GemfileError, %{You need to specify gem names as Strings. Use 'gem "#{name.to_s}"' instead.}
end
normalize_hash(opts)
git_names = @git_sources.keys.map(&:to_s)
invalid_keys = opts.keys - (valid_keys + git_names)
if invalid_keys.any?
message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
message << if invalid_keys.size > 1
"as options for gem '#{name}', but they are invalid."
else
"as an option for gem '#{name}', but it is invalid."
end
message << " Valid options are: #{valid_keys.join(", ")}"
raise InvalidOption, message
end
groups = @groups.dup
opts["group"] = opts.delete("groups") || opts["group"]
groups.concat Array(opts.delete("group"))
groups = [:default] if groups.empty?
platforms = @platforms.dup
opts["platforms"] = opts["platform"] || opts["platforms"]
platforms.concat Array(opts.delete("platforms"))
platforms.map! { |p| p.to_sym }
platforms.each do |p|
next if VALID_PLATFORMS.include?(p)
raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
end
# Save sources passed in a key
if opts.has_key?("source")
source = normalize_source(opts["source"])
opts["source"] = @sources.add_rubygems_source("remotes" => source)
end
git_name = (git_names & opts.keys).last
if @git_sources[git_name]
opts["git"] = @git_sources[git_name].call(opts[git_name])
end
["git", "path"].each do |type|
if param = opts[type]
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
options = opts.merge("name" => name, "version" => $1)
else
options = opts.dup
end
source = send(type, param, options) {}
opts["source"] = source
end
end
opts["source"] ||= @source
opts["env"] ||= @env
opts["platforms"] = platforms.dup
opts["group"] = groups
end
def normalize_source(source)
case source
when :gemcutter, :rubygems, :rubyforge
Bundler.ui.warn "The source :#{source} is deprecated because HTTP " \
"requests are insecure.\nPlease change your source to 'https://" \
"rubygems.org' if possible, or 'http://rubygems.org' if not."
"http://rubygems.org"
when String
source
else
raise GemfileError, "Unknown source '#{source}'"
end
end
end
end

View File

@ -0,0 +1,76 @@
module Bundler
# used for Creating Specifications from the Gemcutter Endpoint
class EndpointSpecification < Gem::Specification
include MatchPlatform
attr_reader :name, :version, :platform, :dependencies
attr_accessor :source, :source_uri
def initialize(name, version, platform, dependencies)
@name = name
@version = version
@platform = platform
@dependencies = dependencies
end
def fetch_platform
@platform
end
# needed for standalone, load required_paths from local gemspec
# after the gem in installed
def require_paths
if @remote_specification
@remote_specification.require_paths
elsif _local_specification
_local_specification.require_paths
else
super
end
end
# needed for binstubs
def executables
if @remote_specification
@remote_specification.executables
elsif _local_specification
_local_specification.executables
else
super
end
end
# needed for bundle clean
def bindir
if @remote_specification
@remote_specification.bindir
elsif _local_specification
_local_specification.bindir
else
super
end
end
# needed for post_install_messages during install
def post_install_message
if @remote_specification
@remote_specification.post_install_message
elsif _local_specification
_local_specification.post_install_message
end
end
def _local_specification
eval(File.read(local_specification_path)) if @loaded_from && File.exist?(local_specification_path)
end
def __swap__(spec)
@remote_specification = spec
end
private
def local_specification_path
"#{base_dir}/specifications/#{full_name}.gemspec"
end
end
end

View File

@ -0,0 +1,56 @@
module Bundler
class Env
def write(io)
io.write(report)
end
def report
out = "Bundler #{Bundler::VERSION}\n"
out << "Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}"
out << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
out << ") [#{RUBY_PLATFORM}]\n"
out << "Rubygems #{Gem::VERSION}\n"
out << "rvm #{ENV['rvm_version']}\n" if ENV['rvm_version']
out << "GEM_HOME #{ENV['GEM_HOME']}\n"
out << "GEM_PATH #{ENV['GEM_PATH']}\n" unless ENV['GEM_PATH'] == ENV['GEM_HOME']
%w(rubygems-bundler open_gem).each do |name|
specs = Gem::Specification.find_all{|s| s.name == name }
out << "#{name} (#{specs.map(&:version).join(',')})\n" unless specs.empty?
end
out << "\nBundler settings\n" unless Bundler.settings.all.empty?
Bundler.settings.all.each do |setting|
out << " #{setting}\n"
Bundler.settings.pretty_values_for(setting).each do |line|
out << " " << line << "\n"
end
end
out << "\n\n" << "Gemfile\n"
out << read_file("Gemfile") << "\n"
out << "\n\n" << "Gemfile.lock\n"
out << read_file("Gemfile.lock") << "\n"
out
end
private
def read_file(filename)
File.read(filename).strip
rescue Errno::ENOENT
"<No #{filename} found>"
rescue => e
"#{e.class}: #{e.message}"
end
end
end

View File

@ -0,0 +1,42 @@
module Bundler
class Environment
attr_reader :root
def initialize(root, definition)
@root = root
@definition = definition
env_file = Bundler.app_config_path.join('environment.rb')
env_file.rmtree if env_file.exist?
end
def inspect
@definition.to_lock.inspect
end
def requested_specs
@definition.requested_specs
end
def specs
@definition.specs
end
def dependencies
@definition.dependencies
end
def current_dependencies
@definition.current_dependencies
end
def lock
@definition.lock(Bundler.default_lockfile)
end
def update(*gems)
# Nothing
end
end
end

View File

@ -0,0 +1,399 @@
require 'bundler/vendored_persistent'
require 'securerandom'
require 'cgi'
module Bundler
# Handles all the fetching with the rubygems server
class Fetcher
# This error is raised if the API returns a 413 (only printed in verbose)
class FallbackError < HTTPError; end
# This is the error raised if OpenSSL fails the cert verification
class CertificateFailureError < HTTPError
def initialize(remote_uri)
super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \
" is a chance you are experiencing a man-in-the-middle attack, but" \
" most likely your system doesn't have the CA certificates needed" \
" for verification. For information about OpenSSL certificates, see" \
" bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \
" sources and change 'https' to 'http'."
end
end
# This is the error raised when a source is HTTPS and OpenSSL didn't load
class SSLError < HTTPError
def initialize(msg = nil)
super msg || "Could not load OpenSSL.\n" \
"You must recompile Ruby with OpenSSL support or change the sources in your " \
"Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \
"using RVM are available at rvm.io/packages/openssl."
end
end
# This error is raised if HTTP authentication is required, but not provided.
class AuthenticationRequiredError < HTTPError
def initialize(remote_uri)
super "Authentication is required for #{remote_uri}.\n" \
"Please supply credentials for this source. You can do this by running:\n" \
" bundle config #{remote_uri} username:password"
end
end
# This error is raised if HTTP authentication is provided, but incorrect.
class BadAuthenticationError < HTTPError
def initialize(remote_uri)
super "Bad username or password for #{remote_uri}.\n" \
"Please double-check your credentials and correct them."
end
end
# Exceptions classes that should bypass retry attempts. If your password didn't work the
# first time, it's not going to the third time.
AUTH_ERRORS = [AuthenticationRequiredError, BadAuthenticationError]
class << self
attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries
def download_gem_from_uri(spec, uri)
spec.fetch_platform
download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir
gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem"
FileUtils.mkdir_p("#{download_path}/cache")
Bundler.rubygems.download_gem(spec, uri, download_path)
if Bundler.requires_sudo?
Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache"
Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}"
end
gem_path
end
def user_agent
@user_agent ||= begin
ruby = Bundler.ruby_version
agent = "bundler/#{Bundler::VERSION}"
agent += " rubygems/#{Gem::VERSION}"
agent += " ruby/#{ruby.version}"
agent += " (#{ruby.host})"
agent += " command/#{ARGV.first}"
if ruby.engine != "ruby"
# engine_version raises on unknown engines
engine_version = ruby.engine_version rescue "???"
agent += " #{ruby.engine}/#{engine_version}"
end
# add a random ID so we can consolidate runs server-side
agent << " " << SecureRandom.hex(8)
end
end
end
def initialize(remote_uri)
@redirect_limit = 5 # How many redirects to allow in one request
@api_timeout = 10 # How long to wait for each API call
@max_retries = 3 # How many retries for the API call
@remote_uri = Bundler::Source.mirror_for(remote_uri)
@public_uri = @remote_uri.dup
@public_uri.user, @public_uri.password = nil, nil # don't print these
Socket.do_not_reverse_lookup = true
connection # create persistent connection
end
def connection
@connection ||= begin
needs_ssl = @remote_uri.scheme == "https" ||
Bundler.settings[:ssl_verify_mode] ||
Bundler.settings[:ssl_client_cert]
raise SSLError if needs_ssl && !defined?(OpenSSL::SSL)
con = Net::HTTP::Persistent.new 'bundler', :ENV
if @remote_uri.scheme == "https"
con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
OpenSSL::SSL::VERIFY_PEER)
con.cert_store = bundler_cert_store
end
if Bundler.settings[:ssl_client_cert]
pem = File.read(Bundler.settings[:ssl_client_cert])
con.cert = OpenSSL::X509::Certificate.new(pem)
con.key = OpenSSL::PKey::RSA.new(pem)
end
con.read_timeout = @api_timeout
con.override_headers["User-Agent"] = self.class.user_agent
con
end
end
def uri
@public_uri
end
# fetch a gem specification
def fetch_spec(spec)
spec = spec - [nil, 'ruby', '']
spec_file_name = "#{spec.join '-'}.gemspec"
uri = URI.parse("#{@remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz")
if uri.scheme == 'file'
Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path))
elsif cached_spec_path = gemspec_cached_path(spec_file_name)
Bundler.load_gemspec(cached_spec_path)
else
Bundler.load_marshal Gem.inflate(fetch(uri))
end
rescue MarshalError
raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \
"Your network or your gem server is probably having issues right now."
end
# cached gem specification path, if one exists
def gemspec_cached_path spec_file_name
paths = Bundler.rubygems.spec_cache_dirs.map { |dir| File.join(dir, spec_file_name) }
paths = paths.select {|path| File.file? path }
paths.first
end
# return the specs in the bundler format as an index
def specs(gem_names, source)
old = Bundler.rubygems.sources
index = Index.new
if gem_names && use_api
specs = fetch_remote_specs(gem_names)
end
if specs.nil?
# API errors mean we should treat this as a non-API source
@use_api = false
specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do
fetch_all_remote_specs
end
end
specs[@remote_uri].each do |name, version, platform, dependencies|
next if name == 'bundler'
spec = nil
if dependencies
spec = EndpointSpecification.new(name, version, platform, dependencies)
else
spec = RemoteSpecification.new(name, version, platform, self)
end
spec.source = source
spec.source_uri = @remote_uri
index << spec
end
index
rescue CertificateFailureError => e
Bundler.ui.info "" if gem_names && use_api # newline after dots
raise e
ensure
Bundler.rubygems.sources = old
end
# fetch index
def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = [])
query_list = gem_names - full_dependency_list
# only display the message on the first run
if Bundler.ui.debug?
Bundler.ui.debug "Query List: #{query_list.inspect}"
else
Bundler.ui.info ".", false
end
return {@remote_uri => last_spec_list} if query_list.empty?
remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do
fetch_dependency_remote_specs(query_list)
end
spec_list, deps_list = remote_specs
returned_gems = spec_list.map {|spec| spec.first }.uniq
fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list)
rescue HTTPError, MarshalError, GemspecError
Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
Bundler.ui.debug "could not fetch from the dependency API, trying the full index"
@use_api = false
return nil
end
def use_api
return @use_api if defined?(@use_api)
if @remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
@use_api = false
elsif fetch(dependency_api_uri)
@use_api = true
end
rescue HTTPError
@use_api = false
end
def inspect
"#<#{self.class}:0x#{object_id} uri=#{uri}>"
end
private
HTTP_ERRORS = [
Timeout::Error, EOFError, SocketError, Errno::ENETDOWN,
Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN,
Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError,
Net::HTTP::Persistent::Error
]
def fetch(uri, counter = 0)
raise HTTPError, "Too many redirects" if counter >= @redirect_limit
response = request(uri)
Bundler.ui.debug("HTTP #{response.code} #{response.message}")
case response
when Net::HTTPRedirection
new_uri = URI.parse(response["location"])
if new_uri.host == uri.host
new_uri.user = uri.user
new_uri.password = uri.password
end
fetch(new_uri, counter + 1)
when Net::HTTPSuccess
response.body
when Net::HTTPRequestEntityTooLarge
raise FallbackError, response.body
else
raise HTTPError, "#{response.class}: #{response.body}"
end
end
def request(uri)
Bundler.ui.debug "HTTP GET #{uri}"
req = Net::HTTP::Get.new uri.request_uri
if uri.user
user = CGI.unescape(uri.user)
password = uri.password ? CGI.unescape(uri.password) : nil
req.basic_auth(user, password)
end
connection.request(uri, req)
rescue Net::HTTPUnauthorized, Net::HTTPForbidden
retry_with_auth { request(uri) }
rescue OpenSSL::SSL::SSLError
raise CertificateFailureError.new(uri)
rescue *HTTP_ERRORS => e
Bundler.ui.trace e
raise HTTPError, "Network error while fetching #{uri}"
end
def dependency_api_uri(gem_names = [])
uri = fetch_uri + "api/v1/dependencies"
uri.query = "gems=#{URI.encode(gem_names.join(","))}" if gem_names.any?
uri
end
# fetch from Gemcutter Dependency Endpoint API
def fetch_dependency_remote_specs(gem_names)
Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(',')}"
marshalled_deps = fetch dependency_api_uri(gem_names)
gem_list = Bundler.load_marshal(marshalled_deps)
deps_list = []
spec_list = gem_list.map do |s|
dependencies = s[:dependencies].map do |name, requirement|
dep = well_formed_dependency(name, requirement.split(", "))
deps_list << dep.name
dep
end
[s[:name], Gem::Version.new(s[:number]), s[:platform], dependencies]
end
[spec_list, deps_list.uniq]
end
# fetch from modern index: specs.4.8.gz
def fetch_all_remote_specs
old_sources = Bundler.rubygems.sources
Bundler.rubygems.sources = [@remote_uri.to_s]
Bundler.rubygems.fetch_all_remote_specs
rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError => e
case e.message
when /certificate verify failed/
raise CertificateFailureError.new(uri)
when /401|403/
# Gemfury uses a 403 for unauthenticated requests instead of a 401, so retry auth
# on both.
retry_with_auth { fetch_all_remote_specs }
else
Bundler.ui.trace e
raise HTTPError, "Could not fetch specs from #{uri}"
end
ensure
Bundler.rubygems.sources = old_sources
end
def well_formed_dependency(name, *requirements)
Gem::Dependency.new(name, *requirements)
rescue ArgumentError => e
illformed = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'
raise e unless e.message.include?(illformed)
puts # we shouldn't print the error message on the "fetching info" status line
raise GemspecError,
"Unfortunately, the gem #{s[:name]} (#{s[:number]}) has an invalid " \
"gemspec. \nPlease ask the gem author to yank the bad version to fix " \
"this issue. For more information, see http://bit.ly/syck-defaultkey."
end
def bundler_cert_store
store = OpenSSL::X509::Store.new
if Bundler.settings[:ssl_ca_cert]
if File.directory? Bundler.settings[:ssl_ca_cert]
store.add_path Bundler.settings[:ssl_ca_cert]
else
store.add_file Bundler.settings[:ssl_ca_cert]
end
else
store.set_default_paths
certs = File.expand_path("../ssl_certs/*.pem", __FILE__)
Dir.glob(certs).each { |c| store.add_file c }
end
store
end
# Attempt to retry with HTTP authentication, if it's appropriate to do so. Yields to a block;
# the caller should use this to re-attempt the failing request with the altered `@remote_uri`.
def retry_with_auth
# Authentication has already been attempted and failed.
raise BadAuthenticationError.new(uri) if @remote_uri.user
auth = Bundler.settings[@remote_uri.to_s]
# Authentication isn't provided at all, by "bundle config" or in the URI.
raise AuthenticationRequiredError.new(uri) if auth.nil?
@remote_uri.user, @remote_uri.password = *auth.split(":", 2)
yield
end
private
def fetch_uri
@fetch_uri ||= begin
if @remote_uri.host == "rubygems.org"
uri = @remote_uri.dup
uri.host = "bundler.rubygems.org"
uri
else
@remote_uri
end
end
end
end
end

View File

@ -0,0 +1,42 @@
require "bundler/vendored_thor"
module Bundler
def self.with_friendly_errors
yield
rescue Bundler::BundlerError => e
Bundler.ui.error e.message, :wrap => true
Bundler.ui.trace e
exit e.status_code
rescue Thor::AmbiguousTaskError => e
Bundler.ui.error e.message
exit 15
rescue Thor::UndefinedTaskError => e
Bundler.ui.error e.message
exit 15
rescue Thor::Error => e
Bundler.ui.error e.message
exit 1
rescue LoadError => e
raise e unless e.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/
Bundler.ui.error "\nCould not load OpenSSL."
Bundler.ui.warn <<-WARN, :wrap => true
You must recompile Ruby with OpenSSL support or change the sources in your \
Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL \
using RVM are available at http://rvm.io/packages/openssl.
WARN
Bundler.ui.trace e
exit 1
rescue Interrupt => e
Bundler.ui.error "\nQuitting..."
Bundler.ui.trace e
exit 1
rescue SystemExit => e
exit e.status
rescue Exception => e
Bundler.ui.error <<-ERR, :wrap => true
Unfortunately, a fatal error has occurred. Please see the Bundler \
troubleshooting documentation at http://bit.ly/bundler-issues. Thanks!
ERR
raise e
end
end

View File

@ -0,0 +1,169 @@
require 'bundler/vendored_thor' unless defined?(Thor)
require 'bundler'
module Bundler
class GemHelper
include Rake::DSL if defined? Rake::DSL
class << self
# set when install'd.
attr_accessor :instance
def install_tasks(opts = {})
new(opts[:dir], opts[:name]).install
end
def gemspec(&block)
gemspec = instance.gemspec
block.call(gemspec) if block
gemspec
end
end
attr_reader :spec_path, :base, :gemspec
def initialize(base = nil, name = nil)
Bundler.ui = UI::Shell.new
@base = (base ||= SharedHelpers.pwd)
gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")]
raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1
@spec_path = gemspecs.first
@gemspec = Bundler.load_gemspec(@spec_path)
end
def install
built_gem_path = nil
desc "Build #{name}-#{version}.gem into the pkg directory."
task 'build' do
built_gem_path = build_gem
end
desc "Build and install #{name}-#{version}.gem into system gems."
task 'install' => 'build' do
install_gem(built_gem_path)
end
desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems"
task 'release' => 'build' do
release_gem(built_gem_path)
end
GemHelper.instance = self
end
def build_gem
file_name = nil
sh("gem build -V '#{spec_path}'") { |out, code|
file_name = File.basename(built_gem_path)
FileUtils.mkdir_p(File.join(base, 'pkg'))
FileUtils.mv(built_gem_path, 'pkg')
Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}."
}
File.join(base, 'pkg', file_name)
end
def install_gem(built_gem_path=nil)
built_gem_path ||= build_gem
out, _ = sh_with_code("gem install '#{built_gem_path}' --local")
raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/]
Bundler.ui.confirm "#{name} (#{version}) installed."
end
def release_gem(built_gem_path=nil)
guard_clean
built_gem_path ||= build_gem
tag_version { git_push } unless already_tagged?
rubygem_push(built_gem_path) if gem_push?
end
protected
def rubygem_push(path)
if Pathname.new("~/.gem/credentials").expand_path.exist?
sh("gem push '#{path}'")
Bundler.ui.confirm "Pushed #{name} #{version} to rubygems.org."
else
raise "Your rubygems.org credentials aren't set. Run `gem push` to set them."
end
end
def built_gem_path
Dir[File.join(base, "#{name}-*.gem")].sort_by{|f| File.mtime(f)}.last
end
def git_push
perform_git_push
perform_git_push ' --tags'
Bundler.ui.confirm "Pushed git commits and tags."
end
def perform_git_push(options = '')
cmd = "git push #{options}"
out, code = sh_with_code(cmd)
raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0
end
def already_tagged?
if sh('git tag').split(/\n/).include?(version_tag)
Bundler.ui.confirm "Tag #{version_tag} has already been created."
true
end
end
def guard_clean
clean? && committed? or raise("There are files that need to be committed first.")
end
def clean?
sh_with_code("git diff --exit-code")[1] == 0
end
def committed?
sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0
end
def tag_version
sh "git tag -a -m \"Version #{version}\" #{version_tag}"
Bundler.ui.confirm "Tagged #{version_tag}."
yield if block_given?
rescue
Bundler.ui.error "Untagging #{version_tag} due to error."
sh_with_code "git tag -d #{version_tag}"
raise
end
def version
gemspec.version
end
def version_tag
"v#{version}"
end
def name
gemspec.name
end
def sh(cmd, &block)
out, code = sh_with_code(cmd, &block)
code == 0 ? out : raise(out.empty? ? "Running `#{cmd}' failed. Run this command directly for more detailed output." : out)
end
def sh_with_code(cmd, &block)
cmd << " 2>&1"
outbuf = ''
Bundler.ui.debug(cmd)
SharedHelpers.chdir(base) {
outbuf = `#{cmd}`
if $? == 0
block.call(outbuf) if block
end
}
[outbuf, $?]
end
def gem_push?
! %w{n no nil false off 0}.include?(ENV['gem_push'].to_s.downcase)
end
end
end

View File

@ -0,0 +1,24 @@
module Bundler
module GemHelpers
GENERIC_CACHE = {}
GENERICS = [
[Gem::Platform.new('java'), Gem::Platform.new('java')],
[Gem::Platform.new('mswin32'), Gem::Platform.new('mswin32')],
[Gem::Platform.new('x64-mingw32'), Gem::Platform.new('x64-mingw32')],
[Gem::Platform.new('x86_64-mingw32'), Gem::Platform.new('x64-mingw32')],
[Gem::Platform.new('mingw32'), Gem::Platform.new('x86-mingw32')]
]
def generic(p)
return p if p == Gem::Platform::RUBY
GENERIC_CACHE[p] ||= begin
_, found = GENERICS.find do |match, _generic|
p.os == match.os && (!match.cpu || p.cpu == match.cpu)
end
found || Gem::Platform::RUBY
end
end
end
end

View File

@ -0,0 +1,9 @@
require 'rubygems/installer'
module Bundler
class GemInstaller < Gem::Installer
def check_executable_overwrite(filename)
# Bundler needs to install gems regardless of binstub overwriting
end
end
end

View File

@ -0,0 +1,8 @@
module Bundler
def self.preserve_gem_path
original_gem_path = ENV["_ORIGINAL_GEM_PATH"]
gem_path = ENV["GEM_PATH"]
ENV["_ORIGINAL_GEM_PATH"] = gem_path if original_gem_path.nil? || original_gem_path == ""
ENV["GEM_PATH"] = original_gem_path if gem_path.nil? || gem_path == ""
end
end

View File

@ -0,0 +1,2 @@
require 'bundler/gem_helper'
Bundler::GemHelper.install_tasks

View File

@ -0,0 +1,166 @@
require 'set'
module Bundler
class Graph
GRAPH_NAME = :Gemfile
def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png")
@env = env
@output_file = output_file
@show_version = show_version
@show_requirements = show_requirements
@output_format = output_format
@groups = []
@relations = Hash.new {|h, k| h[k] = Set.new}
@node_options = {}
@edge_options = {}
_patching_gem_dependency_class
_populate_relations
end
attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format
def viz
GraphVizClient.new(self).run
end
private
def _populate_relations
parent_dependencies = _groups.values.to_set.flatten
while true
if parent_dependencies.empty?
break
else
tmp = Set.new
parent_dependencies.each do |dependency|
child_dependencies = dependency.to_spec.runtime_dependencies.to_set
@relations[dependency.name] += child_dependencies.map(&:name).to_set
tmp += child_dependencies
@node_options[dependency.name] = _make_label(dependency, :node)
child_dependencies.each do |c_dependency|
@edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge)
end
end
parent_dependencies = tmp
end
end
end
def _groups
relations = Hash.new {|h, k| h[k] = Set.new}
@env.current_dependencies.each do |dependency|
dependency.groups.each do |group|
relations[group.to_s].add(dependency)
@relations[group.to_s].add(dependency.name)
@node_options[group.to_s] ||= _make_label(group, :node)
@edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge)
end
end
@groups = relations.keys
relations
end
def _make_label(symbol_or_string_or_dependency, element_type)
case element_type.to_sym
when :node
if symbol_or_string_or_dependency.is_a?(Gem::Dependency)
label = symbol_or_string_or_dependency.name.dup
label << "\n#{symbol_or_string_or_dependency.to_spec.version.to_s}" if @show_version
else
label = symbol_or_string_or_dependency.to_s
end
when :edge
label = nil
if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements
tmp = symbol_or_string_or_dependency.requirements_list.join(", ")
label = tmp if tmp != ">= 0"
end
else
raise ArgumentError, "2nd argument is invalid"
end
label.nil? ? {} : { :label => label }
end
def _patching_gem_dependency_class
# method borrow from rubygems/dependency.rb
# redefinition of matching_specs will also redefine to_spec and to_specs
Gem::Dependency.class_eval do
def matching_specs platform_only = false
matches = Bundler.load.specs.select { |spec|
self.name == spec.name and
requirement.satisfied_by? spec.version
}
if platform_only
matches.reject! { |spec|
not Gem::Platform.match spec.platform
}
end
matches = matches.sort_by { |s| s.sort_obj } # HACK: shouldn't be needed
end
end
end
class GraphVizClient
def initialize(graph_instance)
@graph_name = graph_instance.class::GRAPH_NAME
@groups = graph_instance.groups
@relations = graph_instance.relations
@node_options = graph_instance.node_options
@edge_options = graph_instance.edge_options
@output_file = graph_instance.output_file
@output_format = graph_instance.output_format
end
def g
@g ||= ::GraphViz.digraph(@graph_name, {:concentrate => true, :normalize => true, :nodesep => 0.55}) do |g|
g.edge[:weight] = 2
g.edge[:fontname] = g.node[:fontname] = 'Arial, Helvetica, SansSerif'
g.edge[:fontsize] = 12
end
end
def run
@groups.each do |group|
g.add_nodes(
group,
{:style => 'filled',
:fillcolor => '#B9B9D5',
:shape => "box3d",
:fontsize => 16}.merge(@node_options[group])
)
end
@relations.each do |parent, children|
children.each do |child|
if @groups.include?(parent)
g.add_nodes(child, {:style => 'filled', :fillcolor => '#B9B9D5'}.merge(@node_options[child]))
g.add_edges(parent, child, {:constraint => false}.merge(@edge_options["#{parent}_#{child}"]))
else
g.add_nodes(child, @node_options[child])
g.add_edges(parent, child, @edge_options["#{parent}_#{child}"])
end
end
end
if @output_format.to_s == "debug"
$stdout.puts g.output :none => String
Bundler.ui.info "debugging bundle viz..."
else
begin
g.output @output_format.to_sym => "#{@output_file}.#{@output_format}"
Bundler.ui.info "#{@output_file}.#{@output_format}"
rescue ArgumentError => e
$stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb"
raise e
end
end
end
end
end
end

View File

@ -0,0 +1,203 @@
require "set"
module Bundler
class Index
include Enumerable
def self.build
i = new
yield i
i
end
attr_reader :specs, :all_specs, :sources
protected :specs, :all_specs
def initialize
@sources = []
@cache = {}
@specs = Hash.new { |h,k| h[k] = [] }
@all_specs = Hash.new { |h,k| h[k] = [] }
end
def initialize_copy(o)
super
@sources = @sources.dup
@cache = {}
@specs = Hash.new { |h,k| h[k] = [] }
@all_specs = Hash.new { |h,k| h[k] = [] }
o.specs.each do |name, array|
@specs[name] = array.dup
end
o.all_specs.each do |name, array|
@all_specs[name] = array.dup
end
end
def inspect
"#<#{self.class}:0x#{object_id} sources=#{sources.map{|s| s.inspect}} specs.size=#{specs.size}>"
end
def empty?
each { return false }
true
end
def search_all(name)
all_matches = @all_specs[name] + local_search(name)
@sources.each do |source|
all_matches.concat(source.search_all(name))
end
all_matches
end
# Search this index's specs, and any source indexes that this index knows
# about, returning all of the results.
def search(query, base = nil)
results = local_search(query, base)
seen = Set.new(results.map { |spec| [spec.name, spec.version, spec.platform] })
@sources.each do |source|
source.search(query, base).each do |spec|
lookup = [spec.name, spec.version, spec.platform]
unless seen.include?(lookup)
results << spec
seen << lookup
end
end
end
results.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
end
def local_search(query, base = nil)
case query
when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
when String then specs_by_name(query)
when Gem::Dependency then search_by_dependency(query, base)
else
raise "You can't search for a #{query.inspect}."
end
end
def source_types
sources.map{|s| s.class }.uniq
end
alias [] search
def <<(spec)
arr = specs_by_name(spec.name)
arr.delete_if do |s|
same_version?(s.version, spec.version) && s.platform == spec.platform
end
arr << spec
spec
end
def each(&blk)
specs.values.each do |specs|
specs.each(&blk)
end
end
# returns a list of the dependencies
def unmet_dependency_names
names = []
each{|s| names.push *s.dependencies.map{|d| d.name } }
names.uniq!
names.delete_if{|n| n == "bundler" }
names.select{|n| search(n).empty? }
end
def use(other, override_dupes = false)
return unless other
other.each do |s|
if (dupes = search_by_spec(s)) && dupes.any?
@all_specs[s.name] = [s] + dupes
next unless override_dupes
@specs[s.name] -= dupes
end
@specs[s.name] << s
end
self
end
def size
@sources.inject(@specs.size) do |size, source|
size += source.size
end
end
def ==(o)
all? do |spec|
other_spec = o[spec].first
(spec.dependencies & other_spec.dependencies).empty? && spec.source == other_spec.source
end
end
def add_source(index)
if index.is_a?(Index)
@sources << index
@sources.uniq! # need to use uniq! here instead of checking for the item before adding
else
raise ArgumentError, "Source must be an index, not #{index.class}"
end
end
private
def specs_by_name(name)
@specs[name]
end
def search_by_dependency(dependency, base = nil)
@cache[base || false] ||= {}
@cache[base || false][dependency] ||= begin
specs = specs_by_name(dependency.name) + (base || [])
found = specs.select do |spec|
if base # allow all platforms when searching from a lockfile
dependency.matches_spec?(spec)
else
dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
end
end
wants_prerelease = dependency.requirement.prerelease?
only_prerelease = specs.all? {|spec| spec.version.prerelease? }
unless wants_prerelease || only_prerelease
found.reject! { |spec| spec.version.prerelease? }
end
found
end
end
def search_by_spec(spec)
specs_by_name(spec.name).select do |s|
same_version?(s.version, spec.version) && Gem::Platform.new(s.platform) == Gem::Platform.new(spec.platform)
end
end
if RUBY_VERSION < '1.9'
def same_version?(a, b)
regex = /^(.*?)(?:\.0)*$/
a.to_s[regex, 1] == b.to_s[regex, 1]
end
else
def same_version?(a, b)
a == b
end
end
def spec_satisfies_dependency?(spec, dep)
return false unless dep.name == spec.name
dep.requirement.satisfied_by?(spec.version)
end
end
end

View File

@ -0,0 +1,64 @@
module Bundler
class Injector
def self.inject(new_deps)
injector = new(new_deps)
injector.inject(Bundler.default_gemfile, Bundler.default_lockfile)
end
def initialize(new_deps)
@new_deps = new_deps
end
def inject(gemfile_path, lockfile_path)
if Bundler.settings[:frozen]
# ensure the lock and Gemfile are synced
Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true)
# temporarily remove frozen while we inject
frozen = Bundler.settings.delete(:frozen)
end
# evaluate the Gemfile we have now
builder = Dsl.new
builder.eval_gemfile(gemfile_path)
# don't inject any gems that are already in the Gemfile
@new_deps -= builder.dependencies
# add new deps to the end of the in-memory Gemfile
builder.eval_gemfile("injected gems", new_gem_lines) if @new_deps.any?
# resolve to see if the new deps broke anything
definition = builder.to_definition(lockfile_path, {})
definition.resolve_remotely!
# since nothing broke, we can add those gems to the gemfile
append_to(gemfile_path) if @new_deps.any?
# since we resolved successfully, write out the lockfile
definition.lock(Bundler.default_lockfile)
# return an array of the deps that we added
return @new_deps
ensure
Bundler.settings[:frozen] = '1' if frozen
end
private
def new_gem_lines
@new_deps.map do |d|
%|gem '#{d.name}', '#{d.requirement}'|
end.join("\n")
end
def append_to(gemfile_path)
gemfile_path.open("a") do |f|
f.puts
f.puts "# Added at #{Time.now} by #{`whoami`.chomp}:"
f.puts new_gem_lines
end
end
end
end

View File

@ -0,0 +1,334 @@
require 'erb'
require 'rubygems/dependency_installer'
require 'bundler/parallel_workers'
module Bundler
class Installer < Environment
class << self
attr_accessor :post_install_messages, :ambiguous_gems
Installer.post_install_messages = {}
Installer.ambiguous_gems = []
end
# Begins the installation process for Bundler.
# For more information see the #run method on this class.
def self.install(root, definition, options = {})
installer = new(root, definition)
installer.run(options)
installer
end
# Runs the install procedures for a specific Gemfile.
#
# Firstly, this method will check to see if Bundler.bundle_path exists
# and if not then will create it. This is usually the location of gems
# on the system, be it RVM or at a system path.
#
# Secondly, it checks if Bundler has been configured to be "frozen"
# Frozen ensures that the Gemfile and the Gemfile.lock file are matching.
# This stops a situation where a developer may update the Gemfile but may not run
# `bundle install`, which leads to the Gemfile.lock file not being correctly updated.
# If this file is not correctly updated then any other developer running
# `bundle install` will potentially not install the correct gems.
#
# Thirdly, Bundler checks if there are any dependencies specified in the Gemfile using
# Bundler::Environment#dependencies. If there are no dependencies specified then
# Bundler returns a warning message stating so and this method returns.
#
# Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so
# then proceeds to set up a defintion based on the default gemfile (Gemfile) and the
# default lock file (Gemfile.lock). However, this is not the case if the platform is different
# to that which is specified in Gemfile.lock, or if there are any missing specs for the gems.
#
# Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote.
# This then leads into the gems being installed, along with stubs for their executables,
# but only if the --binstubs option has been passed or Bundler.options[:bin] has been set
# earlier.
#
# Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time
# that a user runs `bundle install` they will receive any updates from this process.
#
# Finally: TODO add documentation for how the standalone process works.
def run(options)
create_bundle_path
if Bundler.settings[:frozen]
@definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment])
end
if dependencies.empty?
Bundler.ui.warn "The Gemfile specifies no dependencies"
lock
return
end
if Bundler.default_lockfile.exist? && !options["update"]
local = Bundler.ui.silence do
begin
tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil)
true unless tmpdef.new_platform? || tmpdef.missing_specs.any?
rescue BundlerError
end
end
end
# Since we are installing, we can resolve the definition
# using remote specs
unless local
options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely!
end
# the order that the resolver provides is significant, since
# dependencies might actually affect the installation of a gem.
# that said, it's a rare situation (other than rake), and parallel
# installation is just SO MUCH FASTER. so we let people opt in.
jobs = [Bundler.settings[:jobs].to_i-1, 1].max
if jobs > 1 && can_install_parallely?
install_in_parallel jobs, options[:standalone]
else
install_sequentially options[:standalone]
end
lock unless Bundler.settings[:frozen]
generate_standalone(options[:standalone]) if options[:standalone]
end
def install_gem_from_spec(spec, standalone = false, worker = 0)
# Fetch the build settings, if there are any
settings = Bundler.settings["build.#{spec.name}"]
install_message = nil
post_install_message = nil
debug_message = nil
Bundler.rubygems.with_build_args [settings] do
install_message, post_install_message, debug_message = spec.source.install(spec)
if install_message.include? 'Installing'
Bundler.ui.confirm install_message
else
Bundler.ui.info install_message
end
Bundler.ui.debug debug_message if debug_message
Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}"
end
if Bundler.settings[:bin] && standalone
generate_standalone_bundler_executable_stubs(spec)
elsif Bundler.settings[:bin]
generate_bundler_executable_stubs(spec, :force => true)
end
post_install_message
rescue Errno::ENOSPC
raise Bundler::InstallError, "Your disk is out of space. Free some " \
"space to be able to install your bundle."
rescue Exception => e
# if install hook failed or gem signature is bad, just die
raise e if e.is_a?(Bundler::InstallHookError) || e.is_a?(Bundler::SecurityError)
# other failure, likely a native extension build failure
Bundler.ui.info ""
Bundler.ui.warn "#{e.class}: #{e.message}"
msg = "An error occurred while installing #{spec.name} (#{spec.version}),"
msg << " and Bundler cannot continue."
unless spec.source.options["git"]
msg << "\nMake sure that `gem install"
msg << " #{spec.name} -v '#{spec.version}'` succeeds before bundling."
end
Bundler.ui.debug e.backtrace.join("\n")
raise Bundler::InstallError, msg
end
def generate_bundler_executable_stubs(spec, options = {})
if options[:binstubs_cmd] && spec.executables.empty?
options = {}
spec.runtime_dependencies.each do |dep|
bins = @definition.specs[dep].first.executables
options[dep.name] = bins unless bins.empty?
end
if options.any?
Bundler.ui.warn "#{spec.name} has no executables, but you may want " +
"one from a gem it depends on."
options.each{|name,bins| Bundler.ui.warn " #{name} has: #{bins.join(', ')}" }
else
Bundler.ui.warn "There are no executables for the gem #{spec.name}."
end
return
end
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = bin_path = Bundler.bin_path
template = template = File.read(File.expand_path('../templates/Executable', __FILE__))
relative_gemfile_path = relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path)
ruby_command = ruby_command = Thor::Util.ruby_command
exists = []
spec.executables.each do |executable|
next if executable == "bundle"
binstub_path = "#{bin_path}/#{executable}"
if File.exist?(binstub_path) && !options[:force]
exists << executable
next
end
File.open(binstub_path, 'w', 0777 & ~File.umask) do |f|
f.puts ERB.new(template, nil, '-').result(binding)
end
end
if options[:binstubs_cmd] && exists.any?
case exists.size
when 1
Bundler.ui.warn "Skipped #{exists[0]} since it already exists."
when 2
Bundler.ui.warn "Skipped #{exists.join(' and ')} since they already exist."
else
items = exists[0...-1].empty? ? nil : exists[0...-1].join(', ')
skipped = [items, exists[-1]].compact.join(' and ')
Bundler.ui.warn "Skipped #{skipped} since they already exist."
end
Bundler.ui.warn "If you want to overwrite skipped stubs, use --force."
end
end
private
def can_install_parallely?
min_rubygems = "2.0.7"
if Bundler.current_ruby.mri? || Bundler.rubygems.provides?(">= #{min_rubygems}")
true
else
Bundler.ui.warn "Rubygems #{Gem::VERSION} is not threadsafe, so your "\
"gems must be installed one at a time. Upgrade to Rubygems " \
"#{min_rubygems} or higher to enable parallel gem installation."
false
end
end
def generate_standalone_bundler_executable_stubs(spec)
# double-assignment to avoid warnings about variables that will be used by ERB
bin_path = Bundler.bin_path
template = File.read(File.expand_path('../templates/Executable.standalone', __FILE__))
ruby_command = ruby_command = Thor::Util.ruby_command
spec.executables.each do |executable|
next if executable == "bundle"
standalone_path = standalone_path = Pathname(Bundler.settings[:path]).expand_path.relative_path_from(bin_path)
executable_path = executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
File.open "#{bin_path}/#{executable}", 'w', 0755 do |f|
f.puts ERB.new(template, nil, '-').result(binding)
end
end
end
def generate_standalone(groups)
standalone_path = Bundler.settings[:path]
bundler_path = File.join(standalone_path, "bundler")
FileUtils.mkdir_p(bundler_path)
paths = []
if groups.empty?
specs = @definition.requested_specs
else
specs = @definition.specs_for groups.map { |g| g.to_sym }
end
specs.each do |spec|
next if spec.name == "bundler"
next if spec.require_paths.nil? # builtin gems
spec.require_paths.each do |path|
full_path = File.join(spec.full_gem_path, path)
gem_path = Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path))
paths << gem_path.to_s.sub("#{Bundler.ruby_version.engine}/#{RbConfig::CONFIG['ruby_version']}", '#{ruby_engine}/#{ruby_version}')
end
end
File.open File.join(bundler_path, "setup.rb"), "w" do |file|
file.puts "require 'rbconfig'"
file.puts "# ruby 1.8.7 doesn't define RUBY_ENGINE"
file.puts "ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'"
file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]"
file.puts "path = File.expand_path('..', __FILE__)"
paths.each do |path|
file.puts %{$:.unshift File.expand_path("\#{path}/#{path}")}
end
end
end
def install_sequentially(standalone)
specs.each do |spec|
message = install_gem_from_spec spec, standalone, 0
if message
Installer.post_install_messages[spec.name] = message
end
end
end
def install_in_parallel(size, standalone)
name2spec = {}
remains = {}
enqueued = {}
specs.each do |spec|
name2spec[spec.name] = spec
remains[spec.name] = true
end
worker_pool = ParallelWorkers.worker_pool size, lambda { |name, worker|
spec = name2spec[name]
message = install_gem_from_spec spec, standalone, worker
{ :name => spec.name, :post_install => message }
}
# Keys in the remains hash represent uninstalled gems specs.
# We enqueue all gem specs that do not have any dependencies.
# Later we call this lambda again to install specs that depended on
# previously installed specifications. We continue until all specs
# are installed.
enqueue_remaining_specs = lambda do
remains.keys.each do |name|
next if enqueued[name]
spec = name2spec[name]
if ready_to_install?(spec, remains)
worker_pool.enq name
enqueued[name] = true
end
end
end
enqueue_remaining_specs.call
until remains.empty?
message = worker_pool.deq
remains.delete message[:name]
if message[:post_install]
Installer.post_install_messages[message[:name]] = message[:post_install]
end
enqueue_remaining_specs.call
end
message
ensure
worker_pool && worker_pool.stop
end
# We only want to install a gem spec if all its dependencies are met.
# If the dependency is no longer in the `remains` hash then it has been met.
# If a dependency is only development or is self referential it can be ignored.
def ready_to_install?(spec, remains)
spec.dependencies.none? do |dep|
next if dep.type == :development || dep.name == spec.name
remains[dep.name]
end
end
def create_bundle_path
Bundler.mkdir_p(Bundler.bundle_path.to_s) unless Bundler.bundle_path.exist?
rescue Errno::EEXIST
raise PathError, "Could not install to path `#{Bundler.settings[:path]}` " +
"because of an invalid symlink. Remove the symlink so the directory can be created."
end
end
end

View File

@ -0,0 +1,83 @@
require "uri"
require "rubygems/spec_fetcher"
require "bundler/match_platform"
module Bundler
class LazySpecification
include MatchPlatform
attr_reader :name, :version, :dependencies, :platform
attr_accessor :source, :source_uri
def initialize(name, version, platform, source = nil)
@name = name
@version = version
@dependencies = []
@platform = platform
@source = source
@specification = nil
end
def full_name
if platform == Gem::Platform::RUBY or platform.nil? then
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
end
end
def ==(other)
identifier == other.identifier
end
def satisfies?(dependency)
@name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version))
end
def to_lock
if platform == Gem::Platform::RUBY or platform.nil?
out = " #{name} (#{version})\n"
else
out = " #{name} (#{version}-#{platform})\n"
end
dependencies.sort_by {|d| d.to_s }.each do |dep|
next if dep.type == :development
out << " #{dep.to_lock}\n"
end
out
end
def __materialize__
@specification = source.specs.search(Gem::Dependency.new(name, version)).last
end
def respond_to?(*args)
super || @specification.respond_to?(*args)
end
def to_s
@__to_s ||= "#{name} (#{version})"
end
def identifier
@__identifier ||= [name, version, source, platform, dependencies].hash
end
private
def to_ary
nil
end
def method_missing(method, *args, &blk)
raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification
return super unless respond_to?(method)
@specification.send(method, *args, &blk)
end
end
end

View File

@ -0,0 +1,157 @@
require "strscan"
# Some versions of the Bundler 1.1 RC series introduced corrupted
# lockfiles. There were two major problems:
#
# * multiple copies of the same GIT section appeared in the lockfile
# * when this happened, those sections got multiple copies of gems
# in those sections.
#
# As a result, Bundler 1.1 contains code that fixes the earlier
# corruption. We will remove this fix-up code in Bundler 1.2.
module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms
DEPENDENCIES = "DEPENDENCIES"
PLATFORMS = "PLATFORMS"
GIT = "GIT"
GEM = "GEM"
PATH = "PATH"
SPECS = " specs:"
OPTIONS = /^ ([a-z]+): (.*)$/i
def initialize(lockfile)
@platforms = []
@sources = []
@dependencies = []
@state = :source
@specs = {}
@rubygems_aggregate = Source::Rubygems.new
if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/)
raise LockfileError, "Your Gemfile.lock contains merge conflicts.\n" \
"Run `git checkout HEAD -- Gemfile.lock` first to get a clean lock."
end
lockfile.split(/(?:\r?\n)+/).each do |line|
if line == DEPENDENCIES
@state = :dependency
elsif line == PLATFORMS
@state = :platform
else
send("parse_#{@state}", line)
end
end
@sources << @rubygems_aggregate
@specs = @specs.values
end
private
TYPES = {
"GIT" => Bundler::Source::Git,
"GEM" => Bundler::Source::Rubygems,
"PATH" => Bundler::Source::Path
}
def parse_source(line)
case line
when GIT, GEM, PATH
@current_source = nil
@opts, @type = {}, line
when SPECS
case @type
when "PATH"
@current_source = TYPES[@type].from_lock(@opts)
@sources << @current_source
when "GIT"
@current_source = TYPES[@type].from_lock(@opts)
# Strip out duplicate GIT sections
if @type == "GIT" && @sources.include?(@current_source)
@current_source = @sources.find { |s| s == @current_source }
else
@sources << @current_source
end
when "GEM"
Array(@opts["remote"]).each do |url|
@rubygems_aggregate.add_remote(url)
end
@current_source = @rubygems_aggregate
end
when OPTIONS
value = $2
value = true if value == "true"
value = false if value == "false"
key = $1
if @opts[key]
@opts[key] = Array(@opts[key])
@opts[key] << value
else
@opts[key] = value
end
else
parse_spec(line)
end
end
NAME_VERSION = '(?! )(.*?)(?: \(([^-]*)(?:-(.*))?\))?'
NAME_VERSION_2 = %r{^ {2}#{NAME_VERSION}(!)?$}
NAME_VERSION_4 = %r{^ {4}#{NAME_VERSION}$}
NAME_VERSION_6 = %r{^ {6}#{NAME_VERSION}$}
def parse_dependency(line)
if line =~ NAME_VERSION_2
name, version, pinned = $1, $2, $4
version = version.split(",").map { |d| d.strip } if version
dep = Bundler::Dependency.new(name, version)
if pinned && dep.name != 'bundler'
spec = @specs.find {|k, v| v.name == dep.name }
dep.source = spec.last.source if spec
# Path sources need to know what the default name / version
# to use in the case that there are no gemspecs present. A fake
# gemspec is created based on the version set on the dependency
# TODO: Use the version from the spec instead of from the dependency
if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path)
dep.source.name = name
dep.source.version = $1
end
end
@dependencies << dep
end
end
def parse_spec(line)
if line =~ NAME_VERSION_4
name, version = $1, Gem::Version.new($2)
platform = $3 ? Gem::Platform.new($3) : Gem::Platform::RUBY
@current_spec = LazySpecification.new(name, version, platform)
@current_spec.source = @current_source
# Avoid introducing multiple copies of the same spec (caused by
# duplicate GIT sections)
@specs[@current_spec.identifier] ||= @current_spec
elsif line =~ NAME_VERSION_6
name, version = $1, $2
version = version.split(',').map { |d| d.strip } if version
dep = Gem::Dependency.new(name, version)
@current_spec.dependencies << dep
end
end
def parse_platform(line)
if line =~ /^ (.*)$/
@platforms << Gem::Platform.new($1)
end
end
end
end

View File

@ -0,0 +1,13 @@
require 'bundler/gem_helpers'
module Bundler
module MatchPlatform
include GemHelpers
def match_platform(p)
Gem::Platform::RUBY == platform or
platform.nil? or p == platform or
generic(Gem::Platform.new(platform)) == p
end
end
end

View File

@ -0,0 +1,18 @@
require 'thread'
require "bundler/parallel_workers/worker"
module Bundler
module ParallelWorkers
autoload :UnixWorker, "bundler/parallel_workers/unix_worker"
autoload :ThreadWorker, "bundler/parallel_workers/thread_worker"
def self.worker_pool(size, job)
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby? || Bundler.current_ruby.rbx?
ThreadWorker.new(size, job)
else
UnixWorker.new(size, job)
end
end
end
end

View File

@ -0,0 +1,30 @@
module Bundler
module ParallelWorkers
class ThreadWorker < Worker
private
# On platforms where fork is not available
# use Threads for parallely downloading gems
#
# @param size [Integer] Size of thread worker pool
# @param func [Proc] Job to be run inside thread worker pool
def prepare_workers(size, func)
@threads = size.times.map do |i|
Thread.start do
loop do
obj = @request_queue.deq
break if obj.equal? POISON
begin
@response_queue.enq func.call(obj, i)
rescue Exception => e
@response_queue.enq(WrappedException.new(e))
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,101 @@
module Bundler
module ParallelWorkers
# UnixWorker is used only on platforms where fork is available. The way
# this code works is, it forks a preconfigured number of workers and then
# It starts preconfigured number of threads that write to the connected pipe.
class UnixWorker < Worker
class JobHandler < Struct.new(:pid, :io_r, :io_w)
def work(obj)
Marshal.dump obj, io_w
Marshal.load io_r
rescue IOError, Errno::EPIPE
nil
end
end
def initialize(size, job)
# Close the persistent connections for the main thread before forking
Net::HTTP::Persistent.new('bundler', :ENV).shutdown
super
end
private
# Start forked workers for downloading gems. This version of worker
# is only used on platforms where fork is available.
#
# @param size [Integer] Size of worker pool
# @param func [Proc] Job that should be executed in the worker
def prepare_workers(size, func)
@workers = size.times.map do |num|
child_read, parent_write = IO.pipe
parent_read, child_write = IO.pipe
pid = Process.fork do
begin
parent_read.close
parent_write.close
while !child_read.eof?
obj = Marshal.load child_read
Marshal.dump func.call(obj, num), child_write
end
rescue Exception => e
begin
Marshal.dump WrappedException.new(e), child_write
rescue Errno::EPIPE
nil
end
ensure
child_read.close
child_write.close
end
end
child_read.close
child_write.close
JobHandler.new pid, parent_read, parent_write
end
end
# Start the threads whose job is basically to wait for incoming messages
# on request queue and write that message to the connected pipe. Also retrieve
# messages from child worker via connected pipe and write the message to response queue
#
# @param size [Integer] Number of threads to be started
def prepare_threads(size)
@threads = size.times.map do |i|
Thread.start do
worker = @workers[i]
loop do
obj = @request_queue.deq
break if obj.equal? POISON
@response_queue.enq worker.work(obj)
end
end
end
end
# Kill the forked workers by sending SIGINT to them
def stop_workers
@workers.each do |worker|
worker.io_r.close unless worker.io_r.closed?
worker.io_w.close unless worker.io_w.closed?
begin
Process.kill :INT, worker.pid
rescue Errno::ESRCH
nil
end
end
@workers.each do |worker|
begin
Process.waitpid worker.pid
rescue Errno::ECHILD
nil
end
end
end
end
end
end

View File

@ -0,0 +1,69 @@
module Bundler
module ParallelWorkers
class Worker
POISON = Object.new
class WrappedException < StandardError
attr_reader :exception
def initialize(exn)
@exception = exn
end
end
# Creates a worker pool of specified size
#
# @param size [Integer] Size of pool
# @param func [Proc] job to run in inside the worker pool
def initialize(size, func)
@request_queue = Queue.new
@response_queue = Queue.new
prepare_workers size, func
prepare_threads size
trap("INT") { @threads.each {|i| i.exit }; stop_workers; exit 1 }
end
# Enqueue a request to be executed in the worker pool
#
# @param obj [String] mostly it is name of spec that should be downloaded
def enq(obj)
@request_queue.enq obj
end
# Retrieves results of job function being executed in worker pool
def deq
result = @response_queue.deq
if result.is_a?(WrappedException)
raise result.exception
end
result
end
# Stop the forked workers and started threads
def stop
stop_threads
stop_workers
end
private
# Stop the worker threads by sending a poison object down the request queue
# so as worker threads after retrieving it, shut themselves down
def stop_threads
@threads.each do
@request_queue.enq POISON
end
@threads.each do |thread|
thread.join
end
end
# To be overridden by child classes
def prepare_threads(size)
end
# To be overridden by child classes
def stop_workers
end
end
end
end

View File

@ -0,0 +1,26 @@
# Psych could be a gem, so try to ask for it
begin
gem 'psych'
rescue LoadError
end if defined?(gem)
# Psych could just be in the stdlib
# but it's too late if Syck is already loaded
begin
require 'psych' unless defined?(Syck)
rescue LoadError
# Apparently Psych wasn't available. Oh well.
end
# At least load the YAML stdlib, whatever that may be
require 'yaml' unless defined?(YAML.dump)
module Bundler
# On encountering invalid YAML,
# Psych raises Psych::SyntaxError
if defined?(::Psych::SyntaxError)
YamlSyntaxError = ::Psych::SyntaxError
else # Syck raises ArgumentError
YamlSyntaxError = ::ArgumentError
end
end

View File

@ -0,0 +1,57 @@
require "uri"
require "rubygems/spec_fetcher"
module Bundler
# Represents a lazily loaded gem specification, where the full specification
# is on the source server in rubygems' "quick" index. The proxy object is to
# be seeded with what we're given from the source's abbreviated index - the
# full specification will only be fetched when necessary.
class RemoteSpecification
include MatchPlatform
attr_reader :name, :version, :platform
attr_accessor :source, :source_uri
def initialize(name, version, platform, spec_fetcher)
@name = name
@version = version
@platform = platform
@spec_fetcher = spec_fetcher
end
# Needed before installs, since the arch matters then and quick
# specs don't bother to include the arch in the platform string
def fetch_platform
@platform = _remote_specification.platform
end
def full_name
if platform == Gem::Platform::RUBY or platform.nil? then
"#{@name}-#{@version}"
else
"#{@name}-#{@version}-#{platform}"
end
end
# Because Rubyforge cannot be trusted to provide valid specifications
# once the remote gem is downloaded, the backend specification will
# be swapped out.
def __swap__(spec)
@specification = spec
end
private
def _remote_specification
@specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
end
def method_missing(method, *args, &blk)
if Gem::Specification.new.respond_to?(method)
_remote_specification.send(method, *args, &blk)
else
super
end
end
end
end

View File

@ -0,0 +1,534 @@
require 'set'
# This is the latest iteration of the gem dependency resolving algorithm. As of now,
# it can resolve (as a success or 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 Specification
def required_by
@required_by ||= []
end
end
class Dependency
def required_by
@required_by ||= []
end
end
end
module Bundler
class Resolver
ALL = Bundler::Dependency::PLATFORM_MAP.values.uniq.freeze
class SpecGroup < Array
include GemHelpers
attr_reader :activated, :required_by
def initialize(a)
super
@required_by = []
@activated = []
@dependencies = nil
@specs = {}
ALL.each do |p|
@specs[p] = reverse.find { |s| s.match_platform(p) }
end
end
def initialize_copy(o)
super
@required_by = o.required_by.dup
@activated = o.activated.dup
end
def to_specs
specs = {}
@activated.each do |p|
if s = @specs[p]
platform = generic(Gem::Platform.new(s.platform))
next if specs[platform]
lazy_spec = LazySpecification.new(name, version, platform, source)
lazy_spec.dependencies.replace s.dependencies
specs[platform] = lazy_spec
end
end
specs.values
end
def activate_platform(platform)
unless @activated.include?(platform)
@activated << platform
return __dependencies[platform] || []
end
[]
end
def name
@name ||= first.name
end
def version
@version ||= first.version
end
def source
@source ||= first.source
end
def for?(platform)
@specs[platform]
end
def to_s
"#{name} (#{version})"
end
private
def __dependencies
@dependencies ||= begin
dependencies = {}
ALL.each do |p|
if spec = @specs[p]
dependencies[p] = []
spec.dependencies.each do |dep|
next if dep.type == :development
dependencies[p] << DepProxy.new(dep, p)
end
end
end
dependencies
end
end
end
attr_reader :errors, :started_at, :iteration_rate, :iteration_counter
# 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<Gem::Dependency>:: The list of dependencies to resolve
#
# ==== Returns
# <GemBundle>,nil:: If the list of dependencies can be resolved, a
# collection of gemspecs is returned. Otherwise, nil is returned.
def self.resolve(requirements, index, source_requirements = {}, base = [])
Bundler.ui.info "Resolving dependencies...", false
base = SpecSet.new(base) unless base.is_a?(SpecSet)
resolver = new(index, source_requirements, base)
result = resolver.start(requirements)
Bundler.ui.info "" # new line now that dots are done
SpecSet.new(result)
rescue => e
Bundler.ui.info "" # new line before the error
raise e
end
def initialize(index, source_requirements, base)
@errors = {}
@base = base
@index = index
@deps_for = {}
@missing_gems = Hash.new(0)
@source_requirements = source_requirements
@iteration_counter = 0
@started_at = Time.now
end
def debug
if ENV['DEBUG_RESOLVER']
debug_info = yield
debug_info = debug_info.inspect unless debug_info.is_a?(String)
$stderr.puts debug_info
end
end
def successify(activated)
activated.values.map { |s| s.to_specs }.flatten.compact
end
def start(reqs)
activated = {}
@gems_size = Hash[reqs.map { |r| [r, gems_size(r)] }]
resolve(reqs, activated)
end
class State < Struct.new(:reqs, :activated, :requirement, :possibles, :depth, :conflicts)
def name
requirement.name
end
end
def handle_conflict(current, states, existing=nil)
until current.nil? && existing.nil?
current_state = find_state(current, states)
existing_state = find_state(existing, states)
return current if state_any?(current_state)
return existing if state_any?(existing_state)
existing = existing.required_by.last if existing
current = current.required_by.last if current
end
end
def state_any?(state)
state && state.possibles.any?
end
def find_state(current, states)
states.detect { |i| current && current.name == i.name }
end
def other_possible?(conflict, states)
return unless conflict
state = states.detect { |i| i.name == conflict.name }
state && state.possibles.any?
end
def find_conflict_state(conflict, states)
return unless conflict
until states.empty? do
state = states.pop
return state if conflict.name == state.name
end
end
def activate_gem(reqs, activated, requirement, current)
requirement.required_by.replace current.required_by
requirement.required_by << current
activated[requirement.name] = requirement
debug { " Activating: #{requirement.name} (#{requirement.version})" }
debug { requirement.required_by.map { |d| " * #{d.name} (#{d.requirement})" }.join("\n") }
dependencies = requirement.activate_platform(current.__platform)
debug { " Dependencies"}
dependencies.each do |dep|
next if dep.type == :development
dep.required_by.replace(current.required_by)
dep.required_by << current
@gems_size[dep] ||= gems_size(dep)
reqs << dep
end
end
def resolve_for_conflict(state)
raise version_conflict if state.nil? || state.possibles.empty?
reqs, activated, depth, conflicts = state.reqs.dup, state.activated.dup, state.depth, state.conflicts.dup
requirement = state.requirement
possible = state.possibles.pop
activate_gem(reqs, activated, possible, requirement)
return reqs, activated, depth, conflicts
end
def resolve_conflict(current, states)
# Find the state where the conflict has occurred
state = find_conflict_state(current, states)
debug { " -> Going to: #{current.name} state" } if current
# Resolve the conflicts by rewinding the state
# when the conflicted gem was activated
reqs, activated, depth, conflicts = resolve_for_conflict(state)
# Keep the state around if it still has other possibilities
states << state unless state.possibles.empty?
clear_search_cache
return reqs, activated, depth, conflicts
end
def resolve(reqs, activated)
states = []
depth = 0
conflicts = Set.new
until reqs.empty?
indicate_progress
debug { print "\e[2J\e[f" ; "==== Iterating ====\n\n" }
reqs = reqs.sort_by do |a|
[ activated[a.name] ? 0 : 1,
a.requirement.prerelease? ? 0 : 1,
@errors[a.name] ? 0 : 1,
activated[a.name] ? 0 : @gems_size[a] ]
end
debug { "Activated:\n" + activated.values.map {|a| " #{a}" }.join("\n") }
debug { "Requirements:\n" + reqs.map {|r| " #{r}"}.join("\n") }
current = reqs.shift
$stderr.puts "#{' ' * depth}#{current}" if ENV['DEBUG_RESOLVER_TREE']
debug { "Attempting:\n #{current}"}
existing = activated[current.name]
if existing || current.name == 'bundler'
# Force the current
if current.name == 'bundler' && !existing
existing = search(DepProxy.new(Gem::Dependency.new('bundler', VERSION), Gem::Platform::RUBY)).first
raise GemNotFound, %Q{Bundler could not find gem "bundler" (#{VERSION})} unless existing
existing.required_by << existing
activated['bundler'] = existing
end
if current.requirement.satisfied_by?(existing.version)
debug { " * [SUCCESS] Already activated" }
@errors.delete(existing.name)
dependencies = existing.activate_platform(current.__platform)
reqs.concat dependencies
dependencies.each do |dep|
next if dep.type == :development
@gems_size[dep] ||= gems_size(dep)
end
depth += 1
next
else
debug { " * [FAIL] Already activated" }
@errors[existing.name] = [existing, current]
conflicts << current.name
parent = current.required_by.last
if existing.respond_to?(:required_by)
parent = handle_conflict(current, states, existing.required_by[-2]) unless other_possible?(parent, states)
else
parent = handle_conflict(current, states) unless other_possible?(parent, states)
end
if parent.nil? && !conflicts.empty?
parent = states.reverse.detect { |i| conflicts.include?(i.name) && state_any?(i)}
end
raise version_conflict if parent.nil? || parent.name == 'bundler'
reqs, activated, depth, conflicts = resolve_conflict(parent, states)
end
else
matching_versions = search(current)
# If we found no versions that match the current requirement
if matching_versions.empty?
# If this is a top-level Gemfile requirement
if current.required_by.empty?
if base = @base[current.name] and !base.empty?
version = base.first.version
message = "You have requested:\n" \
" #{current.name} #{current.requirement}\n\n" \
"The bundle currently has #{current.name} locked at #{version}.\n" \
"Try running `bundle update #{current.name}`"
elsif current.source
name = current.name
versions = @source_requirements[name][name].map { |s| s.version }
message = "Could not find gem '#{current}' in #{current.source}.\n"
if versions.any?
message << "Source contains '#{name}' at: #{versions.join(', ')}"
else
message << "Source does not contain any versions of '#{current}'"
end
else
message = "Could not find gem '#{current}' "
if @index.source_types.include?(Bundler::Source::Rubygems)
message << "in any of the gem sources listed in your Gemfile."
else
message << "in the gems available on this machine."
end
end
raise GemNotFound, message
# This is not a top-level Gemfile requirement
else
@errors[current.name] = [nil, current]
parent = handle_conflict(current, states)
reqs, activated, depth = resolve_conflict(parent, states)
next
end
end
state = State.new(reqs.dup, activated.dup, current, matching_versions, depth, conflicts)
states << state
requirement = state.possibles.pop
activate_gem(reqs, activated, requirement, current)
end
end
successify(activated)
end
def gems_size(dep)
search(dep).size
end
def clear_search_cache
@deps_for = {}
end
def search(dep)
if base = @base[dep.name] and base.any?
reqs = [dep.requirement.as_list, base.first.version.to_s].flatten.compact
d = Gem::Dependency.new(base.first.name, *reqs)
else
d = dep.dep
end
@deps_for[d.hash] ||= begin
index = @source_requirements[d.name] || @index
results = index.search(d, @base[d.name])
if results.any?
version = results.first.version
nested = [[]]
results.each do |spec|
if spec.version != version
nested << []
version = spec.version
end
nested.last << spec
end
deps = nested.map{|a| SpecGroup.new(a) }.select{|sg| sg.for?(dep.__platform) }
else
deps = []
end
end
end
def clean_req(req)
if req.to_s.include?(">= 0")
req.to_s.gsub(/ \(.*?\)$/, '')
else
req.to_s.gsub(/\, (runtime|development)\)$/, ')')
end
end
def version_conflict
VersionConflict.new(errors.keys, error_message)
end
# For a given conflicted requirement, print out what exactly went wrong
def gem_message(requirement, required_by=[])
m = ""
# A requirement that is required by itself is actually in the Gemfile, and does
# not "depend on" itself
if requirement.required_by.first && requirement.required_by.first.name != requirement.name
dependency_tree(m, required_by)
m << "#{clean_req(requirement)}\n"
else
m << " #{clean_req(requirement)}\n"
end
m << "\n"
end
def dependency_tree(m, requirements)
requirements.each_with_index do |i, j|
m << " " << (" " * j)
m << "#{clean_req(i)}"
m << " depends on\n"
end
m << " " << (" " * requirements.size)
end
def error_message
errors.inject("") do |o, (conflict, (origin, requirement))|
# origin is the SpecSet of specs from the Gemfile that is conflicted with
if origin
o << %{Bundler could not find compatible versions for gem "#{origin.name}":\n}
o << " In Gemfile:\n"
required_by = requirement.required_by
o << gem_message(requirement, required_by)
# If the origin is "bundler", the conflict is us
if origin.name == "bundler"
o << " Current Bundler version:\n"
other_bundler_required = !requirement.requirement.satisfied_by?(origin.version)
# If the origin is a LockfileParser, it does not respond_to :required_by
elsif !origin.respond_to?(:required_by) || !(origin.required_by.first)
o << " In snapshot (Gemfile.lock):\n"
end
required_by = origin.required_by[0..-2]
o << gem_message(origin, required_by)
# If the bundle wants a newer bundler than the running bundler, explain
if origin.name == "bundler" && other_bundler_required
o << "This Gemfile requires a different version of Bundler.\n"
o << "Perhaps you need to update Bundler by running `gem install bundler`?"
end
# origin is nil if the required gem and version cannot be found in any of
# the specified sources
else
# if the gem cannot be found because of a version conflict between lockfile and gemfile,
# print a useful error that suggests running `bundle update`, which may fix things
#
# @base is a SpecSet of the gems in the lockfile
# conflict is the name of the gem that could not be found
if locked = @base[conflict].first
o << "Bundler could not find compatible versions for gem #{conflict.inspect}:\n"
o << " In snapshot (Gemfile.lock):\n"
o << " #{clean_req(locked)}\n\n"
o << " In Gemfile:\n"
required_by = requirement.required_by
o << gem_message(requirement, required_by)
o << "Running `bundle update` will rebuild your snapshot from scratch, using only\n"
o << "the gems in your Gemfile, which may resolve the conflict.\n"
# the rest of the time, the gem cannot be found because it does not exist in the known sources
else
if requirement.required_by.first
o << "Could not find gem '#{clean_req(requirement)}', which is required by "
o << "gem '#{clean_req(requirement.required_by.first)}', in any of the sources."
else
o << "Could not find gem '#{clean_req(requirement)} in any of the sources\n"
end
end
end
o
end
end
private
# Indicates progress by writing a '.' every iteration_rate time which is
# approximately every second. iteration_rate is calculated in the first
# second of resolve running.
def indicate_progress
@iteration_counter += 1
if iteration_rate.nil?
if ((Time.now - started_at) % 3600).round >= 1
@iteration_rate = iteration_counter
end
else
if ((iteration_counter % iteration_rate) == 0)
Bundler.ui.info ".", false
end
end
end
end
end

View File

@ -0,0 +1,60 @@
module Bundler
# General purpose class for retrying code that may fail
class Retry
DEFAULT_ATTEMPTS = 2
attr_accessor :name, :total_runs, :current_run
class << self
attr_accessor :attempts
end
def initialize(name, exceptions = nil, attempts = nil)
@name = name
attempts ||= default_attempts
@exceptions = Array(exceptions) || []
@total_runs = attempts.next # will run once, then upto attempts.times
end
def default_attempts
return Integer(self.class.attempts) if self.class.attempts
DEFAULT_ATTEMPTS
end
def attempt(&block)
@current_run = 0
@failed = false
@error = nil
while keep_trying? do
run(&block)
end
@result
end
alias :attempts :attempt
private
def run(&block)
@failed = false
@current_run += 1
@result = block.call
rescue => e
fail(e)
end
def fail(e)
@failed = true
raise e if last_attempt? || @exceptions.any?{ |k| e.is_a?(k) }
return true unless name
Bundler.ui.warn "Retrying#{" #{name}" if name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}"
end
def keep_trying?
return true if current_run.zero?
return false if last_attempt?
return true if @failed
end
def last_attempt?
current_run >= total_runs
end
end
end

View File

@ -0,0 +1,11 @@
module Bundler
module RubyDsl
def ruby(ruby_version, options = {})
raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil?
raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
raise GemfileError, "ruby_version must match the :engine_version for MRI" if options[:engine] == "ruby" && options[:engine_version] && ruby_version != options[:engine_version]
@ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
end
end
end

View File

@ -0,0 +1,117 @@
module Bundler
class RubyVersion
attr_reader :version, :patchlevel, :engine, :engine_version
def initialize(version, patchlevel, engine, engine_version)
# The parameters to this method must satisfy the
# following constraints, which are verified in
# the DSL:
#
# * If an engine is specified, an engine version
# must also be specified
# * If an engine version is specified, an engine
# must also be specified
# * If the engine is "ruby", the engine version
# must not be specified, or the engine version
# specified must match the version.
@version = version
@engine = engine || "ruby"
# keep track of the engine specified by the user
@input_engine = engine
@engine_version = engine_version || version
@patchlevel = patchlevel
end
def to_s
output = "ruby #{version}"
output << "p#{patchlevel}" if patchlevel
output << " (#{engine} #{engine_version})" unless engine == "ruby"
output
end
def ==(other)
version == other.version &&
engine == other.engine &&
engine_version == other.engine_version &&
patchlevel == other.patchlevel
end
# Returns a tuple of thsee things:
# [diff, this, other]
# The priority of attributes are
# 1. engine
# 2. ruby_version
# 3. engine_version
def diff(other)
if engine != other.engine && @input_engine
[ :engine, engine, other.engine ]
elsif version != other.version
[ :version, version, other.version ]
elsif engine_version != other.engine_version && @input_engine
[ :engine_version, engine_version, other.engine_version ]
elsif patchlevel != other.patchlevel && @patchlevel
[ :patchlevel, patchlevel, other.patchlevel ]
else
nil
end
end
def host
@host ||= [
RbConfig::CONFIG["host_cpu"],
RbConfig::CONFIG["host_vendor"],
RbConfig::CONFIG["host_os"]
].join("-")
end
end
# A subclass of RubyVersion that implements version,
# engine and engine_version based upon the current
# information in the system. It can be used anywhere
# a RubyVersion object is expected, and can be
# compared with a RubyVersion object.
class SystemRubyVersion < RubyVersion
def initialize(*)
# override the default initialize, because
# we will implement version, engine and
# engine_version dynamically
end
def version
RUBY_VERSION.dup
end
def gem_version
@gem_version ||= Gem::Version.new(version)
end
def engine
if defined?(RUBY_ENGINE)
RUBY_ENGINE.dup
else
# not defined in ruby 1.8.7
"ruby"
end
end
def engine_version
case engine
when "ruby"
RUBY_VERSION.dup
when "rbx"
Rubinius::VERSION.dup
when "jruby"
JRUBY_VERSION.dup
else
raise BundlerError, "RUBY_ENGINE value #{RUBY_ENGINE} is not recognized"
nil
end
end
def patchlevel
RUBY_PATCHLEVEL.to_s
end
end
end

View File

@ -0,0 +1,167 @@
require 'pathname'
if defined?(Gem::QuickLoader)
# Gem Prelude makes me a sad panda :'(
Gem::QuickLoader.load_full_rubygems_library
end
require 'rubygems'
require 'rubygems/specification'
require 'bundler/match_platform'
module Gem
@loaded_stacks = Hash.new { |h,k| h[k] = [] }
class Specification
attr_accessor :source_uri, :location, :relative_loaded_from
remove_method :source if instance_methods(false).include?(:source)
attr_accessor :source
alias_method :rg_full_gem_path, :full_gem_path
alias_method :rg_loaded_from, :loaded_from
def full_gem_path
source.respond_to?(:path) ?
Pathname.new(loaded_from).dirname.expand_path(Bundler.root).to_s :
rg_full_gem_path
end
def loaded_from
relative_loaded_from ?
source.path.join(relative_loaded_from).to_s :
rg_loaded_from
end
def load_paths
return full_require_paths if respond_to?(:full_require_paths)
require_paths.map do |require_path|
if require_path.include?(full_gem_path)
require_path
else
File.join(full_gem_path, require_path)
end
end
end
if method_defined?(:extension_dir)
alias_method :rg_extension_dir, :extension_dir
def extension_dir
@extension_dir ||= source.respond_to?(:extension_dir_name) ?
File.expand_path(File.join(extensions_dir, source.extension_dir_name)) :
rg_extension_dir
end
end
# RubyGems 1.8+ used only.
remove_method :gem_dir if instance_methods(false).include?(:gem_dir)
def gem_dir
full_gem_path
end
def groups
@groups ||= []
end
def git_version
return unless loaded_from && source.is_a?(Bundler::Source::Git)
" #{source.revision[0..6]}"
end
def to_gemfile(path = nil)
gemfile = "source :gemcutter\n"
gemfile << dependencies_to_gemfile(nondevelopment_dependencies)
unless development_dependencies.empty?
gemfile << "\n"
gemfile << dependencies_to_gemfile(development_dependencies, :development)
end
gemfile
end
def nondevelopment_dependencies
dependencies - development_dependencies
end
private
def dependencies_to_gemfile(dependencies, group = nil)
gemfile = ''
if dependencies.any?
gemfile << "group :#{group} do\n" if group
dependencies.each do |dependency|
gemfile << ' ' if group
gemfile << %|gem "#{dependency.name}"|
req = dependency.requirements_list.first
gemfile << %|, "#{req}"| if req
gemfile << "\n"
end
gemfile << "end\n" if group
end
gemfile
end
end
class Dependency
attr_accessor :source, :groups
alias eql? ==
def encode_with(coder)
to_yaml_properties.each do |ivar|
coder[ivar.to_s.sub(/^@/, '')] = instance_variable_get(ivar)
end
end
def to_yaml_properties
instance_variables.reject { |p| ["@source", "@groups"].include?(p.to_s) }
end
def to_lock
out = " #{name}"
unless requirement == Gem::Requirement.default
reqs = requirement.requirements.map{|o,v| "#{o} #{v}" }.sort.reverse
out << " (#{reqs.join(', ')})"
end
out
end
# Backport of performance enhancement added to Rubygems 1.4
def matches_spec?(spec)
# name can be a Regexp, so use ===
return false unless name === spec.name
return true if requirement.none?
requirement.satisfied_by?(spec.version)
end unless allocate.respond_to?(:matches_spec?)
end
class Requirement
# Backport of performance enhancement added to Rubygems 1.4
def none?
@none ||= (to_s == ">= 0")
end unless allocate.respond_to?(:none?)
end
class Platform
JAVA = Gem::Platform.new('java') unless defined?(JAVA)
MSWIN = Gem::Platform.new('mswin32') unless defined?(MSWIN)
MINGW = Gem::Platform.new('x86-mingw32') unless defined?(MINGW)
X64_MINGW = Gem::Platform.new('x64-mingw32') unless defined?(X64_MINGW)
undef_method :hash if method_defined? :hash
def hash
@cpu.hash ^ @os.hash ^ @version.hash
end
undef_method :eql? if method_defined? :eql?
alias eql? ==
end
end
module Gem
class Specification
include ::Bundler::MatchPlatform
end
end

View File

@ -0,0 +1,578 @@
require 'rubygems'
require 'rubygems/config_file'
module Bundler
class RubygemsIntegration
def self.version
@version ||= Gem::Version.new(Gem::VERSION)
end
def self.provides?(req_str)
Gem::Requirement.new(req_str).satisfied_by?(version)
end
def version
self.class.version
end
def provides?(req_str)
self.class.provides?(req_str)
end
def build_args
Gem::Command.build_args
end
def build_args=(args)
Gem::Command.build_args = args
end
def loaded_specs(name)
Gem.loaded_specs[name]
end
def mark_loaded(spec)
Gem.loaded_specs[spec.name] = spec
end
def path(obj)
obj.to_s
end
def platforms
Gem.platforms
end
def configuration
Gem.configuration
rescue Gem::SystemExitException => e
Bundler.ui.error "#{e.class}: #{e.message}"
Bundler.ui.trace e
raise Gem::SystemExitException
end
def ruby_engine
Gem.ruby_engine
end
def read_binary(path)
Gem.read_binary(path)
end
def inflate(obj)
Gem.inflate(obj)
end
def sources=(val)
# Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc
# If that file exists, its settings (including sources) will overwrite the values we
# are about to set here. In order to avoid that, we force memoizing the config file now.
configuration
Gem.sources = val
end
def sources
Gem.sources
end
def gem_dir
Gem.dir
end
def gem_bindir
Gem.bindir
end
def user_home
Gem.user_home
end
def gem_path
Gem.path
end
def gem_cache
gem_path.map{|p| File.expand_path("cache", p) }
end
def spec_cache_dirs
@spec_cache_dirs ||= begin
dirs = gem_path.map {|dir| File.join(dir, 'specifications')}
dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in Rubygems 2.0.3 or earlier
dirs.uniq.select {|dir| File.directory? dir}
end
end
def marshal_spec_dir
Gem::MARSHAL_SPEC_DIR
end
def config_map
Gem::ConfigMap
end
def repository_subdirectories
%w[cache doc gems specifications]
end
def clear_paths
Gem.clear_paths
end
def bin_path(gem, bin, ver)
Gem.bin_path(gem, bin, ver)
end
def preserve_paths
# this is a no-op outside of Rubygems 1.8
yield
end
def ui=(obj)
Gem::DefaultUserInteraction.ui = obj
end
def fetch_specs(all, pre, &blk)
specs = Gem::SpecFetcher.new.list(all, pre)
specs.each { yield } if block_given?
specs
end
def fetch_prerelease_specs
fetch_specs(false, true)
rescue Gem::RemoteFetcher::FetchError
[] # if we can't download them, there aren't any
end
def fetch_all_remote_specs
# Fetch all specs, minus prerelease specs
spec_list = fetch_specs(true, false)
# Then fetch the prerelease specs
fetch_prerelease_specs.each {|k, v| spec_list[k] += v }
return spec_list
end
def with_build_args(args)
old_args = self.build_args
begin
self.build_args = args
yield
ensure
self.build_args = old_args
end
end
def gem_from_path(path, policy = nil)
require 'rubygems/format'
Gem::Format.from_file_by_path(path, policy)
end
def spec_from_gem(path, policy = nil)
require 'rubygems/security'
gem_from_path(path, security_policies[policy]).spec
rescue Gem::Package::FormatError
raise GemspecError, "Could not read gem at #{path}. It may be corrupted."
rescue Exception, Gem::Exception, Gem::Security::Exception => e
if e.is_a?(Gem::Security::Exception) ||
e.message =~ /unknown trust policy|unsigned gem/i ||
e.message =~ /couldn't verify (meta)?data signature/i
raise SecurityError,
"The gem #{File.basename(path, '.gem')} can't be installed because " \
"the security policy didn't allow it, with the message: #{e.message}"
else
raise e
end
end
def build(spec, skip_validation = false)
require 'rubygems/builder'
Gem::Builder.new(spec).build
end
def build_gem(gem_dir, spec)
SharedHelpers.chdir(gem_dir) { build(spec) }
end
def download_gem(spec, uri, path)
uri = Bundler::Source.mirror_for(uri)
fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy])
fetcher.download(spec, uri, path)
end
def security_policy_keys
%w{High Medium Low AlmostNo No}.map { |level| "#{level}Security" }
end
def security_policies
@security_policies ||= begin
require 'rubygems/security'
Gem::Security::Policies
rescue LoadError, NameError
{}
end
end
def reverse_rubygems_kernel_mixin
# Disable rubygems' gem activation system
::Kernel.class_eval do
if private_method_defined?(:gem_original_require)
alias rubygems_require require
alias require gem_original_require
end
undef gem
end
end
def replace_gem(specs)
reverse_rubygems_kernel_mixin
executables = specs.map { |s| s.executables }.flatten
::Kernel.send(:define_method, :gem) do |dep, *reqs|
if executables.include? File.basename(caller.first.split(':').first)
return
end
reqs.pop if reqs.last.is_a?(Hash)
unless dep.respond_to?(:name) && dep.respond_to?(:requirement)
dep = Gem::Dependency.new(dep, reqs)
end
spec = specs.find { |s| s.name == dep.name }
if spec.nil?
e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile."
e.name = dep.name
if e.respond_to?(:requirement=)
e.requirement = dep.requirement
else
e.version_requirement = dep.requirement
end
raise e
elsif dep !~ spec
e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \
"Make sure all dependencies are added to Gemfile."
e.name = dep.name
if e.respond_to?(:requirement=)
e.requirement = dep.requirement
else
e.version_requirement = dep.requirement
end
raise e
end
true
end
end
def stub_source_index(specs)
Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize)
redefine_method(Gem::SourceIndex, :initialize) do |*args|
@gems = {}
# You're looking at this thinking: Oh! This is how I make those
# rubygems deprecations go away!
#
# You'd be correct BUT using of this method in production code
# must be approved by the rubygems team itself!
#
# This is your warning. If you use this and don't have approval
# we can't protect you.
#
Deprecate.skip_during do
self.spec_dirs = *args
add_specs(*specs)
end
end
end
# Used to make bin stubs that are not created by bundler work
# under bundler. The new Gem.bin_path only considers gems in
# +specs+
def replace_bin_path(specs)
gem_class = (class << Gem ; self ; end)
redefine_method(gem_class, :bin_path) do |name, *args|
exec_name = args.first
if exec_name == 'bundle'
return ENV['BUNDLE_BIN_PATH']
end
spec = nil
if exec_name
spec = specs.find { |s| s.executables.include?(exec_name) }
unless spec.name == name
warn "Bundler is using a binstub that was created for a different gem.\n" \
"This is deprecated, in future versions you may need to `bundle binstub #{name}` " \
"to work around a system/bundle conflict."
end
spec or raise Gem::Exception, "can't find executable #{exec_name}"
else
spec = specs.find { |s| s.name == name }
exec_name = spec.default_executable or raise Gem::Exception, "no default executable for #{spec.full_name}"
end
gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name)
gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name)
File.exist?(gem_bin) ? gem_bin : gem_from_path_bin
end
end
# Because Bundler has a static view of what specs are available,
# we don't #refresh, so stub it out.
def replace_refresh
gem_class = (class << Gem ; self ; end)
redefine_method(gem_class, :refresh) { }
end
# Replace or hook into Rubygems to provide a bundlerized view
# of the world.
def replace_entrypoints(specs)
replace_gem(specs)
stub_rubygems(specs)
replace_bin_path(specs)
replace_refresh
Gem.clear_paths
end
# This backports the correct segment generation code from Rubygems 1.4+
# by monkeypatching it into the method in Rubygems 1.3.6 and 1.3.7.
def backport_segment_generation
redefine_method(Gem::Version, :segments) do
@segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s|
/^\d+$/ =~ s ? s.to_i : s
end
end
end
# This backport fixes the marshaling of @segments.
def backport_yaml_initialize
redefine_method(Gem::Version, :yaml_initialize) do |tag, map|
@version = map['version']
@segments = nil
@hash = nil
end
end
# This backports base_dir which replaces installation path
# Rubygems 1.8+
def backport_base_dir
redefine_method(Gem::Specification, :base_dir) do
return Gem.dir unless loaded_from
File.dirname File.dirname loaded_from
end
end
def backport_cache_file
redefine_method(Gem::Specification, :cache_dir) do
@cache_dir ||= File.join base_dir, "cache"
end
redefine_method(Gem::Specification, :cache_file) do
@cache_file ||= File.join cache_dir, "#{full_name}.gem"
end
end
def backport_spec_file
redefine_method(Gem::Specification, :spec_dir) do
@spec_dir ||= File.join base_dir, "specifications"
end
redefine_method(Gem::Specification, :spec_file) do
@spec_file ||= File.join spec_dir, "#{full_name}.gemspec"
end
end
def redefine_method(klass, method, &block)
if klass.instance_methods(false).include?(method)
klass.send(:remove_method, method)
end
klass.send(:define_method, method, &block)
end
# Rubygems 1.4 through 1.6
class Legacy < RubygemsIntegration
def initialize
super
backport_base_dir
backport_cache_file
backport_spec_file
backport_yaml_initialize
end
def stub_rubygems(specs)
# Rubygems versions lower than 1.7 use SourceIndex#from_gems_in
source_index_class = (class << Gem::SourceIndex ; self ; end)
source_index_class.send(:define_method, :from_gems_in) do |*args|
source_index = Gem::SourceIndex.new
source_index.spec_dirs = *args
source_index.add_specs(*specs)
source_index
end
end
def all_specs
Gem.source_index.gems.values
end
def find_name(name)
Gem.source_index.find_name(name)
end
end
# Rubygems versions 1.3.6 and 1.3.7
class Ancient < Legacy
def initialize
super
backport_segment_generation
end
end
# Rubygems 1.7
class Transitional < Legacy
def stub_rubygems(specs)
stub_source_index(specs)
end
end
# Rubygems 1.8.5-1.8.19
class Modern < RubygemsIntegration
def stub_rubygems(specs)
Gem::Specification.all = specs
Gem.post_reset {
Gem::Specification.all = specs
}
stub_source_index(specs)
end
def all_specs
Gem::Specification.to_a
end
def find_name(name)
Gem::Specification.find_all_by_name name
end
end
# Rubygems 1.8.0 to 1.8.4
class AlmostModern < Modern
# Rubygems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever
# you call Gem::Installer#install with an :install_dir set. We have to
# change it back for our sudo mode to work.
def preserve_paths
old_dir, old_path = gem_dir, gem_path
yield
Gem.use_paths(old_dir, old_path)
end
end
# Rubygems 1.8.20+
class MoreModern < Modern
# Rubygems 1.8.20 and adds the skip_validation parameter, so that's
# when we start passing it through.
def build(spec, skip_validation = false)
require 'rubygems/builder'
Gem::Builder.new(spec).build(skip_validation)
end
end
# Rubygems 2.0
class Future < RubygemsIntegration
def stub_rubygems(specs)
Gem::Specification.all = specs
Gem.post_reset do
Gem::Specification.all = specs
end
end
def all_specs
Gem::Specification.to_a
end
def find_name(name)
Gem::Specification.find_all_by_name name
end
def fetch_specs(source, name)
path = source + "#{name}.#{Gem.marshal_version}.gz"
string = Gem::RemoteFetcher.fetcher.fetch_path(path)
Bundler.load_marshal(string)
rescue Gem::RemoteFetcher::FetchError => e
# it's okay for prerelease to fail
raise e unless name == "prerelease_specs"
end
def fetch_all_remote_specs
# Since SpecFetcher now returns NameTuples, we just fetch directly
# and unmarshal the array ourselves.
hash = {}
Gem.sources.each do |source|
source = URI.parse(source.to_s) unless source.is_a?(URI)
hash[source] = fetch_specs(source, "specs")
pres = fetch_specs(source, "prerelease_specs")
hash[source].push(*pres) if pres && !pres.empty?
end
hash
end
def download_gem(spec, uri, path)
require 'resolv'
uri = Bundler::Source.mirror_for(uri)
proxy, dns = configuration[:http_proxy], Resolv::DNS.new
fetcher = Gem::RemoteFetcher.new(proxy, dns)
fetcher.download(spec, uri, path)
end
def gem_from_path(path, policy = nil)
require 'rubygems/package'
p = Gem::Package.new(path)
p.security_policy = policy if policy
return p
end
def build(spec, skip_validation = false)
require 'rubygems/package'
Gem::Package.build(spec, skip_validation)
end
def repository_subdirectories
Gem::REPOSITORY_SUBDIRECTORIES
end
end
end
if RubygemsIntegration.provides?(">= 1.99.99")
@rubygems = RubygemsIntegration::Future.new
elsif RubygemsIntegration.provides?('>= 1.8.20')
@rubygems = RubygemsIntegration::MoreModern.new
elsif RubygemsIntegration.provides?('>= 1.8.5')
@rubygems = RubygemsIntegration::Modern.new
elsif RubygemsIntegration.provides?('>= 1.8.0')
@rubygems = RubygemsIntegration::AlmostModern.new
elsif RubygemsIntegration.provides?('>= 1.7.0')
@rubygems = RubygemsIntegration::Transitional.new
elsif RubygemsIntegration.provides?('>= 1.4.0')
@rubygems = RubygemsIntegration::Legacy.new
else # Rubygems 1.3.6 and 1.3.7
@rubygems = RubygemsIntegration::Ancient.new
end
class << self
attr_reader :rubygems
end
end

View File

@ -0,0 +1,310 @@
require "digest/sha1"
module Bundler
class Runtime < Environment
include SharedHelpers
def setup(*groups)
groups.map! { |g| g.to_sym }
# Has to happen first
clean_load_path
specs = groups.any? ? @definition.specs_for(groups) : requested_specs
setup_environment
Bundler.rubygems.replace_entrypoints(specs)
# Activate the specs
specs.each do |spec|
unless spec.loaded_from
raise GemNotFound, "#{spec.full_name} is missing. Run `bundle` to get it."
end
if activated_spec = Bundler.rubygems.loaded_specs(spec.name) and activated_spec.version != spec.version
e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \
"but your Gemfile requires #{spec.name} #{spec.version}. Prepending " \
"`bundle exec` to your command may solve this."
e.name = spec.name
if e.respond_to?(:requirement=)
e.requirement = Gem::Requirement.new(spec.version.to_s)
else
e.version_requirement = Gem::Requirement.new(spec.version.to_s)
end
raise e
end
Bundler.rubygems.mark_loaded(spec)
load_paths = spec.load_paths.reject {|path| $LOAD_PATH.include?(path)}
$LOAD_PATH.unshift(*load_paths)
end
setup_manpath
lock
self
end
REGEXPS = [
/^no such file to load -- (.+)$/i,
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
/^Missing API definition file in (.+)$/i,
/^cannot load such file -- (.+)$/i,
/^dlopen\([^)]*\): Library not loaded: (.+)$/i,
]
def require(*groups)
groups.map! { |g| g.to_sym }
groups = [:default] if groups.empty?
@definition.dependencies.each do |dep|
# Skip the dependency if it is not in any of the requested
# groups
next unless ((dep.groups & groups).any? && dep.current_platform?)
required_file = nil
begin
# Loop through all the specified autorequires for the
# dependency. If there are none, use the dependency's name
# as the autorequire.
Array(dep.autorequire || dep.name).each do |file|
# Allow `require: true` as an alias for `require: <name>`
file = dep.name if file == true
required_file = file
Kernel.require file
end
rescue LoadError => e
REGEXPS.find { |r| r =~ e.message }
raise if dep.autorequire || $1 != required_file
if dep.autorequire.nil? && dep.name.include?('-')
begin
namespaced_file = dep.name.gsub('-', '/')
Kernel.require namespaced_file
rescue LoadError
REGEXPS.find { |r| r =~ e.message }
regex_name = $1
raise e if dep.autorequire || (regex_name && regex_name.gsub('-', '/') != namespaced_file)
raise e if regex_name.nil?
end
end
end
end
end
def dependencies_for(*groups)
if groups.empty?
dependencies
else
dependencies.select { |d| (groups & d.groups).any? }
end
end
alias gems specs
def cache(custom_path = nil)
cache_path = cache_path(custom_path)
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
Bundler.ui.info "Updating files in vendor/cache"
specs.each do |spec|
next if spec.name == 'bundler'
spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache)
end
Dir[cache_path.join("*/.git")].each do |git_dir|
FileUtils.rm_rf(git_dir)
FileUtils.touch(File.expand_path("../.bundlecache", git_dir))
end
prune_cache(custom_path) unless Bundler.settings[:no_prune]
end
def prune_cache(custom_path)
cache_path = cache_path(custom_path)
FileUtils.mkdir_p(cache_path) unless File.exist?(cache_path)
resolve = @definition.resolve
prune_gem_cache(resolve, custom_path)
prune_git_and_path_cache(resolve, custom_path)
end
def clean(dry_run = false)
gem_bins = Dir["#{Gem.dir}/bin/*"]
git_dirs = Dir["#{Gem.dir}/bundler/gems/*"]
git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"]
gem_dirs = Dir["#{Gem.dir}/gems/*"]
gem_files = Dir["#{Gem.dir}/cache/*.gem"]
gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"]
spec_gem_paths = []
# need to keep git sources around
spec_git_paths = @definition.spec_git_paths
spec_git_cache_dirs = []
spec_gem_executables = []
spec_cache_paths = []
spec_gemspec_paths = []
specs.each do |spec|
spec_gem_paths << spec.full_gem_path
# need to check here in case gems are nested like for the rails git repo
md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path)
spec_git_paths << md[1] if md
spec_gem_executables << spec.executables.collect do |executable|
e = "#{Bundler.rubygems.gem_bindir}/#{executable}"
[e, "#{e}.bat"]
end
spec_cache_paths << spec.cache_file
spec_gemspec_paths << spec.spec_file
spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git)
end
spec_gem_paths.uniq!
spec_gem_executables.flatten!
stale_gem_bins = gem_bins - spec_gem_executables
stale_git_dirs = git_dirs - spec_git_paths
stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs
stale_gem_dirs = gem_dirs - spec_gem_paths
stale_gem_files = gem_files - spec_cache_paths
stale_gemspec_files = gemspec_files - spec_gemspec_paths
output = stale_gem_dirs.collect do |gem_dir|
full_name = Pathname.new(gem_dir).basename.to_s
parts = full_name.split('-')
name = parts[0..-2].join('-')
version = parts.last
output = "#{name} (#{version})"
if dry_run
Bundler.ui.info "Would have removed #{output}"
else
Bundler.ui.info "Removing #{output}"
FileUtils.rm_rf(gem_dir)
end
output
end + stale_git_dirs.collect do |gem_dir|
full_name = Pathname.new(gem_dir).basename.to_s
parts = full_name.split('-')
name = parts[0..-2].join('-')
revision = parts[-1]
output = "#{name} (#{revision})"
if dry_run
Bundler.ui.info "Would have removed #{output}"
else
Bundler.ui.info "Removing #{output}"
FileUtils.rm_rf(gem_dir)
end
output
end
unless dry_run
stale_gem_bins.each { |bin| FileUtils.rm(bin) if File.exist?(bin) }
stale_gem_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
stale_gemspec_files.each { |file| FileUtils.rm(file) if File.exist?(file) }
stale_git_cache_dirs.each { |dir| FileUtils.rm_rf(dir) if File.exist?(dir) }
end
output
end
def setup_environment
begin
ENV["BUNDLE_BIN_PATH"] = Bundler.rubygems.bin_path("bundler", "bundle", VERSION)
rescue Gem::GemNotFoundException
ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../bin/bundle", __FILE__)
end
# Set PATH
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
paths.unshift "#{Bundler.bundle_path}/bin"
ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
# Set BUNDLE_GEMFILE
ENV["BUNDLE_GEMFILE"] = default_gemfile.to_s
# Set RUBYOPT
rubyopt = [ENV["RUBYOPT"]].compact
if rubyopt.empty? || rubyopt.first !~ /-rbundler\/setup/
rubyopt.unshift %|-rbundler/setup|
ENV["RUBYOPT"] = rubyopt.join(' ')
end
# Set RUBYLIB
rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR)
rubylib.unshift File.expand_path('../..', __FILE__)
ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR)
end
private
def prune_gem_cache(resolve, custom_path)
cached = Dir["#{cache_path(custom_path)}/*.gem"]
cached = cached.delete_if do |path|
spec = Bundler.rubygems.spec_from_gem path
resolve.any? do |s|
s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git)
end
end
if cached.any?
Bundler.ui.info "Removing outdated .gem files from vendor/cache"
cached.each do |path|
Bundler.ui.info " * #{File.basename(path)}"
File.delete(path)
end
end
end
def prune_git_and_path_cache(resolve, custom_path)
cached = Dir["#{cache_path(custom_path)}/*/.bundlecache"]
cached = cached.delete_if do |path|
name = File.basename(File.dirname(path))
resolve.any? do |s|
source = s.source
source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name
end
end
if cached.any?
Bundler.ui.info "Removing outdated git and path gems from vendor/cache"
cached.each do |path|
path = File.dirname(path)
Bundler.ui.info " * #{File.basename(path)}"
FileUtils.rm_rf(path)
end
end
end
def setup_manpath
# Store original MANPATH for restoration later in with_clean_env()
ENV['BUNDLE_ORIG_MANPATH'] = ENV['MANPATH']
# Add man/ subdirectories from activated bundles to MANPATH for man(1)
manuals = $LOAD_PATH.map do |path|
man_subdir = path.sub(/lib$/, 'man')
man_subdir unless Dir[man_subdir + '/man?/'].empty?
end.compact
unless manuals.empty?
ENV['MANPATH'] = manuals.concat(
ENV['MANPATH'].to_s.split(File::PATH_SEPARATOR)
).uniq.join(File::PATH_SEPARATOR)
end
end
def cache_path(custom_path = nil)
path = custom_path || root
path.join("vendor/cache")
end
end
end

View File

@ -0,0 +1,176 @@
module Bundler
class Settings
def initialize(root = nil)
@root = root
@local_config = load_config(local_config_file)
@global_config = load_config(global_config_file)
end
def [](key)
the_key = key_for(key)
value = (@local_config[the_key] || ENV[the_key] || @global_config[the_key])
is_bool(key) ? to_bool(value) : value
end
def []=(key, value)
local_config_file or raise GemfileNotFound, "Could not locate Gemfile"
set_key(key, value, @local_config, local_config_file)
end
alias :set_local :[]=
def delete(key)
@local_config.delete(key_for(key))
end
def set_global(key, value)
set_key(key, value, @global_config, global_config_file)
end
def all
env_keys = ENV.keys.select { |k| k =~ /BUNDLE_.*/ }
keys = @global_config.keys | @local_config.keys | env_keys
keys.map do |key|
key.sub(/^BUNDLE_/, '').gsub(/__/, ".").downcase
end
end
def local_overrides
repos = {}
all.each do |k|
if k =~ /^local\./
repos[$'] = self[k]
end
end
repos
end
def gem_mirrors
all.inject({}) do |h, k|
if k =~ /^mirror\./
uri = normalize_uri($')
h[uri] = normalize_uri(self[k])
end
h
end
end
def locations(key)
key = key_for(key)
locations = {}
locations[:local] = @local_config[key] if @local_config.key?(key)
locations[:env] = ENV[key] if ENV[key]
locations[:global] = @global_config[key] if @global_config.key?(key)
locations
end
def pretty_values_for(exposed_key)
key = key_for(exposed_key)
locations = []
if @local_config.key?(key)
locations << "Set for your local app (#{local_config_file}): #{@local_config[key].inspect}"
end
if value = ENV[key]
locations << "Set via #{key}: #{value.inspect}"
end
if @global_config.key?(key)
locations << "Set for the current user (#{global_config_file}): #{@global_config[key].inspect}"
end
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
locations
end
def without=(array)
self[:without] = (array.empty? ? nil : array.join(":")) if array
end
def without
self[:without] ? self[:without].split(":").map { |w| w.to_sym } : []
end
# @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"]
def path
key = key_for(:path)
path = ENV[key] || @global_config[key]
return path if path && !@local_config.key?(key)
if path = self[:path]
"#{path}/#{Bundler.ruby_scope}"
else
Bundler.rubygems.gem_dir
end
end
def allow_sudo?
!@local_config.key?(key_for(:path))
end
def ignore_config?
ENV['BUNDLE_IGNORE_CONFIG']
end
private
def key_for(key)
key = key.to_s.sub(".", "__").upcase
"BUNDLE_#{key}"
end
def is_bool(key)
%w(frozen cache_all no_prune disable_local_branch_check).include? key.to_s
end
def to_bool(value)
!(value.nil? || value == '' || value =~ /^(false|f|no|n|0)$/i)
end
def set_key(key, value, hash, file)
key = key_for(key)
unless hash[key] == value
hash[key] = value
hash.delete(key) if value.nil?
FileUtils.mkdir_p(file.dirname)
require 'bundler/psyched_yaml'
File.open(file, "w") { |f| f.puts YAML.dump(hash) }
end
value
end
def global_config_file
file = ENV["BUNDLE_CONFIG"] || File.join(Bundler.rubygems.user_home, ".bundle/config")
Pathname.new(file)
end
def local_config_file
Pathname.new(@root).join("config") if @root
end
def load_config(config_file)
valid_file = config_file && config_file.exist? && !config_file.size.zero?
if !ignore_config? && valid_file
config_regex =/^(BUNDLE_.+): (?:['"](.*)['"]|(.+(?:\n(?!BUNDLE).+))|(.+))$/
config_pairs = config_file.read.scan(config_regex).map{|m| m.compact.map { |n| n.gsub(/\n\s?/, "") } }
Hash[config_pairs]
else
{}
end
end
# TODO: duplicates Rubygems#normalize_uri
# TODO: is this the correct place to validate mirror URIs?
def normalize_uri(uri)
uri = uri.to_s
uri = "#{uri}/" unless uri =~ %r[/\Z]
uri = URI(uri)
raise ArgumentError, "Gem mirror sources must be absolute URIs (configured: #{mirror_source})" unless uri.absolute?
uri
end
end
end

View File

@ -0,0 +1,23 @@
require 'bundler/shared_helpers'
if Bundler::SharedHelpers.in_bundle?
require 'bundler'
if STDOUT.tty?
begin
Bundler.setup
rescue Bundler::BundlerError => e
puts "\e[31m#{e.message}\e[0m"
puts e.backtrace.join("\n") if ENV["DEBUG"]
if e.is_a?(Bundler::GemNotFound)
puts "\e[33mRun `bundle install` to install missing gems.\e[0m"
end
exit e.status_code
end
else
Bundler.setup
end
# Add bundler to the load path after disabling system gems
bundler_lib = File.expand_path("../..", __FILE__)
$LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib)
end

View File

@ -0,0 +1,110 @@
require 'pathname'
require 'rubygems'
require 'bundler/constants'
require 'bundler/rubygems_integration'
require 'bundler/current_ruby'
module Gem
class Dependency
if !instance_methods.map { |m| m.to_s }.include?("requirement")
def requirement
version_requirements
end
end
end
end
module Bundler
module SharedHelpers
attr_accessor :gem_loaded
def default_gemfile
gemfile = find_gemfile
raise GemfileNotFound, "Could not locate Gemfile" unless gemfile
Pathname.new(gemfile)
end
def default_lockfile
Pathname.new("#{default_gemfile}.lock")
end
def in_bundle?
find_gemfile
end
if Bundler.current_ruby.mswin? || Bundler.current_ruby.jruby?
require 'monitor'
@chdir_monitor = Monitor.new
def chdir(dir, &blk)
@chdir_monitor.synchronize do
Dir.chdir dir, &blk
end
end
def pwd
@chdir_monitor.synchronize do
Dir.pwd
end
end
else
def chdir(dir, &blk)
Dir.chdir dir, &blk
end
def pwd
Dir.pwd
end
end
def with_clean_git_env(&block)
keys = %w[GIT_DIR GIT_WORK_TREE]
old_env = keys.inject({}) do |h, k|
h.update(k => ENV[k])
end
keys.each {|key| ENV.delete(key) }
block.call
ensure
keys.each {|key| ENV[key] = old_env[key] }
end
private
def find_gemfile
given = ENV['BUNDLE_GEMFILE']
return given if given && !given.empty?
previous = nil
current = File.expand_path(SharedHelpers.pwd)
until !File.directory?(current) || current == previous
if ENV['BUNDLE_SPEC_RUN']
# avoid stepping above the tmp directory when testing
return nil if File.file?(File.join(current, 'bundler.gemspec'))
end
# otherwise return the Gemfile if it's there
filename = File.join(current, 'Gemfile')
return filename if File.file?(filename)
current, previous = File.expand_path("..", current), current
end
end
def clean_load_path
# handle 1.9 where system gems are always on the load path
if defined?(::Gem)
me = File.expand_path("../../", __FILE__)
$LOAD_PATH.reject! do |p|
next if File.expand_path(p) =~ /^#{Regexp.escape(me)}/
p != File.dirname(__FILE__) &&
Bundler.rubygems.gem_path.any?{|gp| p =~ /^#{Regexp.escape(gp)}/ }
end
$LOAD_PATH.uniq!
end
end
extend self
end
end

View File

@ -0,0 +1,63 @@
module Bundler
class SimilarityDetector
SimilarityScore = Struct.new(:string, :distance)
# initialize with an array of words to be matched against
def initialize(corpus)
@corpus = corpus
end
# return an array of words similar to 'word' from the corpus
def similar_words(word, limit=3)
words_by_similarity = @corpus.map{|w| SimilarityScore.new(w, levenshtein_distance(word, w))}
words_by_similarity.select{|s| s.distance<=limit}.sort_by(&:distance).map(&:string)
end
# return the result of 'similar_words', concatenated into a list
# (eg "a, b, or c")
def similar_word_list(word, limit=3)
words = similar_words(word,limit)
if words.length==1
words[0]
elsif words.length>1
[words[0..-2].join(', '), words[-1]].join(' or ')
end
end
protected
# http://www.informit.com/articles/article.aspx?p=683059&seqNum=36
def levenshtein_distance(this, that, ins=2, del=2, sub=1)
# ins, del, sub are weighted costs
return nil if this.nil?
return nil if that.nil?
dm = [] # distance matrix
# Initialize first row values
dm[0] = (0..this.length).collect { |i| i * ins }
fill = [0] * (this.length - 1)
# Initialize first column values
for i in 1..that.length
dm[i] = [i * del, fill.flatten]
end
# populate matrix
for i in 1..that.length
for j in 1..this.length
# critical comparison
dm[i][j] = [
dm[i-1][j-1] +
(this[j-1] == that[i-1] ? 0 : sub),
dm[i][j-1] + ins,
dm[i-1][j] + del
].min
end
end
# The last value in matrix is the Levenshtein distance between the strings
dm[that.length][this.length]
end
end
end

View File

@ -0,0 +1,37 @@
module Bundler
class Source
autoload :Rubygems, 'bundler/source/rubygems'
autoload :Path, 'bundler/source/path'
autoload :Git, 'bundler/source/git'
def self.mirror_for(uri)
uri = URI(uri.to_s) unless uri.is_a?(URI)
# Settings keys are all downcased
mirrors = Bundler.settings.gem_mirrors
normalized_key = URI(uri.to_s.downcase)
mirrors[normalized_key] || uri
end
attr_accessor :dependency_names
def unmet_deps
specs.unmet_dependency_names
end
def version_message(spec)
locked_spec = Bundler.locked_gems.specs.find { |s| s.name == spec.name } if Bundler.locked_gems
locked_spec_version = locked_spec.version if locked_spec
message = "#{spec.name} #{spec.version}"
if locked_spec_version && spec.version != locked_spec_version
message << " (was #{locked_spec_version})"
end
message
end
def can_lock?(spec)
spec.source == self
end
end
end

View File

@ -0,0 +1,290 @@
require 'fileutils'
require 'uri'
require 'digest/sha1'
module Bundler
class Source
class Git < Path
autoload :GitProxy, 'bundler/source/git/git_proxy'
attr_reader :uri, :ref, :branch, :options, :submodules
def initialize(options)
@options = options
@glob = options["glob"] || DEFAULT_GLOB
@allow_cached = false
@allow_remote = false
# Stringify options that could be set as symbols
%w(ref branch tag revision).each{|k| options[k] = options[k].to_s if options[k] }
@uri = options["uri"]
@branch = options["branch"]
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
@submodules = options["submodules"]
@name = options["name"]
@version = options["version"]
@copied = false
@local = false
end
def self.from_lock(options)
new(options.merge("uri" => options.delete("remote")))
end
def to_lock
out = "GIT\n"
out << " remote: #{@uri}\n"
out << " revision: #{revision}\n"
%w(ref branch tag submodules).each do |opt|
out << " #{opt}: #{options[opt]}\n" if options[opt]
end
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
out << " specs:\n"
end
def eql?(o)
o.is_a?(Git) &&
uri == o.uri &&
ref == o.ref &&
branch == o.branch &&
name == o.name &&
version == o.version &&
submodules == o.submodules
end
alias == eql?
def to_s
at = if local?
path
elsif options["ref"]
shortref_for_display(options["ref"])
else
ref
end
"#{uri} (at #{at})"
end
def name
File.basename(@uri, '.git')
end
# This is the path which is going to contain a specific
# checkout of the git repository. When using local git
# repos, this is set to the local repo.
def install_path
@install_path ||= begin
git_scope = "#{base_name}-#{shortref_for_path(revision)}"
path = Bundler.install_path.join(git_scope)
if !path.exist? && Bundler.requires_sudo?
Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope)
else
path
end
end
end
alias :path :install_path
def extension_dir_name
"#{base_name}-#{shortref_for_path(revision)}"
end
def unlock!
git_proxy.revision = nil
@unlocked = true
end
def local_override!(path)
return false if local?
path = Pathname.new(path)
path = path.expand_path(Bundler.root) unless path.relative?
unless options["branch"] || Bundler.settings[:disable_local_branch_check]
raise GitError, "Cannot use local override for #{name} at #{path} because " \
":branch is not specified in Gemfile. Specify a branch or use " \
"`bundle config --delete` to remove the local override"
end
unless path.exist?
raise GitError, "Cannot use local override for #{name} because #{path} " \
"does not exist. Check `bundle config --delete` to remove the local override"
end
set_local!(path)
# Create a new git proxy without the cached revision
# so the Gemfile.lock always picks up the new revision.
@git_proxy = GitProxy.new(path, uri, ref)
if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check]
raise GitError, "Local override for #{name} at #{path} is using branch " \
"#{git_proxy.branch} but Gemfile specifies #{options["branch"]}"
end
changed = cached_revision && cached_revision != git_proxy.revision
if changed && !@unlocked && !git_proxy.contains?(cached_revision)
raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \
"but the current branch in your local override for #{name} does not contain such commit. " \
"Please make sure your branch is up to date."
end
changed
end
# TODO: actually cache git specs
def specs(*)
if has_app_cache? && !local?
set_local!(app_cache_path)
end
if requires_checkout? && !@copied
git_proxy.checkout
git_proxy.copy_to(install_path, submodules)
serialize_gemspecs_in(install_path)
@copied = true
end
local_specs
end
def install(spec)
debug = nil
if requires_checkout? && !@copied
debug = " * Checking out revision: #{ref}"
git_proxy.copy_to(install_path, submodules)
serialize_gemspecs_in(install_path)
@copied = true
end
generate_bin(spec)
if requires_checkout? && spec.post_install_message
Installer.post_install_messages[spec.name] = spec.post_install_message
end
["Using #{version_message(spec)} from #{to_s}", nil, debug]
end
def cache(spec, custom_path = nil)
app_cache_path = app_cache_path(custom_path)
return unless Bundler.settings[:cache_all]
return if path == app_cache_path
cached!
FileUtils.rm_rf(app_cache_path)
git_proxy.checkout if requires_checkout?
git_proxy.copy_to(app_cache_path, @submodules)
serialize_gemspecs_in(app_cache_path)
end
def load_spec_files
super
rescue PathError => e
Bundler.ui.trace e
raise GitError, "#{to_s} is not yet checked out. Run `bundle install` first."
end
# This is the path which is going to contain a cache
# of the git repository. When using the same git repository
# across different projects, this cache will be shared.
# When using local git repos, this is set to the local repo.
def cache_path
@cache_path ||= begin
git_scope = "#{base_name}-#{uri_hash}"
if Bundler.requires_sudo?
Bundler.user_bundle_path.join("cache/git", git_scope)
else
Bundler.cache.join("git", git_scope)
end
end
end
def app_cache_dirname
"#{base_name}-#{shortref_for_path(cached_revision || revision)}"
end
def revision
git_proxy.revision
end
def allow_git_ops?
@allow_remote || @allow_cached
end
private
def serialize_gemspecs_in(destination)
expanded_path = destination.expand_path(Bundler.root)
Dir["#{expanded_path}/#{@glob}"].each do |spec_path|
# Evaluate gemspecs and cache the result. Gemspecs
# in git might require git or other dependencies.
# The gemspecs we cache should already be evaluated.
spec = Bundler.load_gemspec(spec_path)
next unless spec
File.open(spec_path, 'wb') {|file| file.write(spec.to_ruby) }
end
end
def set_local!(path)
@local = true
@local_specs = @git_proxy = nil
@cache_path = @install_path = path
end
def has_app_cache?
cached_revision && super
end
def local?
@local
end
def requires_checkout?
allow_git_ops? && !local?
end
def base_name
File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*},''),".git")
end
def shortref_for_display(ref)
ref[0..6]
end
def shortref_for_path(ref)
ref[0..11]
end
def uri_hash
if uri =~ %r{^\w+://(\w+@)?}
# Downcase the domain component of the URI
# and strip off a trailing slash, if one is present
input = URI.parse(uri).normalize.to_s.sub(%r{/$},'')
else
# If there is no URI scheme, assume it is an ssh/git URI
input = uri
end
Digest::SHA1.hexdigest(input)
end
def cached_revision
options["revision"]
end
def cached?
cache_path.exist?
end
def git_proxy
@git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self)
end
end
end
end

View File

@ -0,0 +1,158 @@
module Bundler
class Source
class Git < Path
class GitNotInstalledError < GitError
def initialize
msg = "You need to install git to be able to use gems from git repositories. "
msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git"
super msg
end
end
class GitNotAllowedError < GitError
def initialize(command)
msg = "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, "
msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues "
msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
super msg
end
end
class GitCommandError < GitError
def initialize(command, path = nil)
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist?
super msg
end
end
# The GitProxy is responsible to interact with git repositories.
# All actions required by the Git source is encapsulated in this
# object.
class GitProxy
attr_accessor :path, :uri, :ref
attr_writer :revision
def initialize(path, uri, ref, revision = nil, git = nil)
@path = path
@uri = uri
@ref = ref
@revision = revision
@git = git
raise GitNotInstalledError.new if allow? && !Bundler.git_present?
end
def revision
@revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
end
def branch
@branch ||= allowed_in_path do
git("branch") =~ /^\* (.*)$/ && $1.strip
end
end
def contains?(commit)
allowed_in_path do
result = git_null("branch --contains #{commit}")
$? == 0 && result =~ /^\* (.*)$/
end
end
def checkout
if path.exist?
return if has_revision_cached?
Bundler.ui.confirm "Updating #{uri}"
in_path do
git_retry %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
end
else
Bundler.ui.info "Fetching #{uri}"
FileUtils.mkdir_p(path.dirname)
git_retry %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks --quiet|
end
end
def copy_to(destination, submodules=false)
unless File.exist?(destination.join(".git"))
FileUtils.mkdir_p(destination.dirname)
FileUtils.rm_rf(destination)
git_retry %|clone --no-checkout --quiet "#{path}" "#{destination}"|
File.chmod((0777 & ~File.umask), destination)
end
SharedHelpers.chdir(destination) do
git_retry %|fetch --force --quiet --tags "#{path}"|
git "reset --hard #{@revision}"
if submodules
git_retry "submodule update --init --recursive"
end
end
end
private
# TODO: Do not rely on /dev/null.
# Given that open3 is not cross platform until Ruby 1.9.3,
# the best solution is to pipe to /dev/null if it exists.
# If it doesn't, everything will work fine, but the user
# will get the $stderr messages as well.
def git_null(command)
git("#{command} 2>#{Bundler::NULL}", false)
end
def git_retry(command)
Bundler::Retry.new("git #{command}", GitNotAllowedError).attempts do
git(command)
end
end
def git(command, check_errors=true)
raise GitNotAllowedError.new(command) unless allow?
out = SharedHelpers.with_clean_git_env { %x{git #{command}} }
raise GitCommandError.new(command, path) if check_errors && !$?.success?
out
end
def has_revision_cached?
return unless @revision
in_path { git("cat-file -e #{@revision}") }
true
rescue GitError
false
end
# Escape the URI for git commands
def uri_escaped
if Bundler::WINDOWS
# Windows quoting requires double quotes only, with double quotes
# inside the string escaped by being doubled.
'"' + uri.gsub('"') {|s| '""'} + '"'
else
# Bash requires single quoted strings, with the single quotes escaped
# by ending the string, escaping the quote, and restarting the string.
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
end
end
def allow?
@git ? @git.allow_git_ops? : true
end
def in_path(&blk)
checkout unless path.exist?
SharedHelpers.chdir(path, &blk)
end
def allowed_in_path
return in_path { yield } if allow?
raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application"
end
end
end
end
end

View File

@ -0,0 +1,225 @@
module Bundler
class Source
class Path < Source
autoload :Installer, 'bundler/source/path/installer'
attr_reader :path, :options
attr_writer :name
attr_accessor :version
DEFAULT_GLOB = "{,*,*/*}.gemspec"
def initialize(options)
@options = options
@glob = options["glob"] || DEFAULT_GLOB
@allow_cached = false
@allow_remote = false
if options["path"]
@path = Pathname.new(options["path"])
@path = expand(@path) unless @path.relative?
end
@name = options["name"]
@version = options["version"]
# Stores the original path. If at any point we move to the
# cached directory, we still have the original path to copy from.
@original_path = @path
end
def remote!
@allow_remote = true
end
def cached!
@allow_cached = true
end
def self.from_lock(options)
new(options.merge("path" => options.delete("remote")))
end
def to_lock
out = "PATH\n"
out << " remote: #{relative_path}\n"
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
out << " specs:\n"
end
def to_s
"source at #{@path}"
end
def hash
self.class.hash
end
def eql?(o)
o.instance_of?(Path) &&
expand(path) == expand(o.path) &&
version == o.version
end
alias == eql?
def name
File.basename(expand(path).to_s)
end
def install(spec)
generate_bin(spec, :disable_extensions)
["Using #{version_message(spec)} from #{to_s}", nil]
end
def cache(spec, custom_path = nil)
app_cache_path = app_cache_path(custom_path)
return unless Bundler.settings[:cache_all]
return if expand(@original_path).to_s.index(Bundler.root.to_s) == 0
unless @original_path.exist?
raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{to_s} is missing!"
end
FileUtils.rm_rf(app_cache_path)
FileUtils.cp_r("#{@original_path}/.", app_cache_path)
FileUtils.touch(app_cache_path.join(".bundlecache"))
end
def local_specs(*)
@local_specs ||= load_spec_files
end
def specs
if has_app_cache?
@path = app_cache_path
end
local_specs
end
def app_cache_dirname
name
end
private
def expand(somepath)
somepath.expand_path(Bundler.root)
rescue ArgumentError => e
Bundler.ui.debug(e)
raise PathError, "There was an error while trying to use the path " \
"`#{somepath}`.\nThe error message was: #{e.message}."
end
def app_cache_path(custom_path = nil)
@app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname)
end
def has_app_cache?
SharedHelpers.in_bundle? && app_cache_path.exist?
end
def load_spec_files
index = Index.new
expanded_path = expand(path)
if File.directory?(expanded_path)
Dir["#{expanded_path}/#{@glob}"].each do |file|
spec = Bundler.load_gemspec(file)
if spec
spec.loaded_from = file.to_s
spec.source = self
index << spec
end
end
if index.empty? && @name && @version
index << Gem::Specification.new do |s|
s.name = @name
s.source = self
s.version = Gem::Version.new(@version)
s.platform = Gem::Platform::RUBY
s.summary = "Fake gemspec for #{@name}"
s.relative_loaded_from = "#{@name}.gemspec"
s.authors = ["no one"]
if expanded_path.join("bin").exist?
executables = expanded_path.join("bin").children
executables.reject!{|p| File.directory?(p) }
s.executables = executables.map{|c| c.basename.to_s }
end
end
end
elsif File.exist?(expanded_path)
raise PathError, "The path `#{expanded_path}` is not a directory."
else
raise PathError, "The path `#{expanded_path}` does not exist."
end
index
end
def relative_path
if path.to_s.match(%r{^#{Regexp.escape Bundler.root.to_s}})
return path.relative_path_from(Bundler.root)
end
path
end
def generate_bin(spec, disable_extensions = false)
gem_dir = Pathname.new(spec.full_gem_path)
# Some gem authors put absolute paths in their gemspec
# and we have to save them from themselves
spec.files = spec.files.map do |p|
next if File.directory?(p)
begin
Pathname.new(p).relative_path_from(gem_dir).to_s
rescue ArgumentError
p
end
end.compact
gem_file = Bundler.rubygems.build_gem gem_dir, spec
installer = Path::Installer.new(spec, :env_shebang => false)
run_hooks(:pre_install, installer)
installer.build_extensions unless disable_extensions
run_hooks(:post_build, installer)
installer.generate_bin
run_hooks(:post_install, installer)
rescue Gem::InvalidSpecificationException => e
Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
"This prevents bundler from installing bins or native extensions, but " \
"that may not affect its functionality."
if !spec.extensions.empty? && !spec.email.empty?
Bundler.ui.warn "If you need to use this package without installing it from a gem " \
"repository, please contact #{spec.email} and ask them " \
"to modify their .gemspec so it can work with `gem build`."
end
Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}"
ensure
if gem_dir && gem_file
FileUtils.rm_rf(gem_dir.join gem_file)
end
end
def run_hooks(type, installer)
hooks_meth = "#{type}_hooks"
return unless Gem.respond_to?(hooks_meth)
Gem.send(hooks_meth).each do |hook|
result = hook.call(installer)
if result == false
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
raise InstallHookError, message
end
end
end
end
end
end

View File

@ -0,0 +1,38 @@
module Bundler
class Source
class Path
class Installer < Bundler::GemInstaller
def initialize(spec, options = {})
@spec = spec
@tmp_bin_dir = "#{Bundler.tmp(spec.full_name)}/bin"
@gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin"
@bin_dir = Bundler.requires_sudo? ? @tmp_bin_dir : @gem_bin_dir
@gem_dir = Bundler.rubygems.path(spec.full_gem_path)
@wrappers = options[:wrappers] || true
@env_shebang = options[:env_shebang] || true
@format_executable = options[:format_executable] || false
@build_args = options[:build_args] || Bundler.rubygems.build_args
end
def generate_bin
return if spec.executables.nil? || spec.executables.empty?
if Bundler.requires_sudo?
FileUtils.mkdir_p(@tmp_bin_dir) unless File.exist?(@tmp_bin_dir)
end
super
if Bundler.requires_sudo?
Bundler.mkdir_p @gem_bin_dir
spec.executables.each do |exe|
Bundler.sudo "cp -R #{@tmp_bin_dir}/#{exe} #{@gem_bin_dir}"
end
end
end
end
end
end
end

View File

@ -0,0 +1,350 @@
require 'uri'
require 'rubygems/user_interaction'
require 'rubygems/spec_fetcher'
module Bundler
class Source
class Rubygems < Source
API_REQUEST_LIMIT = 100 # threshold for switching back to the modern index instead of fetching every spec
attr_reader :remotes, :caches
def initialize(options = {})
@options = options
@remotes = []
@dependency_names = []
@allow_remote = false
@allow_cached = false
@caches = [Bundler.app_cache, *Bundler.rubygems.gem_cache]
Array(options["remotes"] || []).reverse_each{|r| add_remote(r) }
end
def remote!
@allow_remote = true
end
def cached!
@allow_cached = true
end
def hash
@remotes.hash
end
def eql?(o)
o.is_a?(Rubygems) && remotes_equal?(o.remotes)
end
alias == eql?
def can_lock?(spec)
spec.source.is_a?(Rubygems)
end
def options
{ "remotes" => @remotes.map { |r| r.to_s } }
end
def self.from_lock(options)
new(options)
end
def to_lock
out = "GEM\n"
out << remotes.map { |remote|
" remote: #{suppress_configured_credentials remote}\n"
}.join
out << " specs:\n"
end
def to_s
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
"rubygems repository #{remote_names}"
end
alias_method :name, :to_s
def specs
@specs ||= begin
# remote_specs usually generates a way larger Index than the other
# sources, and large_idx.use small_idx is way faster than
# small_idx.use large_idx.
idx = @allow_remote ? remote_specs.dup : Index.new
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
idx.use(installed_specs, :override_dupes)
idx
end
end
def install(spec)
return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any?
# Download the gem to get the spec, because some specs that are returned
# by rubygems.org are broken and wrong.
if spec.source_uri
# Check for this spec from other sources
uris = [spec.source_uri]
uris += source_uris_for_spec(spec)
uris.compact!
uris.uniq!
Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"])
spec.__swap__(s)
end
path = cached_gem(spec)
if Bundler.requires_sudo?
install_path = Bundler.tmp(spec.full_name)
bin_path = install_path.join("bin")
else
install_path = Bundler.rubygems.gem_dir
bin_path = Bundler.system_bindir
end
installed_spec = nil
Bundler.rubygems.preserve_paths do
installed_spec = Bundler::GemInstaller.new(path,
:install_dir => install_path.to_s,
:bin_dir => bin_path.to_s,
:ignore_dependencies => true,
:wrappers => true,
:env_shebang => true
).install
end
# SUDO HAX
if Bundler.requires_sudo?
Bundler.rubygems.repository_subdirectories.each do |name|
src = File.join(install_path, name, "*")
dst = File.join(Bundler.rubygems.gem_dir, name)
if name == "extensions" && Dir.glob(src).any?
src = File.join(src, "*/*")
ext_src = Dir.glob(src).first
ext_src.gsub!(src[0..-6], '')
dst = File.dirname(File.join(dst, ext_src))
end
Bundler.mkdir_p dst
Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any?
end
spec.executables.each do |exe|
Bundler.mkdir_p Bundler.system_bindir
Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/"
end
end
spec.loaded_from = "#{Bundler.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
installed_spec.loaded_from = spec.loaded_from
["Installing #{version_message(spec)}", spec.post_install_message]
ensure
if install_path && Bundler.requires_sudo?
FileUtils.remove_entry_secure(install_path)
end
end
def cache(spec, custom_path = nil)
if builtin_gem?(spec)
cached_path = cached_built_in_gem(spec)
else
cached_path = cached_gem(spec)
end
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
return if File.dirname(cached_path) == Bundler.app_cache.to_s
Bundler.ui.info " * #{File.basename(cached_path)}"
FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
end
def cached_built_in_gem(spec)
cached_path = cached_path(spec)
if cached_path.nil?
remote_spec = remote_specs.search(spec).first
cached_path = fetch_gem(remote_spec)
end
cached_path
end
def add_remote(source)
uri = normalize_uri(source)
@remotes.unshift(uri) unless @remotes.include?(uri)
end
def replace_remotes(other_remotes)
return false if other_remotes == @remotes
@remotes = []
other_remotes.reverse_each do |r|
add_remote r.to_s
end
end
def unmet_deps
if @allow_remote && api_fetchers.any?
remote_specs.unmet_dependency_names
else
[]
end
end
protected
def source_uris_for_spec(spec)
specs.search_all(spec.name).map{|s| s.source_uri }
end
private
def cached_gem(spec)
cached_gem = cached_path(spec)
unless cached_gem
raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
end
cached_gem
end
def cached_path(spec)
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
possibilities.find { |p| File.exist?(p) }
end
def normalize_uri(uri)
uri = uri.to_s
uri = "#{uri}/" unless uri =~ %r'/$'
uri = URI(uri)
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
uri
end
def suppress_configured_credentials(remote)
remote_nouser = remote.dup.tap { |uri| uri.user = uri.password = nil }.to_s
if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser]
remote_nouser
else
remote
end
end
def installed_specs
@installed_specs ||= begin
idx = Index.new
have_bundler = false
Bundler.rubygems.all_specs.reverse.each do |spec|
next if spec.name == 'bundler' && spec.version.to_s != VERSION
have_bundler = true if spec.name == 'bundler'
spec.source = self
idx << spec
end
# Always have bundler locally
unless have_bundler
# We're running bundler directly from the source
# so, let's create a fake gemspec for it (it's a path)
# gemspec
bundler = Gem::Specification.new do |s|
s.name = 'bundler'
s.version = VERSION
s.platform = Gem::Platform::RUBY
s.source = self
s.authors = ["bundler team"]
s.loaded_from = File.expand_path("..", __FILE__)
end
idx << bundler
end
idx
end
end
def cached_specs
@cached_specs ||= begin
idx = installed_specs.dup
path = Bundler.app_cache
Dir["#{path}/*.gem"].each do |gemfile|
next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
s ||= Bundler.rubygems.spec_from_gem(gemfile)
s.source = self
idx << s
end
end
idx
end
def fetchers
@fetchers ||= remotes.map do |url|
Bundler::Fetcher.new(url)
end
end
def api_fetchers
fetchers.select{|f| f.use_api }
end
def remote_specs
@remote_specs ||= Index.build do |idx|
index_fetchers = fetchers - api_fetchers
# gather lists from non-api sites
index_fetchers.each do |f|
Bundler.ui.info "Fetching source index from #{f.uri}"
idx.use f.specs(nil, self)
end
# because ensuring we have all the gems we need involves downloading
# the gemspecs of those gems, if the non-api sites contain more than
# about 100 gems, we just treat all sites as non-api for speed.
allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
if allow_api
api_fetchers.each do |f|
Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug?
idx.use f.specs(dependency_names, self)
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
end
if api_fetchers.any? && api_fetchers.all?{|f| f.use_api }
# it's possible that gems from one source depend on gems from some
# other source, so now we download gemspecs and iterate over those
# dependencies, looking for gems we don't have info on yet.
unmet = idx.unmet_dependency_names
# if there are any cross-site gems we missed, get them now
api_fetchers.each do |f|
Bundler.ui.info "Fetching additional metadata from #{f.uri}", Bundler.ui.debug?
idx.use f.specs(unmet, self)
Bundler.ui.info "" if !Bundler.ui.debug? # new line now that the dots are over
end if unmet.any?
else
allow_api = false
end
end
if !allow_api
api_fetchers.each do |f|
Bundler.ui.info "Fetching source index from #{f.uri}"
idx.use f.specs(nil, self)
end
end
end
end
def fetch_gem(spec)
return false unless spec.source_uri
Fetcher.download_gem_from_uri(spec, spec.source_uri)
end
def builtin_gem?(spec)
# Ruby 2.1, where all included gems have this summary
return true if spec.summary =~ /is bundled with Ruby/
# Ruby 2.0, where gemspecs are stored in specifications/default/
spec.loaded_from && spec.loaded_from.include?("specifications/default/")
end
def remotes_equal?(other_remotes)
remotes.map(&method(:suppress_configured_credentials)) == other_remotes.map(&method(:suppress_configured_credentials))
end
end
end
end

View File

@ -0,0 +1,80 @@
module Bundler
class SourceList
attr_reader :path_sources,
:git_sources,
:rubygems_sources
def initialize
@path_sources = []
@git_sources = []
@rubygems_aggregate = Source::Rubygems.new
@rubygems_sources = [@rubygems_aggregate]
end
def add_path_source(options = {})
add_source_to_list Source::Path.new(options), path_sources
end
def add_git_source(options = {})
add_source_to_list Source::Git.new(options), git_sources
end
def add_rubygems_source(options = {})
add_source_to_list Source::Rubygems.new(options), @rubygems_sources
end
def add_rubygems_remote(uri)
@rubygems_aggregate.add_remote(uri)
@rubygems_aggregate
end
def all_sources
path_sources + git_sources + rubygems_sources
end
def get(source)
source_list_for(source).find { |s| source == s }
end
def lock_sources
lock_sources = (path_sources + git_sources).sort_by(&:to_s)
lock_sources << combine_rubygems_sources
end
def replace_sources!(replacement_sources)
[path_sources, git_sources, rubygems_sources].each do |source_list|
source_list.map! do |source|
replacement_sources.find { |s| s == source } || source
end
end
end
def cached!
all_sources.each(&:cached!)
end
def remote!
all_sources.each(&:remote!)
end
private
def add_source_to_list(source, list)
list.unshift(source).uniq!
source
end
def source_list_for(source)
case source
when Source::Git then git_sources
when Source::Path then path_sources
when Source::Rubygems then rubygems_sources
else raise ArgumentError, "Invalid source: #{source.inspect}"
end
end
def combine_rubygems_sources
Source::Rubygems.new("remotes" => rubygems_sources.map(&:remotes).flatten.uniq.reverse)
end
end
end

View File

@ -0,0 +1,154 @@
require 'tsort'
require 'forwardable'
module Bundler
class SpecSet
extend Forwardable
include TSort, Enumerable
def_delegators :@specs, :<<, :length, :add, :remove
def_delegators :sorted, :each
def initialize(specs)
@specs = specs.sort_by { |s| s.name }
end
def for(dependencies, skip = [], check = false, match_current_platform = false)
handled, deps, specs = {}, dependencies.dup, []
skip << 'bundler'
until deps.empty?
dep = deps.shift
next if handled[dep] || skip.include?(dep.name)
spec = lookup[dep.name].find do |s|
if match_current_platform
Gem::Platform.match(s.platform)
else
s.match_platform(dep.__platform)
end
end
handled[dep] = true
if spec
specs << spec
spec.dependencies.each do |d|
next if d.type == :development
d = DepProxy.new(d, dep.__platform) unless match_current_platform
deps << d
end
elsif check
return false
end
end
if spec = lookup['bundler'].first
specs << spec
end
check ? true : SpecSet.new(specs)
end
def valid_for?(deps)
self.for(deps, [], true)
end
def [](key)
key = key.name if key.respond_to?(:name)
lookup[key].reverse
end
def []=(key, value)
@specs << value
@lookup = nil
@sorted = nil
value
end
def sort!
self
end
def to_a
sorted.dup
end
def to_hash
lookup.dup
end
def materialize(deps, missing_specs = nil)
materialized = self.for(deps, [], false, true).to_a
deps = materialized.map {|s| s.name }.uniq
materialized.map! do |s|
next s unless s.is_a?(LazySpecification)
s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=)
spec = s.__materialize__
if missing_specs
missing_specs << s unless spec
else
raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec
end
spec if spec
end
SpecSet.new(materialized.compact)
end
def merge(set)
arr = sorted.dup
set.each do |s|
next if arr.any? { |s2| s2.name == s.name && s2.version == s.version && s2.platform == s.platform }
arr << s
end
SpecSet.new(arr)
end
private
def sorted
rake = @specs.find { |s| s.name == 'rake' }
begin
@sorted ||= ([rake] + tsort).compact.uniq
rescue TSort::Cyclic => error
cgems = extract_circular_gems(error)
raise CyclicDependencyError, "Your Gemfile requires gems that depend" \
" depend on each other, creating an infinite loop. Please remove" \
" either gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again."
end
end
def extract_circular_gems(error)
if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19?
error.message.scan(/(\w+) \([^)]/).flatten
else
error.message.scan(/@name="(.*?)"/).flatten
end
end
def lookup
@lookup ||= begin
lookup = Hash.new { |h,k| h[k] = [] }
specs = @specs.sort_by do |s|
s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s
end
specs.reverse_each do |s|
lookup[s.name] << s
end
lookup
end
end
def tsort_each_node
@specs.each { |s| yield s }
end
def tsort_each_child(s)
s.dependencies.sort_by { |d| d.name }.each do |d|
next if d.type == :development
lookup[d.name].each { |s2| yield s2 }
end
end
end
end

View File

@ -0,0 +1 @@
# Ignore all files in this directory

View File

@ -0,0 +1,14 @@
-----BEGIN CERTIFICATE-----
MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
-----END CERTIFICATE-----

View File

@ -0,0 +1,23 @@
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN CERTIFICATE-----
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
-----END CERTIFICATE-----

View File

@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
-----END CERTIFICATE-----

View File

@ -0,0 +1,41 @@
require 'fileutils'
module Bundler
module SSLCerts
class CertificateManager
attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs
def self.update_from!(rubygems_path)
new(rubygems_path).update!
end
def initialize(rubygems_path)
rubygems_certs = File.join(rubygems_path, 'lib/rubygems/ssl_certs')
@rubygems_certs = certificates_in(rubygems_certs)
@bundler_cert_path = File.expand_path("..", __FILE__)
@bundler_certs = certificates_in(bundler_cert_path)
end
def up_to_date?
bundler_certs.zip(rubygems_certs).all? do |bc, rc|
File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc)
end
end
def update!
return if up_to_date?
FileUtils.rm bundler_certs
FileUtils.cp rubygems_certs, bundler_cert_path
end
private
def certificates_in(path)
Dir[File.join(path, "*.pem")].sort
end
end
end
end

View File

@ -0,0 +1,16 @@
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
#
# This file was generated by Bundler.
#
# The application '<%= executable %>' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../<%= relative_gemfile_path %>",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('<%= spec.name %>', '<%= executable %>')

View File

@ -0,0 +1,12 @@
#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG['ruby_install_name'] %>
#
# This file was generated by Bundler.
#
# The application '<%= executable %>' is installed as part of a gem, and
# this file is here to facilitate running it.
#
$:.unshift File.expand_path '../<%= standalone_path %>', __FILE__
require 'bundler/setup'
load File.expand_path '../<%= executable_path %>', __FILE__

View File

@ -0,0 +1,4 @@
# A sample Gemfile
source "https://rubygems.org"
# gem "rails"

View File

@ -0,0 +1,3 @@
language: ruby
rvm:
- <%= RUBY_VERSION %>

View File

@ -0,0 +1,4 @@
source 'https://rubygems.org'
# Specify your gem's dependencies in <%=config[:name]%>.gemspec
gemspec

View File

@ -0,0 +1,22 @@
Copyright (c) <%=Time.now.year%> <%=config[:author]%>
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,31 @@
# <%=config[:constant_name]%>
TODO: Write a gem description
## Installation
Add this line to your application's Gemfile:
```ruby
gem '<%=config[:name]%>'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install <%=config[:name]%>
## Usage
TODO: Write usage instructions here
## Contributing
1. Fork it ( https://github.com/[my-github-username]/<%=config[:name]%>/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

Some files were not shown because too many files have changed in this diff Show More