commandable 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.help.to_s.should match(/\(default\)/)
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
- context "when using color_output" do
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
@@ -3,6 +3,10 @@ require "commandable"
3
3
  class ParameterClass
4
4
  extend Commandable
5
5
 
6
+ def non_command_method
7
+ puts "this shouldnt matter"
8
+ end
9
+
6
10
  command "hello world"
7
11
  def foo(int_arg1, number_arg2)
8
12
  [int_arg1, number_arg2]
@@ -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...
@@ -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 every thing out of memory before each test but tests
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.2.3
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: 2011-09-27 00:00:00.000000000Z
12
+ date: 2012-02-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: term-ansicolor-hi
16
- requirement: &2160865720 !ruby/object:Gem::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: *2160865720
24
+ version_requirements: *70273995390760
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &2160865120 !ruby/object:Gem::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: *2160865120
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.markdown
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/commandable.rb
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.10
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
@@ -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
-
@@ -1,5 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
- require 'rspec/expectations'
3
- require 'aruba/cucumber'
4
- require 'fileutils'
5
- require 'commandable'
@@ -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