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,109 @@
1
+ == Command Help
2
+
3
+ Executable Commands can generate help output. It does this by extracting
4
+ the commenst associated with the option methods. A description of the
5
+ command itself is taken from the comment on the `#call` method. Only
6
+ the first line of a comment is used, so the reset of the comment can
7
+ still be catered to documention tools such as YARD and RDoc.
8
+
9
+ Let's setup an example CLI subclass to demonstrate this.
10
+
11
+ class MyCLI < Executable::Command
12
+
13
+ # This is global option -g.
14
+ # Yadda yadda yadda...
15
+ def g=(bool)
16
+ @g = bool
17
+ end
18
+
19
+ def g?; @g; end
20
+
21
+ # Subcommand `c1`.
22
+ class C1 < self
23
+
24
+ # This does c1.
25
+ def call(*args)
26
+ end
27
+
28
+ # This is option --o1 for c1.
29
+ def o1=(value)
30
+ end
31
+
32
+ # This is option --o2 for c1.
33
+ def o2=(value)
34
+ end
35
+
36
+ end
37
+
38
+ # Subcommand `c2`.
39
+ class C2 < self
40
+
41
+ # This does c2.
42
+ def call(*args)
43
+ end
44
+
45
+ # This is option --o1 for c2.
46
+ def o1=(value)
47
+ end
48
+
49
+ # This is option --o2 for c2.
50
+ def o2=(value)
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ === Plain Text
58
+
59
+ The help output,
60
+
61
+ @out = MyCLI::C1.help.to_s
62
+
63
+ should be clearly laid out as follows:
64
+
65
+ Usage: mycli-c1 [options...] [subcommand]
66
+
67
+ This does c1.
68
+
69
+ OPTIONS
70
+ -g This is global option -g.
71
+ --o1=VALUE This is option --o1 for c1.
72
+ --o2=VALUE This is option --o2 for c1.
73
+
74
+ Copyright (c) 2012
75
+
76
+ === Markdown
77
+
78
+ The help feature can also output ronn-style markdown,
79
+
80
+ @out = MyCLI::C1.help.markdown
81
+
82
+ should be clearly laid out as follows:
83
+
84
+ mycli-c1(1) - This does c1.
85
+ ===========================
86
+
87
+ ## SYNOPSIS
88
+
89
+ `mycli-c1` [options...] [subcommand]
90
+
91
+ ## DESCRIPTION
92
+
93
+ This does c1.
94
+
95
+ ## OPTIONS
96
+
97
+ * `-g`:
98
+ This is global option -g.
99
+
100
+ * `--o1=VALUE`:
101
+ This is option --o1 for c1.
102
+
103
+ * `--o2=VALUE`:
104
+ This is option --o2 for c1.
105
+
106
+ ## COPYRIGHT
107
+
108
+ Copyright (c) 2012
109
+
@@ -0,0 +1,14 @@
1
+ == Manpage
2
+
3
+ If a man page is available for a given command using the #show_help
4
+ method will automatically find the manpage and display it.
5
+
6
+ sample = File.dirname(__FILE__) + '/samples'
7
+
8
+ load(sample + '/bin/hello')
9
+
10
+ manpage = Hello.cli.manpage
11
+
12
+ manpage.assert == sample + '/man/hello.1'
13
+
14
+
@@ -0,0 +1,152 @@
1
+ == OptionParser Example
2
+
3
+ This example mimics the one given in optparse.rb documentation.
4
+
5
+ require 'ostruct'
6
+ require 'time'
7
+
8
+ class ExampleCLI < Executable::Command
9
+
10
+ CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
11
+ CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
12
+
13
+ attr :options
14
+
15
+ def initialize
16
+ super
17
+ reset
18
+ end
19
+
20
+ def reset
21
+ @options = OpenStruct.new
22
+ @options.library = []
23
+ @options.inplace = false
24
+ @options.encoding = "utf8"
25
+ @options.transfer_type = :auto
26
+ @options.verbose = false
27
+ end
28
+
29
+ # Require the LIBRARY before executing your script
30
+ def require=(lib)
31
+ options.library << lib
32
+ end
33
+ alias :r= :require=
34
+
35
+ # Edit ARGV files in place (make backup if EXTENSION supplied)
36
+ def inplace=(ext)
37
+ options.inplace = true
38
+ options.extension = ext
39
+ options.extension.sub!(/\A\.?(?=.)/, ".") # ensure extension begins with dot.
40
+ end
41
+ alias :i= :inplace=
42
+
43
+ # Delay N seconds before executing
44
+ # Cast 'delay' argument to a Float.
45
+ def delay=(n)
46
+ options.delay = n.to_float
47
+ end
48
+
49
+ # Begin execution at given time
50
+ # Cast 'time' argument to a Time object.
51
+ def time=(time)
52
+ options.time = Time.parse(time)
53
+ end
54
+ alias :t= :time=
55
+
56
+ # Specify record separator (default \\0)
57
+ # Cast to octal integer.
58
+ def irs=(octal)
59
+ options.record_separator = octal.to_i(8)
60
+ end
61
+ alias :F= :irs=
62
+
63
+ # Example 'list' of arguments
64
+ # List of arguments.
65
+ def list=(args)
66
+ options.list = list.split(',')
67
+ end
68
+
69
+ # Keyword completion. We are specifying a specific set of arguments (CODES
70
+ # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
71
+ # the shortest unambiguous text.
72
+ CODE_LIST = (CODE_ALIASES.keys + CODES)
73
+
74
+ help.option(:code, "Select encoding (#{CODE_LIST})")
75
+
76
+ # Select encoding
77
+ def code=(code)
78
+ codes = CODE_LIST.select{ |x| /^#{code}/ =~ x }
79
+ codes = codes.map{ |x| CODE_ALIASES.key?(x) ? CODE_ALIASES[x] : x }.uniq
80
+ raise ArgumentError unless codes.size == 1
81
+ options.encoding = codes.first
82
+ end
83
+
84
+ # Select transfer type (text, binary, auto)
85
+ # Optional argument with keyword completion.
86
+ def type=(type)
87
+ raise ArgumentError unless %w{text binary auto}.include(type.downcase)
88
+ options.transfer_type = type.downcase
89
+ end
90
+
91
+ # Run verbosely
92
+ # Boolean switch.
93
+ def verbose=(bool)
94
+ options.verbose = bool
95
+ end
96
+ def verbose?
97
+ @options.verbose
98
+ end
99
+ alias :v= :verbose=
100
+ alias :v? :verbose?
101
+
102
+ # Show this message
103
+ # No argument, shows at tail. This will print an options summary.
104
+ def help!
105
+ puts help_text
106
+ exit
107
+ end
108
+ alias :h! :help!
109
+
110
+ # Show version
111
+ # Another typical switch to print the version.
112
+ def version?
113
+ puts Executor::VERSION
114
+ exit
115
+ end
116
+
117
+ #
118
+ def call
119
+ # ... main procedure here ...
120
+ end
121
+ end
122
+
123
+ We will run some scenarios on this example to make sure it works.
124
+
125
+ cli = ExampleCLI.execute('-r=facets')
126
+ cli.options.library.assert == ['facets']
127
+
128
+ Make sure time option parses.
129
+
130
+ cli = ExampleCLI.execute('--time=2010-10-10')
131
+ cli.options.time.assert == Time.parse('2010-10-10')
132
+
133
+ Make sure code lookup words and is limted to the selections provided.
134
+
135
+ cli = ExampleCLI.execute('--code=ji')
136
+ cli.options.encoding.assert == 'iso-2022-jp'
137
+
138
+ expect ArgumentError do
139
+ ExampleCLI.execute('--code=xxx')
140
+ end
141
+
142
+ Ensure +irs+ is set to an octal number.
143
+
144
+ cli = ExampleCLI.execute('-F 32')
145
+ cli.options.record_separator.assert == 032
146
+
147
+ Ensure extension begins with dot and inplace is set to true.
148
+
149
+ cli = ExampleCLI.execute('--inplace txt')
150
+ cli.options.extension.assert == '.txt'
151
+ cli.options.inplace.assert == true
152
+
@@ -0,0 +1,40 @@
1
+ = Subclass Example
2
+
3
+ Lets say we have a class that we would like to work with on
4
+ the command line, but want to keep the class itself unchanaged
5
+ without mixin.
6
+
7
+ class Hello
8
+ attr_accessor :name
9
+
10
+ def initialize(name="World")
11
+ @name = name
12
+ end
13
+
14
+ def hello
15
+ @output = "Hello, #{name}!"
16
+ end
17
+
18
+ def output
19
+ @output
20
+ end
21
+ end
22
+
23
+ Rather then including Exectuable in the class directly, we can
24
+ create a subclass and use it instead.
25
+
26
+ class HelloCommand < Hello
27
+ include Executable
28
+
29
+ def call(*args)
30
+ hello
31
+ end
32
+ end
33
+
34
+ Now we can execute the command perfectly well.
35
+
36
+ cmd = HelloCommand.execute(['hello', '--name=Fred'])
37
+ cmd.output.assert == "Hello, Fred!"
38
+
39
+ And the original class remains undisturbed.
40
+
@@ -0,0 +1,36 @@
1
+ = README Example
2
+
3
+ This is the example used in the documentation.
4
+
5
+ class Example
6
+ include Executable
7
+
8
+ attr_switch :quiet
9
+
10
+ def bread(*args)
11
+ ["bread", quiet?, *args]
12
+ end
13
+
14
+ def butter(*args)
15
+ ["butter", quiet?, *args]
16
+ end
17
+
18
+ # Route call to methods.
19
+ def call(name, *args)
20
+ meth = public_method(name)
21
+ meth.call(*args)
22
+ end
23
+ end
24
+
25
+ Use a subcommand and an argument.
26
+
27
+ c, a = Example.parse(['butter', 'yum'])
28
+ r = c.call(*a)
29
+ r.assert == ["butter", nil, "yum"]
30
+
31
+ A subcommand and a boolean option.
32
+
33
+ c, a = Example.parse(['bread', '--quiet'])
34
+ r = c.call(*a)
35
+ r.assert == ["bread", true]
36
+
@@ -0,0 +1,29 @@
1
+ == Legacy/Dispath
2
+
3
+ The Dispatch mixin, which is also called Legacy b/c this is how older
4
+ version of Executable worked, provides Executable with a `#call` method
5
+ that automatically routes the to a method given by the first argument.
6
+
7
+ class DispatchExample < Executable::Command
8
+ include Legacy
9
+
10
+ attr :result
11
+
12
+ def foo
13
+ @result = :foo
14
+ end
15
+
16
+ def bar
17
+ @result = :bar
18
+ end
19
+
20
+ end
21
+
22
+ Now when we invoke the command, the
23
+
24
+ eg = DispatchExample.run('foo')
25
+ eg.result.assert == :foo
26
+
27
+ eg = DispatchExample.run('bar')
28
+ eg.result.assert == :bar
29
+
@@ -0,0 +1 @@
1
+ require 'ae'
@@ -0,0 +1,4 @@
1
+ When 'should be clearly laid out as follows' do |text|
2
+ text = text.sub('$0', File.basename($0))
3
+ @out.strip.assert == text.strip
4
+ end
@@ -0,0 +1 @@
1
+ require 'executable'
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env
2
+
3
+ require 'executable'
4
+
5
+ class Hello < Executable::Command
6
+ # Say it in uppercase?
7
+ def load=(bool)
8
+ @loud = bool
9
+ end
10
+
11
+ #
12
+ def loud?
13
+ @loud
14
+ end
15
+
16
+ # Show this message.
17
+ def help?
18
+ cli.show_help
19
+ exit
20
+ end
21
+ alias :h? :help?
22
+
23
+ # Say hello.
24
+ def call(name)
25
+ name = name || 'World'
26
+ str = "Hello, #{name}!"
27
+ str = str.upcase if loud?
28
+ puts str
29
+ end
30
+ end
31
+
@@ -0,0 +1,22 @@
1
+ .\" generated with Ronn/v0.7.3
2
+ .\" http://github.com/rtomayko/ronn/tree/0.7.3
3
+ .
4
+ .TH "HELLOCMD" "1" "January 2012" "" ""
5
+ .
6
+ .SH "NAME"
7
+ \fBhellocmd\fR \- Say hello\.
8
+ .
9
+ .SH "SYNOPSIS"
10
+ \fBhellocmd\fR [options\.\.\.] [subcommand]
11
+ .
12
+ .SH "DESCRIPTION"
13
+ Say hello\.
14
+ .
15
+ .SH "OPTIONS"
16
+ .
17
+ .TP
18
+ \fB\-\-load=BOOL\fR
19
+ Say it in uppercase?
20
+ .
21
+ .SH "COPYRIGHT"
22
+ Copyright (c) 2012