From a32e154e6eea73cd6c0e6dd1bbe6eefb4b3ffbaa Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Mon, 20 Jan 2014 17:18:36 -0800 Subject: [PATCH] core: use cleaner UI abstractions --- lib/vagrant/machine.rb | 2 +- lib/vagrant/ui.rb | 86 ++++++++++++++++++---------- test/unit/vagrant/ui_test.rb | 107 +++++++++++++---------------------- 3 files changed, 94 insertions(+), 101 deletions(-) diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb index 2ed27db6b..1f6de52d0 100644 --- a/lib/vagrant/machine.rb +++ b/lib/vagrant/machine.rb @@ -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 diff --git a/lib/vagrant/ui.rb b/lib/vagrant/ui.rb index 7dfd171cf..f594533e4 100644 --- a/lib/vagrant/ui.rb +++ b/lib/vagrant/ui.rb @@ -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 diff --git a/test/unit/vagrant/ui_test.rb b/test/unit/vagrant/ui_test.rb index ba5af8a4f..31ba810ce 100644 --- a/test/unit/vagrant/ui_test.rb +++ b/test/unit/vagrant/ui_test.rb @@ -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