executable 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. data/.ruby +61 -0
  2. data/.yardopts +7 -0
  3. data/COPYING.rdoc +35 -0
  4. data/DEMO.rdoc +568 -0
  5. data/HISTORY.rdoc +55 -0
  6. data/README.rdoc +101 -51
  7. data/Schedule.reap +17 -0
  8. data/demo/00_introduction.rdoc +6 -0
  9. data/demo/01_single_command.rdoc +44 -0
  10. data/demo/02_multiple_commands.rdoc +125 -0
  11. data/demo/03_help_text.rdoc +109 -0
  12. data/demo/04_manpage.rdoc +14 -0
  13. data/demo/05_optparse_example.rdoc +152 -0
  14. data/demo/06_delegate_example.rdoc +40 -0
  15. data/demo/07_command_methods.rdoc +36 -0
  16. data/demo/08_dispatach.rdoc +29 -0
  17. data/demo/applique/ae.rb +1 -0
  18. data/demo/applique/compare.rb +4 -0
  19. data/demo/applique/exec.rb +1 -0
  20. data/demo/samples/bin/hello +31 -0
  21. data/demo/samples/man/hello.1 +22 -0
  22. data/demo/samples/man/hello.1.html +102 -0
  23. data/demo/samples/man/hello.1.ronn +19 -0
  24. data/lib/executable.rb +67 -128
  25. data/lib/executable/core_ext.rb +102 -0
  26. data/lib/executable/dispatch.rb +30 -0
  27. data/lib/executable/domain.rb +106 -0
  28. data/lib/executable/errors.rb +22 -0
  29. data/lib/executable/help.rb +430 -0
  30. data/lib/executable/parser.rb +208 -0
  31. data/lib/executable/utils.rb +41 -0
  32. data/lib/executable/version.rb +23 -0
  33. data/meta/authors +2 -0
  34. data/meta/copyrights +3 -0
  35. data/meta/created +1 -0
  36. data/meta/description +6 -0
  37. data/meta/name +1 -0
  38. data/meta/organization +1 -0
  39. data/meta/repositories +2 -0
  40. data/meta/requirements +6 -0
  41. data/meta/resources +7 -0
  42. data/meta/summary +1 -0
  43. data/meta/version +1 -0
  44. data/test/test_executable.rb +40 -19
  45. metadata +124 -68
  46. data/History.rdoc +0 -35
  47. data/NOTICE.rdoc +0 -23
  48. data/Profile +0 -30
  49. data/Version +0 -1
  50. data/meta/license/Apache2.txt +0 -177
@@ -0,0 +1,55 @@
1
+ = RELEASE HISTORY
2
+
3
+ == 1.2.0 / 2012-01-31
4
+
5
+ Version 1.2.0 is complete rewrite of Executable. Actually it was decided that
6
+ the old design was too simplistic in it design concept, so another library
7
+ that was in the works, called Executioner, and briefly CLI::Base, was
8
+ ported over. And with some API changes, it is now the new Executable project.
9
+ The idea of the project is generally the same, but Executable now offers
10
+ more features, such as good help output and namespace-based subcomamnds.
11
+ Of course, to accommodate all this the API had to change some over
12
+ the previous version, so be sure to read the API documentation.
13
+
14
+ Changes:
15
+
16
+ * Deprecate old implementation.
17
+ * Port Executioner project over to become new Executable project.
18
+ * Supports namespace-base subcommmands.
19
+ * Supports formatted help output in plain text and markdown.
20
+ * Supports manpage look-up and display.
21
+
22
+
23
+ == 1.1.0 / 2011-04-21
24
+
25
+ This release simplifies Executable, removing the #option_missing method
26
+ and using the standard #method_missing callback instead. Along with this
27
+ the special error class, +NoOptionError+, has been removed. This release
28
+ also fixes an issue with inconsistent arguments being passed to the callback.
29
+ Finally it renames the #execute_command method to simple #execute!.
30
+
31
+ Changes:
32
+
33
+ * Name Changes
34
+
35
+ * Renamed `#execute_command` method to `#execute!`.
36
+
37
+ * Deprecations
38
+
39
+ * Rely on #method_missing callback instead of special #option_missing method.
40
+ * The +NoOptionError+ exception class is no longer needed because of above.
41
+
42
+ * Bug Fixes
43
+
44
+ * The #method_missing callback takes the value of the option being set.
45
+
46
+
47
+ == 1.0.0 / 2011-04-15
48
+
49
+ This is the initialize release of Executable (as a stand alone project).
50
+ Executable is a mixin that can turn any class into an commandline interface.
51
+
52
+ * 1 Major Enhancement
53
+
54
+ * Birthday!
55
+
@@ -1,94 +1,144 @@
1
1
  = Executable
2
2
 
3
+ {Website}[http://rubyworks.github.com/executable] |
4
+ {Source Code}[http://github.com/rubyworks/executable] |
5
+ {Report Issue}[http://github.com/rubyworks/executable/features] |
6
+ {#rubyworks}[irc://irc.freenode.org/rubyworks]
7
+
8
+ {<img src="http://travis-ci.org/rubyworks/executable.png"/>}[http://travis-ci.org/rubyworks/executable]
9
+
10
+
3
11
  == DESCRIPTION
4
12
 
5
- The Executable mixin is a very quick and and easy
6
- way to make almost any class usable via a command
7
- line interface. It simply uses writer methods as
8
- option setters, and the first command line argument
9
- as the method to call, with the subsequent arguments
10
- passed to the method.
13
+ Executable is to the commandline, what ActiveRecord is the database.
14
+ You can think of Executable as a *COM*, a Command-line Object Mapper,
15
+ just as ActiveRecord is an ORM (Object Relational Mapper). Any class
16
+ mixing in Executable or subclassing Executable::Command can define
17
+ a complete command line tool using nothing more than Ruby's standard
18
+ syntax. No special DSL is required.
11
19
 
12
20
 
13
21
  == FEATURES
14
22
 
15
- * Super easy to use, just mixin.
23
+ * Easy to use, just mixin or subclass.
24
+ * Define #call to control the command procedure.
16
25
  * Public writers become options.
17
- * Public methods become subcommands.
26
+ * Namespace children become subcommands.
27
+ * Or easily dispatch subcommands to public methods.
28
+ * Generate help in plain text or markdown.
18
29
 
19
30
 
20
- == RESOURCES
31
+ == LIMITATIONS
21
32
 
22
- * http://rubyworks.github.com/executable
23
- * http://github.com/rubyworks/executable
33
+ * Ruby 1.9+ only.
34
+ * Help doesn't handle aliases well (yet).
24
35
 
25
36
 
26
37
  == RELEASE NOTES
27
38
 
28
- Please see HISTORY file.
39
+ Please see HISTORY.rdoc file.
29
40
 
30
41
 
31
42
  == SYNOPSIS
32
43
 
33
- Simply mixin Executable, then call #execute_command.
44
+ CLIs can be built by using a Executable as a mixin, or by subclassing
45
+ `Executable::Command`. Methods seemlessly handle command-line options.
46
+ Writer methods (those ending in '=') correspond to options and query
47
+ methods (those ending in '?') modify them to be boolean switches.
48
+
49
+ For example, here is a simple "Hello, World!" commandline tool.
50
+
51
+ require 'executable'
52
+
53
+ class HelloCommand
54
+ include Executable
55
+
56
+ # Say it in uppercase?
57
+ def load=(bool)
58
+ @loud = bool
59
+ end
60
+
61
+ #
62
+ def loud?
63
+ @loud
64
+ end
65
+
66
+ # Show this message.
67
+ def help?
68
+ cli.show_help
69
+ exit
70
+ end
71
+ alias :h? :help?
72
+
73
+ # Say hello.
74
+ def call(name)
75
+ name = name || 'World'
76
+ str = "Hello, #{name}!"
77
+ str = str.upcase if loud?
78
+ puts str
79
+ end
80
+ end
34
81
 
35
- class X
36
- include Executable
82
+ To make the command available on the command line, add an executable
83
+ to your project passing ARGV to the #execute or #run methods.
37
84
 
38
- attr_accessor :quiet
85
+ #!usr/bin/env ruby
86
+ require 'hello.rb'
87
+ HelloCommand.run
39
88
 
40
- attr_accessor :size
89
+ If we named this file `hello`, set its execute flag and made it available
90
+ on our systems $PATH, then:
41
91
 
42
- def bread(*args)
43
- ["bread", quiet, size, *args]
44
- end
92
+ $ hello
93
+ Hello, World!
45
94
 
46
- def butter(*args)
47
- ["butter", quiet, size, *args]
48
- end
49
- end
95
+ $ hello John
96
+ Hello, John!
50
97
 
51
- x = X.new
98
+ $ hello --loud John
99
+ HELLO, JOHN!
52
100
 
53
- x.execute!("butter yum")
54
- => ["butter", nil, nil, "yum"]
101
+ Executable can also generate help text for commands.
55
102
 
56
- x.execute!("bread --quiet --size=big")
57
- => ["bread", true, "big"]
103
+ $ hello --help
104
+ USAGE: hello [options]
58
105
 
106
+ Say hello.
59
107
 
60
- Notice that Executable requires an equal-sign (<code>=</code>) be used
61
- when specifying values for non-boolean attributes.
108
+ --loud Say it in uppercase?
109
+ --help Show this message
62
110
 
63
- To make the command available on the command line, add an executable
64
- to your project passing ARGV to the #execute_command method.
111
+ If you look back at the class definition you can see it's pulling
112
+ comments from the source to provide descriptions. It pulls the
113
+ description the command itself from the `#call` method.
65
114
 
66
- #!usr/bin/env ruby
67
- require 'x'
68
- x = X.new
69
- x.execute_command(ARGV)
115
+ Basic help like this is fine for personal tools, but for public facing
116
+ production applications it is desirable to utilize manpages. To this end,
117
+ Executable provides Markdown formatted help as well. We can access this,
118
+ for example, via `HelloCommand.help.markdown`. The idea with this is that
119
+ we can save the output to `man/hello.ronn` or copy it the top of our `bin/`
120
+ file, edit it to perfection and then use tools such a {ronn}[https://github.com/rtomayko/ronn],
121
+ {binman}[https://github.com/sunaku/binman] or {md2man}[https://github.com/sunaku/md2man]
122
+ to generate the manpages. What's particularly cool about Executable,
123
+ is that once we have a manpage in the standard `man/` location in our project,
124
+ the `#show_help` method will use it instead of the plain text.
70
125
 
126
+ For a more detail example see {QED}[demo.html]
127
+ and {API}[http://rubydoc.info/gems/executable/frames] documentation.
71
128
 
72
- == INSTALL
73
129
 
74
- $ gem install executable
130
+ == INSTALLATION
75
131
 
132
+ Install with RubyGems in the usual fashion.
76
133
 
77
- == LEGAL
134
+ $ gem install executable
78
135
 
79
- (Apache 2.0)
80
136
 
81
- Copyright (c) 2009 Thomas Sawyer
137
+ == LEGAL
82
138
 
83
- Licensed under the Apache License, Version 2.0 (the "License");
84
- you may not use this program except in compliance with the License.
85
- You may obtain a copy of the License at
139
+ Copyright (c) 2008 Rubyworks
86
140
 
87
- http://www.apache.org/licenses/LICENSE-2.0
141
+ Distributable in accordance with the *BSD-2-Clause* license.
88
142
 
89
- Unless required by applicable law or agreed to in writing, software
90
- distributed under the License is distributed on an "AS IS" BASIS,
91
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
92
- See the License for the specific language governing permissions and
93
- limitations under the License.
143
+ See COPYING.rdoc for licensing details.
94
144
 
@@ -0,0 +1,17 @@
1
+ task :default => [:test]
2
+
3
+ desc "run unit tests (needs rubytest)"
4
+ task :test do
5
+ sh "rubytest -Ilib test/*.rb"
6
+ end
7
+
8
+ desc "render README.rdoc to web/readme.html (need malt)"
9
+ task :readme do
10
+ sh "malt README.rdoc > web/readme.html"
11
+ end
12
+
13
+ # if `README.rdoc` changes generate `web/readme.html`.
14
+ file 'README.rdoc' do
15
+ sh "malt README.rdoc > web/readme.html"
16
+ end
17
+
@@ -0,0 +1,6 @@
1
+ = Executable
2
+
3
+ Require Executable library.
4
+
5
+ require 'executable'
6
+
@@ -0,0 +1,44 @@
1
+ == No Subcommmands
2
+
3
+ This example demonstrates using Executable::Command to create a simple command line
4
+ interface without subcommands. (Note the Executable mixin could be used just
5
+ as well).
6
+
7
+ class NoSubCommandCLI < Executable::Command
8
+
9
+ attr :result
10
+
11
+ def o?
12
+ @o
13
+ end
14
+
15
+ def o=(flag)
16
+ @o = flag
17
+ end
18
+
19
+ def call
20
+ if o?
21
+ @result = "with"
22
+ else
23
+ @result = "without"
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ Execute the CLI on an example command line.
30
+
31
+ cli = NoSubCommandCLI.run('')
32
+ cli.result.assert == 'without'
33
+
34
+ Execute the CLI on an example command line.
35
+
36
+ cli = NoSubCommandCLI.run('-o')
37
+ cli.result.assert == 'with'
38
+
39
+ There are two important things to notices heres. Frist, that #main is being
40
+ called in each case. It is the method called with no other subcommands are
41
+ defined. And second, the fact the a `o?` method is defined to compliment the
42
+ `o=` writer, informs Executable that `-o` is an option _flag_, not taking
43
+ any parameters.
44
+
@@ -0,0 +1,125 @@
1
+ == Multiple Subcommmands
2
+
3
+ Setup an example CLI subclass.
4
+
5
+ class MyCLI < Executable::Command
6
+ attr :result
7
+
8
+ def initialize
9
+ @result = []
10
+ end
11
+
12
+ def g=(value)
13
+ @result << "g" if value
14
+ end
15
+
16
+ def g?
17
+ @result.include?("g")
18
+ end
19
+
20
+ #
21
+ class C1 < self
22
+ def call
23
+ @result << "c1"
24
+ end
25
+
26
+ def o1=(value)
27
+ @result << "c1_o1 #{value}"
28
+ end
29
+
30
+ def o2=(value)
31
+ @result << "c1_o2 #{value}"
32
+ end
33
+ end
34
+
35
+ #
36
+ class C2 < Executable::Command
37
+ attr :result
38
+
39
+ def initialize
40
+ @result = []
41
+ end
42
+
43
+ def call
44
+ @result << "c2"
45
+ end
46
+
47
+ def o1=(value)
48
+ @result << "c2_o1 #{value}"
49
+ end
50
+
51
+ def o2=(value)
52
+ @result << "c2_o2" if value
53
+ end
54
+
55
+ def o2?
56
+ @result.include?("c2_o2")
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ Instantiate and run the class on an example command line.
63
+
64
+ Just a command.
65
+
66
+ cli = MyCLI.run('c1')
67
+ cli.result.assert == ['c1']
68
+
69
+ Command with global option.
70
+
71
+ cli = MyCLI.run('c1 -g')
72
+ cli.result.assert == ['g', 'c1']
73
+
74
+ Command with an option.
75
+
76
+ cli = MyCLI.run('c1 --o1 A')
77
+ cli.result.assert == ['c1_o1 A', 'c1']
78
+
79
+ Command with two options.
80
+
81
+ cli = MyCLI.run('c1 --o1 A --o2 B')
82
+ cli.result.assert == ['c1_o1 A', 'c1_o2 B', 'c1']
83
+
84
+ Try out the second command.
85
+
86
+ cli = MyCLI.run('c2')
87
+ cli.result.assert == ['c2']
88
+
89
+ Seoncd command with an option.
90
+
91
+ cli = MyCLI.run('c2 --o1 A')
92
+ cli.result.assert == ['c2_o1 A', 'c2']
93
+
94
+ Second command with two options.
95
+
96
+ cli = MyCLI.run('c2 --o1 A --o2')
97
+ cli.result.assert == ['c2_o1 A', 'c2_o2', 'c2']
98
+
99
+ Since C1#main takes not arguments, if we try to issue a command
100
+ that will have left over arguments, then an ArgumentError will be raised.
101
+
102
+ expect ArgumentError do
103
+ cli = MyCLI.run('c1 a')
104
+ end
105
+
106
+ How about a non-existenct subcommand.
107
+
108
+ expect NotImplementedError do
109
+ cli = MyCLI.run('q')
110
+ cli.result.assert == ['q']
111
+ end
112
+
113
+ How about an option only.
114
+
115
+ expect NotImplementedError do
116
+ cli = MyCLI.run('-g')
117
+ cli.result.assert == ['-g']
118
+ end
119
+
120
+ How about a non-existant options.
121
+
122
+ expect Executable::NoOptionError do
123
+ MyCLI.run('c1 --foo')
124
+ end
125
+