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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7dbb945fc479c46219fa0aa0f57b6b3a6ed935de
4
- data.tar.gz: d7011478b8db611917ad9adc6826e787b425f3e2
3
+ metadata.gz: 111acf22811807b4d15e0809ce6572d621446e47
4
+ data.tar.gz: 9a6b0db3a509ee87bc9206e1ea2093e108ba7821
5
5
  SHA512:
6
- metadata.gz: eb8c51c3e073bc619f3c2247c085a2b0b40e32eb3acfc6deeb1a4a1a5054ed2cbedbb68056d2d3950c3da01fcfca6609ba33e2d1790929811880f0b5b8bdeebe
7
- data.tar.gz: b8eff80a773703c9921fdddc59d41c0c5b6bfc8e58e3359f9b13130735329d6f8acf24ed70fa381a88af6492b798016f756e4fb6c39908b00bd0501b55c773bf
6
+ metadata.gz: 3e78849c155cdabd3b05dd5740ff4ef2308d67b315d9937f815fb951b5856de314a3b1342dbf6411ba4fd3e6d953114b1d3ab526a0d929877b0c50003d80aa30
7
+ data.tar.gz: 1991a74dd9085e2ddcb09ed80e64845666524803edc5fd10775e4b2d3fb8912d3f075cc5cad788274681581310e2ecee4a8d8943f14056cc9fe6f4444ba6b2a7
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
1
3
  group :test do
2
4
  gem 'rspec'
3
5
  end
@@ -1,4 +1,5 @@
1
1
  GEM
2
+ remote: https://rubygems.org/
2
3
  specs:
3
4
  diff-lcs (1.3)
4
5
  rspec (3.5.0)
@@ -22,4 +23,4 @@ DEPENDENCIES
22
23
  rspec
23
24
 
24
25
  BUNDLED WITH
25
- 1.14.5
26
+ 1.14.6
@@ -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
- # CLI
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 < Struct.new(:args, :opts)
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"]
@@ -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}
@@ -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
+
@@ -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
- Runner.new(*args).run
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
- Runner.new(:help).run
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
@@ -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
@@ -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
@@ -1,28 +1,50 @@
1
+ require 'cl/args'
1
2
  require 'cl/registry'
2
3
 
3
4
  module Cl
4
- module Cmd
5
- def self.included(const)
6
- const.send :include, Registry
7
- const.send :extend, ClassMethods
8
- end
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
- args.any? ? @args = args : @args ||= []
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 purpose(purpose = nil)
16
- purpose ? @purpose = purpose : @purpose
23
+ def cmd(summary = nil)
24
+ @summary = summary
17
25
  end
18
26
 
19
- def on(*args, &block)
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
@@ -10,7 +10,7 @@ module Cl
10
10
 
11
11
  def banner
12
12
  banner = []
13
- banner << "#{cmd.purpose}\n" if cmd.purpose
13
+ banner << "#{cmd.summary}\n" if cmd.summary
14
14
  banner << "Usage: #{Usage.new(cmd).format}\n"
15
15
  banner
16
16
  end
@@ -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
- ["#{$0} #{Usage.new(cmd).format}", cmd.purpose]
18
+ ["#{Usage.new(cmd).format}", cmd.summary]
19
19
  end
20
20
  end
21
21
  end
@@ -2,8 +2,8 @@ module Cl
2
2
  class Format
3
3
  class Usage < Struct.new(:cmd)
4
4
  def format
5
- usage = [name]
6
- usage += cmd.args.map { |arg| "[#{arg}]" }
5
+ usage = [$0.split('/').last, name]
6
+ usage += cmd.args.map(&:to_s)
7
7
  usage << '[options]' if opts?
8
8
  usage.join(' ')
9
9
  end
@@ -2,9 +2,7 @@ require 'cl/format/cmd'
2
2
  require 'cl/format/list'
3
3
 
4
4
  module Cl
5
- class Help < Struct.new(:args, :opts)
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 && Cl[args.join(':')]
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
@@ -1,3 +1,5 @@
1
+ require 'optparse'
2
+
1
3
  module Cl
2
4
  class Options < OptionParser
3
5
  attr_reader :opts
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Cl
2
- VERSION = '0.0.2'
2
+ VERSION = '0.0.3'
3
3
  end
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.2
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-04-09 00:00:00.000000000 Z
11
+ date: 2017-08-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: OptionParser based CLI support.
14
- email: me@svenfuchs.com
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.8
65
+ rubygems_version: 2.6.11
56
66
  signing_key:
57
67
  specification_version: 4
58
68
  summary: OptionParser based CLI support
@@ -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
@@ -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