bundler 0.8.1 → 0.9.0.pre1

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

Potentially problematic release.


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

Files changed (58) hide show
  1. data/README +7 -0
  2. data/bin/bundle +3 -0
  3. data/lib/bundler.rb +72 -37
  4. data/lib/bundler/cli.rb +64 -68
  5. data/lib/bundler/definition.rb +78 -0
  6. data/lib/bundler/dependency.rb +7 -57
  7. data/lib/bundler/dsl.rb +42 -142
  8. data/lib/bundler/environment.rb +94 -54
  9. data/lib/bundler/index.rb +98 -0
  10. data/lib/bundler/installer.rb +137 -0
  11. data/lib/bundler/remote_specification.rb +1 -1
  12. data/lib/bundler/resolver.rb +20 -50
  13. data/lib/bundler/rubygems.rb +22 -0
  14. data/lib/bundler/source.rb +185 -295
  15. data/lib/bundler/specification.rb +22 -0
  16. data/lib/bundler/templates/Gemfile +4 -0
  17. data/lib/bundler/templates/environment.erb +3 -153
  18. data/lib/bundler/ui.rb +51 -0
  19. data/lib/bundler/vendor/thor.rb +241 -0
  20. data/lib/bundler/vendor/thor/actions.rb +274 -0
  21. data/lib/bundler/vendor/thor/actions/create_file.rb +103 -0
  22. data/lib/bundler/vendor/thor/actions/directory.rb +91 -0
  23. data/lib/bundler/vendor/thor/actions/empty_directory.rb +134 -0
  24. data/lib/bundler/vendor/thor/actions/file_manipulation.rb +223 -0
  25. data/lib/bundler/vendor/thor/actions/inject_into_file.rb +101 -0
  26. data/lib/bundler/vendor/thor/base.rb +515 -0
  27. data/lib/bundler/vendor/thor/core_ext/file_binary_read.rb +9 -0
  28. data/lib/bundler/vendor/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  29. data/lib/bundler/vendor/thor/core_ext/ordered_hash.rb +100 -0
  30. data/lib/bundler/vendor/thor/error.rb +27 -0
  31. data/lib/bundler/vendor/thor/group.rb +267 -0
  32. data/lib/bundler/vendor/thor/invocation.rb +178 -0
  33. data/lib/bundler/vendor/thor/parser.rb +4 -0
  34. data/lib/bundler/vendor/thor/parser/argument.rb +67 -0
  35. data/lib/bundler/vendor/thor/parser/arguments.rb +145 -0
  36. data/lib/bundler/vendor/thor/parser/option.rb +132 -0
  37. data/lib/bundler/vendor/thor/parser/options.rb +142 -0
  38. data/lib/bundler/vendor/thor/rake_compat.rb +66 -0
  39. data/lib/bundler/vendor/thor/runner.rb +303 -0
  40. data/lib/bundler/vendor/thor/shell.rb +78 -0
  41. data/lib/bundler/vendor/thor/shell/basic.rb +239 -0
  42. data/lib/bundler/vendor/thor/shell/color.rb +108 -0
  43. data/lib/bundler/vendor/thor/task.rb +111 -0
  44. data/lib/bundler/vendor/thor/util.rb +233 -0
  45. data/lib/bundler/vendor/thor/version.rb +3 -0
  46. metadata +48 -26
  47. data/README.markdown +0 -284
  48. data/Rakefile +0 -81
  49. data/lib/bundler/bundle.rb +0 -314
  50. data/lib/bundler/commands/bundle_command.rb +0 -72
  51. data/lib/bundler/commands/exec_command.rb +0 -36
  52. data/lib/bundler/finder.rb +0 -51
  53. data/lib/bundler/gem_bundle.rb +0 -11
  54. data/lib/bundler/gem_ext.rb +0 -34
  55. data/lib/bundler/runtime.rb +0 -2
  56. data/lib/bundler/templates/app_script.erb +0 -3
  57. data/lib/bundler/templates/environment_picker.erb +0 -4
  58. data/lib/rubygems_plugin.rb +0 -6
@@ -1,85 +1,125 @@
1
- require "rubygems/source_index"
2
-
3
1
  module Bundler
4
- class InvalidCacheArgument < StandardError; end
5
- class SourceNotCached < StandardError; end
6
-
7
2
  class Environment
8
- attr_reader :dependencies
9
- attr_accessor :rubygems, :system_gems
10
-
11
- def initialize(bundle)
12
- @bundle = bundle # TODO: remove this
13
- @default_sources = default_sources
14
- @sources = []
15
- @priority_sources = []
16
- @dependencies = []
17
- @rubygems = true
18
- @system_gems = true
3
+ attr_reader :root
4
+
5
+ def initialize(root, definition)
6
+ @root = root
7
+ @definition = definition
19
8
  end
20
9
 
21
- def environment_rb(specs, options)
22
- load_paths = load_paths_for_specs(specs, options)
23
- bindir = @bundle.bindir.relative_path_from(@bundle.gem_path).to_s
24
- filename = @bundle.gemfile.relative_path_from(@bundle.gem_path).to_s
10
+ def setup(*groups)
11
+ # Has to happen first
12
+ cripple_rubygems
25
13
 
26
- template = File.read(File.join(File.dirname(__FILE__), "templates", "environment.erb"))
27
- erb = ERB.new(template, nil, '-')
28
- erb.result(binding)
14
+ # Activate the specs
15
+ specs_for(*groups).each do |spec|
16
+ Gem.loaded_specs[spec.name] = spec
17
+ $LOAD_PATH.unshift(*spec.load_paths)
18
+ end
19
+ self
29
20
  end
30
21
 
31
- def require_env(env = nil)
32
- dependencies.each { |d| d.require_env(env) }
22
+ def dependencies
23
+ @definition.dependencies
33
24
  end
34
25
 
35
- def sources
36
- sources = @priority_sources + [SystemGemSource.new(@bundle)] + @sources + @default_sources
37
- sources.reject! {|s| !s.local? } if Bundler.local?
38
- sources
26
+ def lock
27
+ FileUtils.mkdir_p("#{root}/vendor")
28
+ write_yml_lock
29
+ write_rb_lock
39
30
  end
40
31
 
41
- def add_source(source)
42
- @sources << source
32
+ def specs_for(*groups)
33
+ return specs if groups.empty?
34
+
35
+ dependencies = @definition.actual_dependencies.select { |d| groups.include?(d.group) }
36
+ Resolver.resolve(dependencies, index)
43
37
  end
44
38
 
45
- def add_priority_source(source)
46
- @priority_sources << source
39
+ def specs
40
+ @specs ||= begin
41
+ source_requirements = {}
42
+ dependencies.each do |dep|
43
+ next unless dep.source && dep.source.respond_to?(:local_specs)
44
+ source_requirements[dep.name] = dep.source.local_specs
45
+ end
46
+
47
+ Resolver.resolve(@definition.actual_dependencies, index, source_requirements)
48
+ end
47
49
  end
48
50
 
49
- def clear_sources
50
- @sources.clear
51
- @default_sources.clear
51
+ def index
52
+ @definition.local_index
52
53
  end
53
54
 
54
- alias rubygems? rubygems
55
- alias system_gems? system_gems
55
+ def pack
56
+ pack_path = "#{root}/vendor/cache/"
57
+ FileUtils.mkdir_p(pack_path)
58
+
59
+ specs.each do |spec|
60
+ next if spec.source && !spec.source.is_a?(Source::Rubygems)
61
+ possibilities = Gem.path.map { |p| "#{p}/cache/#{spec.full_name}.gem" }
62
+ cached_path = possibilities.find { |p| File.exist? p }
63
+ FileUtils.cp(cached_path, pack_path)
64
+ end
65
+ end
56
66
 
57
67
  private
58
68
 
59
- def default_sources
60
- [GemSource.new(@bundle, :uri => "http://gems.rubyforge.org")]
69
+ def sources
70
+ @definition.sources
61
71
  end
62
72
 
63
- def load_paths_for_specs(specs, options)
64
- load_paths = []
65
- specs.each do |spec|
66
- next if spec.no_bundle?
67
- full_gem_path = Pathname.new(spec.full_gem_path)
73
+ def load_paths
74
+ specs.map { |s| s.load_paths }.flatten
75
+ end
76
+
77
+ def cripple_rubygems
78
+ # handle 1.9 where system gems are always on the load path
79
+ if defined?(::Gem)
80
+ me = File.expand_path("../../", __FILE__)
81
+ $LOAD_PATH.reject! do |p|
82
+ p != File.dirname(__FILE__) &&
83
+ Gem.path.any? { |gp| p.include?(gp) }
84
+ end
85
+ $LOAD_PATH.unshift me
86
+ $LOAD_PATH.uniq!
87
+ end
88
+
89
+ # Disable rubygems' gem activation system
90
+ ::Kernel.class_eval do
91
+ if private_method_defined?(:gem_original_require)
92
+ alias require gem_original_require
93
+ end
68
94
 
69
- load_paths << load_path_for(full_gem_path, spec.bindir) if spec.bindir
70
- spec.require_paths.each do |path|
71
- load_paths << load_path_for(full_gem_path, path)
95
+ def gem(*)
96
+ # Silently ignore calls to gem
72
97
  end
73
98
  end
74
- load_paths
75
99
  end
76
100
 
77
- def load_path_for(gem_path, path)
78
- gem_path.join(path).relative_path_from(@bundle.gem_path).to_s
101
+ def write_rb_lock
102
+ template = File.read(File.expand_path("../templates/environment.erb", __FILE__))
103
+ erb = ERB.new(template, nil, '-')
104
+ File.open("#{root}/vendor/environment.rb", 'w') do |f|
105
+ f.puts erb.result(binding)
106
+ end
79
107
  end
80
108
 
81
- def spec_file_for(spec)
82
- spec.loaded_from.relative_path_from(@bundle.gem_path).to_s
109
+ def write_yml_lock
110
+ yml = details.to_yaml
111
+ File.open("#{root}/vendor/lock.yml", 'w') do |f|
112
+ f.puts yml
113
+ end
83
114
  end
115
+
116
+ def details
117
+ details = {}
118
+ details["sources"] = sources.map { |s| { s.class.name.split("::").last => s.options} }
119
+ details["specs"] = specs.map { |s| {s.name => s.version.to_s} }
120
+ details["dependencies"] = dependencies.map { |d| {d.name => d.version_requirements.to_s} }
121
+ details
122
+ end
123
+
84
124
  end
85
- end
125
+ end
@@ -0,0 +1,98 @@
1
+ module Bundler
2
+ class Index
3
+ def self.from_installed_gems
4
+ # TODO: Why can't we memoize this? It is being mutated somewhere
5
+ from_gem_index(Gem::SourceIndex.from_installed_gems)
6
+ end
7
+
8
+ def self.from_gem_index(gem_index)
9
+ index = new
10
+ gem_index.each { |name, spec| index << spec }
11
+ index
12
+ end
13
+
14
+ def initialize
15
+ @cache = {}
16
+ @specs = Hash.new { |h,k| h[k] = [] }
17
+ end
18
+
19
+ def initialize_copy(o)
20
+ super
21
+ @cache = {}
22
+ @specs = Hash.new { |h,k| h[k] = [] }
23
+ merge!(o)
24
+ end
25
+
26
+ def empty?
27
+ @specs.values.flatten.empty?
28
+ end
29
+
30
+ def search(query)
31
+ case query
32
+ when Gem::Specification, RemoteSpecification then search_by_spec(query)
33
+ when String then @specs[query]
34
+ else search_by_dependency(query)
35
+ end
36
+ end
37
+
38
+ alias [] search
39
+
40
+ def <<(spec)
41
+ arr = @specs[spec.name]
42
+
43
+ arr.delete_if do |s|
44
+ s.version == spec.version && s.platform == spec.platform
45
+ end
46
+
47
+ arr << spec
48
+ spec
49
+ end
50
+
51
+ def each
52
+ @specs.values.flatten.each do |spec|
53
+ yield spec
54
+ end
55
+ end
56
+
57
+ def merge!(other)
58
+ other.each do |spec|
59
+ self << spec
60
+ end
61
+ self
62
+ end
63
+
64
+ def merge(other)
65
+ dup.merge!(other)
66
+ end
67
+
68
+ def freeze
69
+ @specs.each do |k,v|
70
+ v.freeze
71
+ end
72
+ super
73
+ end
74
+
75
+ private
76
+
77
+ def search_by_spec(spec)
78
+ @specs[spec.name].select { |s| s.version == spec.version }
79
+ end
80
+
81
+ def search_by_dependency(dependency)
82
+ @cache[dependency.hash] ||= begin
83
+ specs = @specs[dependency.name]
84
+
85
+ wants_prerelease = dependency.version_requirements.prerelease?
86
+ only_prerelease = specs.all? {|spec| spec.version.prerelease? }
87
+ found = specs.select { |spec| dependency =~ spec }
88
+
89
+ unless wants_prerelease || only_prerelease
90
+ found.reject! { |spec| spec.version.prerelease? }
91
+ end
92
+
93
+ found.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
94
+ end
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,137 @@
1
+ require 'rubygems/dependency_installer'
2
+
3
+ module Bundler
4
+ class Installer
5
+ def self.install(root, definition, options)
6
+ new(root, definition).run(options)
7
+ end
8
+
9
+ attr_reader :root
10
+
11
+ def initialize(root, definition)
12
+ @root = root
13
+ @definition = definition
14
+ end
15
+
16
+ def run(options)
17
+ if dependencies.empty?
18
+ Bundler.ui.warn "The Gemfile specifies no dependencies"
19
+ return
20
+ end
21
+
22
+ specs.each do |spec|
23
+ next unless spec.source.respond_to?(:install)
24
+ next if (spec.groups & options[:without]).any?
25
+ spec.source.install(spec)
26
+ end
27
+
28
+ Bundler.ui.confirm "You have bundles. Now, go have fun."
29
+ end
30
+
31
+ def dependencies
32
+ @definition.actual_dependencies
33
+ end
34
+
35
+ def specs
36
+ @specs ||= group_specs(resolve_locally || resolve_remotely)
37
+ end
38
+
39
+ private
40
+
41
+ def sources
42
+ @definition.sources
43
+ end
44
+
45
+ def resolve_locally
46
+ # Return unless all the dependencies have = version requirements
47
+ return unless dependencies.all? { |d| unambiguous?(d) }
48
+
49
+ index = local_index
50
+ sources.each do |source|
51
+ next unless source.respond_to?(:local_specs)
52
+ index = source.local_specs.merge(index)
53
+ end
54
+
55
+ source_requirements = {}
56
+ dependencies.each do |dep|
57
+ next unless dep.source && dep.source.respond_to?(:local_specs)
58
+ source_requirements[dep.name] = dep.source.local_specs
59
+ end
60
+
61
+ # Run a resolve against the locally available gems
62
+ specs = Resolver.resolve(dependencies, local_index, source_requirements)
63
+
64
+ # Simple logic for now. Can improve later.
65
+ specs.length == dependencies.length && specs
66
+ rescue Bundler::GemNotFound
67
+ nil
68
+ end
69
+
70
+ def resolve_remotely
71
+ index # trigger building the index
72
+ Bundler.ui.info "Resolving dependencies... "
73
+ source_requirements = {}
74
+ dependencies.each do |dep|
75
+ next unless dep.source
76
+ source_requirements[dep.name] = dep.source.specs
77
+ end
78
+
79
+ specs = Resolver.resolve(dependencies, index, source_requirements)
80
+ Bundler.ui.info "Done."
81
+ specs
82
+ end
83
+
84
+ def group_specs(specs)
85
+ dependencies.each do |d|
86
+ spec = specs.find { |s| s.name == d.name }
87
+ group_spec(specs, spec, d.group)
88
+ end
89
+ specs
90
+ end
91
+
92
+ def group_spec(specs, spec, group)
93
+ spec.groups << group
94
+ spec.groups.uniq!
95
+ spec.dependencies.select { |d| d.type != :development }.each do |d|
96
+ spec = specs.find { |s| s.name == d.name }
97
+ group_spec(specs, spec, group)
98
+ end
99
+ end
100
+
101
+ def unambiguous?(dep)
102
+ dep.version_requirements.requirements.all? { |op,_| op == '=' }
103
+ end
104
+
105
+ def index
106
+ @index ||= begin
107
+ index = local_index
108
+
109
+ sources.each do |source|
110
+ i = source.specs
111
+ Bundler.ui.info "Source: Processing index... "
112
+ index = i.merge(index).freeze
113
+ Bundler.ui.info "Done."
114
+ end
115
+
116
+ index
117
+ end
118
+ end
119
+
120
+ def local_index
121
+ @local_index ||= begin
122
+ index = Index.from_installed_gems.freeze
123
+
124
+ if File.directory?("#{root}/vendor/cache")
125
+ index = cache_source.specs.merge(index).freeze
126
+ end
127
+
128
+ index
129
+ end
130
+ end
131
+
132
+ def cache_source
133
+ Source::GemCache.new(:path => "#{root}/vendor/cache")
134
+ end
135
+
136
+ end
137
+ end
@@ -29,7 +29,7 @@ module Bundler
29
29
  @specification = spec
30
30
  end
31
31
 
32
- private
32
+ private
33
33
 
34
34
  def _remote_uri
35
35
  # "#{@source_uri}/quick/Marshal.4.8/#{@name}-#{@version}.gemspec.rz"
@@ -1,3 +1,4 @@
1
+ require 'set'
1
2
  # This is the latest iteration of the gem dependency resolving algorithm. As of now,
2
3
  # it can resolve (as a success of failure) any set of gem dependencies we throw at it
3
4
  # in a reasonable amount of time. The most iterations I've seen it take is about 150.
@@ -19,9 +20,6 @@ module Gem
19
20
  end
20
21
 
21
22
  module Bundler
22
- class GemNotFound < StandardError; end
23
- class VersionConflict < StandardError; end
24
-
25
23
  class Resolver
26
24
 
27
25
  attr_reader :errors
@@ -36,21 +34,19 @@ module Bundler
36
34
  # ==== Returns
37
35
  # <GemBundle>,nil:: If the list of dependencies can be resolved, a
38
36
  # collection of gemspecs is returned. Otherwise, nil is returned.
39
- def self.resolve(requirements, sources)
40
- source_requirements = {}
41
-
42
- requirements.each do |r|
43
- next unless r.source
44
- source_requirements[r.name] = r.source
45
- end
46
-
47
- resolver = new(sources, source_requirements)
37
+ def self.resolve(requirements, index, source_requirements = {})
38
+ resolver = new(index, source_requirements)
48
39
  result = catch(:success) do
49
40
  resolver.resolve(requirements, {})
50
41
  output = resolver.errors.inject("") do |o, (conflict, (origin, requirement))|
51
- o << " Conflict on: #{conflict.inspect}:\n"
52
- o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n"
53
- o << " * #{requirement} required by #{requirement.required_by.first}\n"
42
+ if origin
43
+ o << " Conflict on: #{conflict.inspect}:\n"
44
+ o << " * #{conflict} (#{origin.version}) activated by #{origin.required_by.first}\n"
45
+ o << " * #{requirement} required by #{requirement.required_by.first}\n"
46
+ else
47
+ o << " #{requirement} not found in any of the sources\n"
48
+ o << " required by #{requirement.required_by.first}\n"
49
+ end
54
50
  o << " All possible versions of origin requirements conflict."
55
51
  end
56
52
  raise VersionConflict, "No compatible versions could be found for required dependencies:\n #{output}"
@@ -61,7 +57,6 @@ module Bundler
61
57
  # a smaller index in the array.
62
58
  ordered = []
63
59
  result.values.each do |spec1|
64
- spec1.no_bundle = true if source_requirements[spec1.name] == SystemGemSource.instance
65
60
  index = nil
66
61
  place = ordered.detect do |spec2|
67
62
  spec1.dependencies.any? { |d| d.name == spec2.name }
@@ -74,24 +69,11 @@ module Bundler
74
69
  end
75
70
  end
76
71
 
77
- def initialize(sources, source_requirements)
72
+ def initialize(index, source_requirements)
78
73
  @errors = {}
79
74
  @stack = []
80
- @specs = Hash.new { |h,k| h[k] = [] }
81
- @by_gem = source_requirements
82
- @cache = {}
83
- @index = {}
84
-
85
- sources.each do |source|
86
- source.gems.each do |name, specs|
87
- # Hack to work with a regular Gem::SourceIndex
88
- specs = [specs] unless specs.is_a?(Array)
89
- specs.compact.each do |spec|
90
- next if @specs[spec.name].any? { |s| s.version == spec.version && s.platform == spec.platform }
91
- @specs[spec.name] << spec
92
- end
93
- end
94
- end
75
+ @index = index
76
+ @source_requirements = source_requirements
95
77
  end
96
78
 
97
79
  def debug
@@ -166,10 +148,11 @@ module Bundler
166
148
 
167
149
  if matching_versions.empty?
168
150
  if current.required_by.empty?
169
- location = @by_gem[current.name] ? @by_gem[current.name] : "any of the sources"
151
+ location = current.source ? current.source.to_s : "any of the sources"
170
152
  raise GemNotFound, "Could not find gem '#{current}' in #{location}"
153
+ else
154
+ @errors[current.name] = [nil, current]
171
155
  end
172
- Bundler.logger.warn "Could not find gem '#{current}' (required by '#{current.required_by.last}') in any of the sources"
173
156
  end
174
157
 
175
158
  matching_versions.reverse_each do |spec|
@@ -229,22 +212,9 @@ module Bundler
229
212
  retval
230
213
  end
231
214
 
232
- def search(dependency)
233
- @cache[dependency.hash] ||= begin
234
- pinned = @by_gem[dependency.name].gems if @by_gem[dependency.name]
235
- specs = (pinned || @specs)[dependency.name]
236
-
237
- wants_prerelease = dependency.version_requirements.prerelease?
238
- only_prerelease = specs.all? {|spec| spec.version.prerelease? }
239
-
240
- found = specs.select { |spec| dependency =~ spec }
241
-
242
- unless wants_prerelease || (pinned && only_prerelease)
243
- found.reject! { |spec| spec.version.prerelease? }
244
- end
245
-
246
- found.sort_by {|s| [s.version, s.platform.to_s == 'ruby' ? "\0" : s.platform.to_s] }
247
- end
215
+ def search(dep)
216
+ index = @source_requirements[dep.name] || @index
217
+ index.search(dep)
248
218
  end
249
219
  end
250
220
  end