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