executable 1.2.0 → 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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/{HISTORY.rdoc → HISTORY.md} +31 -4
  3. data/LICENSE.txt +22 -0
  4. data/{README.rdoc → README.md} +66 -31
  5. data/demo/{00_introduction.rdoc → 00_introduction.md} +1 -1
  6. data/demo/{01_single_command.rdoc → 01_single_command.md} +1 -1
  7. data/demo/{02_multiple_commands.rdoc → 02_multiple_commands.md} +1 -1
  8. data/demo/{03_help_text.rdoc → 03_help_text.md} +5 -5
  9. data/demo/{04_manpage.rdoc → 04_manpage.md} +4 -4
  10. data/demo/{06_delegate_example.rdoc → 06_delegate_example.md} +1 -1
  11. data/demo/{07_command_methods.rdoc → 07_command_methods.md} +1 -1
  12. data/demo/{08_dispatach.rdoc → 08_dispatach.md} +1 -1
  13. data/demo/{05_optparse_example.rdoc → 09_optparse_example.md} +1 -1
  14. data/demo/samples/bin/hello +9 -2
  15. data/lib/executable/completion.rb +82 -0
  16. data/lib/executable/core_ext/unbound_method.rb +102 -0
  17. data/lib/executable/core_ext.rb +1 -102
  18. data/lib/executable/dispatch.rb +1 -1
  19. data/lib/executable/domain.rb +52 -5
  20. data/lib/executable/help.rb +28 -19
  21. data/lib/executable/parser.rb +7 -3
  22. data/lib/executable/version.rb +2 -22
  23. data/lib/executable.rb +3 -2
  24. metadata +43 -89
  25. data/.ruby +0 -61
  26. data/.yardopts +0 -7
  27. data/COPYING.rdoc +0 -35
  28. data/DEMO.rdoc +0 -568
  29. data/Schedule.reap +0 -17
  30. data/meta/authors +0 -2
  31. data/meta/copyrights +0 -3
  32. data/meta/created +0 -1
  33. data/meta/description +0 -6
  34. data/meta/name +0 -1
  35. data/meta/organization +0 -1
  36. data/meta/repositories +0 -2
  37. data/meta/requirements +0 -6
  38. data/meta/resources +0 -7
  39. data/meta/summary +0 -1
  40. data/meta/version +0 -1
  41. 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
@@ -1,6 +1,33 @@
1
- = RELEASE HISTORY
1
+ # RELEASE HISTORY
2
2
 
3
- == 1.2.0 / 2012-01-31
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
+
18
+ ## 1.2.1 / 2012-12-19
19
+
20
+ This release imporves the help output and manpage lookup
21
+ as well as a few helpful additions the the API.
22
+
23
+ Changes:
24
+
25
+ * Improve manpage lookup.
26
+ * Improve overall help output.
27
+ * Add alias_switch and alias_accessor helpers.
28
+
29
+
30
+ ## 1.2.0 / 2012-01-31
4
31
 
5
32
  Version 1.2.0 is complete rewrite of Executable. Actually it was decided that
6
33
  the old design was too simplistic in it design concept, so another library
@@ -20,7 +47,7 @@ Changes:
20
47
  * Supports manpage look-up and display.
21
48
 
22
49
 
23
- == 1.1.0 / 2011-04-21
50
+ ## 1.1.0 / 2011-04-21
24
51
 
25
52
  This release simplifies Executable, removing the #option_missing method
26
53
  and using the standard #method_missing callback instead. Along with this
@@ -44,7 +71,7 @@ Changes:
44
71
  * The #method_missing callback takes the value of the option being set.
45
72
 
46
73
 
47
- == 1.0.0 / 2011-04-15
74
+ ## 1.0.0 / 2011-04-15
48
75
 
49
76
  This is the initialize release of Executable (as a stand alone project).
50
77
  Executable is a mixin that can turn any class into an commandline interface.
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ BSD-2-Clause License
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice,
7
+ this list of conditions and the following disclaimer.
8
+
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
14
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
15
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
16
+ COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
17
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
22
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -1,14 +1,10 @@
1
- = Executable
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]
3
+ [Source Code](https://github.com/rubyworks/executable) |
4
+ [Report Issue](https://github.com/rubyworks/executable/issues)
7
5
 
8
- {<img src="http://travis-ci.org/rubyworks/executable.png"/>}[http://travis-ci.org/rubyworks/executable]
9
-
10
-
11
- == DESCRIPTION
6
+ [![Gem Version](https://img.shields.io/gem/v/executable.svg?style=flat)](https://rubygems.org/gems/executable)
7
+ [![Build Status](https://github.com/rubyworks/executable/actions/workflows/test.yml/badge.svg)](https://github.com/rubyworks/executable/actions/workflows/test.yml)
12
8
 
13
9
  Executable is to the commandline, what ActiveRecord is the database.
14
10
  You can think of Executable as a *COM*, a Command-line Object Mapper,
@@ -18,7 +14,7 @@ a complete command line tool using nothing more than Ruby's standard
18
14
  syntax. No special DSL is required.
19
15
 
20
16
 
21
- == FEATURES
17
+ ## Features
22
18
 
23
19
  * Easy to use, just mixin or subclass.
24
20
  * Define #call to control the command procedure.
@@ -28,18 +24,13 @@ syntax. No special DSL is required.
28
24
  * Generate help in plain text or markdown.
29
25
 
30
26
 
31
- == LIMITATIONS
27
+ ## Limitations
32
28
 
33
29
  * Ruby 1.9+ only.
34
30
  * Help doesn't handle aliases well (yet).
35
31
 
36
32
 
37
- == RELEASE NOTES
38
-
39
- Please see HISTORY.rdoc file.
40
-
41
-
42
- == SYNOPSIS
33
+ ## Overview
43
34
 
44
35
  CLIs can be built by using a Executable as a mixin, or by subclassing
45
36
  `Executable::Command`. Methods seemlessly handle command-line options.
@@ -48,13 +39,14 @@ methods (those ending in '?') modify them to be boolean switches.
48
39
 
49
40
  For example, here is a simple "Hello, World!" commandline tool.
50
41
 
42
+ ```ruby
51
43
  require 'executable'
52
44
 
53
45
  class HelloCommand
54
46
  include Executable
55
47
 
56
48
  # Say it in uppercase?
57
- def load=(bool)
49
+ def loud=(bool)
58
50
  @loud = bool
59
51
  end
60
52
 
@@ -64,11 +56,11 @@ For example, here is a simple "Hello, World!" commandline tool.
64
56
  end
65
57
 
66
58
  # Show this message.
67
- def help?
59
+ def help!
68
60
  cli.show_help
69
61
  exit
70
62
  end
71
- alias :h? :help?
63
+ alias :h! :help!
72
64
 
73
65
  # Say hello.
74
66
  def call(name)
@@ -78,17 +70,21 @@ For example, here is a simple "Hello, World!" commandline tool.
78
70
  puts str
79
71
  end
80
72
  end
73
+ ```
81
74
 
82
75
  To make the command available on the command line, add an executable
83
- to your project passing ARGV to the #execute or #run methods.
76
+ to your project calling the #execute or #run methods.
84
77
 
78
+ ```ruby
85
79
  #!usr/bin/env ruby
86
80
  require 'hello.rb'
87
81
  HelloCommand.run
82
+ ```
88
83
 
89
84
  If we named this file `hello`, set its execute flag and made it available
90
85
  on our systems $PATH, then:
91
86
 
87
+ ```
92
88
  $ hello
93
89
  Hello, World!
94
90
 
@@ -97,9 +93,11 @@ on our systems $PATH, then:
97
93
 
98
94
  $ hello --loud John
99
95
  HELLO, JOHN!
96
+ ```
100
97
 
101
98
  Executable can also generate help text for commands.
102
99
 
100
+ ```
103
101
  $ hello --help
104
102
  USAGE: hello [options]
105
103
 
@@ -107,38 +105,75 @@ Executable can also generate help text for commands.
107
105
 
108
106
  --loud Say it in uppercase?
109
107
  --help Show this message
108
+ ```
110
109
 
111
110
  If you look back at the class definition you can see it's pulling
112
111
  comments from the source to provide descriptions. It pulls the
113
- description the command itself from the `#call` method.
112
+ description for the command itself from the `#call` method.
114
113
 
115
114
  Basic help like this is fine for personal tools, but for public facing
116
115
  production applications it is desirable to utilize manpages. To this end,
117
116
  Executable provides Markdown formatted help as well. We can access this,
118
117
  for example, via `HelloCommand.help.markdown`. The idea with this is that
119
118
  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]
119
+ file, edit it to perfection and then use tools such a [ronn](https://github.com/rtomayko/ronn),
120
+ [binman](https://github.com/sunaku/binman) or [md2man](https://github.com/sunaku/md2man)
122
121
  to generate the manpages. What's particularly cool about Executable,
123
122
  is that once we have a manpage in the standard `man/` location in our project,
124
123
  the `#show_help` method will use it instead of the plain text.
125
124
 
126
- For a more detail example see {QED}[demo.html]
127
- and {API}[http://rubydoc.info/gems/executable/frames] documentation.
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/).
128
128
 
129
129
 
130
- == INSTALLATION
130
+ ## Installation
131
131
 
132
132
  Install with RubyGems in the usual fashion.
133
133
 
134
+ ```
134
135
  $ gem install executable
136
+ ```
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.
152
+
153
+ The more of these bullet points a pull request covers, the more likely and quickly it will
154
+ be accepted and merged.
155
+
156
+ ### Testing
157
+
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/`.
162
+
163
+ ### Getting In Touch
164
+
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.
135
169
 
136
170
 
137
- == LEGAL
171
+ ## Copyrights
138
172
 
139
- Copyright (c) 2008 Rubyworks
173
+ Executable is copyrighted open source software.
140
174
 
141
- Distributable in accordance with the *BSD-2-Clause* license.
175
+ Copyright (c) 2008 Rubyworks (BSD-2-Clause)
142
176
 
143
- See COPYING.rdoc for licensing details.
177
+ It can be distributed and modified in accordance with the **BSD-2-Clause** license.
144
178
 
179
+ See LICENSE.txt for details.
@@ -1,4 +1,4 @@
1
- = Executable
1
+ # Executable
2
2
 
3
3
  Require Executable library.
4
4
 
@@ -1,4 +1,4 @@
1
- == No Subcommmands
1
+ ## No Subcommmands
2
2
 
3
3
  This example demonstrates using Executable::Command to create a simple command line
4
4
  interface without subcommands. (Note the Executable mixin could be used just
@@ -1,4 +1,4 @@
1
- == Multiple Subcommmands
1
+ ## Multiple Subcommmands
2
2
 
3
3
  Setup an example CLI subclass.
4
4
 
@@ -1,4 +1,4 @@
1
- == Command Help
1
+ ## Command Help
2
2
 
3
3
  Executable Commands can generate help output. It does this by extracting
4
4
  the commenst associated with the option methods. A description of the
@@ -62,7 +62,7 @@ The help output,
62
62
 
63
63
  should be clearly laid out as follows:
64
64
 
65
- Usage: mycli-c1 [options...] [subcommand]
65
+ Usage: my c1 [options...] [subcommand]
66
66
 
67
67
  This does c1.
68
68
 
@@ -81,12 +81,12 @@ The help feature can also output ronn-style markdown,
81
81
 
82
82
  should be clearly laid out as follows:
83
83
 
84
- mycli-c1(1) - This does c1.
85
- ===========================
84
+ my-c1(1) - This does c1.
85
+ ========================
86
86
 
87
87
  ## SYNOPSIS
88
88
 
89
- `mycli-c1` [options...] [subcommand]
89
+ `my c1` [options...] [subcommand]
90
90
 
91
91
  ## DESCRIPTION
92
92
 
@@ -1,14 +1,14 @@
1
- == Manpage
1
+ ### Manpages
2
2
 
3
3
  If a man page is available for a given command using the #show_help
4
4
  method will automatically find the manpage and display it.
5
5
 
6
6
  sample = File.dirname(__FILE__) + '/samples'
7
7
 
8
- load(sample + '/bin/hello')
9
-
10
- manpage = Hello.cli.manpage
8
+ manpage = `ruby #{sample}/bin/hello --manpage`.strip
11
9
 
12
10
  manpage.assert == sample + '/man/hello.1'
13
11
 
12
+ Note: Would rather use #load for this, but without a `.rb` on
13
+ the `hello` file, it doesn't seem possible.
14
14
 
@@ -1,4 +1,4 @@
1
- = Subclass Example
1
+ ## Subclass Example
2
2
 
3
3
  Lets say we have a class that we would like to work with on
4
4
  the command line, but want to keep the class itself unchanaged
@@ -1,4 +1,4 @@
1
- = README Example
1
+ ## README Example
2
2
 
3
3
  This is the example used in the documentation.
4
4
 
@@ -1,4 +1,4 @@
1
- == Legacy/Dispath
1
+ ## Legacy/Dispath
2
2
 
3
3
  The Dispatch mixin, which is also called Legacy b/c this is how older
4
4
  version of Executable worked, provides Executable with a `#call` method
@@ -1,4 +1,4 @@
1
- == OptionParser Example
1
+ ## OptionParser Example
2
2
 
3
3
  This example mimics the one given in optparse.rb documentation.
4
4
 
@@ -1,10 +1,10 @@
1
- #!/usr/bin/env
1
+ #!/usr/bin/env ruby
2
2
 
3
3
  require 'executable'
4
4
 
5
5
  class Hello < Executable::Command
6
6
  # Say it in uppercase?
7
- def load=(bool)
7
+ def loud=(bool)
8
8
  @loud = bool
9
9
  end
10
10
 
@@ -27,5 +27,12 @@ class Hello < Executable::Command
27
27
  str = str.upcase if loud?
28
28
  puts str
29
29
  end
30
+
31
+ # Where's the manpage?
32
+ def manpage!
33
+ puts cli.manpage
34
+ exit
35
+ end
30
36
  end
31
37
 
38
+ Hello.execute
@@ -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