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.
Files changed (42) hide show
  1. data/.gitignore +8 -0
  2. data/Gemfile +2 -0
  3. data/LICENCE +19 -0
  4. data/README.markdown +409 -0
  5. data/Rakefile +29 -0
  6. data/_testing/alias_trap.rb +14 -0
  7. data/autotest/discover.rb +2 -0
  8. data/bin/commandable +18 -0
  9. data/commandable.gemspec +24 -0
  10. data/lib/commandable.rb +4 -0
  11. data/lib/commandable/app_controller.rb +47 -0
  12. data/lib/commandable/commandable.rb +394 -0
  13. data/lib/commandable/exceptions.rb +61 -0
  14. data/lib/commandable/version.rb +4 -0
  15. data/lib/monkey_patch/file_utils.rb +11 -0
  16. data/spec/commandable/command_line_execution_spec.rb +154 -0
  17. data/spec/commandable/commandable_spec.rb +245 -0
  18. data/spec/commandable/help_generator_spec.rb +169 -0
  19. data/spec/commandable/helpers_spec.rb +17 -0
  20. data/spec/commandable/reset_spec.rb +26 -0
  21. data/spec/commandable/xor_groups_spec.rb +43 -0
  22. data/spec/source_code_examples/class_command_no_command.rb +27 -0
  23. data/spec/source_code_examples/class_methods.rb +20 -0
  24. data/spec/source_code_examples/class_methods_nested.rb +31 -0
  25. data/spec/source_code_examples/command_no_command.rb +27 -0
  26. data/spec/source_code_examples/deep_class.rb +14 -0
  27. data/spec/source_code_examples/default_method.rb +17 -0
  28. data/spec/source_code_examples/default_method_no_params.rb +17 -0
  29. data/spec/source_code_examples/multi_line_description.rb +17 -0
  30. data/spec/source_code_examples/multi_line_description_no_params.rb +17 -0
  31. data/spec/source_code_examples/no_description.rb +10 -0
  32. data/spec/source_code_examples/parameter_class.rb +27 -0
  33. data/spec/source_code_examples/parameter_free.rb +22 -0
  34. data/spec/source_code_examples/required_methods.rb +18 -0
  35. data/spec/source_code_examples/super_deep_class.rb +28 -0
  36. data/spec/source_code_examples/test_class.rb +13 -0
  37. data/spec/source_code_examples/xor_class.rb +37 -0
  38. data/spec/source_code_for_errors/class_bad.rb +7 -0
  39. data/spec/source_code_for_errors/default_method_bad.rb +17 -0
  40. data/spec/source_code_for_errors/private_methods_bad.rb +10 -0
  41. data/spec/spec_helper.rb +55 -0
  42. 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,14 @@
1
+ @@command_options
2
+ require "commandable"
3
+
4
+ module TopModule
5
+ class ParentClass
6
+ class DeepClass
7
+ extend Commandable
8
+ command 'this is a deep method call'
9
+ def deep_method
10
+ "you called a deep method"
11
+ end
12
+ end
13
+ end
14
+ 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