cl 0.0.2 → 0.0.3
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.
- 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 [](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
|