commandable 0.2.3 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.rbenv-gemsets +2 -0
- data/{README.markdown → README.md} +64 -64
- data/Rakefile +2 -6
- data/bin/commandable +1 -0
- data/commandable.gemspec +1 -3
- data/lib/commandable.rb +15 -2
- data/lib/commandable/app_controller.rb +21 -39
- data/lib/commandable/argument_parser.rb +45 -0
- data/lib/commandable/coloring.rb +100 -0
- data/lib/commandable/command_parser.rb +139 -0
- data/lib/commandable/controller.rb +24 -0
- data/lib/commandable/exceptions.rb +1 -1
- data/lib/commandable/execution_controller.rb +122 -0
- data/lib/commandable/help_text.rb +73 -0
- data/lib/commandable/version.rb +4 -3
- data/spec/commandable/app_controller_spec.rb +2 -30
- data/spec/commandable/coloring_spec.rb +96 -0
- data/spec/commandable/command_line_execution_spec.rb +7 -1
- data/spec/commandable/execution_controller_spec.rb +17 -0
- data/spec/commandable/help_generator_spec.rb +5 -66
- data/spec/source_code_examples/parameter_class.rb +4 -0
- data/spec/source_code_for_errors/private_methods_bad.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- metadata +20 -38
- data/features/ansicolor.feature +0 -11
- data/features/copy_examples.feature +0 -13
- data/features/download_widget.feature +0 -18
- data/features/setup/env.rb +0 -5
- data/features/step-definitions/step-definitions.rb +0 -15
- data/lib/commandable/commandable.rb +0 -447
- data/lib/monkey_patch/file_utils.rb +0 -11
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Commandable
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
# An array of methods that can be executed from the command line
|
8
|
+
def commands
|
9
|
+
@@commands.dup
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash of instances created when calling instance methods
|
13
|
+
# It's keyed using the class name: {"ClassName"=>#<ClassName:0x00000100b1f188>}
|
14
|
+
def class_cache
|
15
|
+
@@class_cache
|
16
|
+
end
|
17
|
+
|
18
|
+
# Access the command array using the method name (symbol or string)
|
19
|
+
def [](index)
|
20
|
+
raise AccessorError unless index.is_a? String or index.is_a? Symbol
|
21
|
+
@@commands[index.to_sym]
|
22
|
+
end
|
23
|
+
|
24
|
+
# Clears all methods from the list of available commands
|
25
|
+
# This is mostly useful for testing.
|
26
|
+
def clear_commands
|
27
|
+
@@command_options = nil
|
28
|
+
@@commands = HELP_COMMAND.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
# Convenience method to iterate over the array of commands using the Commandable module
|
32
|
+
def each(&block)
|
33
|
+
@@commands.each do |key, value|
|
34
|
+
yield key => value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# This is where the magic happens!
|
43
|
+
# It lets you add a method to the list of command line methods
|
44
|
+
def command(*cmd_parameters)
|
45
|
+
|
46
|
+
@@attribute = nil
|
47
|
+
@@method_file = nil
|
48
|
+
@@method_line = nil
|
49
|
+
@@command_options = {}
|
50
|
+
|
51
|
+
# Include Commandable in singleton classes so class level methods work
|
52
|
+
include Commandable unless self.include? Commandable
|
53
|
+
|
54
|
+
# parse command parameters
|
55
|
+
while (param = cmd_parameters.shift)
|
56
|
+
case param
|
57
|
+
when Symbol
|
58
|
+
if param == :xor
|
59
|
+
@@command_options.merge!(param=>:xor)
|
60
|
+
else
|
61
|
+
@@command_options.merge!(param=>true)
|
62
|
+
end
|
63
|
+
when Hash
|
64
|
+
@@command_options.merge!(param)
|
65
|
+
when String
|
66
|
+
@@command_options.merge!(:description=>param)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
@@command_options[:priority] ||= 0
|
70
|
+
|
71
|
+
# only one default allowed
|
72
|
+
raise ConfigurationError, "Only one default method is allowed." if @@default_method and @@command_options[:default]
|
73
|
+
|
74
|
+
set_trace_func proc { |event, file, line, id, binding, classname|
|
75
|
+
|
76
|
+
@@attribute = id if [:attr_accessor, :attr_writer].include?(id)
|
77
|
+
|
78
|
+
# Traps the line where the method is defined so we can look up
|
79
|
+
# the method source code later if there are optional parameters
|
80
|
+
if event == "line" and !@@method_file
|
81
|
+
@@method_file = file
|
82
|
+
@@method_line = line
|
83
|
+
end
|
84
|
+
|
85
|
+
# Raise an error if there is no method following a command definition
|
86
|
+
if event == "end"
|
87
|
+
set_trace_func(nil)
|
88
|
+
raise SyntaxError, "A command was specified but no method follows"
|
89
|
+
end
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
# Add a method to the list of available command line methods
|
94
|
+
def add_command(meth)
|
95
|
+
@@commands.delete(:help)
|
96
|
+
|
97
|
+
if @@attribute
|
98
|
+
argument_list = "value"
|
99
|
+
meth = meth.to_s.delete("=").to_sym if @@attribute == :attr_writer
|
100
|
+
else
|
101
|
+
argument_list = parse_arguments(@@command_options[:parameters])
|
102
|
+
end
|
103
|
+
@@command_options.merge!(:argument_list=>argument_list,:class => self.name)
|
104
|
+
|
105
|
+
@@commands.merge!(meth => @@command_options)
|
106
|
+
@@default_method = {meth => @@command_options} if @@command_options[:default]
|
107
|
+
|
108
|
+
@@commands.sort.each {|com| @@commands.merge!(com[0]=>@@commands.delete(com[0]))}
|
109
|
+
|
110
|
+
@@commands.merge!(HELP_COMMAND.dup) # makes sure the help command is always last
|
111
|
+
@@command_options = nil
|
112
|
+
@@attribute = nil
|
113
|
+
end
|
114
|
+
|
115
|
+
# Trap method creation after a command call
|
116
|
+
def method_added(meth)
|
117
|
+
set_trace_func(nil)
|
118
|
+
return super(meth) if meth == :initialize || @@command_options == nil
|
119
|
+
|
120
|
+
if @@attribute
|
121
|
+
#synthesize parameter
|
122
|
+
@@command_options.merge!(:parameters=>[[:writer, :value]],:class_method=>false)
|
123
|
+
else
|
124
|
+
# create parameter
|
125
|
+
@@command_options.merge!(:parameters=>self.instance_method(meth).parameters,:class_method=>false)
|
126
|
+
end
|
127
|
+
|
128
|
+
add_command(meth)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Trap class methods too
|
132
|
+
def singleton_method_added(meth)
|
133
|
+
set_trace_func(nil)
|
134
|
+
return super(meth) if meth == :initialize || @@command_options == nil
|
135
|
+
@@command_options.merge!(:parameters=>method(meth).parameters, :class_method=>true)
|
136
|
+
add_command(meth)
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Commandable
|
2
|
+
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# Resets the class to default values clearing any commands
|
6
|
+
# and setting the colors back to their default values.
|
7
|
+
def reset_all
|
8
|
+
clear_commands
|
9
|
+
reset_colors
|
10
|
+
reset_screen_clearing
|
11
|
+
|
12
|
+
@app_info = nil
|
13
|
+
@app_exe = nil
|
14
|
+
@verbose_parameters = true
|
15
|
+
@@default_method = nil
|
16
|
+
@@class_cache = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
# Inititializes Commandable's settings when it's first loaded
|
22
|
+
reset_all
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Extending your class with this module allows you to
|
4
|
+
# use the #command method above your method.
|
5
|
+
# This makes them executable from the command line.
|
6
|
+
module Commandable
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# A wrapper for the execution_queue that runs the queue and traps errors.
|
11
|
+
# If an error occurs inside this method it will print out a complete list
|
12
|
+
# of availavle methods with usage instructions and exit gracefully.
|
13
|
+
#
|
14
|
+
# If you do not want the output from your methods to be printed out automatically
|
15
|
+
# run the execute command with silent set to anything other than false or nil.
|
16
|
+
def execute(argv=ARGV.clone, silent=false)
|
17
|
+
begin
|
18
|
+
command_queue = execution_queue(argv)
|
19
|
+
command_queue.each do |com|
|
20
|
+
return_value = com[:proc].call
|
21
|
+
puts return_value if !silent || com[:method] == :help
|
22
|
+
end
|
23
|
+
rescue SystemExit => kernel_exit
|
24
|
+
Kernel.exit kernel_exit.status
|
25
|
+
rescue Exception => exception
|
26
|
+
if exception.respond_to?(:friendly_name)
|
27
|
+
set_colors
|
28
|
+
puts help("\n #{@c_error_word}Error:#{@c_reset} #{@c_error_name}#{exception.friendly_name}#{@c_reset}\n #{@c_error_description}#{exception.message}#{@c_reset}\n\n")
|
29
|
+
else
|
30
|
+
puts exception.inspect
|
31
|
+
puts exception.backtrace.collect{|line| " #{line}"}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns an array of executable procs based on the given array of commands and parameters
|
37
|
+
# Normally this would come from the command line parameters in the ARGV array.
|
38
|
+
def execution_queue(argv)
|
39
|
+
arguments = argv.dup
|
40
|
+
method_hash = {}
|
41
|
+
last_method = nil
|
42
|
+
|
43
|
+
if arguments.empty?
|
44
|
+
arguments << @@default_method.keys[0] if @@default_method and !@@default_method.values[0][:parameters].flatten.include?(:req)
|
45
|
+
end
|
46
|
+
arguments << "help" if arguments.empty?
|
47
|
+
|
48
|
+
# Parse the command line into methods and their parameters
|
49
|
+
|
50
|
+
arguments.each do |arg|
|
51
|
+
if Commandable[arg]
|
52
|
+
last_method = arg.to_sym
|
53
|
+
method_hash.merge!(last_method=>[])
|
54
|
+
else
|
55
|
+
unless last_method
|
56
|
+
default = find_by_subkey(:default)
|
57
|
+
|
58
|
+
# Raise an error if there is no default method and the first item isn't a method
|
59
|
+
raise UnknownCommandError, arguments.first if default.empty?
|
60
|
+
|
61
|
+
# Raise an error if there is a default method but it doesn't take any parameters
|
62
|
+
raise UnknownCommandError, (arguments.first) if default.values[0][:argument_list] == ""
|
63
|
+
|
64
|
+
last_method = default.keys.first
|
65
|
+
method_hash.merge!(last_method=>[])
|
66
|
+
end
|
67
|
+
method_hash[last_method] << arg
|
68
|
+
end
|
69
|
+
# Test for missing required switches
|
70
|
+
@@commands.select do |key, value|
|
71
|
+
if value[:required] and method_hash[key].nil?
|
72
|
+
# If the required switch is also a default have the error be a missing parameter instead of a missing command
|
73
|
+
if value[:default]
|
74
|
+
method_hash.merge!(key=>[])
|
75
|
+
else
|
76
|
+
raise MissingRequiredCommandError, key
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Build an array of procs to be called for each method and its given parameters
|
83
|
+
proc_array = []
|
84
|
+
method_hash.each do |meth, params|
|
85
|
+
command = @@commands[meth]
|
86
|
+
|
87
|
+
if command[:parameters] && !command[:parameters].empty?
|
88
|
+
|
89
|
+
#Change the method name for attr_writers
|
90
|
+
meth = "#{meth}=" if command[:parameters][0][0] == :writer
|
91
|
+
|
92
|
+
# Get a list of required parameters and make sure all of them were provided
|
93
|
+
required = command[:parameters].select{|param| [:req, :writer].include?(param[0])}
|
94
|
+
required.shift(params.count)
|
95
|
+
raise MissingRequiredParameterError, {:method=>meth, :parameters=>required.collect!{|meth| meth[1]}.to_s[1...-1].gsub(":",""), :default=>command[:default]} unless required.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
# Test for duplicate XORs
|
99
|
+
proc_array.select{|x| x[:xor] and x[:xor]==command[:xor] }.each {|bad| raise ExclusiveMethodClashError, "#{meth}, #{bad[:method]}"}
|
100
|
+
|
101
|
+
klass = Object
|
102
|
+
command[:class].split(/::/).each { |name| klass = klass.const_get(name) }
|
103
|
+
## Look for class in class cache
|
104
|
+
unless command[:class_method]
|
105
|
+
klass = (@@class_cache[klass.name] ||= klass.new)
|
106
|
+
end
|
107
|
+
proc_array << {:method=>meth, :xor=>command[:xor], :parameters=>params, :priority=>command[:priority], :proc=>lambda{klass.send(meth, *params)}}
|
108
|
+
end
|
109
|
+
proc_array.sort{|a,b| a[:priority] <=> b[:priority]}.reverse
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Look through commands for a specific subkey
|
116
|
+
def find_by_subkey(key, value=true)
|
117
|
+
@@commands.select {|meth, meth_value| meth_value[key]==value}
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Commandable
|
2
|
+
|
3
|
+
# Default command that always gets added to end of the command list
|
4
|
+
HELP_COMMAND = {:help => {:description => "you're looking at it now", :argument_list => "", :class=>"Commandable", :class_method=>true}}
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
# Describes your application, printed at the top of help/usage messages
|
9
|
+
attr_accessor :app_info
|
10
|
+
|
11
|
+
# Used when building the usage line, e.g. Usage: app_exe [command] [parameters]
|
12
|
+
attr_accessor :app_exe
|
13
|
+
|
14
|
+
# If optional parameters show default values, true by default
|
15
|
+
attr_accessor :verbose_parameters
|
16
|
+
|
17
|
+
# Generates an array of the available commands with a
|
18
|
+
# list of their parameters and the method's description.
|
19
|
+
# This includes the applicaiton info and app name if given.
|
20
|
+
# It's meant to be printed to the command line.
|
21
|
+
def help(additional_info=nil)
|
22
|
+
|
23
|
+
set_colors
|
24
|
+
set_screen_clear
|
25
|
+
|
26
|
+
cmd_length = "Command".length
|
27
|
+
parm_length = "Parameters".length
|
28
|
+
max_command = [(@@commands.keys.max_by{|key| key.to_s.length }).to_s.length, cmd_length].max
|
29
|
+
max_parameter = @@commands[@@commands.keys.max_by{|key| @@commands[key][:argument_list].length }][:argument_list].length
|
30
|
+
max_parameter = [parm_length, max_parameter].max if max_parameter > 0
|
31
|
+
|
32
|
+
usage_text = " #{@c_usage}Usage:#{@c_reset} "
|
33
|
+
|
34
|
+
if Commandable.app_exe
|
35
|
+
cmd_text = "<#{@c_command + @c_bold}command#{@c_reset}>"
|
36
|
+
parm_text = " [#{@c_parameter + @c_bold}parameters#{@c_reset}]" if max_parameter > 0
|
37
|
+
usage_text += "#{@c_app_exe + app_exe + @c_reset} #{cmd_text}#{parm_text} [#{cmd_text}#{parm_text}...]"
|
38
|
+
end
|
39
|
+
|
40
|
+
array = [usage_text, ""]
|
41
|
+
|
42
|
+
array.unshift additional_info if additional_info
|
43
|
+
array.unshift (@c_app_info + Commandable.app_info + @c_reset) if Commandable.app_info
|
44
|
+
array.unshift @s_clear_screen_code
|
45
|
+
|
46
|
+
header_text = " #{" "*(max_command-cmd_length)}#{@c_command + @c_bold}Command#{@c_reset} "
|
47
|
+
header_text += "#{@c_parameter + @c_bold}Parameters #{@c_reset}#{" "*(max_parameter-parm_length)}" if max_parameter > 0
|
48
|
+
header_text += "#{@c_description + @c_bold}Description#{@c_reset}"
|
49
|
+
|
50
|
+
array << header_text
|
51
|
+
|
52
|
+
array += @@commands.keys.collect do |key|
|
53
|
+
is_default = (@@default_method and key == @@default_method.keys[0])
|
54
|
+
default_color = is_default ? @c_bold : ""
|
55
|
+
|
56
|
+
help_line = " #{" "*(max_command-key.length)}#{@c_command + default_color + key.to_s + @c_reset}"+
|
57
|
+
" #{default_color + @c_parameter + @@commands[key][:argument_list] + @c_reset}"
|
58
|
+
help_line += "#{" "*(max_parameter-@@commands[key][:argument_list].length)} " if max_parameter > 0
|
59
|
+
|
60
|
+
# indent new lines
|
61
|
+
description = @@commands[key][:description].gsub("\n", "\n" + (" "*(max_command + max_parameter + (max_parameter > 0 ? 1 : 0) + 4)))
|
62
|
+
|
63
|
+
help_line += ": #{default_color + @c_description}#{"<#{@@commands[key][:xor]}> " if @@commands[key][:xor]}" +
|
64
|
+
"#{description}" +
|
65
|
+
"#{" (default)" if is_default}#{@c_reset}"
|
66
|
+
end
|
67
|
+
array << nil
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
data/lib/commandable/version.rb
CHANGED
@@ -18,37 +18,9 @@ describe Commandable do
|
|
18
18
|
# or it won't be able to use the settings
|
19
19
|
load 'commandable/app_controller.rb'
|
20
20
|
}
|
21
|
-
|
22
|
-
context "when running the widget command" do
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
it "should inform them they need Git" do
|
27
|
-
Commandable::AppController.stub(:git_installed?){false}
|
28
|
-
execute_output_s(["widget"]).should match(/Git must be installed/)
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
32
|
-
context "when git is installed" do
|
33
|
-
|
34
|
-
context "and it's able to install the files" do
|
35
|
-
it "should download Widget from github" do
|
36
|
-
Commandable::AppController.stub(:download_widget){0}
|
37
|
-
execute_output_s(["widget"]).should_not include("Unable to download")
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
context "but it's not able to install the files" do
|
42
|
-
it "should download Widget from github" do
|
43
|
-
Commandable::AppController.stub(:download_widget){1}
|
44
|
-
execute_output_s(["widget"]).should include("Unable to download")
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
|
49
|
-
end
|
22
|
+
it "should have a test for examples"
|
50
23
|
|
51
|
-
|
52
|
-
end
|
24
|
+
it "should have a test for the readme file"
|
53
25
|
|
54
26
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Commandable.reset_all
|
7
|
+
load 'private_methods_bad.rb'
|
8
|
+
Commandable.app_exe = "mycoolapp"
|
9
|
+
Commandable.app_info =
|
10
|
+
""" My Cool App - It does stuff and things!
|
11
|
+
Copyright (c) 2011 Acme Inc."""
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:c) {Term::ANSIColor}
|
15
|
+
|
16
|
+
context "when screen clearing" do
|
17
|
+
|
18
|
+
it "should clear the screen if screen clearing is on" do
|
19
|
+
Commandable.clear_screen = true
|
20
|
+
clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
|
21
|
+
Commandable.help.join.should match(clear_screen_code)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should not clear the screen if screen clearing is off" do
|
25
|
+
Commandable.clear_screen = true
|
26
|
+
clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
|
27
|
+
Commandable.clear_screen = false
|
28
|
+
Commandable.help.join.should_not match(clear_screen_code)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should change how the screen is cleared" do
|
32
|
+
Commandable.clear_screen = true
|
33
|
+
clear_code = "FlabbityJibbity"
|
34
|
+
Commandable.clear_screen_code = clear_code
|
35
|
+
Commandable.help.join.should match(clear_code)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not be disabled when color output is disabled" do
|
39
|
+
Commandable.clear_screen = true
|
40
|
+
clear_screen_code = Regexp.escape("#{Commandable.clear_screen_code}")
|
41
|
+
Commandable.color_output = false
|
42
|
+
Commandable.help.join.should match(clear_screen_code)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when a setting color is changed" do
|
48
|
+
|
49
|
+
before(:each) { Commandable.color_output = true }
|
50
|
+
|
51
|
+
it "should include colors if colored output is enabled" do
|
52
|
+
Commandable.color_output = true
|
53
|
+
Commandable.help.join.should match(Regexp.escape(Commandable.color_app_info))
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should not include colors if colored output is disabled" do
|
57
|
+
Commandable.color_output = false
|
58
|
+
Commandable.help.join.should_not match(Regexp.escape(Commandable.color_app_info))
|
59
|
+
end
|
60
|
+
|
61
|
+
# This seems ripe for meta-zation
|
62
|
+
context "and app_info is changed" do
|
63
|
+
specify {lambda {Commandable.color_app_info = c.black}.should change{Commandable.help}}
|
64
|
+
end
|
65
|
+
|
66
|
+
context "and app_exe is changed" do
|
67
|
+
specify {lambda {Commandable.color_app_exe = c.black}.should change{Commandable.help}}
|
68
|
+
end
|
69
|
+
|
70
|
+
context "and color_command is changed" do
|
71
|
+
specify {lambda {Commandable.color_command = c.black}.should change{Commandable.help}}
|
72
|
+
end
|
73
|
+
|
74
|
+
context "and color_description is changed" do
|
75
|
+
specify {lambda {Commandable.color_description = c.black}.should change{Commandable.help}}
|
76
|
+
end
|
77
|
+
|
78
|
+
context "and color_parameter is changed" do
|
79
|
+
specify {lambda {Commandable.color_parameter = c.black}.should change{Commandable.help}}
|
80
|
+
end
|
81
|
+
|
82
|
+
context "and color_usage is changed" do
|
83
|
+
specify {lambda {Commandable.color_usage = c.black}.should change{Commandable.help}}
|
84
|
+
end
|
85
|
+
|
86
|
+
context "and there is an error" do
|
87
|
+
|
88
|
+
specify { lambda {Commandable.color_error_word = c.magenta}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
89
|
+
specify { lambda {Commandable.color_error_name = c.intense_red}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
90
|
+
specify { lambda {Commandable.color_error_description = c.black + c.bold}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|