commandable 0.2.0.beta01
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/.gitignore +8 -0
- data/Gemfile +2 -0
- data/LICENCE +19 -0
- data/README.markdown +409 -0
- data/Rakefile +29 -0
- data/_testing/alias_trap.rb +14 -0
- data/autotest/discover.rb +2 -0
- data/bin/commandable +18 -0
- data/commandable.gemspec +24 -0
- data/lib/commandable.rb +4 -0
- data/lib/commandable/app_controller.rb +47 -0
- data/lib/commandable/commandable.rb +394 -0
- data/lib/commandable/exceptions.rb +61 -0
- data/lib/commandable/version.rb +4 -0
- data/lib/monkey_patch/file_utils.rb +11 -0
- data/spec/commandable/command_line_execution_spec.rb +154 -0
- data/spec/commandable/commandable_spec.rb +245 -0
- data/spec/commandable/help_generator_spec.rb +169 -0
- data/spec/commandable/helpers_spec.rb +17 -0
- data/spec/commandable/reset_spec.rb +26 -0
- data/spec/commandable/xor_groups_spec.rb +43 -0
- data/spec/source_code_examples/class_command_no_command.rb +27 -0
- data/spec/source_code_examples/class_methods.rb +20 -0
- data/spec/source_code_examples/class_methods_nested.rb +31 -0
- data/spec/source_code_examples/command_no_command.rb +27 -0
- data/spec/source_code_examples/deep_class.rb +14 -0
- data/spec/source_code_examples/default_method.rb +17 -0
- data/spec/source_code_examples/default_method_no_params.rb +17 -0
- data/spec/source_code_examples/multi_line_description.rb +17 -0
- data/spec/source_code_examples/multi_line_description_no_params.rb +17 -0
- data/spec/source_code_examples/no_description.rb +10 -0
- data/spec/source_code_examples/parameter_class.rb +27 -0
- data/spec/source_code_examples/parameter_free.rb +22 -0
- data/spec/source_code_examples/required_methods.rb +18 -0
- data/spec/source_code_examples/super_deep_class.rb +28 -0
- data/spec/source_code_examples/test_class.rb +13 -0
- data/spec/source_code_examples/xor_class.rb +37 -0
- data/spec/source_code_for_errors/class_bad.rb +7 -0
- data/spec/source_code_for_errors/default_method_bad.rb +17 -0
- data/spec/source_code_for_errors/private_methods_bad.rb +10 -0
- data/spec/spec_helper.rb +55 -0
- metadata +140 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
before(:each) { Commandable.reset_all }
|
6
|
+
|
7
|
+
# Mostly de-brittled these tests...
|
8
|
+
|
9
|
+
context "when setting verbose_parameters" do
|
10
|
+
|
11
|
+
it "prints short parameters when false" do
|
12
|
+
Commandable.verbose_parameters = false
|
13
|
+
load 'parameter_class.rb'
|
14
|
+
Commandable.help.to_s.should_not match("1492")
|
15
|
+
end
|
16
|
+
|
17
|
+
it "prints short parameters when false" do
|
18
|
+
Commandable.verbose_parameters = true
|
19
|
+
load 'parameter_class.rb'
|
20
|
+
Commandable.help.to_s.should match("1492")
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when generating the help command" do
|
26
|
+
|
27
|
+
it "has a help command when no commands have been added" do
|
28
|
+
Commandable.commands.first[0].should == :help
|
29
|
+
end
|
30
|
+
|
31
|
+
it "still has the help command after Commandable is cleared" do
|
32
|
+
load 'parameter_class.rb'
|
33
|
+
Commandable.clear_commands
|
34
|
+
Commandable.commands.first[0].should == :help
|
35
|
+
end
|
36
|
+
|
37
|
+
it "always has the help command as the last command (so it's pretty)" do
|
38
|
+
load 'parameter_class.rb'
|
39
|
+
Commandable.help.compact.last.should match(/help/)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when formating the help message" do
|
45
|
+
|
46
|
+
it "formats a basic help message with commands sorted alphabetically (help last)" do
|
47
|
+
load 'parameter_class.rb'
|
48
|
+
Commandable.help.to_s.should match(/Usage:(.*)Command(.*)bar(.*)baz(.*)foo(.*)qux(.*)help/)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "adds the application name to the help output if it's given" do
|
52
|
+
load 'parameter_class.rb'
|
53
|
+
Commandable.app_name = "mycoolapp"
|
54
|
+
Commandable.help.to_s.should match(/Usage: mycoolapp <command>/)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "adds appliction information if given" do
|
58
|
+
load 'parameter_class.rb'
|
59
|
+
Commandable.app_name = "mycoolapp"
|
60
|
+
app_info =
|
61
|
+
"""
|
62
|
+
My Cool App - It does stuff and things!
|
63
|
+
Copyright (c) 2011 Acme Inc.
|
64
|
+
"""
|
65
|
+
Commandable.app_info = app_info
|
66
|
+
Commandable.help.inspect.should match(/My Cool App - It does stuff and things(.*)Copyright \(c\) 2011 Acme Inc/)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "adds (default) to the end of the default command description when printing" do
|
70
|
+
load 'default_method.rb'
|
71
|
+
Commandable.help.to_s.should match(/\(default\)/)
|
72
|
+
end
|
73
|
+
|
74
|
+
context "and there are no parameters" do
|
75
|
+
|
76
|
+
before(:each){load 'parameter_free.rb' }
|
77
|
+
|
78
|
+
it "hides the Parameters header" do
|
79
|
+
execute_output_s([]).should_not match(/Command Parameters/)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "hides the doesn't show [parameters] in the usage instructions" do
|
83
|
+
Commandable.app_name = "fakeapp"
|
84
|
+
execute_output_s([]).should_not match(/\[parameters\]/)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
context "and there is a new line in a description" do
|
91
|
+
|
92
|
+
it "indents the new line to match the preceding line" do
|
93
|
+
load("multi_line_description.rb")
|
94
|
+
execute_output_s(["blah"]).should match(/ so you can have really long descriptions\n And another line/)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "indents the new line to match the preceding line" do
|
98
|
+
load("multi_line_description_no_params.rb")
|
99
|
+
execute_output_s(["blah"]).should match(/ so you can have really long descriptions\n And another line/)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when using color_output" do
|
107
|
+
|
108
|
+
before(:each) do
|
109
|
+
load 'private_methods_bad.rb'
|
110
|
+
Commandable.app_name = "mycoolapp"
|
111
|
+
Commandable.app_info =
|
112
|
+
""" My Cool App - It does stuff and things!
|
113
|
+
Copyright (c) 2011 Acme Inc."""
|
114
|
+
end
|
115
|
+
|
116
|
+
let(:c) {Term::ANSIColor}
|
117
|
+
|
118
|
+
it "changes the output if color is enabled" do
|
119
|
+
Commandable.color_output = false
|
120
|
+
lambda {Commandable.color_output = true}.should change{Commandable.help}
|
121
|
+
end
|
122
|
+
|
123
|
+
it "resets text to plain if colors are turned off" do
|
124
|
+
Commandable.color_output = true
|
125
|
+
lambda {Commandable.color_output = false}.should change{Commandable.help}
|
126
|
+
end
|
127
|
+
|
128
|
+
context "when a specific setting's color is changed" do
|
129
|
+
|
130
|
+
before(:each) { Commandable.color_output = true }
|
131
|
+
|
132
|
+
# This seems ripe for meta-zation
|
133
|
+
context "when app_info is changed" do
|
134
|
+
specify {lambda {Commandable.color_app_info = c.black}.should change{Commandable.help}}
|
135
|
+
end
|
136
|
+
|
137
|
+
context "when app_name is changed" do
|
138
|
+
specify {lambda {Commandable.color_app_name = c.black}.should change{Commandable.help}}
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when color_command is changed" do
|
142
|
+
specify {lambda {Commandable.color_command = c.black}.should change{Commandable.help}}
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when color_description is changed" do
|
146
|
+
specify {lambda {Commandable.color_description = c.black}.should change{Commandable.help}}
|
147
|
+
end
|
148
|
+
|
149
|
+
context "when color_parameter is changed" do
|
150
|
+
specify {lambda {Commandable.color_parameter = c.black}.should change{Commandable.help}}
|
151
|
+
end
|
152
|
+
|
153
|
+
context "when color_usage is changed" do
|
154
|
+
specify {lambda {Commandable.color_usage = c.black}.should change{Commandable.help}}
|
155
|
+
end
|
156
|
+
|
157
|
+
context "when there is an error" do
|
158
|
+
|
159
|
+
specify { lambda {Commandable.color_error_word = c.magenta}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
160
|
+
specify { lambda {Commandable.color_error_name = c.intense_red}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
161
|
+
specify { lambda {Commandable.color_error_description = c.black + c.bold}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
context "when parsing optional parameters" do
|
6
|
+
|
7
|
+
before(:each) {load 'private_methods_bad.rb'}
|
8
|
+
|
9
|
+
specify {PrivateMethods.send(:parse_optional, "def bar(x=14243)", "x").should == "14243"}
|
10
|
+
specify {PrivateMethods.send(:parse_optional,"def bar x = 144444", "x").should == "144444"}
|
11
|
+
specify {PrivateMethods.send(:parse_optional,"def bar x=12", "x").should == "12"}
|
12
|
+
specify {PrivateMethods.send(:parse_optional,'def bar (x="42", y)', "x").should == "\"42\""}
|
13
|
+
specify {PrivateMethods.send(:parse_optional,'def bar(y="kjljlj",x="I love Ruby")', "x").should == "\"I love Ruby\""}
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Commandable.reset_all
|
7
|
+
load 'parameter_class.rb'
|
8
|
+
Commandable.app_name = "mycoolapp"
|
9
|
+
Commandable.app_info =
|
10
|
+
""" My Cool App - It does stuff and things!
|
11
|
+
Copyright (c) 2011 Acme Inc."""
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when reseting all values" do
|
15
|
+
|
16
|
+
specify {lambda{Commandable.reset_all}.should change{Commandable.app_name}.from("mycoolapp").to(nil)}
|
17
|
+
specify {lambda{Commandable.reset_all}.should change{Commandable.app_info}.
|
18
|
+
from(%{ My Cool App - It does stuff and things!\n Copyright (c) 2011 Acme Inc.}).
|
19
|
+
to(nil)
|
20
|
+
}
|
21
|
+
specify {lambda{Commandable.reset_all}.should change{Commandable.commands.length}.from(5).to(1)}
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
Commandable.reset_all
|
7
|
+
Commandable.color_output = true
|
8
|
+
load 'xor_class.rb'
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when exclusive methods are specified" do
|
12
|
+
|
13
|
+
# this seems like I'm testing internal state instead of specifying behavior...
|
14
|
+
specify {Commandable.commands[:xor_method1][:xor].should == :xor}
|
15
|
+
specify {Commandable.commands[:normal_method][:xor].should be_nil}
|
16
|
+
specify {Commandable.commands[:xor_method3][:xor].should == :xor_group}
|
17
|
+
|
18
|
+
context "when more than one members of an exclusive group is used" do
|
19
|
+
|
20
|
+
specify{lambda{Commandable.execution_queue(["xor_method1", "xor_method2"])}.should raise_error(Commandable::ExclusiveMethodClashError)}
|
21
|
+
specify{lambda{Commandable.execution_queue(["xor_method3", "xor_method4"])}.should raise_error(Commandable::ExclusiveMethodClashError)}
|
22
|
+
specify{lambda{Commandable.execution_queue(["xor_method1", "xor_method3"])}.should_not raise_error}
|
23
|
+
specify{lambda{Commandable.execution_queue(["normal_method", "xor_method3"])}.should_not raise_error}
|
24
|
+
specify{lambda{Commandable.execution_queue(["normal_method", "normal_method2"])}.should_not raise_error}
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when printing help" do
|
29
|
+
|
30
|
+
it "puts the default xor group :xor in the description" do
|
31
|
+
execute_output_s(["help"]).should match(/xor/)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "puts the xor group :xor_group in the description" do
|
35
|
+
execute_output_s(["help"]).should match(/xor/)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class ClassCommandNoCommand
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command 'a method with a command'
|
8
|
+
def class_command_method1
|
9
|
+
"class_command_method1"
|
10
|
+
end
|
11
|
+
|
12
|
+
# this method should not be in the commands list
|
13
|
+
def class_no_command_method1(flappity)
|
14
|
+
"class_no_command_method1: #{flappity}"
|
15
|
+
end
|
16
|
+
|
17
|
+
command 'another method with a command'
|
18
|
+
def class_command_method2(some_parameter)
|
19
|
+
"class_command_method2: #{some_parameter}"
|
20
|
+
end
|
21
|
+
|
22
|
+
# this method shouldn't be in the list either
|
23
|
+
def class_no_command_method2(flippity)
|
24
|
+
"class_no_command_method2: #{flippity}"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class ClassMethods
|
5
|
+
|
6
|
+
extend Commandable
|
7
|
+
|
8
|
+
command 'does some stuff'
|
9
|
+
def self.class_method(string_arg1)
|
10
|
+
string_arg1
|
11
|
+
end
|
12
|
+
|
13
|
+
command "another one"
|
14
|
+
class << self
|
15
|
+
def class_method2(integer_arg1)
|
16
|
+
integer_arg1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class ClassMethodsNested
|
5
|
+
|
6
|
+
class << self
|
7
|
+
extend Commandable
|
8
|
+
|
9
|
+
command "class foo, look at you!"
|
10
|
+
def class_foo(int_arg1, number_arg2)
|
11
|
+
[int_arg1, number_arg2]
|
12
|
+
end
|
13
|
+
|
14
|
+
command "classy bar? probably not"
|
15
|
+
def class_bar(int_arg1, string_arg2="Number 42")
|
16
|
+
[int_arg1, string_arg2]
|
17
|
+
end
|
18
|
+
|
19
|
+
command "run me for stuff to happen"
|
20
|
+
def class_qux string_arg1 ="1492", string_arg2 = "I'm a tricky one"
|
21
|
+
[string_arg1, string_arg2]
|
22
|
+
end
|
23
|
+
|
24
|
+
command "I'm another function!"
|
25
|
+
def class_baz number_arg1, string_arg2 = "blorp", *array_arg3
|
26
|
+
[number_arg1, string_arg2, array_arg3]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class CommandNoCommand
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command 'a method with a command'
|
8
|
+
def command_method1
|
9
|
+
"command_method1"
|
10
|
+
end
|
11
|
+
|
12
|
+
# this method should not be in the commands list
|
13
|
+
def no_command_method1(flappity)
|
14
|
+
"no_command_method1"
|
15
|
+
end
|
16
|
+
|
17
|
+
command 'another method with a command'
|
18
|
+
def command_method2(some_parameter)
|
19
|
+
"command_method2"
|
20
|
+
end
|
21
|
+
|
22
|
+
# this method shouldn't be in the list either
|
23
|
+
def no_command_method2(flippity)
|
24
|
+
"no_command_method2"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class DefaultMethods
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command 'the default method', :default
|
8
|
+
def default_method(name)
|
9
|
+
"default method called with: #{name}"
|
10
|
+
end
|
11
|
+
|
12
|
+
command 'does other stuff'
|
13
|
+
def not_a_default_method(name="Cleveland", age)
|
14
|
+
["not a default method, called with: #{name}", age]
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class DefaultMethodNoParams
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command 'does some stuff', :default
|
8
|
+
def default_no_params
|
9
|
+
"default method called, has no params"
|
10
|
+
end
|
11
|
+
|
12
|
+
command 'does other stuff'
|
13
|
+
def not_a_default_method(name="Cleveland", age)
|
14
|
+
["not a default method: #{name}", age]
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class MultiLineDescription
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command "this description will be on multiple lines\nso you can have really long descriptions\nAnd another line\nAnd another"
|
8
|
+
def multiline_function1
|
9
|
+
"multiline_function1"
|
10
|
+
end
|
11
|
+
|
12
|
+
command "Line one is short line two blah, blah! Oh, and blah!"
|
13
|
+
def multiline_function2(foo)
|
14
|
+
"multiline_function2: #{foo}"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
@@command_options
|
2
|
+
require "commandable"
|
3
|
+
|
4
|
+
class MultiLineDescriptionNoParams
|
5
|
+
extend Commandable
|
6
|
+
|
7
|
+
command "this description will be on multiple lines\nso you can have really long descriptions\nAnd another line\nAnd another"
|
8
|
+
def multiline_function_no_params1
|
9
|
+
"multiline_function_no_params1"
|
10
|
+
end
|
11
|
+
|
12
|
+
command "Line one is short line two blah, blah! Oh, and blah!"
|
13
|
+
def multiline_function_no_params2
|
14
|
+
"multiline_function_no_params2"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|