core: use cleaner UI abstractions

This commit is contained in:
Mitchell Hashimoto 2014-01-20 17:18:36 -08:00
parent fc86a10796
commit a32e154e6e
3 changed files with 94 additions and 101 deletions

View File

@ -96,7 +96,7 @@ module Vagrant
@provider_config = provider_config
@provider_name = provider_name
@provider_options = provider_options
@ui = @env.ui.scope(@name)
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
# Read the ID, which is usually in local storage
@id = nil

View File

@ -1,3 +1,4 @@
require "delegate"
require "thread"
require "log4r"
@ -43,15 +44,6 @@ module Vagrant
def machine(type, *data)
@logger.info("Machine: #{type} #{data.inspect}")
end
# Returns a new UI class that is scoped to the given resource name.
# Subclasses can then use this scope name to do whatever they please.
#
# @param [String] scope_name
# @return [Interface]
def scope(scope_name)
self
end
end
# This is a UI implementation that does nothing.
@ -99,10 +91,6 @@ module Vagrant
safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}")
end
end
def scope(scope_name)
BasicScope.new(self, scope_name)
end
end
# This is a UI implementation that outputs the text as is. It
@ -110,9 +98,6 @@ module Vagrant
class Basic < Interface
include Util::SafePuts
# The prefix for `output` messages.
OUTPUT_PREFIX = "==> "
def initialize
super
@ -194,17 +179,64 @@ module Vagrant
# do this.
Thread.new do
@lock.synchronize do
safe_puts(format_message(type, message, opts),
safe_puts(format_message(type, message, **opts),
:io => channel, :printer => printer)
end
end.join
end
def scope(scope_name)
BasicScope.new(self, scope_name)
def format_message(type, message, **opts)
message
end
end
# Prefixed wraps an existing UI and adds a prefix to it.
class Prefixed < Interface
# The prefix for `output` messages.
OUTPUT_PREFIX = "==> "
def initialize(ui, prefix)
super()
@prefix = prefix
@ui = ui
end
# Use some light meta-programming to create the various methods to
# output text to the UI. These all delegate the real functionality
# to `say`.
[:ask, :detail, :info, :warn, :error, :output, :success].each do |method|
class_eval <<-CODE
def #{method}(message, *args, **opts)
super(message)
opts[:bold] = #{method.inspect} != :detail if !opts.has_key?(:bold)
@ui.#{method}(format_message(#{method.inspect}, message, **opts), *args, **opts)
end
CODE
end
[:clear_line, :report_progress].each do |method|
# By default do nothing, these aren't formatted
define_method(method) { |*args| @ui.send(method, *args) }
end
# For machine-readable output, set the prefix in the
# options hash and continue it on.
def machine(type, *data)
opts = {}
opts = data.pop if data.last.is_a?(Hash)
opts[:scope] = @prefix
data << opts
@ui.machine(type, *data)
end
# Return the parent's opts.
#
# @return [Hash]
def opts
@ui.opts
end
# This is called by `say` to format the message for output.
def format_message(type, message, **opts)
prefix = ""
if !opts.has_key?(:prefix) || opts[:prefix]
@ -216,7 +248,9 @@ module Vagrant
return message if prefix.empty?
# Otherwise, make sure to prefix every line properly
message.split("\n").map { |line| "#{prefix}#{line}" }.join("\n")
message.split("\n").map do |line|
"#{prefix}#{@prefix}: #{line}"
end.join("\n")
end
end
@ -231,13 +265,6 @@ module Vagrant
@scope = scope
end
# Return the parent's opts.
#
# @return [Hash]
def opts
@ui.opts
end
[:ask, :detail, :warn, :error, :info, :output, :success].each do |method|
define_method(method) do |message, opts=nil|
opts ||= {}
@ -288,9 +315,6 @@ module Vagrant
opts = @opts.merge(opts)
# Default the bold option if its not given
opts[:bold] = type == :output if !opts.has_key?(:bold)
# Special case some colors for certain message types
opts[:color] = :red if type == :error
opts[:color] = :yellow if type == :warn

View File

@ -58,58 +58,11 @@ describe Vagrant::UI::Basic do
subject.error("foo")
end
end
describe "#detail" do
it "prefixes with spaces" do
subject.should_receive(:safe_puts).with(" foo", anything)
subject.detail("foo")
end
it "doesn't prefix if told not to" do
subject.should_receive(:safe_puts).with("foo", anything)
subject.detail("foo", prefix: false)
end
it "prefixes every line" do
subject.should_receive(:safe_puts).with(" foo\n bar", anything)
subject.detail("foo\nbar")
end
end
describe "#output" do
it "prefixes with ==>" do
subject.should_receive(:safe_puts).with("==> foo", anything)
subject.output("foo")
end
it "doesn't prefix if told not to" do
subject.should_receive(:safe_puts).with("foo", anything)
subject.output("foo", prefix: false)
end
it "prefixes every line" do
subject.should_receive(:safe_puts).with("==> foo\n==> bar", anything)
subject.output("foo\nbar")
end
end
describe "#scope" do
it "creates a basic scope" do
scope = subject.scope("foo")
expect(scope.scope).to eql("foo")
expect(scope.ui).to be(subject)
end
end
end
describe Vagrant::UI::Colored do
include_context "unit"
before do
# We don't want any prefixes on anything...
subject.opts[:prefix] = false
end
describe "#detail" do
it "colors output nothing by default" do
subject.should_receive(:safe_puts).with("foo", anything)
@ -147,7 +100,7 @@ describe Vagrant::UI::Colored do
subject.opts[:color] = :red
subject.should_receive(:safe_puts).with do |message, *args|
expect(message).to start_with("\033[1;31m")
expect(message).to start_with("\033[0;31m")
expect(message).to end_with("\033[0m")
end
@ -158,22 +111,22 @@ describe Vagrant::UI::Colored do
subject.opts[:color] = :red
subject.should_receive(:safe_puts).with do |message, *args|
expect(message).to start_with("\033[1;32m")
expect(message).to start_with("\033[0;32m")
expect(message).to end_with("\033[0m")
end
subject.output("foo", color: :green)
end
it "doesn't bold the output if specified" do
it "bolds the output if specified" do
subject.opts[:color] = :red
subject.should_receive(:safe_puts).with do |message, *args|
expect(message).to start_with("\033[0;31m")
expect(message).to start_with("\033[1;31m")
expect(message).to end_with("\033[0m")
end
subject.output("foo", bold: false)
subject.output("foo", bold: true)
end
end
@ -189,20 +142,37 @@ describe Vagrant::UI::Colored do
end
end
describe Vagrant::UI::BasicScope do
let(:scope) { "foo" }
let(:ui) { double("ui") }
describe Vagrant::UI::Prefixed do
let(:prefix) { "foo" }
let(:ui) { Vagrant::UI::Basic.new }
subject { described_class.new(ui, scope) }
subject { described_class.new(ui, prefix) }
describe "#detail" do
it "prefixes with spaces and the message" do
ui.should_receive(:safe_puts).with(" #{prefix}: foo", anything)
subject.detail("foo")
end
it "prefixes every line" do
ui.should_receive(:detail).with(" #{prefix}: foo\n #{prefix}: bar", bold: false)
subject.detail("foo\nbar")
end
it "doesn't prefix if requestsed" do
ui.should_receive(:detail).with("foo", prefix: false, bold: false)
subject.detail("foo", prefix: false)
end
end
describe "#machine" do
it "sets the scope option" do
ui.should_receive(:machine).with(:foo, scope: scope)
ui.should_receive(:machine).with(:foo, scope: prefix)
subject.machine(:foo)
end
it "preserves existing options" do
ui.should_receive(:machine).with(:foo, :bar, foo: :bar, scope: scope)
ui.should_receive(:machine).with(:foo, :bar, foo: :bar, scope: prefix)
subject.machine(:foo, :bar, foo: :bar)
end
end
@ -215,24 +185,23 @@ describe Vagrant::UI::BasicScope do
end
describe "#output" do
it "prefixes with the scope" do
ui.should_receive(:output).with("#{scope}: foo", anything)
it "prefixes with an arrow and the message" do
ui.should_receive(:output).with("==> #{prefix}: foo", anything)
subject.output("foo")
end
it "does not prefix if told not to" do
ui.should_receive(:output).with("foo", anything)
subject.output("foo", prefix: false)
end
it "prefixes every line" do
ui.should_receive(:output).with(
"#{scope}: foo\n#{scope}: bar", anything)
ui.should_receive(:output).with("==> #{prefix}: foo\n==> #{prefix}: bar", anything)
subject.output("foo\nbar")
end
it "puts the scope into the options hash" do
ui.should_receive(:output).with(anything, scope: scope)
it "doesn't prefix if requestsed" do
ui.should_receive(:output).with("foo", prefix: false, bold: true)
subject.output("foo", prefix: false)
end
it "requests bolding" do
ui.should_receive(:output).with("==> #{prefix}: foo", bold: true)
subject.output("foo")
end
end