Permalink
Browse files

Scope specs in the lock file by source

  • Loading branch information...
1 parent 8c1dde4 commit 2b9094e9d0f0cc21d8acf503da98fd908e29f6ff Carl Lerche committed May 10, 2010
View
@@ -103,9 +103,11 @@ def load
end
def definition
- configure
- lockfile = root.join("Gemfile.lock")
- Definition.build(default_gemfile, lockfile)
+ @definition ||= begin
+ configure
+ lockfile = root.join("Gemfile.lock")
+ Definition.build(default_gemfile, lockfile)
+ end
end
def home
View
@@ -98,7 +98,7 @@ def install(path = nil)
desc "update", "update the current environment"
def update(*gems)
- gems = Bundler.definition.specs.map { |s| s.name } if gems.empty?
+ gems = Bundler.definition.locked_specs.map { |s| s.name } if gems.empty?
Installer.install(Bundler.root, Bundler.definition, gems, {})
end
@@ -20,15 +20,14 @@ def initialize(name, version, options = {}, &blk)
def to_lock
out = " #{name}"
+
unless requirement == Gem::Requirement.default
out << " (#{requirement.to_s})"
end
- if @source
- out << ":\n #{@source.to_lock}\n"
- else
- out << "\n"
- end
+ out << '!' if source
+
+ out << "\n"
end
end
end
@@ -66,42 +66,35 @@ def write_rb_lock
def write_yml_lock
File.open("#{root}/Gemfile.lock", 'w') do |f|
- f.puts details
+ f.puts lock_content
end
end
- def details
- output = ""
-
- pinned_sources = dependencies.map {|d| d.source }
- all_sources = @definition.sources.map {|s| s }
-
- specified_sources = all_sources - pinned_sources
-
- unless specified_sources.length == 1 && specified_sources.first.remotes.empty?
- output << "sources:\n"
-
- specified_sources.each do |source|
- o = source.to_lock
- output << " #{source.to_lock}\n" unless o.empty?
+ def lock_content
+ out = ""
+
+ @definition.sources.each do |source|
+ # Add the source header
+ out << source.to_lock
+ # Find all specs for this source
+ specs.
+ select { |s| s.source == source }.
+ sort_by { |s| s.name }.
+ each do |spec|
+ out << spec.to_lock
end
- output << "\n"
+ out << "\n"
end
- unless @definition.dependencies.empty?
- output << "dependencies:\n"
- @definition.dependencies.sort_by {|d| d.name }.each do |dependency|
- output << dependency.to_lock
- end
- output << "\n"
- end
+ out << "DEPENDENCIES\n"
- output << "specs:\n"
- specs.sort_by {|s| s.name }.each do |spec|
- output << spec.to_lock
+ @definition.dependencies.
+ sort_by { |d| d.name }.
+ each do |dep|
+ out << dep.to_lock
end
- output
+ out
end
end
end
@@ -4,11 +4,13 @@
module Bundler
class LazySpecification
attr_reader :name, :version, :dependencies
+ attr_accessor :source
def initialize(name, version)
@name = name
@version = version
@dependencies = []
+ @source = nil
end
def satisfies?(dependency)
@@ -4,92 +4,82 @@ module Bundler
class LockfileParser
attr_reader :sources, :dependencies, :specs
- # Do stuff
def initialize(lockfile)
- @rg_source = Source::Rubygems.new
@sources = []
@dependencies = []
@specs = []
+ @state = :source
lockfile.split(/\n+/).each do |line|
- case line
- when "sources:"
- @state = :source
- when "dependencies:"
- @state = :dependencies
- when "specs:"
- @state = :specs
+ if line == "DEPENDENCIES"
+ @state = :dependency
else
send("parse_#{@state}", line)
end
end
-
- @sources << @rg_source
- @sources.uniq!
end
private
TYPES = {
- "git" => Bundler::Source::Git,
- "gem" => Bundler::Source::Rubygems,
- "path" => Bundler::Source::Path
+ "GIT" => Bundler::Source::Git,
+ "GEM" => Bundler::Source::Rubygems,
+ "PATH" => Bundler::Source::Path
}
def parse_source(line)
- @sources << parse_source_line(line)
- end
-
- def parse_source_line(line, extra_opts = {})
- type, source, option_line = line.match(/^\s+(\w+): ([^\s]*?)(?: (.*))?$/).captures
- options = extract_options(option_line)
- # There should only be one instance of a rubygem source
- if type == 'gem'
- @rg_source.add_remote source
- @sources << @rg_source
- @rg_source
+ case line
+ when "GIT", "GEM", "PATH"
+ @current_source = nil
+ @opts, @type = {}, line
+ when " specs:"
+ @current_source = TYPES[@type].from_lock(@opts)
+ @sources << @current_source
+ when /^ ([a-z]+): (.*)$/i
+ if @opts[$1]
+ @opts[$1] = Array(@opts[$1])
+ @opts[$1] << $2
+ else
+ @opts[$1] = $2
+ end
else
- TYPES[type].from_lock(source, extra_opts.merge(options))
+ parse_spec(line)
end
end
- NAME_VERSION = '(?! )(.*?)(?: \((.*)\))?:?'
+ NAME_VERSION = '(?! )(.*?)(?: \((.*)\))?'
- def parse_dependencies(line)
- if line =~ %r{^ {2}#{NAME_VERSION}$}
- name, version = $1, $2
+ def parse_dependency(line)
+ if line =~ %r{^ {2}#{NAME_VERSION}(!)?$}
+ name, version, pinned = $1, $2, $3
- if version =~ /^= (.+)$/
- @last_version = $1
- end
+ dep = Bundler::Dependency.new(name, version)
- @current = Bundler::Dependency.new(name, version)
- @dependencies << @current
- else
- @current.source = parse_source_line(line, "name" => @current.name, "version" => @last_version)
- @sources.unshift @current.source
- end
- end
+ if pinned
+ dep.source = @specs.find { |s| s.name == dep.name }.source
- def parse_specs(line)
- if line =~ %r{^ {2}#{NAME_VERSION}$}
- @current = LazySpecification.new($1, $2)
- @specs << @current
- else
- line =~ %r{^ {4}#{NAME_VERSION}$}
- @current.dependencies << Gem::Dependency.new($1, $2)
+ # 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 =~ /^= (.+)$/ && dep.source.is_a?(Bundler::Source::Path)
+ dep.source.name = name
+ dep.source.version = $1
+ end
+ end
+
+ @dependencies << dep
end
end
- def extract_options(line)
- options = {}
- return options unless line
-
- line.scan(/(\w+):"((?:|.*?[^\\])(?:\\\\)*)" ?/) do |k,v|
- options[k] = v
+ def parse_spec(line)
+ if line =~ %r{^ {4}#{NAME_VERSION}$}
+ @current_spec = LazySpecification.new($1, $2)
+ @current_spec.source = @current_source
+ @specs << @current_spec
+ elsif line =~ %r{^ {6}#{NAME_VERSION}$}
+ @current_spec.dependencies << Gem::Dependency.new($1, $2)
end
-
- options
end
end
end
@@ -31,12 +31,13 @@ def git_version
end
def to_lock
- out = " #{name} (#{version})"
- out << (dependencies.empty? ? "\n" : ":\n")
+ out = " #{name} (#{version})\n"
+
dependencies.sort_by {|d| d.name }.each do |dep|
next if dep.type == :development
- out << " #{dep.to_lock}\n"
+ out << " #{dep.to_lock}\n"
end
+
out
end
View
@@ -25,12 +25,16 @@ def options
{ "remotes" => @remotes.map { |r| r.to_s } }
end
- def self.from_lock(uri, options)
- new("uri" => URI.unescape(uri))
+ def self.from_lock(options)
+ s = new(options)
+ Array(options["remote"]).each { |r| s.add_remote(r) }
+ s
end
def to_lock
- remotes.map {|r| "gem: #{URI.escape(r.to_s)}" }.join("\n ")
+ out = "GEM\n"
+ out << remotes.map {|r| " remote: #{r}\n" }.join
+ out << " specs:\n"
end
def to_s
@@ -181,6 +185,8 @@ def download_gem_from_uri(spec, uri)
class Path
attr_reader :path, :options
+ # Kind of a hack, but needed for the lock file parser
+ attr_accessor :name, :version
DEFAULT_GLOB = "{,*/}*.gemspec"
@@ -196,14 +202,15 @@ def initialize(options)
@version = options["version"]
end
- def self.from_lock(uri, options)
- new(options.merge("path" => URI.unescape(uri)))
+ def self.from_lock(options)
+ new(options.merge("path" => options.delete("remote")))
end
def to_lock
- out = "path: #{URI.escape(relative_path.to_s)}"
- out << %{ glob:"#{@glob}"} unless @glob == DEFAULT_GLOB
- out
+ out = "PATH\n"
+ out << " remote: #{relative_path}\n"
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " specs:\n"
end
def to_s
@@ -355,12 +362,16 @@ def initialize(options)
@ref = options["ref"] || options["branch"] || options["tag"] || 'master'
end
- def self.from_lock(uri, options)
- new(options.merge("uri" => URI.unescape(uri)))
+ def self.from_lock(options)
+ new(options.merge("uri" => options.delete("remote")))
end
def to_lock
- %{git: #{URI.escape(@uri.to_s)} ref:"#{shortref_for(revision)}"}
+ out = "GIT\n"
+ out << " remote: #{@uri}\n"
+ out << " ref: #{shortref_for(revision)}\n"
+ out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
+ out << " specs:\n"
end
def to_s
@@ -1,6 +1,10 @@
require "spec_helper"
describe "the lockfile format" do
+ before :each do
+ pending
+ end
+
it "generates a simple lockfile for a single source, gem" do
flex_install_gemfile <<-G
source "file://#{gem_repo1}"
@@ -54,7 +58,7 @@ def be_with_diff(expected)
def lockfile_should_be(expected)
lock = File.read(bundled_app("Gemfile.lock"))
- lock.should be_with_diff(expected)
+ expected.should be_with_diff(lock)
end
it "generates a simple lockfile for a single source, gem with a version requirement" do
@@ -4,9 +4,11 @@
before :each do
system_gems "rack-1.0.0"
# clear memoized method results
+ # TODO: Don't reset internal ivars
Bundler.instance_eval do
@load = nil
@runtime = nil
+ @definition = nil
end
end

0 comments on commit 2b9094e

Please sign in to comment.