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 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