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.
- data/.ruby +61 -0
- data/.yardopts +7 -0
- data/COPYING.rdoc +35 -0
- data/DEMO.rdoc +568 -0
- data/HISTORY.rdoc +55 -0
- data/README.rdoc +101 -51
- data/Schedule.reap +17 -0
- data/demo/00_introduction.rdoc +6 -0
- data/demo/01_single_command.rdoc +44 -0
- data/demo/02_multiple_commands.rdoc +125 -0
- data/demo/03_help_text.rdoc +109 -0
- data/demo/04_manpage.rdoc +14 -0
- data/demo/05_optparse_example.rdoc +152 -0
- data/demo/06_delegate_example.rdoc +40 -0
- data/demo/07_command_methods.rdoc +36 -0
- data/demo/08_dispatach.rdoc +29 -0
- data/demo/applique/ae.rb +1 -0
- data/demo/applique/compare.rb +4 -0
- data/demo/applique/exec.rb +1 -0
- data/demo/samples/bin/hello +31 -0
- data/demo/samples/man/hello.1 +22 -0
- data/demo/samples/man/hello.1.html +102 -0
- data/demo/samples/man/hello.1.ronn +19 -0
- data/lib/executable.rb +67 -128
- data/lib/executable/core_ext.rb +102 -0
- data/lib/executable/dispatch.rb +30 -0
- data/lib/executable/domain.rb +106 -0
- data/lib/executable/errors.rb +22 -0
- data/lib/executable/help.rb +430 -0
- data/lib/executable/parser.rb +208 -0
- data/lib/executable/utils.rb +41 -0
- data/lib/executable/version.rb +23 -0
- data/meta/authors +2 -0
- data/meta/copyrights +3 -0
- data/meta/created +1 -0
- data/meta/description +6 -0
- data/meta/name +1 -0
- data/meta/organization +1 -0
- data/meta/repositories +2 -0
- data/meta/requirements +6 -0
- data/meta/resources +7 -0
- data/meta/summary +1 -0
- data/meta/version +1 -0
- data/test/test_executable.rb +40 -19
- metadata +124 -68
- data/History.rdoc +0 -35
- data/NOTICE.rdoc +0 -23
- data/Profile +0 -30
- data/Version +0 -1
- data/meta/license/Apache2.txt +0 -177
data/HISTORY.rdoc
ADDED
@@ -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
|
+
|
data/README.rdoc
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
*
|
23
|
+
* Easy to use, just mixin or subclass.
|
24
|
+
* Define #call to control the command procedure.
|
16
25
|
* Public writers become options.
|
17
|
-
*
|
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
|
-
==
|
31
|
+
== LIMITATIONS
|
21
32
|
|
22
|
-
*
|
23
|
-
*
|
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
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
85
|
+
#!usr/bin/env ruby
|
86
|
+
require 'hello.rb'
|
87
|
+
HelloCommand.run
|
39
88
|
|
40
|
-
|
89
|
+
If we named this file `hello`, set its execute flag and made it available
|
90
|
+
on our systems $PATH, then:
|
41
91
|
|
42
|
-
|
43
|
-
|
44
|
-
end
|
92
|
+
$ hello
|
93
|
+
Hello, World!
|
45
94
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
95
|
+
$ hello John
|
96
|
+
Hello, John!
|
50
97
|
|
51
|
-
|
98
|
+
$ hello --loud John
|
99
|
+
HELLO, JOHN!
|
52
100
|
|
53
|
-
|
54
|
-
=> ["butter", nil, nil, "yum"]
|
101
|
+
Executable can also generate help text for commands.
|
55
102
|
|
56
|
-
|
57
|
-
|
103
|
+
$ hello --help
|
104
|
+
USAGE: hello [options]
|
58
105
|
|
106
|
+
Say hello.
|
59
107
|
|
60
|
-
|
61
|
-
|
108
|
+
--loud Say it in uppercase?
|
109
|
+
--help Show this message
|
62
110
|
|
63
|
-
|
64
|
-
to
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
130
|
+
== INSTALLATION
|
75
131
|
|
132
|
+
Install with RubyGems in the usual fashion.
|
76
133
|
|
77
|
-
|
134
|
+
$ gem install executable
|
78
135
|
|
79
|
-
(Apache 2.0)
|
80
136
|
|
81
|
-
|
137
|
+
== LEGAL
|
82
138
|
|
83
|
-
|
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
|
-
|
141
|
+
Distributable in accordance with the *BSD-2-Clause* license.
|
88
142
|
|
89
|
-
|
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
|
|
data/Schedule.reap
ADDED
@@ -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,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
|
+
|