subcommander 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.markdown +54 -0
  2. data/lib/subcommander.rb +154 -0
  3. metadata +68 -0
@@ -0,0 +1,54 @@
1
+ Subcommander is a wrapper around Ruby's OptionParser class that allows you to have sub-commands like we see in Git and other tools. To get help for your tool, just type in the tool name with no sub-command (or the word "help" as a sub-command). To get help for a sub-command type:
2
+
3
+ your-tool help sub-command
4
+
5
+ More description coming... this is basically a placeholder for now.
6
+
7
+ ### Simple Example:
8
+
9
+ require 'subcommander'
10
+ include Subcommander
11
+
12
+ subcommander.version = '1.0.0'
13
+ subcommander.desc = "SimpleTool is ephemeral and only here to demonstrate Subcommander."
14
+
15
+ subcommand :run, "Runs the simple tool" do |sc|
16
+ sc.opt :just_kidding, '-k', '--just-kidding', "Don't really run the simple tool"
17
+ sc.exec {
18
+ # This is where the subcommand has its implementation
19
+ if sc[:just_kidding]
20
+ puts "Not really running"
21
+ else
22
+ puts "Running!"
23
+ end
24
+ }
25
+ end
26
+
27
+ subcommander.go!
28
+
29
+ ---------------------------
30
+
31
+ # subcommander #
32
+
33
+ Properties you can set:
34
+
35
+ __version__ - The version of the tool you're making. It's printed at the bottom of help.
36
+
37
+ __desc__ - A description of the tool you're using. It's printed at the beginning of help.
38
+
39
+
40
+ ---------------------------
41
+ # subcommand #
42
+
43
+ Properties you can set:
44
+
45
+ __arity__ - This says how many arguemnts are expected to be passed to this sub-command, excluding options and their arguemnts.
46
+
47
+ __help__ - Help that shows up when the user asks for help about the sub-command
48
+
49
+ __usage__ - The typical usage pattern like "simple-tool [options] args"
50
+
51
+ __opt__ - These are exactly the same as OptionParser opt.on() argument lists except the first argument is a token. When the option is invoked, it will put any option arguemnts that were passed into the subcommand keyed by that token. So subcommand[:yoursymbol] is where you'll find it when you're looking for it in your exec block.
52
+
53
+ __exec__ - The implementation for your sub-command should be in a block that's passed to this method.
54
+
@@ -0,0 +1,154 @@
1
+
2
+ require 'optparse'
3
+
4
+ module Subcommander
5
+
6
+ def pop! stack
7
+ [stack[0], stack[1..-1]]
8
+ end
9
+
10
+ class Subcommander
11
+ attr_accessor :desc,
12
+ :args,
13
+ :version
14
+
15
+ def initialize
16
+ @commands = {}
17
+ @descriptions = {}
18
+ @file_order = []
19
+ unless ARGV.empty?
20
+ args = ARGV.clone
21
+ @sub_cmd, @args = pop!(args)
22
+ else
23
+ @args = []
24
+ end
25
+ @desc = "This command needs a description"
26
+ end
27
+
28
+ def subcommand cmd_name, desc, &block
29
+ name = cmd_name.to_s
30
+ sub_cmd = Subcommand.new(@args.clone, cmd_name, desc, &block)
31
+ @commands[name] = sub_cmd
32
+ @file_order << name
33
+ end
34
+
35
+ def go!
36
+ if @sub_cmd && @sub_cmd == "help"
37
+ print_help()
38
+ end
39
+ sub_cmd = @commands[@sub_cmd]
40
+ unless sub_cmd # no subcommand
41
+ print_usage()
42
+ end
43
+ sub_cmd.go!
44
+ end
45
+
46
+ private
47
+ def print_usage
48
+ puts "\n#{@desc}\n\n"
49
+ puts " Subcommands:"
50
+ @file_order.each do |cmd|
51
+ puts " #{slop(cmd)} #{@commands[cmd].desc}"
52
+ end
53
+ puts "\nv " + @version if @version
54
+ exit
55
+ end
56
+
57
+ def slop cmd_name
58
+ cmd_name + ' ' * (@file_order.max.length - cmd_name.length)
59
+ end
60
+
61
+ def print_help
62
+ if @args.empty?
63
+ print_usage
64
+ else
65
+ sub_cmd = @commands[@args[0]]
66
+ if sub_cmd
67
+ sub_cmd.print_help()
68
+ end
69
+ exit
70
+ end
71
+ end
72
+ end
73
+
74
+ def subcommander
75
+ unless defined?(@@subcommander)
76
+ @@subcommander = Subcommander.new
77
+ end
78
+ @@subcommander
79
+ end
80
+
81
+ def subcommand name, desc, &block
82
+ @@subcommander.subcommand(name, desc, &block)
83
+ end
84
+
85
+ class Subcommand
86
+ attr_accessor :arity,
87
+ :name,
88
+ :desc,
89
+ :help,
90
+ :usage,
91
+ :block
92
+
93
+ def initialize args, cmd_name, desc, &block
94
+ @args = args
95
+ @name = cmd_name
96
+ @desc = desc
97
+ @opts = OptionParser.new
98
+ @opts.banner = ""
99
+ @arity = -1 # Don't care how many args
100
+ @props = {}
101
+ block.call(self)
102
+ end
103
+
104
+ def [] key
105
+ @props[key]
106
+ end
107
+
108
+ def banner= str
109
+ @opts.banner = "Usage: #{str}"
110
+ end
111
+
112
+ def opt *orig_args
113
+ args = orig_args.clone()
114
+ prop, args = pop!(args)
115
+ @opts.on *args do |arg|
116
+ @props[prop] = arg
117
+ end
118
+ end
119
+
120
+ def exec &block
121
+ @block = block
122
+ end
123
+
124
+ def go!
125
+ parse_args()
126
+ num_remaining = @props[:remaining_args].length
127
+ if @arity > -1 && num_remaining != @arity
128
+ puts "We expected #{@arity} arguments and #{num_remaining} were provided."
129
+ exit
130
+ end
131
+ @block.call if @block
132
+ end
133
+
134
+ def print_help
135
+ if @help
136
+ puts
137
+ puts @help
138
+ end
139
+ if (@usage)
140
+ @opts.banner = "Usage: #{@usage}"
141
+ puts
142
+ end
143
+ puts @opts.to_s
144
+ puts
145
+ end
146
+
147
+ private
148
+ def parse_args
149
+ @props[:remaining_args] = @opts.parse(@args)
150
+ end
151
+ end
152
+ end
153
+
154
+ # vim: set expandtab tabstop=2 shiftwidth=2 autoindent smartindent:
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: subcommander
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Tom Santos
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-05-12 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: A library for cleanly handling subcommands (and options)
23
+ email: santos.tom@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - README.markdown
32
+ - lib/subcommander.rb
33
+ has_rdoc: true
34
+ homepage: http://github.com/tsantos/subcommander
35
+ licenses: []
36
+
37
+ post_install_message:
38
+ rdoc_options: []
39
+
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ hash: 3
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.7
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: A library for cleanly handling subcommands (and options)
67
+ test_files: []
68
+