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
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'rubygems/specification'
3
+
4
+ module Gem
5
+ @loaded_stacks = Hash.new { |h,k| h[k] = [] }
6
+
7
+ class Specification
8
+ attr_accessor :source, :location
9
+
10
+ def load_paths
11
+ require_paths.map {|p| File.join(full_gem_path, p) }
12
+ end
13
+
14
+ def groups
15
+ @groups ||= []
16
+ end
17
+ end
18
+
19
+ class Dependency
20
+ attr_accessor :source, :group
21
+ end
22
+ end
@@ -1,357 +1,247 @@
1
- module Bundler
2
- class DirectorySourceError < StandardError; end
3
- class GitSourceError < StandardError ; end
4
- # Represents a source of rubygems. Initially, this is only gem repositories, but
5
- # eventually, this will be git, svn, HTTP
6
- class Source
7
- attr_reader :bundle
8
-
9
- def initialize(bundle, options)
10
- @bundle = bundle
11
- end
1
+ require "rubygems/remote_fetcher"
2
+ require "rubygems/format"
3
+ require "digest/sha1"
12
4
 
13
- private
14
-
15
- def process_source_gems(gems)
16
- new_gems = Hash.new { |h,k| h[k] = [] }
17
- gems.values.each do |spec|
18
- spec.source = self
19
- new_gems[spec.name] << spec
5
+ module Bundler
6
+ module Source
7
+ class Rubygems
8
+ attr_reader :uri, :options
9
+
10
+ def initialize(options = {})
11
+ @options = options
12
+ @uri = options[:uri]
13
+ @uri = URI.parse(@uri) unless @uri.is_a?(URI)
14
+ raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
20
15
  end
21
- new_gems
22
- end
23
- end
24
-
25
- class GemSource < Source
26
- attr_reader :uri
27
-
28
- def initialize(bundle, options)
29
- super
30
- @uri = options[:uri]
31
- @uri = URI.parse(@uri) unless @uri.is_a?(URI)
32
- raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
33
- end
34
-
35
- def local?
36
- false
37
- end
38
-
39
- def gems
40
- @specs ||= fetch_specs
41
- end
42
16
 
43
- def ==(other)
44
- uri == other.uri
45
- end
46
-
47
- def to_s
48
- @uri.to_s
49
- end
50
-
51
- class RubygemsRetardation < StandardError; end
52
-
53
- def download(spec)
54
- Bundler.logger.info "Downloading #{spec.full_name}.gem"
55
-
56
- destination = bundle.gem_path
57
-
58
- unless destination.writable?
59
- raise RubygemsRetardation, "destination: #{destination} is not writable"
17
+ def specs
18
+ @specs ||= fetch_specs
60
19
  end
61
20
 
62
- # Download the gem
63
- Gem::RemoteFetcher.fetcher.download(spec, uri, destination)
64
-
65
- # Re-read the gemspec from the downloaded gem to correct
66
- # any errors that were present in the Rubyforge specification.
67
- new_spec = Gem::Format.from_file_by_path(destination.join('cache', "#{spec.full_name}.gem")).spec
68
- spec.__swap__(new_spec)
69
- end
21
+ def install(spec)
22
+ Bundler.ui.info "* #{spec.name} (#{spec.version})"
23
+ if Index.from_installed_gems[spec].any?
24
+ Bundler.ui.info " * already installed... skipping"
25
+ return
26
+ end
70
27
 
71
- private
28
+ destination = Gem.dir
72
29
 
73
- def fetch_specs
74
- Bundler.logger.info "Updating source: #{to_s}"
75
- build_gem_index(fetch_main_specs + fetch_prerelease_specs)
76
- end
30
+ Bundler.ui.info " * Downloading..."
31
+ gem_path = Gem::RemoteFetcher.fetcher.download(spec, uri, destination)
32
+ Bundler.ui.info " * Installing..."
33
+ installer = Gem::Installer.new gem_path,
34
+ :install_dir => Gem.dir,
35
+ :ignore_dependencies => true
77
36
 
78
- def build_gem_index(index)
79
- gems = Hash.new { |h,k| h[k] = [] }
80
- index.each do |name, version, platform|
81
- spec = RemoteSpecification.new(name, version, platform, @uri)
82
- spec.source = self
83
- gems[spec.name] << spec if Gem::Platform.match(spec.platform)
37
+ installer.install
84
38
  end
85
- gems
86
- end
87
39
 
88
- def fetch_main_specs
89
- Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/specs.4.8.gz"))
90
- rescue Gem::RemoteFetcher::FetchError => e
91
- raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
92
- end
40
+ private
93
41
 
94
- def fetch_prerelease_specs
95
- Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/prerelease_specs.4.8.gz"))
96
- rescue Gem::RemoteFetcher::FetchError
97
- Bundler.logger.warn "Source '#{uri}' does not support prerelease gems"
98
- []
99
- end
100
- end
42
+ def fetch_specs
43
+ index = Index.new
44
+ Bundler.ui.info "Source: Fetching remote index for `#{uri}`... "
45
+ (main_specs + prerelease_specs).each do |name, version, platform|
46
+ spec = RemoteSpecification.new(name, version, platform, @uri)
47
+ spec.source = self
48
+ index << spec
49
+ end
50
+ Bundler.ui.info "done."
51
+ index.freeze
52
+ end
101
53
 
102
- class SystemGemSource < Source
54
+ def main_specs
55
+ Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/specs.4.8.gz"))
56
+ rescue Gem::RemoteFetcher::FetchError => e
57
+ raise ArgumentError, "#{to_s} is not a valid source: #{e.message}"
58
+ end
103
59
 
104
- def self.instance
105
- @instance
60
+ def prerelease_specs
61
+ Marshal.load(Gem::RemoteFetcher.fetcher.fetch_path("#{uri}/prerelease_specs.4.8.gz"))
62
+ rescue Gem::RemoteFetcher::FetchError
63
+ Bundler.logger.warn "Source '#{uri}' does not support prerelease gems"
64
+ []
65
+ end
106
66
  end
107
67
 
108
- def self.new(*args)
109
- @instance ||= super
110
- end
68
+ class GemCache
69
+ def initialize(options)
70
+ @path = options[:path]
71
+ end
111
72
 
112
- def initialize(bundle, options = {})
113
- super
114
- @source = Gem::SourceIndex.from_installed_gems
115
- end
73
+ def specs
74
+ @specs ||= begin
75
+ index = Index.new
116
76
 
117
- def local?
118
- false
119
- end
77
+ Dir["#{@path}/*.gem"].each do |gemfile|
78
+ spec = Gem::Format.from_file_by_path(gemfile).spec
79
+ spec.source = self
80
+ index << spec
81
+ end
120
82
 
121
- def gems
122
- @gems ||= process_source_gems(@source.gems)
123
- end
83
+ index.freeze
84
+ end
85
+ end
124
86
 
125
- def ==(other)
126
- other.is_a?(SystemGemSource)
127
- end
87
+ def install(spec)
88
+ destination = Gem.dir
128
89
 
129
- def to_s
130
- "system"
131
- end
90
+ installer = Gem::Installer.new "#{@path}/#{spec.full_name}.gem",
91
+ :install_dir => Gem.dir,
92
+ :ignore_dependencies => true
132
93
 
133
- def download(spec)
134
- gemfile = Pathname.new(spec.loaded_from)
135
- gemfile = gemfile.dirname.join('..', 'cache', "#{spec.full_name}.gem")
136
- bundle.cache(gemfile)
94
+ installer.install
95
+ end
137
96
  end
138
97
 
139
- private
140
-
141
- end
98
+ class Path
99
+ attr_reader :path, :options
142
100
 
143
- class GemDirectorySource < Source
144
- attr_reader :location
145
-
146
- def initialize(bundle, options)
147
- super
148
- @location = options[:location]
149
- end
101
+ def initialize(options)
102
+ @options = options
103
+ @glob = options[:glob] || "{,*/}*.gemspec"
104
+ @path = options[:path]
105
+ end
150
106
 
151
- def local?
152
- true
153
- end
107
+ def default_spec(*args)
108
+ return @default_spec if args.empty?
109
+ name, version = *args
110
+ @default_spec = Specification.new do |s|
111
+ s.name = name
112
+ s.source = self
113
+ s.version = Gem::Version.new(version)
114
+ s.relative_loaded_from = "#{name}.gemspec"
115
+ end
116
+ end
154
117
 
155
- def gems
156
- @specs ||= fetch_specs
157
- end
118
+ def local_specs
119
+ @local_specs ||= begin
120
+ index = Index.new
121
+
122
+ if File.directory?(path)
123
+ Dir["#{path}/#{@glob}"].each do |file|
124
+ file = Pathname.new(file)
125
+ if spec = eval(File.read(file))
126
+ spec = Specification.from_gemspec(spec)
127
+ spec.loaded_from = file
128
+ spec.source = self
129
+ index << spec
130
+ end
131
+ end
132
+
133
+ index << default_spec if default_spec && index.empty?
134
+ end
158
135
 
159
- def ==(other)
160
- location == other.location
161
- end
136
+ index.freeze
137
+ end
138
+ end
162
139
 
163
- def to_s
164
- location.to_s
165
- end
140
+ alias specs local_specs
166
141
 
167
- def download(spec)
168
- # raise NotImplementedError
169
142
  end
170
143
 
171
- private
172
-
173
- def fetch_specs
174
- specs = Hash.new { |h,k| h[k] = [] }
144
+ class Git < Path
145
+ attr_reader :uri, :ref
175
146
 
176
- Dir["#{@location}/*.gem"].each do |gemfile|
177
- spec = Gem::Format.from_file_by_path(gemfile).spec
178
- spec.source = self
179
- specs[spec.name] << spec
147
+ def initialize(options)
148
+ @options = options
149
+ @glob = options[:glob] || "{,*/}*.gemspec"
150
+ @uri = options[:uri]
151
+ @ref = options[:ref] || options[:branch] || 'master'
180
152
  end
181
153
 
182
- specs
183
- end
184
- end
185
-
186
- class DirectorySource < Source
187
- attr_reader :location, :specs, :required_specs
188
-
189
- def initialize(bundle, options)
190
- super
191
- if options[:location]
192
- @location = Pathname.new(options[:location]).expand_path
154
+ def options
155
+ @options.merge(:ref => revision)
193
156
  end
194
- @glob = options[:glob] || "**/*.gemspec"
195
- @specs = {}
196
- @required_specs = []
197
- end
198
157
 
199
- def add_spec(path, name, version, require_paths = %w(lib))
200
- raise DirectorySourceError, "already have a gem defined for '#{path}'" if @specs[path.to_s]
201
- @specs[path.to_s] = Gem::Specification.new do |s|
202
- s.name = name
203
- s.version = Gem::Version.new(version)
158
+ def path
159
+ Bundler.install_path.join("#{base_name}-#{uri_hash}-#{ref}")
204
160
  end
205
- end
206
161
 
207
- def local?
208
- true
209
- end
210
-
211
- def gems
212
- @gems ||= begin
213
- # Locate all gemspecs from the directory
214
- specs = locate_gemspecs
215
- specs = merge_defined_specs(specs)
162
+ def to_s
163
+ @uri
164
+ end
216
165
 
217
- required_specs.each do |required|
218
- unless specs.any? {|k,v| v.name == required }
219
- raise DirectorySourceError, "No gemspec for '#{required}' was found in" \
220
- " '#{location}'. Please explicitly specify a version."
166
+ def specs
167
+ @specs ||= begin
168
+ index = Index.new
169
+ # Start by making sure the git cache is up to date
170
+ cache
171
+ # Find all gemspecs in the repo
172
+ in_cache do
173
+ out = %x(git ls-tree -r #{revision}).strip
174
+ lines = out.split("\n").select { |l| l =~ /\.gemspec$/ }
175
+ # Loop over the lines and extract the relative path and the
176
+ # git hash
177
+ lines.each do |line|
178
+ next unless line =~ %r{^(\d+) (blob|tree) ([a-zf0-9]+)\t(.*)$}
179
+ hash, file = $3, $4
180
+ # Read the gemspec
181
+ if spec = eval(%x(git cat-file blob #{$3}))
182
+ spec = Specification.from_gemspec(spec)
183
+ spec.relative_loaded_from = file
184
+ spec.source = self
185
+ index << spec
186
+ end
187
+ end
221
188
  end
222
- end
223
189
 
224
- process_source_gems(specs)
225
- end
226
- end
190
+ index << default_spec if default_spec && index.empty?
227
191
 
228
- def locate_gemspecs
229
- Dir["#{location}/#{@glob}"].inject({}) do |specs, file|
230
- file = Pathname.new(file)
231
- if spec = eval(File.read(file)) # and validate_gemspec(file.dirname, spec)
232
- spec.location = file.dirname.expand_path
233
- specs[spec.full_name] = spec
192
+ index.freeze
234
193
  end
235
- specs
236
194
  end
237
- end
238
195
 
239
- def merge_defined_specs(specs)
240
- @specs.each do |path, spec|
241
- # Set the spec location
242
- spec.location = "#{location}/#{path}"
243
-
244
- if existing = specs.values.find { |s| s.name == spec.name }
245
- if existing.version != spec.version
246
- raise DirectorySourceError, "The version you specified for #{spec.name}" \
247
- " is #{spec.version}. The gemspec is #{existing.version}."
248
- # Not sure if this is needed
249
- # ====
250
- # elsif File.expand_path(existing.location) != File.expand_path(spec.location)
251
- # raise DirectorySourceError, "The location you specified for #{spec.name}" \
252
- # " is '#{spec.location}'. The gemspec was found at '#{existing.location}'."
196
+ def install(spec)
197
+ @installed ||= begin
198
+ FileUtils.mkdir_p(path)
199
+ Dir.chdir(path) do
200
+ unless File.exist?(".git")
201
+ %x(git clone --recursive --no-checkout #{cache_path} #{path})
202
+ end
203
+ %x(git fetch --quiet)
204
+ %x(git reset --hard #{revision})
205
+ %x(git submodule init)
206
+ %x(git submodule update)
253
207
  end
254
- # elsif !validate_gemspec(spec.location, spec)
255
- # raise "Your gem definition is not valid: #{spec}"
256
- else
257
- specs[spec.full_name] = spec
208
+ true
258
209
  end
259
210
  end
260
- specs
261
- end
262
211
 
263
- def validate_gemspec(path, spec)
264
- path = Pathname.new(path)
265
- msg = "Gemspec for #{spec.name} (#{spec.version}) is invalid:"
266
- # Check the require_paths
267
- (spec.require_paths || []).each do |require_path|
268
- unless path.join(require_path).directory?
269
- Bundler.logger.warn "#{msg} Missing require path: '#{require_path}'"
270
- return false
271
- end
272
- end
212
+ private
273
213
 
274
- # Check the executables
275
- (spec.executables || []).each do |exec|
276
- unless path.join(spec.bindir, exec).file?
277
- Bundler.logger.warn "#{msg} Missing executable: '#{File.join(spec.bindir, exec)}'"
278
- return false
279
- end
214
+ def base_name
215
+ File.basename(uri, ".git")
280
216
  end
281
217
 
282
- true
283
- end
284
-
285
- def ==(other)
286
- # TMP HAX
287
- other.is_a?(DirectorySource)
288
- end
289
-
290
- def to_s
291
- "directory: '#{location}'"
292
- end
293
-
294
- def download(spec)
295
- # Nothing needed here
296
- end
297
- end
298
-
299
- class GitSource < DirectorySource
300
- attr_reader :ref, :uri, :branch
301
-
302
- def initialize(bundle, options)
303
- super
304
- @uri = options[:uri]
305
- @branch = options[:branch] || 'master'
306
- @ref = options[:ref] || "origin/#{@branch}"
307
- end
308
-
309
- def local?
310
- raise SourceNotCached, "Git repository '#{@uri}' has not been cloned yet" unless location.directory?
311
- super
312
- end
313
-
314
- def location
315
- # TMP HAX to get the *.gemspec reading to work
316
- bundle.gem_path.join('dirs', File.basename(@uri, '.git'))
317
- end
318
-
319
- def gems
320
- update if Bundler.remote?
321
- checkout if Bundler.writable?
322
- super
323
- end
324
-
325
- def download(spec)
326
- # Nothing needed here
327
- end
218
+ def uri_hash
219
+ Digest::SHA1.hexdigest(URI.parse(uri).normalize.to_s.sub(%r{/$}, ''))
220
+ end
328
221
 
329
- def to_s
330
- "git: #{uri}"
331
- end
222
+ def cache_path
223
+ @cache_path ||= Bundler.cache.join("git", "#{base_name}-#{uri_hash}")
224
+ end
332
225
 
333
- private
334
- def update
335
- if location.directory?
336
- fetch
226
+ def cache
227
+ if cache_path.exist?
228
+ Bundler.ui.info "Source: Updating `#{uri}`... "
229
+ in_cache { `git fetch --quiet #{uri} master:master` }
337
230
  else
338
- clone
231
+ Bundler.ui.info "Source: Cloning `#{uri}`... "
232
+ FileUtils.mkdir_p(cache_path.dirname)
233
+ `git clone #{uri} #{cache_path} --bare --no-hardlinks`
339
234
  end
235
+ Bundler.ui.info "Done."
340
236
  end
341
237
 
342
- def fetch
343
- Bundler.logger.info "Fetching git repository at: #{@uri}"
344
- Dir.chdir(location) { `git fetch origin` }
238
+ def revision
239
+ @revision ||= in_cache { `git rev-parse #{ref}`.strip }
345
240
  end
346
241
 
347
- def clone
348
- Bundler.logger.info "Cloning git repository at: #{@uri}"
349
- FileUtils.mkdir_p(location.dirname)
350
- `git clone #{@uri} #{location} --no-hardlinks`
351
- end
352
-
353
- def checkout
354
- Dir.chdir(location) { `git checkout --quiet #{@ref}` }
242
+ def in_cache(&blk)
243
+ Dir.chdir(cache_path, &blk)
355
244
  end
245
+ end
356
246
  end
357
- end
247
+ end