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
@@ -50,6 +50,12 @@ describe Commandable do
|
|
50
50
|
lambda{execute_queue(["fly", "navy"])}.should raise_error(Commandable::UnknownCommandError)
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
context "and even when silent is on" do
|
55
|
+
it "prints usage/help instructions" do
|
56
|
+
capture_output{Commandable.execute(["help"], :silent)}.to_s.should match(/Usage:/)
|
57
|
+
end
|
58
|
+
end
|
53
59
|
|
54
60
|
context "and running commands automatically via execute" do
|
55
61
|
|
@@ -179,4 +185,4 @@ describe Commandable do
|
|
179
185
|
|
180
186
|
end
|
181
187
|
|
182
|
-
end
|
188
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commandable do
|
4
|
+
|
5
|
+
before(:each) {Commandable.reset_all}
|
6
|
+
|
7
|
+
context "when using Commandable directly" do
|
8
|
+
|
9
|
+
before(:each) { load 'test_class.rb' }
|
10
|
+
|
11
|
+
it "should use ARGV by default" do
|
12
|
+
expect {capture_output{Commandable.execute()}}.should_not raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -69,7 +69,10 @@ describe Commandable do
|
|
69
69
|
|
70
70
|
it "adds (default) to the end of the default command description when printing" do
|
71
71
|
load 'default_method.rb'
|
72
|
-
Commandable.
|
72
|
+
Commandable.color_output = true
|
73
|
+
Commandable.help.join.should match(/\(default\)/)
|
74
|
+
Commandable.color_output = false
|
75
|
+
Commandable.help.join.should match(/\(default\)/)
|
73
76
|
end
|
74
77
|
|
75
78
|
context "and there are no parameters" do
|
@@ -85,7 +88,6 @@ describe Commandable do
|
|
85
88
|
execute_output_s([]).should_not match(/\[parameters\]/)
|
86
89
|
end
|
87
90
|
|
88
|
-
|
89
91
|
end
|
90
92
|
|
91
93
|
context "and there is a new line in a description" do
|
@@ -104,67 +106,4 @@ describe Commandable do
|
|
104
106
|
|
105
107
|
end
|
106
108
|
|
107
|
-
|
108
|
-
|
109
|
-
before(:each) do
|
110
|
-
load 'private_methods_bad.rb'
|
111
|
-
Commandable.app_exe = "mycoolapp"
|
112
|
-
Commandable.app_info =
|
113
|
-
""" My Cool App - It does stuff and things!
|
114
|
-
Copyright (c) 2011 Acme Inc."""
|
115
|
-
end
|
116
|
-
|
117
|
-
let(:c) {Term::ANSIColorHI}
|
118
|
-
|
119
|
-
it "changes the output if color is enabled" do
|
120
|
-
Commandable.color_output = false
|
121
|
-
lambda {Commandable.color_output = true}.should change{Commandable.help}
|
122
|
-
end
|
123
|
-
|
124
|
-
it "resets text to plain if colors are turned off" do
|
125
|
-
Commandable.color_output = true
|
126
|
-
lambda {Commandable.color_output = false}.should change{Commandable.help}
|
127
|
-
end
|
128
|
-
|
129
|
-
context "when a specific setting's color is changed" do
|
130
|
-
|
131
|
-
before(:each) { Commandable.color_output = true }
|
132
|
-
|
133
|
-
# This seems ripe for meta-zation
|
134
|
-
context "when app_info is changed" do
|
135
|
-
specify {lambda {Commandable.color_app_info = c.black}.should change{Commandable.help}}
|
136
|
-
end
|
137
|
-
|
138
|
-
context "when app_exe is changed" do
|
139
|
-
specify {lambda {Commandable.color_app_exe = c.black}.should change{Commandable.help}}
|
140
|
-
end
|
141
|
-
|
142
|
-
context "when color_command is changed" do
|
143
|
-
specify {lambda {Commandable.color_command = c.black}.should change{Commandable.help}}
|
144
|
-
end
|
145
|
-
|
146
|
-
context "when color_description is changed" do
|
147
|
-
specify {lambda {Commandable.color_description = c.black}.should change{Commandable.help}}
|
148
|
-
end
|
149
|
-
|
150
|
-
context "when color_parameter is changed" do
|
151
|
-
specify {lambda {Commandable.color_parameter = c.black}.should change{Commandable.help}}
|
152
|
-
end
|
153
|
-
|
154
|
-
context "when color_usage is changed" do
|
155
|
-
specify {lambda {Commandable.color_usage = c.black}.should change{Commandable.help}}
|
156
|
-
end
|
157
|
-
|
158
|
-
context "when there is an error" do
|
159
|
-
|
160
|
-
specify { lambda {Commandable.color_error_word = c.magenta}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
161
|
-
specify { lambda {Commandable.color_error_name = c.intense_red}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
162
|
-
specify { lambda {Commandable.color_error_description = c.black + c.bold}.should change{capture_output{Commandable.execute(["fly", "navy"])}}}
|
163
|
-
|
164
|
-
end
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
109
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require "commandable"
|
2
2
|
|
3
|
-
# This is just a not so clever way of getting at the instance methods of Commandable
|
3
|
+
# This is just a not so clever way of getting at the instance methods of Commandable.
|
4
4
|
# Accessing the private methods of a class/module is a bad idea but I really need to
|
5
5
|
# test them. Plus making a helper module just to test them is also against best practices
|
6
6
|
# so...
|
data/spec/spec_helper.rb
CHANGED
@@ -12,7 +12,7 @@ require 'commandable'
|
|
12
12
|
|
13
13
|
# A note on the specs:
|
14
14
|
# Since Commandable uses singletons the tests sometimes get confused about their state.
|
15
|
-
# I'm working on a way to properly clear
|
15
|
+
# I'm working on a way to properly clear everything out of memory before each test but tests
|
16
16
|
# doesn't pass in autotest try running the test again or run rspec yourself and they'll pass.
|
17
17
|
|
18
18
|
# Debug print
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: commandable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-02-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name: term-ansicolor
|
16
|
-
requirement: &
|
15
|
+
name: term-ansicolor
|
16
|
+
requirement: &70273995390760 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 1.0.7
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70273995390760
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70273995389900 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,29 +32,7 @@ dependencies:
|
|
32
32
|
version: '2.5'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: cucumber
|
38
|
-
requirement: &2160864460 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: '0.10'
|
44
|
-
type: :development
|
45
|
-
prerelease: false
|
46
|
-
version_requirements: *2160864460
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: aruba
|
49
|
-
requirement: &2160863860 !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ~>
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.3'
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: *2160863860
|
35
|
+
version_requirements: *70273995389900
|
58
36
|
description: ! 'The easiest way to add command line control to your Ruby app. You
|
59
37
|
can add a single line above an existing method and that method will be available
|
60
38
|
from the command line.
|
@@ -73,29 +51,31 @@ files:
|
|
73
51
|
- .gemrc
|
74
52
|
- .gemtest
|
75
53
|
- .gitignore
|
54
|
+
- .rbenv-gemsets
|
76
55
|
- BUGS.txt
|
77
56
|
- Gemfile
|
78
57
|
- LICENCE
|
79
|
-
- README.
|
58
|
+
- README.md
|
80
59
|
- Rakefile
|
81
60
|
- autotest/discover.rb
|
82
61
|
- bin/commandable
|
83
62
|
- commandable.gemspec
|
84
|
-
- features/ansicolor.feature
|
85
|
-
- features/copy_examples.feature
|
86
|
-
- features/download_widget.feature
|
87
|
-
- features/setup/env.rb
|
88
|
-
- features/step-definitions/step-definitions.rb
|
89
63
|
- lib/commandable.rb
|
90
64
|
- lib/commandable/app_controller.rb
|
91
|
-
- lib/commandable/
|
65
|
+
- lib/commandable/argument_parser.rb
|
66
|
+
- lib/commandable/coloring.rb
|
67
|
+
- lib/commandable/command_parser.rb
|
68
|
+
- lib/commandable/controller.rb
|
92
69
|
- lib/commandable/exceptions.rb
|
70
|
+
- lib/commandable/execution_controller.rb
|
71
|
+
- lib/commandable/help_text.rb
|
93
72
|
- lib/commandable/version.rb
|
94
|
-
- lib/monkey_patch/file_utils.rb
|
95
73
|
- spec/commandable/app_controller_spec.rb
|
96
74
|
- spec/commandable/attr_accessor_spec.rb
|
75
|
+
- spec/commandable/coloring_spec.rb
|
97
76
|
- spec/commandable/command_line_execution_spec.rb
|
98
77
|
- spec/commandable/commandable_spec.rb
|
78
|
+
- spec/commandable/execution_controller_spec.rb
|
99
79
|
- spec/commandable/help_generator_spec.rb
|
100
80
|
- spec/commandable/helpers_spec.rb
|
101
81
|
- spec/commandable/instance_methods_spec.rb
|
@@ -151,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
131
|
version: '0'
|
152
132
|
requirements: []
|
153
133
|
rubyforge_project:
|
154
|
-
rubygems_version: 1.8.
|
134
|
+
rubygems_version: 1.8.15
|
155
135
|
signing_key:
|
156
136
|
specification_version: 3
|
157
137
|
summary: The easiest way to add command line control to your Ruby apps.
|
@@ -159,8 +139,10 @@ test_files:
|
|
159
139
|
- autotest/discover.rb
|
160
140
|
- spec/commandable/app_controller_spec.rb
|
161
141
|
- spec/commandable/attr_accessor_spec.rb
|
142
|
+
- spec/commandable/coloring_spec.rb
|
162
143
|
- spec/commandable/command_line_execution_spec.rb
|
163
144
|
- spec/commandable/commandable_spec.rb
|
145
|
+
- spec/commandable/execution_controller_spec.rb
|
164
146
|
- spec/commandable/help_generator_spec.rb
|
165
147
|
- spec/commandable/helpers_spec.rb
|
166
148
|
- spec/commandable/instance_methods_spec.rb
|
data/features/ansicolor.feature
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
Feature: Ansicolor
|
2
|
-
In order to not have horrible looking colors
|
3
|
-
As a programmer
|
4
|
-
I want to make sure I'm using ANSIColorHI
|
5
|
-
And it doesn't conflict with the original ANSIColor
|
6
|
-
|
7
|
-
|
8
|
-
Scenario: Run a feature
|
9
|
-
When I run cucumber
|
10
|
-
Then it doesn't give an error
|
11
|
-
|
@@ -1,13 +0,0 @@
|
|
1
|
-
Feature: Copy Examples
|
2
|
-
In order to learn how to use Commandable
|
3
|
-
As a developer
|
4
|
-
I want to look at some example files
|
5
|
-
|
6
|
-
Scenario: Copy example files from specs into the default directory
|
7
|
-
When I run `commandable examples`
|
8
|
-
Then a directory named "examples" should exist
|
9
|
-
|
10
|
-
Scenario: Copy example files from specs into a specified directory
|
11
|
-
When I run `commandable examples shazam`
|
12
|
-
Then a directory named "shazam" should exist
|
13
|
-
|
@@ -1,18 +0,0 @@
|
|
1
|
-
Feature: Download widget
|
2
|
-
In order to learn how to use Commandable by looking at a fully functional application
|
3
|
-
As a developer
|
4
|
-
I want to download the latest source code for Widget from Github
|
5
|
-
|
6
|
-
Background:
|
7
|
-
Given Git is installed
|
8
|
-
|
9
|
-
@download_widget
|
10
|
-
Scenario: Download widget from github into the default directory
|
11
|
-
When I run `commandable widget`
|
12
|
-
Then a directory named "widget" should exist
|
13
|
-
|
14
|
-
@download_widget
|
15
|
-
Scenario: Download widget from github into a specified directory
|
16
|
-
When I run `commandable widget potato`
|
17
|
-
Then a directory named "potato" should exist
|
18
|
-
|
data/features/setup/env.rb
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
Before do
|
2
|
-
@aruba_timeout_seconds = 30
|
3
|
-
end
|
4
|
-
|
5
|
-
When /^I run cucumber$/ do
|
6
|
-
# These are just to see color output is working
|
7
|
-
end
|
8
|
-
|
9
|
-
Then /^it doesn't give an error$/ do
|
10
|
-
# These are just to see color output is working
|
11
|
-
end
|
12
|
-
|
13
|
-
Given /^Git is installed$/ do
|
14
|
-
raise Exception, "\n#{"ATTENION! " * 5}\nYou need git installed to run the Cucumber tests!\n\n" if `which git`.empty?
|
15
|
-
end
|
@@ -1,447 +0,0 @@
|
|
1
|
-
require 'term/ansicolorhi'
|
2
|
-
require 'set'
|
3
|
-
|
4
|
-
# This library allows you to incredibly easily make
|
5
|
-
# your methods directly available from the command line.
|
6
|
-
#
|
7
|
-
# Author:: Mike Bethany (mailto:mikbe.tk@gmail.com)
|
8
|
-
# Copyright:: Copyright (c) 2011 Mike Bethany
|
9
|
-
# License:: Distributed under the MIT licence (See LICENCE file)
|
10
|
-
|
11
|
-
# Extending your class with this module allows you to use the #command
|
12
|
-
# method above your method. This makes them executable from the command line.
|
13
|
-
module Commandable
|
14
|
-
|
15
|
-
# Default command that always gets added to end of the command list
|
16
|
-
HELP_COMMAND = {:help => {:description => "you're looking at it now", :argument_list => "", :class=>"Commandable", :class_method=>true}}
|
17
|
-
|
18
|
-
class << self
|
19
|
-
|
20
|
-
# Describes your application, printed at the top of help/usage messages
|
21
|
-
attr_accessor :app_info
|
22
|
-
|
23
|
-
# Used when building the usage line, e.g. Usage: app_exe [command] [parameters]
|
24
|
-
attr_accessor :app_exe
|
25
|
-
|
26
|
-
# If optional parameters show default values, true by default
|
27
|
-
attr_accessor :verbose_parameters
|
28
|
-
|
29
|
-
# Boolean: If help/usage messages will print in color
|
30
|
-
attr_accessor :color_output
|
31
|
-
# What color the app_info text will be in the help message
|
32
|
-
attr_accessor :color_app_info
|
33
|
-
# What color the app_exe will be in the usage line in the help message
|
34
|
-
attr_accessor :color_app_exe
|
35
|
-
# What color the word "command" and the commands themselves will be in the help message
|
36
|
-
attr_accessor :color_command
|
37
|
-
# What color the description column header and text will be in the help message
|
38
|
-
attr_accessor :color_description
|
39
|
-
# What color the word "parameter" and the parameters themselves will be in the help message
|
40
|
-
attr_accessor :color_parameter
|
41
|
-
# What color the word "Usage:" will be in the help message
|
42
|
-
attr_accessor :color_usage
|
43
|
-
|
44
|
-
# What color the word "Error:" text will be in error messages
|
45
|
-
attr_accessor :color_error_word
|
46
|
-
# What color the friendly name of the error will be in error messages
|
47
|
-
attr_accessor :color_error_name
|
48
|
-
# What color the error description will be in error messages
|
49
|
-
attr_accessor :color_error_description
|
50
|
-
|
51
|
-
# An array of methods that can be executed from the command line
|
52
|
-
def commands
|
53
|
-
@@commands.dup
|
54
|
-
end
|
55
|
-
|
56
|
-
# A hash of instances created when calling instance methods
|
57
|
-
# It's keyed using the class name: {"ClassName"=>#<ClassName:0x00000100b1f188>}
|
58
|
-
def class_cache
|
59
|
-
@@class_cache
|
60
|
-
end
|
61
|
-
|
62
|
-
# Access the command array using the method name (symbol or string)
|
63
|
-
def [](index)
|
64
|
-
raise AccessorError unless index.is_a? String or index.is_a? Symbol
|
65
|
-
@@commands[index.to_sym]
|
66
|
-
end
|
67
|
-
|
68
|
-
# Resets the class to default values clearing any commands
|
69
|
-
# and setting the color back to their default values.
|
70
|
-
def reset_all
|
71
|
-
clear_commands
|
72
|
-
reset_colors
|
73
|
-
@app_info = nil
|
74
|
-
@app_exe = nil
|
75
|
-
@verbose_parameters = true
|
76
|
-
@@default_method = nil
|
77
|
-
@@class_cache = {}
|
78
|
-
end
|
79
|
-
|
80
|
-
# Clears all methods from the list of available commands
|
81
|
-
# This is mostly useful for testing.
|
82
|
-
def clear_commands
|
83
|
-
@@commands = HELP_COMMAND.dup
|
84
|
-
end
|
85
|
-
|
86
|
-
# Convenience method to iterate over the array of commands using the Commandable module
|
87
|
-
def each(&block)
|
88
|
-
@@commands.each do |key, value|
|
89
|
-
yield key => value
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# Generates an array of the available commands with a
|
94
|
-
# list of their parameters and the method's description.
|
95
|
-
# This includes the applicaiton info and app name if given.
|
96
|
-
# It's meant to be printed to the command line.
|
97
|
-
def help(additional_info=nil)
|
98
|
-
|
99
|
-
set_colors
|
100
|
-
|
101
|
-
cmd_length = "Command".length
|
102
|
-
parm_length = "Parameters".length
|
103
|
-
max_command = [(@@commands.keys.max_by{|key| key.to_s.length }).to_s.length, cmd_length].max
|
104
|
-
max_parameter = @@commands[@@commands.keys.max_by{|key| @@commands[key][:argument_list].length }][:argument_list].length
|
105
|
-
max_parameter = [parm_length, max_parameter].max if max_parameter > 0
|
106
|
-
|
107
|
-
usage_text = " #{@c_usage}Usage:#{@c_reset} "
|
108
|
-
|
109
|
-
if Commandable.app_exe
|
110
|
-
cmd_text = "<#{@c_command + @c_bold}command#{@c_reset}>"
|
111
|
-
parm_text = " [#{@c_parameter + @c_bold}parameters#{@c_reset}]" if max_parameter > 0
|
112
|
-
usage_text += "#{@c_app_exe + app_exe + @c_reset} #{cmd_text}#{parm_text} [#{cmd_text}#{parm_text}...]"
|
113
|
-
end
|
114
|
-
|
115
|
-
array = [usage_text, ""]
|
116
|
-
|
117
|
-
array.unshift additional_info if additional_info
|
118
|
-
array.unshift ("\e[2A" + @c_app_info + Commandable.app_info + @c_reset) if Commandable.app_info
|
119
|
-
array.unshift "\e[H\e[2J"
|
120
|
-
|
121
|
-
header_text = " #{" "*(max_command-cmd_length)}#{@c_command + @c_bold}Command#{@c_reset} "
|
122
|
-
header_text += "#{@c_parameter + @c_bold}Parameters #{@c_reset}#{" "*(max_parameter-parm_length)}" if max_parameter > 0
|
123
|
-
header_text += "#{@c_description + @c_bold}Description#{@c_reset}"
|
124
|
-
|
125
|
-
array << header_text
|
126
|
-
|
127
|
-
array += @@commands.keys.collect do |key|
|
128
|
-
default = (@@default_method and key == @@default_method.keys[0]) ? @color_bold : ""
|
129
|
-
|
130
|
-
help_line = " #{" "*(max_command-key.length)}#{@c_command + default + key.to_s + @c_reset}"+
|
131
|
-
" #{default + @c_parameter + @@commands[key][:argument_list] + @c_reset}"
|
132
|
-
help_line += "#{" "*(max_parameter-@@commands[key][:argument_list].length)} " if max_parameter > 0
|
133
|
-
|
134
|
-
# indent new lines
|
135
|
-
description = @@commands[key][:description].gsub("\n", "\n" + (" "*(max_command + max_parameter + (max_parameter > 0 ? 1 : 0) + 4)))
|
136
|
-
|
137
|
-
help_line += ": #{default + @c_description}#{"<#{@@commands[key][:xor]}> " if @@commands[key][:xor]}" +
|
138
|
-
"#{description}" +
|
139
|
-
"#{" (default)" unless default == ""}#{@c_reset}"
|
140
|
-
end
|
141
|
-
array << nil
|
142
|
-
end
|
143
|
-
|
144
|
-
# A wrapper for the execution_queue that runs the queue and traps errors.
|
145
|
-
# If an error occurs inside this method it will print out a complete list
|
146
|
-
# of availavle methods with usage instructions and exit gracefully.
|
147
|
-
#
|
148
|
-
# If you do not want the output from your methods to be printed out automatically
|
149
|
-
# run the execute command with silent set to anything other than false or nil.
|
150
|
-
def execute(argv, silent=false)
|
151
|
-
begin
|
152
|
-
command_queue = execution_queue(argv)
|
153
|
-
command_queue.each do |com|
|
154
|
-
return_value = com[:proc].call
|
155
|
-
puts return_value unless silent
|
156
|
-
end
|
157
|
-
rescue SystemExit => kernel_exit
|
158
|
-
Kernel.exit kernel_exit.status
|
159
|
-
rescue Exception => exception
|
160
|
-
if exception.respond_to?(:friendly_name)
|
161
|
-
set_colors
|
162
|
-
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")
|
163
|
-
else
|
164
|
-
puts exception.inspect
|
165
|
-
puts exception.backtrace.collect{|line| " #{line}"}
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
# Returns an array of executable procs based on the given array of commands and parameters
|
171
|
-
# Normally this would come from the command line parameters in the ARGV array.
|
172
|
-
def execution_queue(argv)
|
173
|
-
arguments = argv.dup
|
174
|
-
method_hash = {}
|
175
|
-
last_method = nil
|
176
|
-
|
177
|
-
if arguments.empty?
|
178
|
-
arguments << @@default_method.keys[0] if @@default_method and !@@default_method.values[0][:parameters].flatten.include?(:req)
|
179
|
-
end
|
180
|
-
arguments << "help" if arguments.empty?
|
181
|
-
|
182
|
-
# Parse the command line into methods and their parameters
|
183
|
-
|
184
|
-
arguments.each do |arg|
|
185
|
-
if Commandable[arg]
|
186
|
-
last_method = arg.to_sym
|
187
|
-
method_hash.merge!(last_method=>[])
|
188
|
-
else
|
189
|
-
unless last_method
|
190
|
-
default = find_by_subkey(:default)
|
191
|
-
|
192
|
-
# Raise an error if there is no default method and the first item isn't a method
|
193
|
-
raise UnknownCommandError, arguments.first if default.empty?
|
194
|
-
|
195
|
-
# Raise an error if there is a default method but it doesn't take any parameters
|
196
|
-
raise UnknownCommandError, (arguments.first) if default.values[0][:argument_list] == ""
|
197
|
-
|
198
|
-
last_method = default.keys.first
|
199
|
-
method_hash.merge!(last_method=>[])
|
200
|
-
end
|
201
|
-
method_hash[last_method] << arg
|
202
|
-
end
|
203
|
-
# Test for missing required switches
|
204
|
-
@@commands.select do |key, value|
|
205
|
-
if value[:required] and method_hash[key].nil?
|
206
|
-
# If the required switch is also a default have the error be a missing parameter instead of a missing command
|
207
|
-
if value[:default]
|
208
|
-
method_hash.merge!(key=>[])
|
209
|
-
else
|
210
|
-
raise MissingRequiredCommandError, key
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
#puts "method_hash: #{method_hash}" if method_hash.to_s.include?("required_default")
|
216
|
-
|
217
|
-
# Build an array of procs to be called for each method and its given parameters
|
218
|
-
proc_array = []
|
219
|
-
method_hash.each do |meth, params|
|
220
|
-
command = @@commands[meth]
|
221
|
-
|
222
|
-
if command[:parameters] && !command[:parameters].empty?
|
223
|
-
|
224
|
-
#Change the method name for attr_writers
|
225
|
-
meth = "#{meth}=" if command[:parameters][0][0] == :writer
|
226
|
-
|
227
|
-
# Get a list of required parameters and make sure all of them were provided
|
228
|
-
required = command[:parameters].select{|param| [:req, :writer].include?(param[0])}
|
229
|
-
required.shift(params.count)
|
230
|
-
raise MissingRequiredParameterError, {:method=>meth, :parameters=>required.collect!{|meth| meth[1]}.to_s[1...-1].gsub(":",""), :default=>command[:default]} unless required.empty?
|
231
|
-
end
|
232
|
-
|
233
|
-
# Test for duplicate XORs
|
234
|
-
proc_array.select{|x| x[:xor] and x[:xor]==command[:xor] }.each {|bad| raise ExclusiveMethodClashError, "#{meth}, #{bad[:method]}"}
|
235
|
-
|
236
|
-
klass = Object
|
237
|
-
command[:class].split(/::/).each { |name| klass = klass.const_get(name) }
|
238
|
-
## Look for class in class cache
|
239
|
-
unless command[:class_method]
|
240
|
-
klass = (@@class_cache[klass.name] ||= klass.new)
|
241
|
-
end
|
242
|
-
proc_array << {:method=>meth, :xor=>command[:xor], :parameters=>params, :priority=>command[:priority], :proc=>lambda{klass.send(meth, *params)}}
|
243
|
-
end
|
244
|
-
proc_array.sort{|a,b| a[:priority] <=> b[:priority]}.reverse
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
|
-
# Set colors to their default values
|
249
|
-
def reset_colors
|
250
|
-
@color_output ||= true
|
251
|
-
|
252
|
-
# Build the default colors
|
253
|
-
Term::ANSIColorHI.coloring = color_output
|
254
|
-
c = Term::ANSIColorHI
|
255
|
-
@color_app_info = c.intense_white + c.bold
|
256
|
-
@color_app_exe = c.intense_green + c.bold
|
257
|
-
@color_command = c.intense_yellow
|
258
|
-
@color_description = c.intense_white
|
259
|
-
@color_parameter = c.intense_cyan
|
260
|
-
@color_usage = c.intense_black + c.bold
|
261
|
-
|
262
|
-
@color_error_word = c.intense_black + c.bold
|
263
|
-
@color_error_name = c.intense_red + c.bold
|
264
|
-
@color_error_description = c.intense_white + c.bold
|
265
|
-
|
266
|
-
@color_bold = c.bold
|
267
|
-
@color_reset = c.reset
|
268
|
-
@screen_clear = "\e[H\e[2J"
|
269
|
-
end
|
270
|
-
|
271
|
-
private
|
272
|
-
|
273
|
-
# Look through commands for a specific subkey
|
274
|
-
def find_by_subkey(key, value=true)
|
275
|
-
@@commands.select {|meth, meth_value| meth_value[key]==value}
|
276
|
-
end
|
277
|
-
|
278
|
-
# Changes the colors used when print the help/usage instructions to those set by the user.
|
279
|
-
def set_colors
|
280
|
-
if color_output
|
281
|
-
@c_app_info = @color_app_info
|
282
|
-
@c_app_exe = @color_app_exe
|
283
|
-
@c_command = @color_command
|
284
|
-
@c_description = @color_description
|
285
|
-
@c_parameter = @color_parameter
|
286
|
-
@c_usage = @color_usage
|
287
|
-
|
288
|
-
@c_error_word = @color_error_word
|
289
|
-
@c_error_name = @color_error_name
|
290
|
-
@c_error_description = @color_error_description
|
291
|
-
|
292
|
-
@c_bold = @color_bold
|
293
|
-
@c_reset = @color_reset
|
294
|
-
else
|
295
|
-
@c_app_info, @c_app_exe, @c_command, @c_description,
|
296
|
-
@c_parameter, @c_usage, @c_bold, @c_reset, @c_error_word,
|
297
|
-
@c_error_name, @c_error_description = [""]*12
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
end
|
302
|
-
|
303
|
-
# inititialize the Commandable's settings when it's loaded
|
304
|
-
reset_all
|
305
|
-
|
306
|
-
private
|
307
|
-
|
308
|
-
# This is where the magic happens!
|
309
|
-
# It lets you add a method to the list of command line methods
|
310
|
-
def command(*cmd_parameters)
|
311
|
-
|
312
|
-
@@attribute = nil
|
313
|
-
@@method_file = nil
|
314
|
-
@@method_line = nil
|
315
|
-
@@command_options = {}
|
316
|
-
|
317
|
-
# Include Commandable in singleton classes so class level methods work
|
318
|
-
include Commandable unless self.include? Commandable
|
319
|
-
|
320
|
-
# parse command parameters
|
321
|
-
while (param = cmd_parameters.shift)
|
322
|
-
case param
|
323
|
-
when Symbol
|
324
|
-
if param == :xor
|
325
|
-
@@command_options.merge!(param=>:xor)
|
326
|
-
else
|
327
|
-
@@command_options.merge!(param=>true)
|
328
|
-
end
|
329
|
-
when Hash
|
330
|
-
@@command_options.merge!(param)
|
331
|
-
when String
|
332
|
-
@@command_options.merge!(:description=>param)
|
333
|
-
end
|
334
|
-
end
|
335
|
-
@@command_options[:priority] ||= 0
|
336
|
-
|
337
|
-
# only one default allowed
|
338
|
-
raise ConfigurationError, "Only one default method is allowed." if @@default_method and @@command_options[:default]
|
339
|
-
|
340
|
-
set_trace_func proc { |event, file, line, id, binding, classname|
|
341
|
-
|
342
|
-
@@attribute = id if [:attr_accessor, :attr_writer].include?(id)
|
343
|
-
|
344
|
-
# Traps the line where the method is defined so we can look up
|
345
|
-
# the method source code later if there are optional parameters
|
346
|
-
if event == "line" and !@@method_file
|
347
|
-
@@method_file = file
|
348
|
-
@@method_line = line
|
349
|
-
end
|
350
|
-
|
351
|
-
# Raise an error if there is no method following a command definition
|
352
|
-
if event == "end"
|
353
|
-
set_trace_func(nil)
|
354
|
-
raise SyntaxError, "A command was specified but no method follows"
|
355
|
-
end
|
356
|
-
}
|
357
|
-
end
|
358
|
-
|
359
|
-
# Add a method to the list of available command line methods
|
360
|
-
def add_command(meth)
|
361
|
-
@@commands.delete(:help)
|
362
|
-
|
363
|
-
if @@attribute
|
364
|
-
argument_list = "value"
|
365
|
-
meth = meth.to_s.delete("=").to_sym if @@attribute == :attr_writer
|
366
|
-
else
|
367
|
-
argument_list = parse_arguments(@@command_options[:parameters])
|
368
|
-
end
|
369
|
-
@@command_options.merge!(:argument_list=>argument_list,:class => self.name)
|
370
|
-
|
371
|
-
@@commands.merge!(meth => @@command_options)
|
372
|
-
@@default_method = {meth => @@command_options} if @@command_options[:default]
|
373
|
-
|
374
|
-
@@commands.sort.each {|com| @@commands.merge!(com[0]=>@@commands.delete(com[0]))}
|
375
|
-
|
376
|
-
@@commands.merge!(HELP_COMMAND.dup) # makes sure the help command is always last
|
377
|
-
@@command_options = nil
|
378
|
-
@@attribute = nil
|
379
|
-
end
|
380
|
-
|
381
|
-
# Trap method creation after a command call
|
382
|
-
def method_added(meth)
|
383
|
-
set_trace_func(nil)
|
384
|
-
return super(meth) if meth == :initialize || @@command_options == nil
|
385
|
-
|
386
|
-
if @@attribute
|
387
|
-
#synthesize parameter
|
388
|
-
@@command_options.merge!(:parameters=>[[:writer, :value]],:class_method=>false)
|
389
|
-
else
|
390
|
-
# create parameter
|
391
|
-
@@command_options.merge!(:parameters=>self.instance_method(meth).parameters,:class_method=>false)
|
392
|
-
end
|
393
|
-
|
394
|
-
add_command(meth)
|
395
|
-
end
|
396
|
-
|
397
|
-
# Trap class methods too
|
398
|
-
def singleton_method_added(meth)
|
399
|
-
set_trace_func(nil)
|
400
|
-
return super(meth) if meth == :initialize || @@command_options == nil
|
401
|
-
@@command_options.merge!(:parameters=>method(meth).parameters, :class_method=>true)
|
402
|
-
add_command(meth)
|
403
|
-
end
|
404
|
-
|
405
|
-
# Parse a method's parameters building the argument list for printing help/usage
|
406
|
-
def parse_arguments(parameters)
|
407
|
-
parameter_string = ""
|
408
|
-
method_definition = nil
|
409
|
-
parameters.each do |parameter|
|
410
|
-
arg_type = parameter[0]
|
411
|
-
arg = parameter[1]
|
412
|
-
case arg_type
|
413
|
-
when :req
|
414
|
-
parameter_string += " #{arg}"
|
415
|
-
when :opt
|
416
|
-
if Commandable.verbose_parameters
|
417
|
-
# figure out what the default value is
|
418
|
-
method_definition ||= readline(@@method_file, @@method_line)
|
419
|
-
default = parse_optional(method_definition, arg)
|
420
|
-
parameter_string += " [#{arg}=#{default}]"
|
421
|
-
else
|
422
|
-
parameter_string += " [#{arg}]"
|
423
|
-
end
|
424
|
-
when :rest
|
425
|
-
parameter_string += " *#{arg}"
|
426
|
-
when :block
|
427
|
-
parameter_string += " &#{arg}"
|
428
|
-
end
|
429
|
-
end
|
430
|
-
parameter_string.strip
|
431
|
-
end
|
432
|
-
|
433
|
-
# Reads a line from a source code file.
|
434
|
-
def readline(file, line_number)
|
435
|
-
current_line = 0
|
436
|
-
File.open(file).each do |line_text|
|
437
|
-
current_line += 1
|
438
|
-
return line_text.strip if current_line == line_number
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
# Parses a method defition for the optional values of given argument.
|
443
|
-
def parse_optional(method_def, argument)
|
444
|
-
method_def.scan(/#{argument}\s*=\s*("[^"\r\n]*"|'[^'\r\n]*'|[0-9]*)/)[0][0]
|
445
|
-
end
|
446
|
-
|
447
|
-
end
|