commandable 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ commandable
2
+ global
@@ -14,31 +14,14 @@ The whole process can take as little as four lines of code:
14
14
 
15
15
  Now any method you want to make accessible from the command line requires just a single line of code and it's right where your method is so it's easy to find when you do want to change some functionality.
16
16
 
17
- Don't think of **Commandable** as a way to add command line switches, it's much more than that. Think of it as a way to allow your app to be driven directly from the command line.
17
+ Don't think of **Commandable** as a way to add command line switches, think of it as a way to allow your app to be driven directly from the command line in a natural language kind of way.
18
18
 
19
19
  You can now "use your words" to let people interact with your apps in a natural way. No more confusing switches that mean one thing in one program and something completely different in another. Can you believe some apps actually use `-v` for something other than "version" and `-h` for something other than "help?" Madness I say! Madness!
20
20
 
21
21
 
22
- ## Testing for a Better Tomorrow ##
23
-
24
- In an effort to make the best possible software I'm asking anyone that would like to help out to run the BDD/TDD tests included with the gem. If you don't already have the `rubygems-test` gem installed please install it:
25
-
26
- $ gem install rubygems-test
27
-
28
- And then run the tests on your machine:
29
-
30
- $ gem test commandable
31
-
32
- And of course upload them when it asks you if it can. You can take a look at the test results yourself here:
33
-
34
- <http://test.rubygems.org/gems/commandable/v/0.2.0>
35
-
36
- Thanks for your help.
37
-
38
-
39
22
  ## Latest version
40
23
 
41
- 2011-09-27 - Version: 0.2.3
24
+ 2012-02-07 - Version: 0.3.1
42
25
 
43
26
  See change history for specific changes.
44
27
 
@@ -48,8 +31,8 @@ I've tried to follow the principle of least surprise so Commandable should just
48
31
 
49
32
  ## Requirements ##
50
33
 
51
- * Ruby 1.9.2
52
- * OS X or any Posix OS (It works, mostly, on Windows but ironically some of the tests won't run because of some Unix commands I use in the tests)
34
+ * Ruby 1.9.2 or greater
35
+ * OS X or any Posix OS (It works, mostly, on Windows but it won't pass the tests because of some Unix commands I use in the tests but no in the code base)
53
36
 
54
37
  ## Installation
55
38
  From the command line:
@@ -65,16 +48,26 @@ After installing the **Commandable** gem require it somewhere that gets loaded b
65
48
 
66
49
  Extend your class with the **Commandable** module:
67
50
 
68
- class Widget
51
+ require 'commandable'
52
+
53
+ class Foo
69
54
  extend Commandable
55
+
56
+ end
70
57
 
71
58
  Then put `command` and a description above the method you want to make accessible. The description is optional but can be helpful
72
59
  since it's used when automatically building your help/usage instructions.
73
60
 
74
- command "create a new widget"
75
- def new(name)
76
- ...
61
+ require 'commandable'
62
+
63
+ class Foo
64
+ extend Commandable
65
+
66
+ command "Explain what the command does"
67
+ def bar
68
+ puts "Foo::bar"
77
69
  end
70
+ end
78
71
 
79
72
  ### The "`command`" command and its options
80
73
 
@@ -218,20 +211,13 @@ A typical help output looks something like this:
218
211
  readme : displays the readme file (default)
219
212
  v : <xor> Application Version
220
213
  version : <xor> Application Version
221
- widget [path] : Downloads a fully working app demonstrating how
222
- to use Commandable with RSpec and Cucumber
223
- help : you're looking at it now
224
-
214
+ help : you're looking at it now
225
215
 
226
216
 
227
217
  ### Complete demonstration app and some example classes ###
228
- For a fully working example with RSpec and Cucumber tests run this command:
218
+ **Commandable** uses **Commandable** for its own command line options so you can look at the `lib/commandable/app_controller.rb` class for an example.
229
219
 
230
- $ commandable widget [path]
231
-
232
- In addition **Commandable** uses **Commandable** for its own command line options so you can also look at the `lib/commandable/app_controller.rb class` for an example.
233
-
234
- If you would like to see a bunch of simple classes that demonstrate **Commandable**'s uses run:
220
+ If you would like to see a bunch of simple classes that demonstrate how to use **Commandable** run:
235
221
 
236
222
  $ commandable examples [path]
237
223
 
@@ -260,20 +246,30 @@ If set to true help instructions will include the default values in the paramete
260
246
  # Will print:
261
247
  command arg1 [arg2]
262
248
 
249
+ ###Screen Clearing Options
250
+
251
+ **Commandable.clear\_screen**
252
+ _default = false_
253
+ Set to true to enable clearing the screen when printing help/usage instructions.
254
+
255
+ **Commandable.color\clear\_screen\_code**
256
+ _default = "\e[H\e[2J"
257
+ The escape codes used to clear the screen.
258
+
263
259
  ### Colorized Output Options
264
260
 
265
- The help information can be colored using the standard ANSI escape commands found in the `term-ansicolor-hi` gem. The `term-ansicolor-hi` gem it installed as a dependency but just in case you have problems you can install it yourself by running:
261
+ The help information can be colored using the standard ANSI escape commands found in the `term-ansicolor` gem. The `term-ansicolor` gem is installed as a dependency but just in case you have problems you can install it yourself by running:
266
262
 
267
- $ [sudo] gem install term-ansicolor-hi
263
+ $ [sudo] gem install term-ansicolor
268
264
 
269
265
  Then you can do something like this:
270
266
 
271
- require 'term/ansicolorhi'
267
+ require 'term/ansicolor'
272
268
 
273
- c = Term::ANSIColorHI
269
+ c = Term::ANSIColor
274
270
 
275
271
  Commandable.color_app_info = c.intense_white + c.bold
276
- Commandable.color_app_exe = c.intense_green + c.bold
272
+ Commandable.color_app_exe = c.intense_green + c.bold
277
273
  Commandable.color_command = c.intense_yellow
278
274
  Commandable.color_description = c.intense_white
279
275
  Commandable.color_parameter = c.intense_cyan
@@ -286,8 +282,8 @@ Then you can do something like this:
286
282
  ###Color options
287
283
 
288
284
  **Commandable.color\_output**
289
- _default = true_
290
- Set to false to disable colorized help/usage instructions. You might find the colors really, really annoying...
285
+ _default = false_
286
+ Set to true to enable colorized help/usage instructions.
291
287
 
292
288
  **Commandable.color\_app\_info**
293
289
  _default = intense\_white_ + bold
@@ -322,13 +318,11 @@ _default = intense\_cyan_
322
318
  The color the friendly name of the error will be in error messages
323
319
 
324
320
  **Commandable.color\_error\_description**
325
- _default = intense\_black_ + bold
321
+ \_default = intense\_black_ + bold
326
322
  The color the error description will be in error messages
327
323
 
328
-
329
324
  The best way to see what all this means it just type `commandable help` and you'll see the help instructions in color.
330
325
 
331
-
332
326
  ### Executing the Command Line
333
327
 
334
328
  There are two ways of using **Commandable** to run your methods. You can use its built in execute method to automatically run whatever is entered on the command line or you can have **Commandable** build an array of procs that you can execute yourself. This allows you to have finer grain control over the execution of the commands as you can deal with the return values as you run each command.
@@ -400,7 +394,6 @@ You just need to configure your bin file with the app settings and then run `Com
400
394
 
401
395
  # Make sure you require your app after Commandable, or use
402
396
  # a configuration file to load the settings then your app
403
- # See the Widget app for an example of this.
404
397
  require 'yourappname'
405
398
  Commandable.execute(ARGV)
406
399
 
@@ -410,9 +403,7 @@ You just need to configure your bin file with the app settings and then run `Com
410
403
  # Commandable.execute(ARGV, :silent)
411
404
 
412
405
 
413
- I actually prefer to create a separate file for my **Commandable** configuration and load it in my main app file in the `lib` directory. Again, take a look at `Widget` to see what I mean.
414
-
415
- You can get a copy of widget by running `commandable widget [path]` and it will copy the example app to the directory you specify.
406
+ I actually prefer to create a separate file for my **Commandable** configuration and load it in my main app file in the `lib` directory.
416
407
 
417
408
  ## In closing... ##
418
409
 
@@ -427,6 +418,24 @@ Most of all it should be simple to use so if you have any problems please drop m
427
418
 
428
419
  ## Version History ##
429
420
 
421
+ 2012-02-07 - Version: 0.3.1
422
+
423
+ * Fixed bug where an error was raised if a non-commandable method was before a commandable method (uninitialized class variable @@command_options)
424
+
425
+ 2012-01-20 - Version: 0.3.0
426
+
427
+ * All features are off by default to make Commandable more friendly across platforms.
428
+ * Added ability to disable screen clearing and to set your own screen clear escape code. (Based on [John Sumsion](https://github.com/jdsumsion)'s idea)
429
+ * Decoupled screen clearing from coloring; you can have neither, either, or both.
430
+ * Fixed bug that disabled help output in silent mode. (fixed by [John Sumsion](https://github.com/jdsumsion))
431
+ * Fixed bug that didn't label the default method if screen colors were off.
432
+ * Removed monkey patch from FileUtils. It was more philosophical than necessary.
433
+ * Changed terminal coloring gem back to official the term-ansicolor from my own fork now that my changes have been pulled into it.
434
+ * Code clean up. While mostly not a real refactor I've separated out the code into functional groups (aka files).
435
+ * Removed Widget gem example - it was making the examples too tightly coupled to HashModel.
436
+ * Removed Cucumber features.
437
+
438
+
430
439
  2011-09-27 - Version: 0.2.3
431
440
 
432
441
  * Removed annoying error message print out.
@@ -447,28 +456,19 @@ Most of all it should be simple to use so if you have any problems please drop m
447
456
 
448
457
  * First public release. It's 0.2.0 because 0.1.0, going by the name of Cloptions, wasn't released.
449
458
 
450
- ## To Do
451
-
452
- Add ability to easily use nested argument trees without hard-coding case blocks. For instance a command that takes set of commands like `foo remote set "ftp_svr" 10.250.1.100` or `foo remote delete "ftp_svr"`.
453
-
454
- ### Next major version:
459
+ ### Possible future upgrades/improvements
455
460
 
461
+ * See if there's a elegant way to add nested argument trees without case blocks. e.g. a command that takes set of commands like `foo remote set "ftp_svr" 10.250.1.100` and `foo remote delete "ftp_svr"`.
456
462
  * Add a way to use or discover version numbers. Might have to force standardization and not allow configuration since it should be DRY.
457
- * Needs a massive refactoring.
458
463
  * Add a generator to automatically add Commandable support to your app.
459
- * Reorganize docs to be more logical and less the result of my scribblings as I develop.
460
- * Try to figure out how to trap `alias` so I don't you don't have to use an additional `command`.
461
- * Better formatting of help/usage instructions, the existing one is fairly ugly.
462
- * Use comments below `command` as the description text so you don't have to repeat yourself to get RDoc to give you docs for your functions.
464
+ * Try to figure out how to trap `alias` so you don't have to use an additional `command`.
465
+ * Use comments below `command` as the description text so you don't have to repeat yourself when documenting your code.
463
466
  * Clean up RSpecs. I'm doing too many ugly tests instead of specifying behavior.
464
- * Allow sorting of commands alphabetically or by priority
465
-
466
- ###Future versions:
467
-
468
- * Accepting your suggestions...
467
+ * Allow sorting of commands alphabetically or by priority in the help output
469
468
  * Make the help/usage directions format available to programmers without having to hack the code.
470
469
  * More edge case testing.
471
470
  * Allow optional parameters values to be reloaded so changing things like verbose_parameters makes the command list change. (**very** low priority)
471
+ * Accepting your suggestions...
472
472
 
473
473
 
474
474
  .
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'bundler'
2
2
  Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'rake'
5
- require 'rake/rdoctask'
5
+ require 'rdoc/task'
6
6
  require 'rspec/core/rake_task'
7
7
  require 'cucumber/rake/task'
8
8
 
@@ -12,12 +12,8 @@ RSpec::Core::RakeTask.new(:spec) do |t|
12
12
  t.verbose = true
13
13
  end
14
14
 
15
- Cucumber::Rake::Task.new(:cucumber) do |t|
16
- t.cucumber_opts = %w{--tags ~@jruby} unless defined?(JRUBY_VERSION)
17
- end
18
-
19
15
  task :default => [:test, :build]
20
- task :test =>[:cucumber, :spec]
16
+ task :test =>[:spec]
21
17
 
22
18
  task :clobber do
23
19
  rm_rf 'pkg'
@@ -2,6 +2,7 @@
2
2
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
3
3
  require 'commandable'
4
4
  Commandable.color_output = true
5
+ Commandable.clear_screen = true
5
6
  Commandable.verbose_parameters = false
6
7
  Commandable.app_exe = "commandable"
7
8
  Commandable.app_info =
@@ -19,11 +19,9 @@ EOF
19
19
 
20
20
  s.license = 'MIT'
21
21
 
22
- s.add_dependency("term-ansicolor-hi", "~>1.0.7")
22
+ s.add_dependency("term-ansicolor", "~>1.0.7")
23
23
 
24
24
  s.add_development_dependency("rspec", "~>2.5")
25
- s.add_development_dependency("cucumber", "~>0.10")
26
- s.add_development_dependency("aruba", "~>0.3")
27
25
 
28
26
  s.files = `git ls-files`.split("\n")
29
27
  s.test_files = `git ls-files -- {spec,autotest}/*`.split("\n")
@@ -1,4 +1,17 @@
1
- require 'monkey_patch/file_utils'
1
+ # This library allows you to incredibly easily make
2
+ # your methods directly available from the command line.
3
+ #
4
+ # Author:: Mike Bethany (mailto:mikbe.tk@gmail.com)
5
+ # Copyright:: Copyright (c) 2012 Mike Bethany
6
+ # License:: Distributed under the MIT licence (See LICENCE file)
7
+ # website:: http://mikbe.kt
8
+
2
9
  require 'commandable/version'
3
10
  require 'commandable/exceptions'
4
- require 'commandable/commandable'
11
+
12
+ require 'commandable/argument_parser'
13
+ require 'commandable/coloring'
14
+ require 'commandable/help_text'
15
+ require 'commandable/command_parser'
16
+ require 'commandable/execution_controller'
17
+ require 'commandable/controller'
@@ -1,57 +1,32 @@
1
+ # Extending your class with this module allows you to
2
+ # use the #command method above your method.
3
+ # This makes them executable from the command line.
1
4
  module Commandable
2
-
5
+
3
6
  # A helper to display the read me file and generate an example app
4
7
  class AppController
5
- WIDGET_GITHUB ||= "://github.com/mikbe/widget"
6
8
 
7
9
  class << self
8
10
  extend Commandable
9
-
11
+
10
12
  # Displays the readme file
11
13
  command "displays the readme file", :default
12
14
  def readme
13
- `open #{File.expand_path((File.dirname(__FILE__) + '/../../readme.markdown'))}`
14
- end
15
-
16
- command "Downloads a fully working app demonstrating how\nto use Commandable with RSpec and Cucumber"
17
- # Creates a simple example app demonstrating a fully working app
18
- def widget(path="./widget")
19
- # Test for Git
20
- unless git_installed?
21
- puts "Git must be installed to download Widget (You're a developer and you don't have Git installed?)"
22
- return
23
- end
24
- # Git already has all of its own error trapping so
25
- # it would be horrible coupling and duplication
26
- # of effort to do anything on my end for failures.
27
- puts "\nUnable to download Widget. You can find the souce code here:\nhttps#{WIDGET_GITHUB}" unless download_widget(path) == 0
15
+ `open #{File.expand_path((File.dirname(__FILE__) + '/../../readme.md'))}`
28
16
  end
29
17
 
30
- # Downloads Widget from the git repository
31
- # This is external to the widget command so it can be stubbed for testing
32
- def download_widget(path)
33
- `git clone git#{WIDGET_GITHUB}.git #{path}`
34
- $?.exitstatus
35
- end
36
-
37
- # Checks to see if Git is installed
38
- # This is external to the widget command so it can be stubbed for testing
39
- def git_installed?
40
- !`git --version`.chomp.match(/^git version/i).nil?
41
- end
42
-
43
18
  command "Copies the test classes to a folder so\nyou can see a bunch of small examples"
44
19
  # Copies the test classes to a folder so you can see a bunch of small examples
45
20
  def examples(path="./examples")
46
- FileUtils.copy_dir(File.expand_path(File.dirname(__FILE__) + '/../../spec/source_code_examples'),path)
21
+ copy_dir(File.expand_path(File.dirname(__FILE__) + '/../../spec/source_code_examples'),path)
47
22
  end
48
23
 
49
24
  command "Will raise a programmer error, not a user error\nso you can see what happens when you have bad code"
50
25
  # Causes an error so you can see what it will look like if you have an error in your code.
51
26
  def error
52
- raise Exception, "An example of a non-user error caused by your bad code trapped in Commandable.execute()"
27
+ raise Exception, "An example of what an error looks like if you make a mistake using Commandable."
53
28
  end
54
-
29
+
55
30
  command "Application Version", :xor
56
31
  # Version
57
32
  def v
@@ -59,11 +34,18 @@ module Commandable
59
34
  end
60
35
  command "Application Version", :xor
61
36
  alias :version :v
62
-
37
+
38
+ private
39
+
40
+ # Copy a directy and its contents
41
+ def copy_dir(source, dest)
42
+ files = Dir.glob("#{source}/**")
43
+ mkdir_p dest
44
+ cp_r files, dest
45
+ end
46
+
63
47
  end
64
-
65
- end
66
-
67
- end
68
48
 
49
+ end
69
50
 
51
+ end
@@ -0,0 +1,45 @@
1
+ module Commandable
2
+
3
+ # Parse a method's parameters building the argument list for printing help/usage
4
+ def parse_arguments(parameters)
5
+ parameter_string = ""
6
+ method_definition = nil
7
+ parameters.each do |parameter|
8
+ arg_type = parameter[0]
9
+ arg = parameter[1]
10
+ case arg_type
11
+ when :req
12
+ parameter_string += " #{arg}"
13
+ when :opt
14
+ if Commandable.verbose_parameters
15
+ # figure out what the default value is
16
+ method_definition ||= readline(@@method_file, @@method_line)
17
+ default = parse_optional(method_definition, arg)
18
+ parameter_string += " [#{arg}=#{default}]"
19
+ else
20
+ parameter_string += " [#{arg}]"
21
+ end
22
+ when :rest
23
+ parameter_string += " *#{arg}"
24
+ when :block
25
+ parameter_string += " &#{arg}"
26
+ end
27
+ end
28
+ parameter_string.strip
29
+ end
30
+
31
+ # Reads a line from a source code file.
32
+ def readline(file, line_number)
33
+ current_line = 0
34
+ File.open(file).each do |line_text|
35
+ current_line += 1
36
+ return line_text.strip if current_line == line_number
37
+ end
38
+ end
39
+
40
+ # Parses a method defition for the optional values of given argument.
41
+ def parse_optional(method_def, argument)
42
+ method_def.scan(/#{argument}\s*=\s*("[^"\r\n]*"|'[^'\r\n]*'|[0-9]*)/)[0][0]
43
+ end
44
+
45
+ end
@@ -0,0 +1,100 @@
1
+ require 'term/ansicolor'
2
+
3
+ module Commandable
4
+
5
+ class << self
6
+
7
+ # If the output will be colorized or not
8
+ attr_accessor :color_output
9
+
10
+ # What color the app_info text will be in the help message
11
+ attr_accessor :color_app_info
12
+ # What color the app_exe will be in the usage line in the help message
13
+ attr_accessor :color_app_exe
14
+ # What color the word "command" and the commands themselves will be in the help message
15
+ attr_accessor :color_command
16
+ # What color the description column header and text will be in the help message
17
+ attr_accessor :color_description
18
+ # What color the word "parameter" and the parameters themselves will be in the help message
19
+ attr_accessor :color_parameter
20
+ # What color the word "Usage:" will be in the help message
21
+ attr_accessor :color_usage
22
+
23
+ # What color the word "Error:" text will be in error messages
24
+ attr_accessor :color_error_word
25
+ # What color the friendly name of the error will be in error messages
26
+ attr_accessor :color_error_name
27
+ # What color the error description will be in error messages
28
+ attr_accessor :color_error_description
29
+
30
+ # If the screen should be cleared before printing help
31
+ attr_accessor :clear_screen
32
+
33
+ # What escape code will be used to clear the screen
34
+ attr_accessor :clear_screen_code
35
+
36
+ # Resets colors to their default values and disables color output
37
+ def reset_colors
38
+ @color_output = false
39
+
40
+ #Term::ANSIColor.coloring = true
41
+ c = Term::ANSIColor
42
+ @color_app_info = c.intense_white + c.bold
43
+ @color_app_exe = c.intense_green + c.bold
44
+ @color_command = c.intense_yellow
45
+ @color_description = c.intense_white
46
+ @color_parameter = c.intense_cyan
47
+ @color_usage = c.intense_black + c.bold
48
+
49
+ @color_error_word = c.intense_black + c.bold
50
+ @color_error_name = c.intense_red + c.bold
51
+ @color_error_description = c.intense_white + c.bold
52
+
53
+ @color_bold = c.bold
54
+ @color_reset = c.reset
55
+ end
56
+
57
+ # Resets the escape code used for screen clearing and disables screen clearing.
58
+ def reset_screen_clearing
59
+ @clear_screen = false
60
+ @clear_screen_code = "\e[H\e[2J"
61
+ end
62
+
63
+ private
64
+
65
+ # Changes the colors used when printing the help/usage instructions to those set by the user.
66
+ def set_colors
67
+ if @color_output
68
+ @c_app_info = @color_app_info
69
+ @c_app_exe = @color_app_exe
70
+ @c_command = @color_command
71
+ @c_description = @color_description
72
+ @c_parameter = @color_parameter
73
+ @c_usage = @color_usage
74
+
75
+ @c_error_word = @color_error_word
76
+ @c_error_name = @color_error_name
77
+ @c_error_description = @color_error_description
78
+
79
+ @c_bold = @color_bold
80
+ @c_reset = @color_reset
81
+ else
82
+ @c_app_info, @c_app_exe, @c_command,
83
+ @c_description, @c_parameter, @c_usage,
84
+ @c_bold, @c_reset, @c_error_word,
85
+ @c_error_name, @c_error_description = [""]*11
86
+ end
87
+ end
88
+
89
+ # Changes the screen clearing code used when printing the help/usage instructions to the one set by the user.
90
+ def set_screen_clear
91
+ if @clear_screen
92
+ @s_clear_screen_code = @clear_screen_code
93
+ else
94
+ @s_clear_screen_code = ""
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ end