commandable 0.2.3 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|