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
@@ -1,102 +1 @@
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
1
+ require 'executable/core_ext/unbound_method'
@@ -2,7 +2,7 @@ module Executable
2
2
 
3
3
  # Variation of Executable which provides basic compatibility with
4
4
  # previous versions of Executable. It provides a call method that
5
- # automatically dispatches to publich methods.
5
+ # automatically dispatches to public methods.
6
6
  #
7
7
  # Among other uses, Dispatch can be useful for dropping into any class
8
8
  # as a quick and dirty way to work with it on the command line.
@@ -3,6 +3,28 @@ module Executable
3
3
  #
4
4
  module Domain
5
5
 
6
+ # TODO: Should this be in Help class?
7
+ def usage_name
8
+ list = []
9
+ ancestors.each do |ancestor|
10
+ break if Executable == ancestor
11
+ list.unshift calculate_command_name(ancestor).to_s.strip
12
+ end
13
+ list.reject{|n| n.empty?}.join(" ")
14
+ end
15
+
16
+ #
17
+ def calculate_command_name(ancestor)
18
+ if ancestor.methods(false).include?(:command_name)
19
+ command_name.to_s
20
+ else
21
+ cname = ancestor.name.sub(/\#\<.*?\>\:\:/,'').split('::').last.downcase
22
+ cname.chomp('command').chomp('cli')
23
+ end
24
+ end
25
+
26
+ private :calculate_command_name
27
+
6
28
  #
7
29
  # Helper method for creating switch attributes.
8
30
  #
@@ -16,14 +38,36 @@ module Executable
16
38
  # @name
17
39
  # end
18
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.
19
44
  #
20
45
  def attr_switch(name)
21
- attr_writer name
22
- 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
23
51
  def #{name}?
24
52
  @#{name}
25
53
  end
26
- }
54
+ END
55
+ end
56
+
57
+ #
58
+ # Alias a switch.
59
+ #
60
+ def alias_switch(name, origin)
61
+ alias_method "#{name}=", "#{origin}="
62
+ alias_method "#{name}?", "#{origin}?"
63
+ end
64
+
65
+ #
66
+ # Alias an accessor.
67
+ #
68
+ def alias_accessor(name, origin)
69
+ alias_method "#{name}=", "#{origin}="
70
+ alias_method "#{name}", "#{origin}"
27
71
  end
28
72
 
29
73
  #
@@ -34,7 +78,8 @@ module Executable
34
78
  end
35
79
 
36
80
  #
37
- # 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.
38
83
  #
39
84
  def to_s
40
85
  cli.to_s
@@ -93,7 +138,9 @@ module Executable
93
138
  consts.inject({}) do |h, c|
94
139
  c = const_get(c)
95
140
  if Class === c && Executable > c
96
- n = c.name.split('::').last.downcase
141
+ n = c.name.split('::').last
142
+ n = n.chomp('Command').chomp('CLI')
143
+ n = n.downcase
97
144
  h[n] = c
98
145
  end
99
146
  h
@@ -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
 
@@ -63,10 +62,7 @@ module Executable
63
62
  # @method name(text=nil)
64
63
  #
65
64
  section(:name) do
66
- str = cli_class.name.sub(/\#\<.*?\>\:\:/,'').downcase.gsub('::','-')
67
- str.chomp!('command')
68
- str.chomp!('cli')
69
- str
65
+ cli_class.usage_name
70
66
  end
71
67
 
72
68
  #
@@ -122,7 +118,7 @@ module Executable
122
118
  #
123
119
  # Show help.
124
120
  #
125
- # @todo man-pages will probably fail on Windows
121
+ # @todo man-pages will probably fail on Windows.
126
122
  #
127
123
  def show_help(hint=nil)
128
124
  if file = manpage(hint)
@@ -132,6 +128,9 @@ module Executable
132
128
  end
133
129
  end
134
130
 
131
+ #
132
+ alias :show :show_help
133
+
135
134
  #
136
135
  def show_manpage(file)
137
136
  #exec "man #{file}"
@@ -155,8 +154,10 @@ module Executable
155
154
  dir = File.dirname(file)
156
155
  end
157
156
 
157
+ man_name = name.gsub(/\s+/, '-') + '.1'
158
+
158
159
  if dir
159
- glob = "man/{man1/,}#{name}.1"
160
+ glob = "man/{man1/,}#{man_name}"
160
161
  lookup(glob, dir)
161
162
  else
162
163
  nil
@@ -196,8 +197,8 @@ module Executable
196
197
  end
197
198
 
198
199
  if !options.empty?
199
- s << "OPTIONS\n" + options.map{ |max, opt|
200
- " %2s%-#{max}s %s" % [opt.mark, opt.usage, opt.description]
200
+ s << "OPTIONS\n" + options.map{ |max, opts, desc|
201
+ " %-#{max}s %s" % [opts.join(' '), desc]
201
202
  }.join("\n")
202
203
  end
203
204
 
@@ -216,10 +217,11 @@ module Executable
216
217
  def markdown
217
218
  commands = text_subcommands
218
219
  options = text_options
220
+ dashname = name.sub(/\s+/, '-')
219
221
 
220
222
  s = []
221
223
 
222
- h = "#{name}(1) - #{text_description}"
224
+ h = "#{dashname}(1) - #{text_description}"
223
225
  s << h + "\n" + ("=" * h.size)
224
226
 
225
227
  s << "## SYNOPSIS"
@@ -237,8 +239,8 @@ module Executable
237
239
 
238
240
  if !options.empty?
239
241
  s << "## OPTIONS"
240
- s << options.map{ |max, opt|
241
- " * `#{opt.mark}%s`:\n %s" % [opt.usage, opt.description]
242
+ s << options.map{ |max, opts, desc|
243
+ " * `%s`:\n %s" % [opts.join(' '), desc]
242
244
  }.join("\n\n")
243
245
  end
244
246
 
@@ -298,10 +300,17 @@ module Executable
298
300
  end
299
301
  end
300
302
 
301
- max = option_list.map{ |opt| opt.usage.size }.max.to_i + 2
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
302
311
 
303
- option_list.map do |opt|
304
- [max, opt]
312
+ list.map do |opts, desc|
313
+ [max, opts, desc]
305
314
  end
306
315
  end
307
316
 
@@ -347,11 +356,11 @@ module Executable
347
356
  -1
348
357
  ancestors = cli_class.ancestors[0...stop_at]
349
358
  ancestors.reverse_each do |a|
350
- a.instance_methods(false).each do |m|
359
+ a.public_instance_methods(false).each do |m|
351
360
  list << cli_class.instance_method(m)
352
361
  end
353
362
  end
354
- list
363
+ list #.uniq
355
364
  end
356
365
 
357
366
  #
@@ -25,7 +25,12 @@ module Executable
25
25
 
26
26
  cmd, argv = parse_subcommand(argv)
27
27
  cli = cmd.new
28
- args = parse_arguments(cli, argv)
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.
@@ -1,23 +1,3 @@
1
- module Exectuable
2
-
3
- #
4
- DIRECTORY = File.dirname(__FILE__)
5
-
6
- #
7
- def self.version
8
- @package ||= (
9
- require 'yaml'
10
- YAML.load(File.new(DIRECTORY + '/version.yml'))
11
- )
12
- end
13
-
14
- #
15
- def self.const_missing(name)
16
- version[name.to_s.downcase] || super(name)
17
- end
18
-
19
- # because Ruby 1.8~ gets in the way
20
- remove_const(:VERSION) if const_defined?(:VERSION)
21
-
1
+ module Executable
2
+ VERSION = '1.3.0'
22
3
  end
23
-
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'
@@ -22,7 +23,7 @@ module Executable
22
23
 
23
24
  #
24
25
  # Default initializer, simply takes a hash of settings
25
- # to set attributes via writer methods. Not existnt
26
+ # to set attributes via writer methods. Not existant
26
27
  # attributes are simply ignored.
27
28
  #
28
29
  def initialize(settings={})
@@ -58,7 +59,7 @@ public
58
59
  end
59
60
 
60
61
  #
61
- # Access to underlying Help instance.
62
+ # Access to underlying cli instance.
62
63
  #
63
64
  def cli
64
65
  self.class.cli
metadata CHANGED
@@ -1,90 +1,63 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: executable
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
5
- prerelease:
4
+ version: 1.3.0
6
5
  platform: ruby
7
6
  authors:
8
- - 7rans
9
- autorequire:
7
+ - Trans
10
8
  bindir: bin
11
9
  cert_chain: []
12
- date: 2012-02-01 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
13
11
  dependencies:
14
12
  - !ruby/object:Gem::Dependency
15
- name: qed
16
- requirement: &16160360 !ruby/object:Gem::Requirement
17
- none: false
13
+ name: rake
14
+ requirement: !ruby/object:Gem::Requirement
18
15
  requirements:
19
- - - ! '>='
16
+ - - ">="
20
17
  - !ruby/object:Gem::Version
21
- version: '0'
18
+ version: '13'
22
19
  type: :development
23
20
  prerelease: false
24
- version_requirements: *16160360
25
- - !ruby/object:Gem::Dependency
26
- name: ae
27
- requirement: &16158800 !ruby/object:Gem::Requirement
28
- none: false
21
+ version_requirements: !ruby/object:Gem::Requirement
29
22
  requirements:
30
- - - ! '>='
23
+ - - ">="
31
24
  - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :development
34
- prerelease: false
35
- version_requirements: *16158800
25
+ version: '13'
36
26
  - !ruby/object:Gem::Dependency
37
- name: detroit
38
- requirement: &16156720 !ruby/object:Gem::Requirement
39
- none: false
27
+ name: minitest
28
+ requirement: !ruby/object:Gem::Requirement
40
29
  requirements:
41
- - - ! '>='
30
+ - - ">="
42
31
  - !ruby/object:Gem::Version
43
- version: '0'
32
+ version: '5'
44
33
  type: :development
45
34
  prerelease: false
46
- version_requirements: *16156720
47
- - !ruby/object:Gem::Dependency
48
- name: simplecov
49
- requirement: &16155820 !ruby/object:Gem::Requirement
50
- none: false
35
+ version_requirements: !ruby/object:Gem::Requirement
51
36
  requirements:
52
- - - ! '>='
37
+ - - ">="
53
38
  - !ruby/object:Gem::Version
54
- version: '0'
55
- type: :development
56
- prerelease: false
57
- version_requirements: *16155820
58
- description: ! 'Think of Executable as a *COM*, a Commandline Object Mapper,
59
-
60
- in much the same way that ActiveRecord is an ORM,
61
-
62
- an Object Relational Mapper. A class utilizing Executable
63
-
64
- can define a complete command line tool using nothing more
65
-
66
- than Ruby''s own method definitions.'
39
+ version: '5'
40
+ description: Think of Executable as a COM, a Commandline Object Mapper, in much the
41
+ same way that ActiveRecord is an ORM. A class utilizing Executable can define a
42
+ complete command line tool using nothing more than Ruby's own method definitions.
67
43
  email:
68
44
  - transfire@gmail.com
69
45
  executables: []
70
46
  extensions: []
71
- extra_rdoc_files:
72
- - HISTORY.rdoc
73
- - DEMO.rdoc
74
- - README.rdoc
75
- - COPYING.rdoc
47
+ extra_rdoc_files: []
76
48
  files:
77
- - .ruby
78
- - .yardopts
79
- - demo/00_introduction.rdoc
80
- - demo/01_single_command.rdoc
81
- - demo/02_multiple_commands.rdoc
82
- - demo/03_help_text.rdoc
83
- - demo/04_manpage.rdoc
84
- - demo/05_optparse_example.rdoc
85
- - demo/06_delegate_example.rdoc
86
- - demo/07_command_methods.rdoc
87
- - demo/08_dispatach.rdoc
49
+ - HISTORY.md
50
+ - LICENSE.txt
51
+ - README.md
52
+ - demo/00_introduction.md
53
+ - demo/01_single_command.md
54
+ - demo/02_multiple_commands.md
55
+ - demo/03_help_text.md
56
+ - demo/04_manpage.md
57
+ - demo/06_delegate_example.md
58
+ - demo/07_command_methods.md
59
+ - demo/08_dispatach.md
60
+ - demo/09_optparse_example.md
88
61
  - demo/applique/ae.rb
89
62
  - demo/applique/compare.rb
90
63
  - demo/applique/exec.rb
@@ -92,7 +65,10 @@ files:
92
65
  - demo/samples/man/hello.1
93
66
  - demo/samples/man/hello.1.html
94
67
  - demo/samples/man/hello.1.ronn
68
+ - lib/executable.rb
69
+ - lib/executable/completion.rb
95
70
  - lib/executable/core_ext.rb
71
+ - lib/executable/core_ext/unbound_method.rb
96
72
  - lib/executable/dispatch.rb
97
73
  - lib/executable/domain.rb
98
74
  - lib/executable/errors.rb
@@ -100,47 +76,25 @@ files:
100
76
  - lib/executable/parser.rb
101
77
  - lib/executable/utils.rb
102
78
  - lib/executable/version.rb
103
- - lib/executable.rb
104
- - meta/authors
105
- - meta/copyrights
106
- - meta/created
107
- - meta/description
108
- - meta/name
109
- - meta/organization
110
- - meta/repositories
111
- - meta/requirements
112
- - meta/resources
113
- - meta/summary
114
- - meta/version
115
- - test/test_executable.rb
116
- - HISTORY.rdoc
117
- - DEMO.rdoc
118
- - Schedule.reap
119
- - README.rdoc
120
- - COPYING.rdoc
121
- homepage: http://rubyworks.github.com/executable
79
+ homepage: https://github.com/rubyworks/executable
122
80
  licenses:
123
81
  - BSD-2-Clause
124
- post_install_message:
82
+ metadata: {}
125
83
  rdoc_options: []
126
84
  require_paths:
127
85
  - lib
128
86
  required_ruby_version: !ruby/object:Gem::Requirement
129
- none: false
130
87
  requirements:
131
- - - ! '>='
88
+ - - ">="
132
89
  - !ruby/object:Gem::Version
133
- version: '0'
90
+ version: '3.1'
134
91
  required_rubygems_version: !ruby/object:Gem::Requirement
135
- none: false
136
92
  requirements:
137
- - - ! '>='
93
+ - - ">="
138
94
  - !ruby/object:Gem::Version
139
95
  version: '0'
140
96
  requirements: []
141
- rubyforge_project:
142
- rubygems_version: 1.8.11
143
- signing_key:
144
- specification_version: 3
97
+ rubygems_version: 3.6.9
98
+ specification_version: 4
145
99
  summary: Commandline Object Mapper
146
100
  test_files: []
data/.ruby DELETED
@@ -1,61 +0,0 @@
1
- ---
2
- source:
3
- - meta
4
- authors:
5
- - name: 7rans
6
- email: transfire@gmail.com
7
- copyrights:
8
- - holder: Rubyworks
9
- year: '2008'
10
- license: BSD-2-Clause
11
- replacements: []
12
- alternatives: []
13
- requirements:
14
- - name: qed
15
- groups:
16
- - test
17
- development: true
18
- - name: ae
19
- groups:
20
- - test
21
- development: true
22
- - name: detroit
23
- groups:
24
- - build
25
- development: true
26
- - name: simplecov
27
- groups:
28
- - build
29
- development: true
30
- dependencies: []
31
- conflicts: []
32
- repositories:
33
- - uri: git://github.com/rubyworks/executable.git
34
- scm: git
35
- name: upstream
36
- resources:
37
- home: http://rubyworks.github.com/executable
38
- code: http://github.com/rubyworks/executable
39
- bugs: http://github.com/rubyworks/executable/issues
40
- mail: http://groups.google.com/rubyworks-mailinglist
41
- chat: irc://chat.us.freenode.net#rubyworks
42
- extra: {}
43
- load_path:
44
- - lib
45
- revision: 0
46
- created: '2008-08-08'
47
- summary: Commandline Object Mapper
48
- version: 1.2.0
49
- name: executable
50
- title: Executable
51
- description: ! 'Think of Executable as a *COM*, a Commandline Object Mapper,
52
-
53
- in much the same way that ActiveRecord is an ORM,
54
-
55
- an Object Relational Mapper. A class utilizing Executable
56
-
57
- can define a complete command line tool using nothing more
58
-
59
- than Ruby''s own method definitions.'
60
- organization: rubyworks
61
- date: '2012-01-31'
data/.yardopts DELETED
@@ -1,7 +0,0 @@
1
- --title "Executable"
2
- --readme README.rdoc
3
- --protected
4
- --private
5
- lib
6
- -
7
- [A-Z]*.*