commandeer 0.0.1
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.
- data/.gitignore +4 -0
- data/Gemfile +1 -0
- data/commandeer.gemspec +17 -0
- data/lib/commandeer.rb +138 -0
- data/test/helpers/all_override.rb +11 -0
- data/test/helpers/command.rb +28 -0
- data/test/helpers/namespaced.rb +11 -0
- data/test/helpers/sub_of_real_command.rb +55 -0
- data/test/helpers/subcommand.rb +28 -0
- data/test/test_commands.rb +0 -0
- data/test/test_helper.rb +4 -0
- data/test/test_output.rb +0 -0
- metadata +66 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
source "http://rubygems.org"
|
data/commandeer.gemspec
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/commandeer', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["John E. Vincent"]
|
6
|
+
gem.email = ["lusis.org+github.com@gmail.com"]
|
7
|
+
gem.description = %q{Class-based CLI utility}
|
8
|
+
gem.summary = "Commandeer allows you to make any class a git style command or subcommand"
|
9
|
+
gem.homepage = "https://github.com/lusis/commandeer"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "commandeer"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Commandeer::VERSION
|
17
|
+
end
|
data/lib/commandeer.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
module Commandeer
|
2
|
+
VERSION = "0.0.1"
|
3
|
+
|
4
|
+
@commands = {}
|
5
|
+
|
6
|
+
def self.commands
|
7
|
+
@commands
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.reset!
|
11
|
+
@commands = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.add_command(opts)
|
15
|
+
command = opts[:command].to_s
|
16
|
+
parent = opts[:parent].to_s
|
17
|
+
klass = opts[:klass].to_s
|
18
|
+
parser = opts[:parser].to_s
|
19
|
+
h = {}
|
20
|
+
case parent
|
21
|
+
when "top"
|
22
|
+
h = {:parser => parser, :klass => klass}
|
23
|
+
@commands[command] ||= {}
|
24
|
+
@commands[command].merge!(h)
|
25
|
+
else
|
26
|
+
h[command] = {:parser => parser, :klass => klass}
|
27
|
+
@commands[parent] ||= {}
|
28
|
+
@commands[parent]["subcommands"] ||= {}
|
29
|
+
@commands[parent]["subcommands"].merge!(h)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.parse!(args, script_name=__FILE__)
|
34
|
+
@script_name = script_name
|
35
|
+
# We haz no commands registered
|
36
|
+
if @commands.size == 0
|
37
|
+
puts "No known commands!"
|
38
|
+
exit(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
# No args. Let's show what we have registered
|
42
|
+
if (args.size == 0) || (args[0] =~ /^-/)
|
43
|
+
puts "Usage: #{@script_name} [command options] or [command subcommand options]\n\n"
|
44
|
+
puts "Registered commands\n"
|
45
|
+
@commands.each do |command, options|
|
46
|
+
next if (command==:klass || command==:parser)
|
47
|
+
puts " #{command}"
|
48
|
+
unless options["subcommands"].nil?
|
49
|
+
puts " subcommands:"
|
50
|
+
options["subcommands"].each do |sub, opts|
|
51
|
+
puts " #{sub}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
# Workflow
|
59
|
+
# Check if current scope is a valid command
|
60
|
+
scope = args.shift
|
61
|
+
|
62
|
+
# set the current command or return help
|
63
|
+
@commands.has_key?(scope) ? command=@commands[scope] : (puts "Unknown command: #{scope}\n"; self.parse!("-h"))
|
64
|
+
|
65
|
+
subcommands = command["subcommands"]
|
66
|
+
|
67
|
+
if subcommands
|
68
|
+
output = ''
|
69
|
+
output << "`#{scope}` has the following registered subcommands:\n"
|
70
|
+
subcommands.keys.each {|x| output << "\t#{x}" }
|
71
|
+
puts output
|
72
|
+
end if args.size == 0
|
73
|
+
|
74
|
+
if command.has_key?(:parser)
|
75
|
+
output = ''
|
76
|
+
output << "\n`#{scope}` also takes options"
|
77
|
+
output << "\ntry running '#{@script_name} #{scope} --help'"
|
78
|
+
puts output
|
79
|
+
end if args.size == 0
|
80
|
+
|
81
|
+
if args.size > 0
|
82
|
+
if subcommands.has_key?(args[0])
|
83
|
+
# Okay so the next arg is a registered subcommand. Let's shift args
|
84
|
+
new_scope = args.shift
|
85
|
+
warning =<<-EOF
|
86
|
+
Warning! `#{new_scope}` is a registered subcommand for `#{scope}` but `#{scope}` also takes options.
|
87
|
+
This can cause unexpected results if `#{scope}` has an option named `#{new_scope}`
|
88
|
+
EOF
|
89
|
+
puts warning
|
90
|
+
|
91
|
+
parser = subcommands[new_scope][:parser]
|
92
|
+
klass = subcommands[new_scope][:klass]
|
93
|
+
else
|
94
|
+
parser = command[:parser]
|
95
|
+
klass = command[:klass]
|
96
|
+
end
|
97
|
+
else
|
98
|
+
parser = command[:parser]
|
99
|
+
klass = command[:klass]
|
100
|
+
end
|
101
|
+
begin
|
102
|
+
handler = constantize(klass)
|
103
|
+
handler.send parser.to_sym, args
|
104
|
+
rescue NoMethodError
|
105
|
+
if command[:parser]
|
106
|
+
puts "#{handler}##{parser} method does not exist"
|
107
|
+
end
|
108
|
+
rescue Exception => e
|
109
|
+
puts e.message
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Ripped from the ActiveSupport headlines!
|
114
|
+
def self.constantize(camel_cased_word)
|
115
|
+
names = camel_cased_word.split('::')
|
116
|
+
names.shift if names.empty? || names.first.empty?
|
117
|
+
|
118
|
+
constant = Object
|
119
|
+
names.each do |name|
|
120
|
+
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
121
|
+
end
|
122
|
+
constant
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.included(base)
|
126
|
+
base.extend ClassMethods
|
127
|
+
end
|
128
|
+
|
129
|
+
module ClassMethods
|
130
|
+
def command(command, opts={})
|
131
|
+
opts[:command] = command
|
132
|
+
opts[:parent] ||= :top
|
133
|
+
opts[:klass] ||= self.to_s
|
134
|
+
opts[:parser] ||= :parse
|
135
|
+
Commandeer.add_command(opts)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class PrimaryCommand
|
4
|
+
include Commandeer
|
5
|
+
|
6
|
+
command "foo"
|
7
|
+
|
8
|
+
def self.parse(args)
|
9
|
+
options = {}
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.banner = "foo [options]"
|
12
|
+
|
13
|
+
opts.on("-f", "--foo FOOTHING", "The option for foo") do |f|
|
14
|
+
options[:f] = f
|
15
|
+
end
|
16
|
+
opts.on_tail("-h", "--help", "foo help") do
|
17
|
+
puts opts
|
18
|
+
exit(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
opts.parse!(args)
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
puts e
|
25
|
+
puts opts
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class Parent
|
4
|
+
include Commandeer
|
5
|
+
|
6
|
+
command "parent"
|
7
|
+
|
8
|
+
def self.parse(args)
|
9
|
+
options = {}
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.banner = "parent [options]"
|
12
|
+
|
13
|
+
opts.on("-p", "--parent PARENTTHING", "The option for parent") do |f|
|
14
|
+
options[:f] = f
|
15
|
+
end
|
16
|
+
opts.on_tail("-h", "--help", "parent help") do
|
17
|
+
puts opts
|
18
|
+
exit(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
opts.parse!(args)
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
puts e
|
25
|
+
puts opts
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Child
|
31
|
+
include Commandeer
|
32
|
+
|
33
|
+
command "child", :parent => 'parent'
|
34
|
+
|
35
|
+
def self.parse(args)
|
36
|
+
options = {}
|
37
|
+
opts = OptionParser.new do |opts|
|
38
|
+
opts.banner = "child [options]"
|
39
|
+
|
40
|
+
opts.on("-c", "--child CHILDTHING", "The option for child") do |f|
|
41
|
+
options[:f] = f
|
42
|
+
end
|
43
|
+
opts.on_tail("-h", "--help", "child help") do
|
44
|
+
puts opts
|
45
|
+
exit(1)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
begin
|
49
|
+
opts.parse!(args)
|
50
|
+
rescue OptionParser::InvalidOption => e
|
51
|
+
puts e
|
52
|
+
puts opts
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
class SubCommand
|
4
|
+
include Commandeer
|
5
|
+
|
6
|
+
command "bar", :parent => 'fakeparent'
|
7
|
+
|
8
|
+
def self.parse(args)
|
9
|
+
options = {}
|
10
|
+
opts = OptionParser.new do |opts|
|
11
|
+
opts.banner = "bar [options]"
|
12
|
+
|
13
|
+
opts.on("-b", "--bar BARTHING", "The option for bar") do |f|
|
14
|
+
options[:f] = f
|
15
|
+
end
|
16
|
+
opts.on_tail("-h", "--help", "bar help") do
|
17
|
+
puts opts
|
18
|
+
exit(1)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
opts.parse!(args)
|
23
|
+
rescue OptionParser::InvalidOption => e
|
24
|
+
puts e
|
25
|
+
puts opts
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
File without changes
|
data/test/test_helper.rb
ADDED
data/test/test_output.rb
ADDED
File without changes
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: commandeer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- John E. Vincent
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-11-28 00:00:00 Z
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Class-based CLI utility
|
17
|
+
email:
|
18
|
+
- lusis.org+github.com@gmail.com
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files: []
|
24
|
+
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- Gemfile
|
28
|
+
- commandeer.gemspec
|
29
|
+
- lib/commandeer.rb
|
30
|
+
- test/helpers/all_override.rb
|
31
|
+
- test/helpers/command.rb
|
32
|
+
- test/helpers/namespaced.rb
|
33
|
+
- test/helpers/sub_of_real_command.rb
|
34
|
+
- test/helpers/subcommand.rb
|
35
|
+
- test/test_commands.rb
|
36
|
+
- test/test_helper.rb
|
37
|
+
- test/test_output.rb
|
38
|
+
homepage: https://github.com/lusis/commandeer
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
requirements: []
|
59
|
+
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 1.8.6
|
62
|
+
signing_key:
|
63
|
+
specification_version: 3
|
64
|
+
summary: Commandeer allows you to make any class a git style command or subcommand
|
65
|
+
test_files: []
|
66
|
+
|