Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
First pass at bbl lock
Browse files Browse the repository at this point in the history
  • Loading branch information
Carlhuda committed Jan 19, 2010
1 parent 13f4a88 commit 88741e6
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 25 deletions.
11 changes: 9 additions & 2 deletions lib/bubble.rb
@@ -1,4 +1,5 @@
require 'pathname'
require 'yaml'
require 'bubble/rubygems'

module Bubble
Expand All @@ -18,17 +19,23 @@ module Bubble
class GemfileNotFound < StandardError; end
class GemNotFound < StandardError; end
class VersionConflict < StandardError; end
class GemfileError < StandardError; end

def self.setup(gemfile = nil)
load(gemfile).setup
end

def self.load(gemfile = nil)
Environment.new(definition(gemfile))
Environment.new definition(gemfile)
end

def self.definition(gemfile = nil)
Definition.from_gemfile(gemfile)
lockfile = Pathname.new(gemfile || Definition.default_gemfile).dirname.join('omg.yml')
if lockfile.exist?
Definition.from_lock(lockfile)
else
Definition.from_gemfile(gemfile)
end
end

def self.home
Expand Down
9 changes: 9 additions & 0 deletions lib/bubble/cli.rb
Expand Up @@ -8,6 +8,10 @@

module Bubble
class CLI < Thor
def self.banner(task)
task.formatted_usage(self, false)
end

desc "init", "Generates a Gemfile into the current working directory"
def init
if File.exist?("Gemfile")
Expand Down Expand Up @@ -43,6 +47,11 @@ def install
Installer.install(Bubble.definition)
end

desc "lock", "Locks a resolve"
def lock
Bubble.load.lock
end

private

def with_rescue
Expand Down
74 changes: 69 additions & 5 deletions lib/bubble/definition.rb
Expand Up @@ -7,9 +7,7 @@ def self.from_gemfile(gemfile)
raise GemfileNotFound, "`#{gemfile}` not found"
end

definition = new
Dsl.evaluate(gemfile, definition)
definition
Dsl.evaluate(gemfile)
end

def self.default_gemfile
Expand All @@ -24,10 +22,76 @@ def self.default_gemfile
raise GemfileNotFound, "The default Gemfile was not found"
end

def self.from_lock(lockfile)
gemfile_definition = from_gemfile(nil)

details = YAML.load_file(lockfile)
sources = details["sources"].map do |args|
name, options = args.to_a.flatten
Bubble::Source.const_get(name).new(options)
end

dependencies = details["dependencies"].map do |args|
Gem::Dependency.new(*args.to_a.flatten)
end

specs = details["specs"].map do |args|
Gem::Dependency.new(*args.to_a.flatten)
end

locked_definition = new(dependencies, sources, specs)

raise GemfileError unless gemfile_definition.matches?(locked_definition) &&
locked_definition.matches?(gemfile_definition)

locked_definition
end

attr_reader :dependencies, :sources

def initialize
@dependencies, @sources = [], Gem.sources.map { |s| Source::Rubygems.new(:uri => s) }
def initialize(dependencies, sources, resolved_dependencies = nil)
@dependencies = dependencies
@sources = sources

if resolved_dependencies
@specs = resolved_dependencies.map do |dep|
index.search(dep).first
end
end
end

def matches?(other)
dependencies.all? do |dep|
dep =~ other.specs.find {|spec| spec.name == dep.name }
end
end

def specs
@specs ||= Resolver.resolve(dependencies, index)
end

def index
@index ||= begin
index = Index.new
sources.reverse_each do |source|
index.merge! source.local_specs
end
index
end
end

def to_yaml(options = {})
details.to_yaml(options)
end

private

def details
{}.tap do |det|
det["sources"] = sources.map { |s| { s.class.name.split("::").last => s.options} }
det["specs"] = specs.map { |s| {s.name => s.version.to_s} }
det["dependencies"] = dependencies.map { |d| {d.name => d.version_requirements.to_s} }
end
end
end
end
19 changes: 12 additions & 7 deletions lib/bubble/dsl.rb
Expand Up @@ -2,14 +2,15 @@ module Bubble
class DslError < StandardError; end

class Dsl
def self.evaluate(gemfile, definition)
builder = new(definition)
def self.evaluate(gemfile)
builder = new
builder.instance_eval(File.read(gemfile.to_s), gemfile.to_s, 1)
definition
builder.to_definition
end

def initialize(definition)
@definition = definition
def initialize
@sources = Gem.sources.map { |s| Source::Rubygems.new(:uri => s) }
@dependencies = []
@git = nil
@git_sources = {}
end
Expand All @@ -18,7 +19,7 @@ def gem(name, *args)
options = Hash === args.last ? args.pop : {}
version = args.last || ">= 0"

@definition.dependencies << Dependency.new(name, version, options)
@dependencies << Dependency.new(name, version, options)
end

def source(source)
Expand All @@ -28,7 +29,7 @@ def source(source)
else source
end

@definition.sources << source
@sources << source
end

def path(path, options = {})
Expand All @@ -39,5 +40,9 @@ def git(uri, options = {})
source Source::Git.new(options.merge(:uri => uri))
end

def to_definition
Definition.new(@dependencies, @sources)
end

end
end
21 changes: 13 additions & 8 deletions lib/bubble/environment.rb
@@ -1,5 +1,9 @@
module Bubble
class Environment
def self.from_gemfile(gemfile)
new Definition.from_gemfile(gemfile)
end

def initialize(definition)
@definition = definition
end
Expand All @@ -16,18 +20,19 @@ def dependencies
@definition.dependencies
end

def lock
yml = @definition.to_yaml
File.open("#{Definition.default_gemfile.dirname}/omg.yml", 'w') do |f|
f.puts yml
end
end

def specs
@specs ||= Resolver.resolve(dependencies, index)
@definition.specs
end

def index
@index ||= begin
index = Index.new
@definition.sources.reverse_each do |source|
index.merge! source.local_specs
end
index
end
@definition.index
end

end
Expand Down
11 changes: 9 additions & 2 deletions lib/bubble/source.rb
Expand Up @@ -4,9 +4,10 @@
module Bubble
module Source
class Rubygems
attr_reader :uri
attr_reader :uri, :options

def initialize(options = {})
@options = options
@uri = options[:uri]
@uri = URI.parse(@uri) unless @uri.is_a?(URI)
raise ArgumentError, "The source must be an absolute URI" unless @uri.absolute?
Expand Down Expand Up @@ -52,9 +53,10 @@ def prerelease_specs
end

class Path
attr_reader :path
attr_reader :path, :options

def initialize(options)
@options = options
@glob = options[:glob] || "{,*/}*.gemspec"
@path = options[:path]
end
Expand Down Expand Up @@ -86,11 +88,16 @@ class Git < Path
attr_reader :uri, :ref

def initialize(options)
@options = options
@glob = options[:glob] || "{,*/}*.gemspec"
@uri = options[:uri]
@ref = options[:ref] || options[:branch] || 'master'
end

def to_yaml(options = {})
{ :uri => @uri.to_s, :ref => @ref, :glob => @glob }.to_yaml
end

def path
Bubble.install_path.join("#{base_name}-#{uri_hash}-#{ref}")
end
Expand Down
23 changes: 23 additions & 0 deletions spec/lock/gems_spec.rb
@@ -0,0 +1,23 @@
require File.expand_path('../../spec_helper', __FILE__)

describe "bbl lock with gems" do
before :each do
in_app_root
end

it "locks the gemfile resolve to the versions available at the time" do
gemfile <<-G
gem "rack"
G

system_gems "rack-0.9.1" do
bbl :lock
end

system_gems "rack-1.0.0", "rack-0.9.1" do
should_be_available "rack 0.9.1"
end
end

it "includes the ruby version as a dependency of the lock"
end
84 changes: 84 additions & 0 deletions spec/runtime/load_spec.rb
Expand Up @@ -37,4 +37,88 @@
Bubble.load("omg.rb")
}.should raise_error(Bubble::GemfileNotFound, /omg\.rb/)
end

describe "when locked" do
before :each do
system_gems "rack-1.0.0", "activesupport-2.3.2", "activerecord-2.3.2", "activerecord-2.3.1"
end

it "raises an exception if the Gemfile adds a dependency" do
gemfile <<-G
gem "rack"
G

bbl :lock

gemfile <<-G
gem "rack"
gem "activerecord"
G

lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
end

it "raises an exception if the Gemfile removes a dependency" do
gemfile <<-G
gem "rack"
gem "activerecord"
G

bbl :lock

gemfile <<-G
gem "rack"
G

lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
end

it "raises an exception if the Gemfile changes a dependency in an incompatible way" do
gemfile <<-G
gem "rack"
gem "activerecord"
G

bbl :lock

gemfile <<-G
gem "rack"
gem "activerecord", "2.3.1"
G

lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
end

it "raises an exception if the Gemfile replaces a root with a child dep of the root" do
gemfile <<-G
gem "rack"
gem "activerecord"
G

bbl :lock

gemfile <<-G
gem "rack"
gem "activesupport"
G

lambda { Bubble.load }.should raise_error(Bubble::GemfileError)
end

it "works if the Gemfile changes in a compatible way" do
gemfile <<-G
gem "rack"
gem "activerecord", "2.3.2"
G

bbl :lock

gemfile <<-G
gem "rack"
gem "activerecord", ">= 2.0.0"
G

lambda { Bubble.load }.should_not raise_error(Bubble::GemfileError)
end
end
end
2 changes: 1 addition & 1 deletion spec/support/builders.rb
Expand Up @@ -23,7 +23,7 @@ def build_repo1
build_gem "actionpack", "2.3.2" do |s|
s.add_dependency "activesupport", "2.3.2"
end
build_gem "activerecord", "2.3.2" do |s|
build_gem "activerecord", ["2.3.1", "2.3.2"] do |s|
s.add_dependency "activesupport", "2.3.2"
end
build_gem "actionmailer", "2.3.2" do |s|
Expand Down

0 comments on commit 88741e6

Please sign in to comment.