executable 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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