cl 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/Gemfile.lock +2 -1
- data/NOTES.md +25 -0
- data/README.md +3 -4
- data/examples/args/cast.rb +63 -0
- data/examples/args/opts.rb +32 -0
- data/examples/args/required.rb +35 -0
- data/examples/args/splat.rb +52 -0
- data/examples/gem.rb +81 -0
- data/examples/heroku.rb +41 -0
- data/examples/multi.rb +50 -0
- data/lib/cl.rb +30 -4
- data/lib/cl/arg.rb +60 -0
- data/lib/cl/args.rb +66 -0
- data/lib/cl/cmd.rb +33 -11
- data/lib/cl/format/cmd.rb +1 -1
- data/lib/cl/format/list.rb +2 -2
- data/lib/cl/format/usage.rb +2 -2
- data/lib/cl/help.rb +6 -4
- data/lib/cl/options.rb +2 -0
- data/lib/cl/runner/default.rb +42 -0
- data/lib/cl/runner/multi.rb +33 -0
- data/lib/cl/version.rb +1 -1
- metadata +16 -6
- data/lib/cl/cmds.rb +0 -28
- data/lib/cl/runner.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 111acf22811807b4d15e0809ce6572d621446e47
|
4
|
+
data.tar.gz: 9a6b0db3a509ee87bc9206e1ea2093e108ba7821
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e78849c155cdabd3b05dd5740ff4ef2308d67b315d9937f815fb951b5856de314a3b1342dbf6411ba4fd3e6d953114b1d3ab526a0d929877b0c50003d80aa30
|
7
|
+
data.tar.gz: 1991a74dd9085e2ddcb09ed80e64845666524803edc5fd10775e4b2d3fb8912d3f075cc5cad788274681581310e2ecee4a8d8943f14056cc9fe6f4444ba6b2a7
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/NOTES.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Usage: gem release [gemspec] [options]
|
2
|
+
|
3
|
+
Options:
|
4
|
+
-t, --[no-]tag Create a git tag and push it to the destination (defaults to false)
|
5
|
+
-d, --destination DESTINATION Destination git repository (defaults to origin)
|
6
|
+
-k, --key KEY Use the given API key from ~/.gem/credentials (not set by default)
|
7
|
+
--host HOST Push to a gemcutter-compatible host other than rubygems.org (not set by default)
|
8
|
+
|
9
|
+
|
10
|
+
Common Options:
|
11
|
+
-h, --help Get help on this command
|
12
|
+
-V, --[no-]verbose Set the verbose level of output
|
13
|
+
-q, --quiet Silence command progress meter
|
14
|
+
--silent Silence rubygems output
|
15
|
+
--config-file FILE Use this config file instead of default
|
16
|
+
--backtrace Show stack backtrace on errors
|
17
|
+
--debug Turn on Ruby debugging
|
18
|
+
--norc Avoid loading any .gemrc file
|
19
|
+
|
20
|
+
|
21
|
+
Arguments:
|
22
|
+
gemspec - optional gemspec file name, will use the first *.gemspec if not specified
|
23
|
+
|
24
|
+
Summary:
|
25
|
+
Build gem from a gemspec and push to rubygems.org
|
data/README.md
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# CL [![Build Status](https://travis-ci.org/svenfuchs/cl.svg?branch=master)](https://travis-ci.org/svenfuchs/cl)
|
2
2
|
|
3
3
|
This library wraps Ruby's `OptionParser` in order to make it easier to use it in an object oriented context.
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
7
|
-
```
|
7
|
+
```ruby
|
8
8
|
module Owners
|
9
|
-
class Add <
|
10
|
-
include Cli::Cmd
|
9
|
+
class Add < Cli::Cmd
|
11
10
|
|
12
11
|
register 'owners:add'
|
13
12
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
class Bool < Cl::Cmd
|
4
|
+
arg :bool, type: :bool
|
5
|
+
|
6
|
+
def run
|
7
|
+
[self.registry_key, bool: bool]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Types < Cl::Cmd
|
12
|
+
arg :a, type: :bool
|
13
|
+
arg :b, type: :int
|
14
|
+
arg :c, type: :float
|
15
|
+
arg :d
|
16
|
+
|
17
|
+
def run
|
18
|
+
[self.registry_key, a: a, b: b, c: c, d: d]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def output(cmd, args)
|
23
|
+
args = args.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
24
|
+
puts "Called #{cmd} with #{args}"
|
25
|
+
end
|
26
|
+
|
27
|
+
output *Cl.run(*%w(bool on))
|
28
|
+
# Output:
|
29
|
+
# Called bool with bool=true
|
30
|
+
|
31
|
+
output *Cl.run(*%w(bool on))
|
32
|
+
# Output:
|
33
|
+
# Called bool with bool=true
|
34
|
+
|
35
|
+
output *Cl.run(*%w(bool on))
|
36
|
+
# Output:
|
37
|
+
# Called bool with bool=true
|
38
|
+
|
39
|
+
output *Cl.run(*%w(bool on))
|
40
|
+
# Output:
|
41
|
+
# Called bool with bool=true
|
42
|
+
|
43
|
+
output *Cl.run(*%w(types true 1 1.2 foo))
|
44
|
+
# Output:
|
45
|
+
# Called types with a=true b=1 c=1.2 d="foo"
|
46
|
+
|
47
|
+
output *Cl.run(*%w(types true 1 1.2))
|
48
|
+
# Output:
|
49
|
+
# Too many arguments (given: 5, allowed: 4)
|
50
|
+
#
|
51
|
+
# Usage: cast.rb types [a (bool)] [b (int)] [c (float)] [d]
|
52
|
+
|
53
|
+
output *Cl.run(*%w(types true one 1.2))
|
54
|
+
# Output:
|
55
|
+
# Wrong argument type (given: "one", expected: int)
|
56
|
+
#
|
57
|
+
# Usage: cast.rb types [a (bool)] [b (int)] [c (float)] [d]
|
58
|
+
|
59
|
+
output *Cl.run(*%w(types true 1 one))
|
60
|
+
# Output:
|
61
|
+
# Wrong argument type (given: "one", expected: float)
|
62
|
+
#
|
63
|
+
# Usage: cast.rb types [a (bool)] [b (int)] [c (float)] [d]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
class Opts < Cl::Cmd
|
4
|
+
opt '-p', '--path PATH' do |value|
|
5
|
+
opts[:path] = value
|
6
|
+
end
|
7
|
+
|
8
|
+
opt '-v', '--verbose' do
|
9
|
+
opts[:verbose] = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
[self.registry_key, args, opts]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def output(cmd, args, opts)
|
18
|
+
puts "Called #{cmd} with args=#{args} opts=#{opts}"
|
19
|
+
end
|
20
|
+
|
21
|
+
output *Cl.run(*%w(opts -p path -v))
|
22
|
+
# Output:
|
23
|
+
# Called cast with args=[] opts={:path=>"path", :verbose=>true}
|
24
|
+
|
25
|
+
output *Cl.run(*%w(opts --path path --verbose))
|
26
|
+
# Output:
|
27
|
+
# Called cast with args=[] opts={:path=>"path", :verbose=>true}
|
28
|
+
|
29
|
+
output *Cl.run(*%w(opts one -p path two -v three))
|
30
|
+
# Output:
|
31
|
+
# Called cast with args=["one", "two", "three"] opts={:path=>"path", :verbose=>true}
|
32
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
class Required < Cl::Cmd
|
4
|
+
arg :one, required: true
|
5
|
+
arg :two
|
6
|
+
|
7
|
+
def run
|
8
|
+
[self.registry_key, one: one, two: two]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def output(cmd, args)
|
13
|
+
args = args.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
14
|
+
puts "Called #{cmd} with #{args}"
|
15
|
+
end
|
16
|
+
|
17
|
+
output *Cl.run(*%w(required one two))
|
18
|
+
# Output:
|
19
|
+
# Called required with one="one" two="two"
|
20
|
+
|
21
|
+
output *Cl.run(*%w(required one))
|
22
|
+
# Output:
|
23
|
+
# Called required with one="one" two=nil
|
24
|
+
|
25
|
+
output *Cl.run(*%w(required))
|
26
|
+
# Output:
|
27
|
+
# Missing arguments (given: 0, required: 1)
|
28
|
+
#
|
29
|
+
# Usage: args.rb required one [two]
|
30
|
+
|
31
|
+
output *Cl.run(*%w(required one two three))
|
32
|
+
# Output:
|
33
|
+
# Too many arguments (given: 3, allowed: 2)
|
34
|
+
#
|
35
|
+
# Usage: args.rb required one [two]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
class SplatLeft < Cl::Cmd
|
4
|
+
register 'splat:left'
|
5
|
+
|
6
|
+
arg :one, type: :array
|
7
|
+
args :two, :three
|
8
|
+
|
9
|
+
def run
|
10
|
+
[self.registry_key, one: one, two: two, three: three]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class SplatMiddle < Cl::Cmd
|
15
|
+
register 'splat:middle'
|
16
|
+
|
17
|
+
arg :one
|
18
|
+
arg :two, type: :array
|
19
|
+
arg :three
|
20
|
+
|
21
|
+
def run
|
22
|
+
[self.registry_key, one: one, two: two, three: three]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class SplatRight < Cl::Cmd
|
27
|
+
register 'splat:right'
|
28
|
+
|
29
|
+
args :one, :two
|
30
|
+
arg :three, type: :array
|
31
|
+
|
32
|
+
def run
|
33
|
+
[self.registry_key, one: one, two: two, three: three]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def output(cmd, args)
|
38
|
+
args = args.map { |key, value| "#{key}=#{value.inspect}" }.join(' ')
|
39
|
+
puts "Called #{cmd} with #{args}"
|
40
|
+
end
|
41
|
+
|
42
|
+
output *Cl.run(*%w(splat left foo bar baz buz))
|
43
|
+
# Output:
|
44
|
+
# Called splat:left with one=["foo", "bar"] two="baz" three="buz"
|
45
|
+
|
46
|
+
output *Cl.run(*%w(splat middle foo bar baz buz))
|
47
|
+
# Output:
|
48
|
+
# Called splat:middle with one="foo" two=["bar", "baz"] three="buz"
|
49
|
+
|
50
|
+
output *Cl.run(*%w(splat right foo bar baz buz))
|
51
|
+
# Output:
|
52
|
+
# Called splat:middle with one="foo" two="bar" three=["baz", "buz"]
|
data/examples/gem.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
module Gem
|
4
|
+
module Release
|
5
|
+
module Cmds
|
6
|
+
class Release < Cl::Cmd
|
7
|
+
arg :gemspec
|
8
|
+
|
9
|
+
opt '-h', '--host HOST' do |value|
|
10
|
+
opts[:host] = value
|
11
|
+
end
|
12
|
+
|
13
|
+
opt '-k', '--key KEY' do |value|
|
14
|
+
opts[:key] = value
|
15
|
+
end
|
16
|
+
|
17
|
+
opt '-q', '--quiet' do
|
18
|
+
opts[:version] = true
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
[self.registry_key, args, opts]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Bump < Cl::Cmd
|
27
|
+
OPTS = {
|
28
|
+
commit: true
|
29
|
+
}
|
30
|
+
|
31
|
+
opt '-v', '--version VERSION', 'the version to bump to [1.1.1|major|minor|patch|pre|rc|release]' do |value|
|
32
|
+
opts[:version] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
opt '--no-commit', 'bump the version, but do not commit' do
|
36
|
+
opts[:commit] = false
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
[self.registry_key, args, opts]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Cl.runner = :multi
|
48
|
+
Cl.run(*%w(help))
|
49
|
+
puts
|
50
|
+
|
51
|
+
# Type "gem.rb help COMMAND [SUBCOMMAND]" for more details:
|
52
|
+
#
|
53
|
+
# gem.rb release [gemspec] [options]
|
54
|
+
# gem.rb bump [options]
|
55
|
+
|
56
|
+
Cl.run(*%w(help release))
|
57
|
+
puts
|
58
|
+
|
59
|
+
# Usage: gem.rb release [gemspec] [options]
|
60
|
+
#
|
61
|
+
# -h --host HOST
|
62
|
+
# -k --key KEY
|
63
|
+
# -q --quiet
|
64
|
+
|
65
|
+
Cl.run(*%w(help bump))
|
66
|
+
puts
|
67
|
+
|
68
|
+
# Usage: gem.rb bump [options]
|
69
|
+
#
|
70
|
+
# -v --version VERSION # the version to bump to [1.1.1|major|minor|patch|pre|rc|release]
|
71
|
+
# --no-commit # bump the version, but do not commit
|
72
|
+
|
73
|
+
cmds = Cl.run(*%w(bump -v 1.1.1 release foo.gemspec -h host -k key -q))
|
74
|
+
puts 'Commands run:'
|
75
|
+
cmds.each do |(cmd, args, opts)|
|
76
|
+
puts "#{cmd} with args=#{args} opts=#{opts}"
|
77
|
+
end
|
78
|
+
|
79
|
+
# Commands run:
|
80
|
+
# bump with args=[] opts={:version=>"1.1.1"}
|
81
|
+
# release with args=["foo.gemspec"] opts={:host=>"host", :key=>"key", :version=>true}
|
data/examples/heroku.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
module Heroku
|
4
|
+
module Apps
|
5
|
+
class Create < Cl::Cmd
|
6
|
+
register 'apps:create'
|
7
|
+
|
8
|
+
arg :name, required: true
|
9
|
+
|
10
|
+
opt '-o', '--org ORG' do |value|
|
11
|
+
opts[:org] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def run; [registry_key, args, opts] end
|
15
|
+
end
|
16
|
+
|
17
|
+
class List < Cl::Cmd
|
18
|
+
register 'apps:info'
|
19
|
+
|
20
|
+
opt '-a', '--app APP' do |value|
|
21
|
+
opts[:app] = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def run; [registry_key, args, opts] end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def output(cmd, args, opts)
|
30
|
+
puts "Called #{cmd} with args=#{args} opts=#{opts}"
|
31
|
+
end
|
32
|
+
|
33
|
+
output *Cl.run(*%w(apps:create name -o org))
|
34
|
+
# Called apps:create with args=["name"] opts={:org=>"org"}
|
35
|
+
|
36
|
+
output *Cl.run(*%w(apps create name -o org))
|
37
|
+
# Called apps:create with args=["name"] opts={:org=>"org"}
|
38
|
+
|
39
|
+
output *Cl.run(*%w(apps:info -a app))
|
40
|
+
# Called apps:create with args=["app"] opts={}
|
41
|
+
|
data/examples/multi.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'cl'
|
2
|
+
|
3
|
+
module Rakeish
|
4
|
+
module Db
|
5
|
+
class Create < Cl::Cmd
|
6
|
+
register 'db:create'
|
7
|
+
|
8
|
+
arg :name
|
9
|
+
|
10
|
+
def run; [registry_key, args, opts] end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Drop < Cl::Cmd
|
14
|
+
register 'db:drop'
|
15
|
+
|
16
|
+
arg :name
|
17
|
+
|
18
|
+
opt '-f', '--force' do
|
19
|
+
opts[:force] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def run; [registry_key, args, opts] end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Migrate < Cl::Cmd
|
26
|
+
register 'db:migrate'
|
27
|
+
|
28
|
+
arg :name
|
29
|
+
|
30
|
+
opt '-v', '--version VERSION' do |value|
|
31
|
+
opts[:version] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def run; [registry_key, args, opts] end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def output(result)
|
40
|
+
result.each do |cmd, args, opts|
|
41
|
+
puts "Called #{cmd} with args=#{args} opts=#{opts}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Cl.runner = :multi
|
46
|
+
output Cl.run(*%w(db:drop production -f db:create db:migrate production -v 1))
|
47
|
+
# Output:
|
48
|
+
# Called db:drop with args=["production"] opts={:force=>true}
|
49
|
+
# Called db:create with args=[] opts={}
|
50
|
+
# Called db:migrate with args=["production"] opts={:version=>"1"}
|
data/lib/cl.rb
CHANGED
@@ -1,18 +1,44 @@
|
|
1
1
|
require 'cl/cmd'
|
2
2
|
require 'cl/help'
|
3
|
-
require 'cl/runner'
|
3
|
+
require 'cl/runner/default'
|
4
|
+
require 'cl/runner/multi'
|
4
5
|
|
5
6
|
module Cl
|
7
|
+
class Error < StandardError
|
8
|
+
MSGS = {
|
9
|
+
missing_args: 'Missing arguments (given: %s, required: %s)',
|
10
|
+
too_many_args: 'Too many arguments (given: %s, allowed: %s)',
|
11
|
+
wrong_type: 'Wrong argument type (given: %s, expected: %s)'
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(msg, *args)
|
15
|
+
super(MSGS[msg] ? MSGS[msg] % args : msg)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ArgumentError = Class.new(Error)
|
20
|
+
|
6
21
|
def included(const)
|
7
22
|
const.send(:include, Cmd)
|
8
23
|
end
|
9
24
|
|
10
25
|
def run(*args)
|
11
|
-
|
26
|
+
runner(*args).run
|
27
|
+
rescue Error => e
|
28
|
+
abort [e.message, runner(:help, *args).cmd.help].join("\n\n")
|
12
29
|
end
|
13
30
|
|
14
|
-
def help
|
15
|
-
|
31
|
+
def help(*args)
|
32
|
+
runner(:help, *args).run
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_writer :runner
|
36
|
+
@runner = :default
|
37
|
+
|
38
|
+
def runner(*args)
|
39
|
+
args = args.flatten
|
40
|
+
runner = args.first.to_s == 'help' ? :default : @runner
|
41
|
+
Runner.const_get(runner.to_s.capitalize).new(*args)
|
16
42
|
end
|
17
43
|
|
18
44
|
extend self
|
data/lib/cl/arg.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Cl
|
2
|
+
class Arg < Struct.new(:name, :opts)
|
3
|
+
TRUE = /^(true|yes|on)$/
|
4
|
+
FALSE = /^(false|no|off)$/
|
5
|
+
|
6
|
+
def define(const)
|
7
|
+
const.send(:attr_accessor, name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(cmd, value)
|
11
|
+
cmd.send(:"#{name}=", cast(value))
|
12
|
+
end
|
13
|
+
|
14
|
+
def type
|
15
|
+
opts[:type] || :str
|
16
|
+
end
|
17
|
+
|
18
|
+
def splat?
|
19
|
+
type == :array
|
20
|
+
end
|
21
|
+
|
22
|
+
def required?
|
23
|
+
!!opts[:required]
|
24
|
+
end
|
25
|
+
|
26
|
+
def cast(value)
|
27
|
+
case type
|
28
|
+
when nil
|
29
|
+
value
|
30
|
+
when :array
|
31
|
+
Array(value).flatten.map { |value| value.split(',') }.flatten
|
32
|
+
when :string, :str
|
33
|
+
value.to_s
|
34
|
+
when :boolean, :bool
|
35
|
+
return true if value.to_s =~ TRUE
|
36
|
+
return false if value.to_s =~ FALSE
|
37
|
+
!!value
|
38
|
+
when :integer, :int
|
39
|
+
Integer(value)
|
40
|
+
when :float
|
41
|
+
Float(value)
|
42
|
+
else
|
43
|
+
raise ArgumentError, "Unknown type: #{type}" if value
|
44
|
+
end
|
45
|
+
rescue ::ArgumentError => e
|
46
|
+
raise ArgumentError.new(:wrong_type, value.inspect, type)
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
str = name
|
51
|
+
case type
|
52
|
+
when :array then str = "#{str}.."
|
53
|
+
when :integer, :int then str = "#{str} (int)"
|
54
|
+
when :boolean, :bool then str = "#{str} (bool)"
|
55
|
+
when :float then str = "#{str} (float)"
|
56
|
+
end
|
57
|
+
required? ? str : "[#{str}]"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/cl/args.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'cl/arg'
|
2
|
+
|
3
|
+
module Cl
|
4
|
+
class Args
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def define(const, name, opts = {})
|
8
|
+
arg = Arg.new(name, opts)
|
9
|
+
arg.define(const)
|
10
|
+
args << arg
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply(cmd, args)
|
14
|
+
return args unless self.args.any?
|
15
|
+
args = grouped(args)
|
16
|
+
validate(args)
|
17
|
+
args.map { |(arg, value)| arg.set(cmd, value) }.flatten(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
def each(&block)
|
21
|
+
args.each(&block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def index(*args, &block)
|
25
|
+
self.args.index(*args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def args
|
29
|
+
@args ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate(args)
|
35
|
+
raise ArgumentError.new(:missing_args, args.size, required) if args.size < required
|
36
|
+
raise ArgumentError.new(:too_many_args, args.size, allowed) if args.size > allowed && !splat?
|
37
|
+
end
|
38
|
+
|
39
|
+
def allowed
|
40
|
+
args.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def splat?
|
44
|
+
args.any?(&:splat?)
|
45
|
+
end
|
46
|
+
|
47
|
+
def required
|
48
|
+
args.select { |arg| arg.required? }.size
|
49
|
+
end
|
50
|
+
|
51
|
+
def grouped(values)
|
52
|
+
values.inject([0, {}]) do |(ix, group), value|
|
53
|
+
arg = args[ix]
|
54
|
+
if arg && arg.splat?
|
55
|
+
group[arg] ||= []
|
56
|
+
group[arg] << value
|
57
|
+
ix += 1 if args.size + group[arg].size > values.size
|
58
|
+
else
|
59
|
+
group[arg] = value
|
60
|
+
ix += 1
|
61
|
+
end
|
62
|
+
[ix, group]
|
63
|
+
end.last
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/cl/cmd.rb
CHANGED
@@ -1,28 +1,50 @@
|
|
1
|
+
require 'cl/args'
|
1
2
|
require 'cl/registry'
|
2
3
|
|
3
4
|
module Cl
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
class Cmd < Struct.new(:args, :opts)
|
6
|
+
include Registry
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def inherited(cmd)
|
10
|
+
cmd.register underscore(cmd.name.split('::').last)
|
11
|
+
end
|
9
12
|
|
10
|
-
module ClassMethods
|
11
13
|
def args(*args)
|
12
|
-
|
14
|
+
return @args ||= Args.new unless args.any?
|
15
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
16
|
+
args.each { |arg| arg(arg, opts) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def arg(name, opts = {})
|
20
|
+
args.define(self, name, opts)
|
13
21
|
end
|
14
22
|
|
15
|
-
def
|
16
|
-
|
23
|
+
def cmd(summary = nil)
|
24
|
+
@summary = summary
|
17
25
|
end
|
18
26
|
|
19
|
-
|
27
|
+
attr_reader :summary
|
28
|
+
|
29
|
+
def opt(*args, &block)
|
20
30
|
opts << [args, block]
|
21
31
|
end
|
22
32
|
|
23
33
|
def opts
|
24
|
-
@opts ||= superclass.respond_to?(:opts) ? superclass.opts : []
|
34
|
+
@opts ||= superclass != Cmd && superclass.respond_to?(:opts) ? superclass.opts.dup : []
|
35
|
+
end
|
36
|
+
|
37
|
+
def underscore(string)
|
38
|
+
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
39
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
40
|
+
downcase
|
25
41
|
end
|
26
42
|
end
|
43
|
+
|
44
|
+
def initialize(args, opts)
|
45
|
+
args = self.class.args.apply(self, args)
|
46
|
+
opts = self.class::OPTS.merge(opts) if self.class.const_defined?(:OPTS)
|
47
|
+
super
|
48
|
+
end
|
27
49
|
end
|
28
50
|
end
|
data/lib/cl/format/cmd.rb
CHANGED
data/lib/cl/format/list.rb
CHANGED
@@ -4,7 +4,7 @@ require 'cl/format/usage'
|
|
4
4
|
module Cl
|
5
5
|
class Format
|
6
6
|
class List < Struct.new(:cmds)
|
7
|
-
HEAD = %(Type "#{$0} help COMMAND [SUBCOMMAND]" for more details:\n)
|
7
|
+
HEAD = %(Type "#{$0.split('/').last} help COMMAND [SUBCOMMAND]" for more details:\n)
|
8
8
|
|
9
9
|
def format
|
10
10
|
[HEAD, Format::Table.new(list).format].join("\n")
|
@@ -15,7 +15,7 @@ module Cl
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def format_cmd(cmd)
|
18
|
-
["#{
|
18
|
+
["#{Usage.new(cmd).format}", cmd.summary]
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|
data/lib/cl/format/usage.rb
CHANGED
data/lib/cl/help.rb
CHANGED
@@ -2,9 +2,7 @@ require 'cl/format/cmd'
|
|
2
2
|
require 'cl/format/list'
|
3
3
|
|
4
4
|
module Cl
|
5
|
-
class Help <
|
6
|
-
include Cl::Cmd
|
7
|
-
|
5
|
+
class Help < Cl::Cmd
|
8
6
|
register :help
|
9
7
|
|
10
8
|
def run
|
@@ -25,7 +23,11 @@ module Cl
|
|
25
23
|
end
|
26
24
|
|
27
25
|
def cmd
|
28
|
-
args
|
26
|
+
args.inject([[], []]) do |(args, cmds), arg|
|
27
|
+
args << arg
|
28
|
+
cmds << Cl[args.join(':')]
|
29
|
+
[args, cmds.compact]
|
30
|
+
end.last.last
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
data/lib/cl/options.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'cl/options'
|
2
|
+
|
3
|
+
module Cl
|
4
|
+
module Runner
|
5
|
+
class Default
|
6
|
+
attr_reader :const, :args, :opts
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
args = args.flatten.map(&:to_s)
|
10
|
+
@const, @args, @opts = lookup(args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
cmd.run
|
15
|
+
end
|
16
|
+
|
17
|
+
def cmd
|
18
|
+
const.new(args, opts)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def lookup(args)
|
24
|
+
cmd = keys_for(args).map { |key| Cl[key] }.compact.last
|
25
|
+
cmd || abort("Unknown command: #{args.join(' ')}")
|
26
|
+
opts = Options.new(cmd.opts, args).opts unless cmd == Help
|
27
|
+
[cmd, args - cmds_for(cmd, args), opts]
|
28
|
+
end
|
29
|
+
|
30
|
+
def cmds_for(cmd, args)
|
31
|
+
name = cmd.registry_key.to_s
|
32
|
+
args.take_while do |arg|
|
33
|
+
name = name.sub(/#{arg}(:|$)/, '') if name =~ /#{arg}(:|$)/
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def keys_for(args)
|
38
|
+
args.inject([]) { |keys, key| keys << [keys.last, key].compact.join(':') }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'cl/options'
|
2
|
+
|
3
|
+
module Cl
|
4
|
+
module Runner
|
5
|
+
class Multi
|
6
|
+
attr_reader :cmds
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
@cmds = build(group(args))
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
cmds.map(&:run)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def group(args, cmds = [])
|
19
|
+
args.flatten.map(&:to_s).inject([[]]) do |cmds, arg|
|
20
|
+
cmd = Cl[arg]
|
21
|
+
cmd ? cmds << [cmd] : cmds.last << arg
|
22
|
+
cmds.reject(&:empty?)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def build(cmds)
|
27
|
+
cmds.map do |(cmd, *args)|
|
28
|
+
cmd.new(args, Options.new(cmd.opts, args).opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/cl/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sven Fuchs
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-08-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: OptionParser based CLI support.
|
14
|
-
email:
|
14
|
+
email:
|
15
15
|
executables: []
|
16
16
|
extensions: []
|
17
17
|
extra_rdoc_files: []
|
@@ -19,10 +19,19 @@ files:
|
|
19
19
|
- Gemfile
|
20
20
|
- Gemfile.lock
|
21
21
|
- MIT_LICENSE.md
|
22
|
+
- NOTES.md
|
22
23
|
- README.md
|
24
|
+
- examples/args/cast.rb
|
25
|
+
- examples/args/opts.rb
|
26
|
+
- examples/args/required.rb
|
27
|
+
- examples/args/splat.rb
|
28
|
+
- examples/gem.rb
|
29
|
+
- examples/heroku.rb
|
30
|
+
- examples/multi.rb
|
23
31
|
- lib/cl.rb
|
32
|
+
- lib/cl/arg.rb
|
33
|
+
- lib/cl/args.rb
|
24
34
|
- lib/cl/cmd.rb
|
25
|
-
- lib/cl/cmds.rb
|
26
35
|
- lib/cl/format/cmd.rb
|
27
36
|
- lib/cl/format/list.rb
|
28
37
|
- lib/cl/format/table.rb
|
@@ -30,7 +39,8 @@ files:
|
|
30
39
|
- lib/cl/help.rb
|
31
40
|
- lib/cl/options.rb
|
32
41
|
- lib/cl/registry.rb
|
33
|
-
- lib/cl/runner.rb
|
42
|
+
- lib/cl/runner/default.rb
|
43
|
+
- lib/cl/runner/multi.rb
|
34
44
|
- lib/cl/version.rb
|
35
45
|
homepage: https://github.com/svenfuchs/cl
|
36
46
|
licenses:
|
@@ -52,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
62
|
version: '0'
|
53
63
|
requirements: []
|
54
64
|
rubyforge_project:
|
55
|
-
rubygems_version: 2.6.
|
65
|
+
rubygems_version: 2.6.11
|
56
66
|
signing_key:
|
57
67
|
specification_version: 4
|
58
68
|
summary: OptionParser based CLI support
|
data/lib/cl/cmds.rb
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'cl/options'
|
2
|
-
|
3
|
-
module Cl
|
4
|
-
class Cmds < Struct.new(:args)
|
5
|
-
def lookup
|
6
|
-
cmd || abort("Unknown command: #{args.join(' ')}")
|
7
|
-
opts = Options.new(cmd.opts, args).opts
|
8
|
-
[cmd, args - cmds, opts]
|
9
|
-
end
|
10
|
-
|
11
|
-
private
|
12
|
-
|
13
|
-
def cmds
|
14
|
-
name = cmd.registry_key.to_s
|
15
|
-
args.take_while do |arg|
|
16
|
-
name = name.sub(/#{arg}(:|$)/, '') if name =~ /#{arg}(:|$)/
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def cmd
|
21
|
-
@cmd ||= keys.map { |key| Cl[key] }.compact.last
|
22
|
-
end
|
23
|
-
|
24
|
-
def keys
|
25
|
-
@keys ||= args.inject([]) { |keys, key| keys << [keys.last, key].compact.join(':') }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
data/lib/cl/runner.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'cl/cmds'
|
2
|
-
|
3
|
-
module Cl
|
4
|
-
class Runner
|
5
|
-
attr_reader :const, :args, :opts
|
6
|
-
|
7
|
-
def initialize(*args)
|
8
|
-
args = args.flatten.map(&:to_s)
|
9
|
-
@const, @args, @opts = Cmds.new(args).lookup
|
10
|
-
end
|
11
|
-
|
12
|
-
def run
|
13
|
-
cmd.run
|
14
|
-
end
|
15
|
-
|
16
|
-
def cmd
|
17
|
-
const.new(args, opts)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|