commandable 0.2.3 → 0.3.1

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.
@@ -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