executable 1.2.1 → 1.3.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.
- checksums.yaml +7 -0
- data/HISTORY.md +15 -0
- data/README.md +37 -17
- data/lib/executable/completion.rb +82 -0
- data/lib/executable/core_ext/unbound_method.rb +102 -0
- data/lib/executable/core_ext.rb +1 -102
- data/lib/executable/domain.rb +14 -7
- data/lib/executable/help.rb +18 -12
- data/lib/executable/parser.rb +7 -3
- data/lib/executable/version.rb +2 -19
- data/lib/executable.rb +2 -1
- metadata +30 -88
- data/.index +0 -71
- data/.ruby +0 -1
- data/.yardopts +0 -7
- data/Config.rb +0 -16
- data/DEMO.md +0 -568
- data/lib/executable.yml +0 -71
- data/test/test_executable.rb +0 -59
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dd281be8d7d039a36b1628be53a86817d12d30f1d69258d1f5b0dfa84cc99ad4
|
|
4
|
+
data.tar.gz: 459d53385e0333dd991c4d93f1a2b06e14cf8af188f541d7a0eb50f13b265e45
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c519372eba2ab84e43c919859e52117fbd3d0715ded1721b99f0b471221630ebdd55b8a58485dc6544f3e557a43d71d2fea6e828179dab02a76fbc084388e4a3
|
|
7
|
+
data.tar.gz: b08c3be0ddf83ac87fb68e29404add4b847b95c6aaaa748bb40bbfd8e585c6a6b126fba8649750776d8d544a5c0600b1e1ee0216bad9990e7a9b4b974bcabeec
|
data/HISTORY.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# RELEASE HISTORY
|
|
2
2
|
|
|
3
|
+
## 1.3.0 / 2026-04-05
|
|
4
|
+
|
|
5
|
+
Maintenance release. Modernized project tooling.
|
|
6
|
+
|
|
7
|
+
Changes:
|
|
8
|
+
|
|
9
|
+
* Replace custom Indexer system with standard gemspec.
|
|
10
|
+
* Replace Travis CI with GitHub Actions.
|
|
11
|
+
* Update Rakefile.
|
|
12
|
+
* Fix typo in version.rb module name (Exectuable -> Executable).
|
|
13
|
+
* Switch tests to minitest.
|
|
14
|
+
* Update URLs to HTTPS.
|
|
15
|
+
* Clean up obsolete files and .gitignore.
|
|
16
|
+
|
|
17
|
+
|
|
3
18
|
## 1.2.1 / 2012-12-19
|
|
4
19
|
|
|
5
20
|
This release imporves the help output and manpage lookup
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
[Report Issue](http://github.com/rubyworks/executable/features) |
|
|
3
|
-
[Source Code](http://github.com/rubyworks/executable)
|
|
4
|
-
( [](http://travis-ci.org/rubyworks/indexer) )
|
|
1
|
+
# Executable
|
|
5
2
|
|
|
3
|
+
[Source Code](https://github.com/rubyworks/executable) |
|
|
4
|
+
[Report Issue](https://github.com/rubyworks/executable/issues)
|
|
6
5
|
|
|
7
|
-
|
|
6
|
+
[](https://rubygems.org/gems/executable)
|
|
7
|
+
[](https://github.com/rubyworks/executable/actions/workflows/test.yml)
|
|
8
8
|
|
|
9
9
|
Executable is to the commandline, what ActiveRecord is the database.
|
|
10
10
|
You can think of Executable as a *COM*, a Command-line Object Mapper,
|
|
@@ -30,7 +30,7 @@ syntax. No special DSL is required.
|
|
|
30
30
|
* Help doesn't handle aliases well (yet).
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
##
|
|
33
|
+
## Overview
|
|
34
34
|
|
|
35
35
|
CLIs can be built by using a Executable as a mixin, or by subclassing
|
|
36
36
|
`Executable::Command`. Methods seemlessly handle command-line options.
|
|
@@ -56,11 +56,11 @@ For example, here is a simple "Hello, World!" commandline tool.
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Show this message.
|
|
59
|
-
def help
|
|
59
|
+
def help!
|
|
60
60
|
cli.show_help
|
|
61
61
|
exit
|
|
62
62
|
end
|
|
63
|
-
alias :h
|
|
63
|
+
alias :h! :help!
|
|
64
64
|
|
|
65
65
|
# Say hello.
|
|
66
66
|
def call(name)
|
|
@@ -122,8 +122,9 @@ to generate the manpages. What's particularly cool about Executable,
|
|
|
122
122
|
is that once we have a manpage in the standard `man/` location in our project,
|
|
123
123
|
the `#show_help` method will use it instead of the plain text.
|
|
124
124
|
|
|
125
|
-
For a more
|
|
126
|
-
|
|
125
|
+
For a more detailed example see [QED](http://rubyworks.github.com/executable/demo.html),
|
|
126
|
+
[API](http://rubydoc.info/gems/executable/frames) documentation and, in particular,
|
|
127
|
+
the [Wiki](http://wiki.github.com/rubyworks/).
|
|
127
128
|
|
|
128
129
|
|
|
129
130
|
## Installation
|
|
@@ -134,26 +135,45 @@ Install with RubyGems in the usual fashion.
|
|
|
134
135
|
$ gem install executable
|
|
135
136
|
```
|
|
136
137
|
|
|
137
|
-
##
|
|
138
|
+
## Contributing
|
|
139
|
+
|
|
140
|
+
Executable is a [Rubyworks](http://rubyworks.github.com) project. As such it largely
|
|
141
|
+
uses in-house tools for development.
|
|
142
|
+
|
|
143
|
+
### Submitting Patches
|
|
144
|
+
|
|
145
|
+
If it is a very small change, just pasting it to an issue is fine. For anything more than
|
|
146
|
+
this please send us a traditional patch, but even better use Github pull requests.
|
|
147
|
+
Good contributions have the following:
|
|
148
|
+
|
|
149
|
+
* Well documented code following the conventions of the project.
|
|
150
|
+
* Clearly written tests with good test coverage written using the project's chosen test framework.
|
|
151
|
+
* Use of a git topic branch to keep the change set well isolated.
|
|
138
152
|
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
The more of these bullet points a pull request covers, the more likely and quickly it will
|
|
154
|
+
be accepted and merged.
|
|
141
155
|
|
|
142
156
|
### Testing
|
|
143
157
|
|
|
144
|
-
QED and Microtest
|
|
158
|
+
[QED](http://rubyworks.github.com/qed) and [Microtest](http://rubyworks.github.com/microtest)
|
|
159
|
+
are used for this project. To run the QED demos just run the `qed` command, probably with bundler,
|
|
160
|
+
so `bundle exec qed`. And to run the microtests you can use `rubytest test/`, again with bundler,
|
|
161
|
+
`bundle exec rubytest test/`.
|
|
145
162
|
|
|
146
163
|
### Getting In Touch
|
|
147
164
|
|
|
148
|
-
|
|
165
|
+
For direct dialog we have an IRC channel, #rubyworks on freenode. But it's not always manned,
|
|
166
|
+
so a [mailing list](http://groups.google.com/groups/rubyworks-mailinglist) is also available.
|
|
167
|
+
Of course these days, the GitHub [issues page](http://github.com/rubyworks/executable) is
|
|
168
|
+
generally the place get in touch for anything specific to this project.
|
|
149
169
|
|
|
150
170
|
|
|
151
171
|
## Copyrights
|
|
152
172
|
|
|
153
173
|
Executable is copyrighted open source software.
|
|
154
174
|
|
|
155
|
-
Copyright (c) 2008 Rubyworks (BSD-2-Clause
|
|
175
|
+
Copyright (c) 2008 Rubyworks (BSD-2-Clause)
|
|
156
176
|
|
|
157
|
-
It can be distributed and modified in accordance with the
|
|
177
|
+
It can be distributed and modified in accordance with the **BSD-2-Clause** license.
|
|
158
178
|
|
|
159
179
|
See LICENSE.txt for details.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module Executable
|
|
2
|
+
|
|
3
|
+
# Encpsulates command completion.
|
|
4
|
+
#
|
|
5
|
+
class Completion
|
|
6
|
+
|
|
7
|
+
#
|
|
8
|
+
# Setup new completion object.
|
|
9
|
+
#
|
|
10
|
+
def initialize(cli_class)
|
|
11
|
+
@cli_class = cli_class
|
|
12
|
+
|
|
13
|
+
@subcommands = nil
|
|
14
|
+
@options = nil
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
#
|
|
18
|
+
alias_method :inspect, :to_s
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# The Executable subclass to which this help applies.
|
|
22
|
+
#
|
|
23
|
+
attr :cli_class
|
|
24
|
+
|
|
25
|
+
#
|
|
26
|
+
# List of subcommands converted to a printable string.
|
|
27
|
+
# But will return +nil+ if there are no subcommands.
|
|
28
|
+
#
|
|
29
|
+
# @return [String,NilClass] subcommand list text
|
|
30
|
+
#
|
|
31
|
+
def subcommands
|
|
32
|
+
@subcommands ||= @cli_class.subcommands.keys
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#
|
|
36
|
+
def options
|
|
37
|
+
@options ||= (
|
|
38
|
+
method_list.map do |meth|
|
|
39
|
+
case meth.name
|
|
40
|
+
when /^(.*?)[\!\=]$/
|
|
41
|
+
name = meth.name.to_s.chomp('!').chomp('=')
|
|
42
|
+
mark = name.to_s.size == 1 ? '-' : '--'
|
|
43
|
+
mark + name
|
|
44
|
+
end
|
|
45
|
+
end.compact.sort
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#
|
|
50
|
+
def to_s
|
|
51
|
+
(subcommands + options).join(' ')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
#
|
|
55
|
+
def call(*args)
|
|
56
|
+
puts self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
#
|
|
62
|
+
# Produce a list relavent methods.
|
|
63
|
+
#
|
|
64
|
+
def method_list
|
|
65
|
+
list = []
|
|
66
|
+
methods = []
|
|
67
|
+
stop_at = cli_class.ancestors.index(Executable::Command) ||
|
|
68
|
+
cli_class.ancestors.index(Executable) ||
|
|
69
|
+
-1
|
|
70
|
+
ancestors = cli_class.ancestors[0...stop_at]
|
|
71
|
+
ancestors.reverse_each do |a|
|
|
72
|
+
a.instance_methods(false).each do |m|
|
|
73
|
+
list << cli_class.instance_method(m)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
list
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
class UnboundMethod
|
|
2
|
+
if !method_defined?(:source_location)
|
|
3
|
+
if Proc.method_defined? :__file__ # /ree/
|
|
4
|
+
def source_location
|
|
5
|
+
[__file__, __line__] rescue nil
|
|
6
|
+
end
|
|
7
|
+
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
|
|
8
|
+
require 'java'
|
|
9
|
+
def source_location
|
|
10
|
+
to_java.source_location(Thread.current.to_java.getContext())
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
def comment
|
|
17
|
+
Source.get_above_comment(*source_location)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Source lookup.
|
|
21
|
+
#
|
|
22
|
+
module Source
|
|
23
|
+
extend self
|
|
24
|
+
|
|
25
|
+
# Read and cache file.
|
|
26
|
+
#
|
|
27
|
+
# @param file [String] filename, should be full path
|
|
28
|
+
#
|
|
29
|
+
# @return [Array] file content in array of lines
|
|
30
|
+
def read(file)
|
|
31
|
+
@read ||= {}
|
|
32
|
+
@read[file] ||= File.readlines(file)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Get comment from file searching up from given line number.
|
|
36
|
+
#
|
|
37
|
+
# @param file [String] filename, should be full path
|
|
38
|
+
# @param line [Integer] line number in file
|
|
39
|
+
#
|
|
40
|
+
def get_above_comment(file, line)
|
|
41
|
+
get_above_comment_lines(file, line).join("\n").strip
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get comment from file searching up from given line number.
|
|
45
|
+
#
|
|
46
|
+
# @param file [String] filename, should be full path
|
|
47
|
+
# @param line [Integer] line number in file
|
|
48
|
+
#
|
|
49
|
+
def get_above_comment_lines(file, line)
|
|
50
|
+
text = read(file)
|
|
51
|
+
index = line - 1
|
|
52
|
+
while index >= 0 && text[index] !~ /^\s*\#/
|
|
53
|
+
return [] if text[index] =~ /^\s*end/
|
|
54
|
+
index -= 1
|
|
55
|
+
end
|
|
56
|
+
rindex = index
|
|
57
|
+
while text[index] =~ /^\s*\#/
|
|
58
|
+
index -= 1
|
|
59
|
+
end
|
|
60
|
+
result = text[index..rindex]
|
|
61
|
+
result = result.map{ |s| s.strip }
|
|
62
|
+
result = result.reject{ |s| s[0,1] != '#' }
|
|
63
|
+
result = result.map{ |s| s.sub(/^#/,'').strip }
|
|
64
|
+
#result = result.reject{ |s| s == "" }
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get comment from file searching down from given line number.
|
|
69
|
+
#
|
|
70
|
+
# @param file [String] filename, should be full path
|
|
71
|
+
# @param line [Integer] line number in file
|
|
72
|
+
#
|
|
73
|
+
def get_following_comment(file, line)
|
|
74
|
+
get_following_comment_lines(file, line).join("\n").strip
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Get comment from file searching down from given line number.
|
|
78
|
+
#
|
|
79
|
+
# @param file [String] filename, should be full path
|
|
80
|
+
# @param line [Integer] line number in file
|
|
81
|
+
#
|
|
82
|
+
def get_following_comment_lines(file, line)
|
|
83
|
+
text = read(file)
|
|
84
|
+
index = line || 0
|
|
85
|
+
while text[index] !~ /^\s*\#/
|
|
86
|
+
return nil if text[index] =~ /^\s*(class|module)/
|
|
87
|
+
index += 1
|
|
88
|
+
end
|
|
89
|
+
rindex = index
|
|
90
|
+
while text[rindex] =~ /^\s*\#/
|
|
91
|
+
rindex += 1
|
|
92
|
+
end
|
|
93
|
+
result = text[index..(rindex-2)]
|
|
94
|
+
result = result.map{ |s| s.strip }
|
|
95
|
+
result = result.reject{ |s| s[0,1] != '#' }
|
|
96
|
+
result = result.map{ |s| s.sub(/^#/,'').strip }
|
|
97
|
+
result.join("\n").strip
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
data/lib/executable/core_ext.rb
CHANGED
|
@@ -1,102 +1 @@
|
|
|
1
|
-
|
|
2
|
-
if !method_defined?(:source_location)
|
|
3
|
-
if Proc.method_defined? :__file__ # /ree/
|
|
4
|
-
def source_location
|
|
5
|
-
[__file__, __line__] rescue nil
|
|
6
|
-
end
|
|
7
|
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
|
|
8
|
-
require 'java'
|
|
9
|
-
def source_location
|
|
10
|
-
to_java.source_location(Thread.current.to_java.getContext())
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
#
|
|
16
|
-
def comment
|
|
17
|
-
Source.get_above_comment(*source_location)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Source lookup.
|
|
21
|
-
#
|
|
22
|
-
module Source
|
|
23
|
-
extend self
|
|
24
|
-
|
|
25
|
-
# Read and cache file.
|
|
26
|
-
#
|
|
27
|
-
# @param file [String] filename, should be full path
|
|
28
|
-
#
|
|
29
|
-
# @return [Array] file content in array of lines
|
|
30
|
-
def read(file)
|
|
31
|
-
@read ||= {}
|
|
32
|
-
@read[file] ||= File.readlines(file)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Get comment from file searching up from given line number.
|
|
36
|
-
#
|
|
37
|
-
# @param file [String] filename, should be full path
|
|
38
|
-
# @param line [Integer] line number in file
|
|
39
|
-
#
|
|
40
|
-
def get_above_comment(file, line)
|
|
41
|
-
get_above_comment_lines(file, line).join("\n").strip
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Get comment from file searching up from given line number.
|
|
45
|
-
#
|
|
46
|
-
# @param file [String] filename, should be full path
|
|
47
|
-
# @param line [Integer] line number in file
|
|
48
|
-
#
|
|
49
|
-
def get_above_comment_lines(file, line)
|
|
50
|
-
text = read(file)
|
|
51
|
-
index = line - 1
|
|
52
|
-
while index >= 0 && text[index] !~ /^\s*\#/
|
|
53
|
-
return [] if text[index] =~ /^\s*end/
|
|
54
|
-
index -= 1
|
|
55
|
-
end
|
|
56
|
-
rindex = index
|
|
57
|
-
while text[index] =~ /^\s*\#/
|
|
58
|
-
index -= 1
|
|
59
|
-
end
|
|
60
|
-
result = text[index..rindex]
|
|
61
|
-
result = result.map{ |s| s.strip }
|
|
62
|
-
result = result.reject{ |s| s[0,1] != '#' }
|
|
63
|
-
result = result.map{ |s| s.sub(/^#/,'').strip }
|
|
64
|
-
#result = result.reject{ |s| s == "" }
|
|
65
|
-
result
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Get comment from file searching down from given line number.
|
|
69
|
-
#
|
|
70
|
-
# @param file [String] filename, should be full path
|
|
71
|
-
# @param line [Integer] line number in file
|
|
72
|
-
#
|
|
73
|
-
def get_following_comment(file, line)
|
|
74
|
-
get_following_comment_lines(file, line).join("\n").strip
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Get comment from file searching down from given line number.
|
|
78
|
-
#
|
|
79
|
-
# @param file [String] filename, should be full path
|
|
80
|
-
# @param line [Integer] line number in file
|
|
81
|
-
#
|
|
82
|
-
def get_following_comment_lines(file, line)
|
|
83
|
-
text = read(file)
|
|
84
|
-
index = line || 0
|
|
85
|
-
while text[index] !~ /^\s*\#/
|
|
86
|
-
return nil if text[index] =~ /^\s*(class|module)/
|
|
87
|
-
index += 1
|
|
88
|
-
end
|
|
89
|
-
rindex = index
|
|
90
|
-
while text[rindex] =~ /^\s*\#/
|
|
91
|
-
rindex += 1
|
|
92
|
-
end
|
|
93
|
-
result = text[index..(rindex-2)]
|
|
94
|
-
result = result.map{ |s| s.strip }
|
|
95
|
-
result = result.reject{ |s| s[0,1] != '#' }
|
|
96
|
-
result = result.map{ |s| s.sub(/^#/,'').strip }
|
|
97
|
-
result.join("\n").strip
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
end
|
|
1
|
+
require 'executable/core_ext/unbound_method'
|
data/lib/executable/domain.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Executable
|
|
|
3
3
|
#
|
|
4
4
|
module Domain
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# TODO: Should this be in Help class?
|
|
7
7
|
def usage_name
|
|
8
8
|
list = []
|
|
9
9
|
ancestors.each do |ancestor|
|
|
@@ -38,18 +38,24 @@ module Executable
|
|
|
38
38
|
# @name
|
|
39
39
|
# end
|
|
40
40
|
#
|
|
41
|
+
# TODO: Currently there is an unfortunate issue with using
|
|
42
|
+
# this helper method. If does not correctly record the location
|
|
43
|
+
# the method is called, so default help message is wrong.
|
|
41
44
|
#
|
|
42
45
|
def attr_switch(name)
|
|
43
|
-
|
|
44
|
-
module_eval
|
|
46
|
+
file, line = *caller[0].split(':')[0..1]
|
|
47
|
+
module_eval(<<-END, file, line.to_i)
|
|
48
|
+
def #{name}=(value)
|
|
49
|
+
@#{name}=(value)
|
|
50
|
+
end
|
|
45
51
|
def #{name}?
|
|
46
52
|
@#{name}
|
|
47
53
|
end
|
|
48
|
-
|
|
54
|
+
END
|
|
49
55
|
end
|
|
50
56
|
|
|
51
57
|
#
|
|
52
|
-
#
|
|
58
|
+
# Alias a switch.
|
|
53
59
|
#
|
|
54
60
|
def alias_switch(name, origin)
|
|
55
61
|
alias_method "#{name}=", "#{origin}="
|
|
@@ -57,7 +63,7 @@ module Executable
|
|
|
57
63
|
end
|
|
58
64
|
|
|
59
65
|
#
|
|
60
|
-
#
|
|
66
|
+
# Alias an accessor.
|
|
61
67
|
#
|
|
62
68
|
def alias_accessor(name, origin)
|
|
63
69
|
alias_method "#{name}=", "#{origin}="
|
|
@@ -72,7 +78,8 @@ module Executable
|
|
|
72
78
|
end
|
|
73
79
|
|
|
74
80
|
#
|
|
75
|
-
# Returns `help.to_s`.
|
|
81
|
+
# Returns `help.to_s`. If you want to provide your own help
|
|
82
|
+
# text you can override this method in your command subclass.
|
|
76
83
|
#
|
|
77
84
|
def to_s
|
|
78
85
|
cli.to_s
|
data/lib/executable/help.rb
CHANGED
|
@@ -5,9 +5,6 @@ module Executable
|
|
|
5
5
|
# Encpsulates command help for defining and displaying well formated help
|
|
6
6
|
# output in plain text, markdown or via manpages if found.
|
|
7
7
|
#
|
|
8
|
-
# TODO: Currently doesn't hande aliases/shortcuts well and simply
|
|
9
|
-
# lists them as separate entries.
|
|
10
|
-
#
|
|
11
8
|
# Creating text help in the fly is fine for personal projects, but
|
|
12
9
|
# for production app, ideally you want to have man pages. You can
|
|
13
10
|
# use the #markdown method to generate `.ronn` files and use the
|
|
@@ -30,7 +27,9 @@ module Executable
|
|
|
30
27
|
}
|
|
31
28
|
end
|
|
32
29
|
|
|
30
|
+
#
|
|
33
31
|
# Setup new help object.
|
|
32
|
+
#
|
|
34
33
|
def initialize(cli_class)
|
|
35
34
|
@cli_class = cli_class
|
|
36
35
|
|
|
@@ -198,8 +197,8 @@ module Executable
|
|
|
198
197
|
end
|
|
199
198
|
|
|
200
199
|
if !options.empty?
|
|
201
|
-
s << "OPTIONS\n" + options.map{ |max,
|
|
202
|
-
"
|
|
200
|
+
s << "OPTIONS\n" + options.map{ |max, opts, desc|
|
|
201
|
+
" %-#{max}s %s" % [opts.join(' '), desc]
|
|
203
202
|
}.join("\n")
|
|
204
203
|
end
|
|
205
204
|
|
|
@@ -240,8 +239,8 @@ module Executable
|
|
|
240
239
|
|
|
241
240
|
if !options.empty?
|
|
242
241
|
s << "## OPTIONS"
|
|
243
|
-
s << options.map{ |max,
|
|
244
|
-
" *
|
|
242
|
+
s << options.map{ |max, opts, desc|
|
|
243
|
+
" * `%s`:\n %s" % [opts.join(' '), desc]
|
|
245
244
|
}.join("\n\n")
|
|
246
245
|
end
|
|
247
246
|
|
|
@@ -301,10 +300,17 @@ module Executable
|
|
|
301
300
|
end
|
|
302
301
|
end
|
|
303
302
|
|
|
304
|
-
|
|
303
|
+
# if two options have the same description, they must aliases
|
|
304
|
+
aliased_options = option_list.group_by{ |opt| opt.description }
|
|
305
|
+
|
|
306
|
+
list = aliased_options.map do |desc, opts|
|
|
307
|
+
[opts.map{ |o| "%s%s" % [o.mark, o.usage] }, desc]
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
max = list.map{ |opts, desc| opts.join(' ').size }.max.to_i + 2
|
|
305
311
|
|
|
306
|
-
|
|
307
|
-
[max,
|
|
312
|
+
list.map do |opts, desc|
|
|
313
|
+
[max, opts, desc]
|
|
308
314
|
end
|
|
309
315
|
end
|
|
310
316
|
|
|
@@ -350,11 +356,11 @@ module Executable
|
|
|
350
356
|
-1
|
|
351
357
|
ancestors = cli_class.ancestors[0...stop_at]
|
|
352
358
|
ancestors.reverse_each do |a|
|
|
353
|
-
a.
|
|
359
|
+
a.public_instance_methods(false).each do |m|
|
|
354
360
|
list << cli_class.instance_method(m)
|
|
355
361
|
end
|
|
356
362
|
end
|
|
357
|
-
list
|
|
363
|
+
list #.uniq
|
|
358
364
|
end
|
|
359
365
|
|
|
360
366
|
#
|
data/lib/executable/parser.rb
CHANGED
|
@@ -25,7 +25,12 @@ module Executable
|
|
|
25
25
|
|
|
26
26
|
cmd, argv = parse_subcommand(argv)
|
|
27
27
|
cli = cmd.new
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
if argv.first == "_"
|
|
30
|
+
cli = Completion.new(cli.class)
|
|
31
|
+
else
|
|
32
|
+
args = parse_arguments(cli, argv)
|
|
33
|
+
end
|
|
29
34
|
|
|
30
35
|
return cli, args
|
|
31
36
|
end
|
|
@@ -141,8 +146,7 @@ module Executable
|
|
|
141
146
|
end
|
|
142
147
|
end
|
|
143
148
|
|
|
144
|
-
# TODO: parse_flags needs some thought concerning character
|
|
145
|
-
# spliting and arguments.
|
|
149
|
+
# TODO: parse_flags needs some thought concerning character spliting and arguments.
|
|
146
150
|
|
|
147
151
|
#
|
|
148
152
|
# Parse single-dash command-line option.
|
data/lib/executable/version.rb
CHANGED
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
module
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
DIRECTORY = File.dirname(__FILE__)
|
|
5
|
-
|
|
6
|
-
#
|
|
7
|
-
def self.const_missing(name)
|
|
8
|
-
index[name.to_s.downcase] || super(name)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
#
|
|
12
|
-
def self.index
|
|
13
|
-
@index ||= (
|
|
14
|
-
require 'yaml'
|
|
15
|
-
YAML.load(File.new(DIRECTORY + '/../executable.yml'))
|
|
16
|
-
)
|
|
17
|
-
end
|
|
18
|
-
|
|
1
|
+
module Executable
|
|
2
|
+
VERSION = '1.3.0'
|
|
19
3
|
end
|
|
20
|
-
|
data/lib/executable.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require 'executable/errors'
|
|
4
4
|
require 'executable/parser'
|
|
5
5
|
require 'executable/help'
|
|
6
|
+
require 'executable/completion'
|
|
6
7
|
require 'executable/utils'
|
|
7
8
|
require 'executable/domain'
|
|
8
9
|
require 'executable/dispatch'
|
|
@@ -58,7 +59,7 @@ public
|
|
|
58
59
|
end
|
|
59
60
|
|
|
60
61
|
#
|
|
61
|
-
# Access to underlying
|
|
62
|
+
# Access to underlying cli instance.
|
|
62
63
|
#
|
|
63
64
|
def cli
|
|
64
65
|
self.class.cli
|