fuelcell 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.codeclimate.yml +12 -0
- data/.gitignore +13 -0
- data/.rspec +5 -0
- data/.travis.yml +15 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +51 -0
- data/Rakefile +5 -0
- data/bin/console +9 -0
- data/bin/example.rb +9 -0
- data/bin/setup +7 -0
- data/bin/test +20 -0
- data/bin/world.rb +6 -0
- data/fuelcell.gemspec +26 -0
- data/lib/fuelcell/action/arg_definition.rb +36 -0
- data/lib/fuelcell/action/arg_results.rb +57 -0
- data/lib/fuelcell/action/args_manager.rb +66 -0
- data/lib/fuelcell/action/callable.rb +54 -0
- data/lib/fuelcell/action/command.rb +72 -0
- data/lib/fuelcell/action/not_found.rb +55 -0
- data/lib/fuelcell/action/opt_definition.rb +79 -0
- data/lib/fuelcell/action/opt_results.rb +68 -0
- data/lib/fuelcell/action/opts_manager.rb +80 -0
- data/lib/fuelcell/action/root.rb +76 -0
- data/lib/fuelcell/action/subcommands.rb +81 -0
- data/lib/fuelcell/action.rb +11 -0
- data/lib/fuelcell/cli.rb +89 -0
- data/lib/fuelcell/help/base_formatter.rb +24 -0
- data/lib/fuelcell/help/builder.rb +71 -0
- data/lib/fuelcell/help/cmds_formatter.rb +57 -0
- data/lib/fuelcell/help/desc_formatter.rb +21 -0
- data/lib/fuelcell/help/opts_formatter.rb +62 -0
- data/lib/fuelcell/help/usage_formatter.rb +88 -0
- data/lib/fuelcell/help.rb +27 -0
- data/lib/fuelcell/parser/arg_handler.rb +31 -0
- data/lib/fuelcell/parser/base_handler.rb +133 -0
- data/lib/fuelcell/parser/cmd_args_strategy.rb +28 -0
- data/lib/fuelcell/parser/ignore_handler.rb +25 -0
- data/lib/fuelcell/parser/opt_handler.rb +58 -0
- data/lib/fuelcell/parser/opt_name_handler.rb +89 -0
- data/lib/fuelcell/parser/opt_value_equal_handler.rb +26 -0
- data/lib/fuelcell/parser/parsing_strategy.rb +80 -0
- data/lib/fuelcell/parser/short_opt_no_space_handler.rb +54 -0
- data/lib/fuelcell/parser.rb +4 -0
- data/lib/fuelcell/shell.rb +102 -0
- data/lib/fuelcell/version.rb +3 -0
- data/lib/fuelcell.rb +114 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NmY4YWExNmViMzhhMjBkM2Y4MWQ4YzgyYWE3OTBkNzIxMDhiNzU5Yg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTk4ZDFkYTJiMzc4MmUxNTU4NjQxODQ4ZjcwNTA4MDkxNTc3ZjU3MA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YWQ0ZWI5MDE5OWQ1YjgxMTI4MDlkZjJiODI3MGZiZDQ4ZjlmM2ZjZmNmMTk2
|
10
|
+
ZDJjNmE2NDdmMzRhM2I1MWYzODZjZDY5MjdhNjBmMWRkODI2NDRhM2I3YTM5
|
11
|
+
ODI0YjBjZGExNzlkMDhhYjdkNGY4M2FlNWM2MDllNmVkYjMwNTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YWMyY2EzMmYwYTM0ZjFhM2QxMjE5MDBmOGJmZDRkOTc0MmFiNTNkMTJmNjY0
|
14
|
+
NzRiNGU4Y2JhZTdjMWYwZDI4MWFmYzk2MDNlMzRkNmI3MmM0MDg0NTg3Zjhh
|
15
|
+
YTExODA0MzMxNzYzMDI1ZTNhYjUxZjFjMGJkMDQ0M2Y1ZjAzODU=
|
data/.codeclimate.yml
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.2.2
|
4
|
+
before_install: gem install bundler -v 1.11
|
5
|
+
sudo: false
|
6
|
+
addons:
|
7
|
+
code_climate:
|
8
|
+
repo_token: 29cdb1e4095c85dd0a2e9a0a1264b074c6d4008bc2069706ef283e6a68364ce5
|
9
|
+
deploy:
|
10
|
+
on:
|
11
|
+
tags: true
|
12
|
+
provider: rubygems
|
13
|
+
api_key:
|
14
|
+
secure: Z4sBUqVeIuNJNKxXa+oYDxnfXlVaCAYl3f+8rWm4pKnHpLQYP1M8vj5LM3cmdHtUQiy/085zkvt5C6p2045PZ5sX3rueojoShqfvmNHgr3wYWQ2MZ3oHy3Vl6UJ3JYGoM/2wEclmqWQ49s1Zk4gnNZR4QyNZ/P4SWZ4FmJ9VmlI=
|
15
|
+
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Robert Scott-Buccleuch
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Fuelcell
|
2
|
+
[![Code Climate](https://codeclimate.com/github/engine8/fuelcell/badges/gpa.svg)](https://codeclimate.com/github/engine8/fuelcell)
|
3
|
+
[![Test Coverage](https://codeclimate.com/github/engine8/fuelcell/badges/coverage.svg)](https://codeclimate.com/github/engine8/fuelcell/coverage)
|
4
|
+
[![Build Status](https://travis-ci.org/engine8/fuelcell.svg?branch=master)](https://travis-ci.org/engine8/fuelcell)
|
5
|
+
|
6
|
+
## Description
|
7
|
+
Fuelcell is tool for parsing command line arguments & options, allowing you to
|
8
|
+
concentrate more on your application and less on the logic needed to interface
|
9
|
+
with your users through the command line. It handles mapping arguments to
|
10
|
+
executable actions, defining options & arguments, self documenting help system,
|
11
|
+
through a dsl that is small and simple to learn.
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'fuelcell'
|
19
|
+
```
|
20
|
+
|
21
|
+
And then execute:
|
22
|
+
|
23
|
+
$ bundle
|
24
|
+
|
25
|
+
Or install it yourself as:
|
26
|
+
|
27
|
+
$ gem install fuelcell
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
```ruby
|
31
|
+
require 'fuelcell'
|
32
|
+
|
33
|
+
Fuelcell.start(ARGV.dup) do
|
34
|
+
desc 'this is a basic hello world app'
|
35
|
+
arg 'name', default: 'World'
|
36
|
+
run ->(opts, args, shell) {
|
37
|
+
shell.puts "Hello #{args.name}"
|
38
|
+
}
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
## Development
|
43
|
+
|
44
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake false` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
45
|
+
|
46
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
47
|
+
|
48
|
+
## Contributing
|
49
|
+
|
50
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/fuelcell. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
51
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/example.rb
ADDED
data/bin/setup
ADDED
data/bin/test
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require "bundler/setup"
|
5
|
+
require "fuelcell"
|
6
|
+
|
7
|
+
Fuelcell.start(ARGV.dup, backtrace: true) do
|
8
|
+
require_relative 'example'
|
9
|
+
desc 'this is an example of how a commands work with fuelcell'
|
10
|
+
opt 'all|a', banner: 'all of something'
|
11
|
+
arg 'foo', required: true
|
12
|
+
run->(opts, args, shell) {
|
13
|
+
shell.puts "testing #{args.foo}"
|
14
|
+
}
|
15
|
+
command 'fiz' do
|
16
|
+
run ->(opts, args, shell) {
|
17
|
+
shell.puts 'called by options'
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
data/bin/world.rb
ADDED
data/fuelcell.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fuelcell/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "fuelcell"
|
8
|
+
spec.version = Fuelcell::VERSION
|
9
|
+
spec.authors = ["Robert Scott-Buccleuch"]
|
10
|
+
spec.email = ["rsb.code@gmail.com"]
|
11
|
+
|
12
|
+
spec.description = %q{framework which simplifies working with the command-line.}
|
13
|
+
spec.summary = spec.description
|
14
|
+
spec.homepage = "https://github.com/rsb/fuelcell"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "pry", "~> 0.10"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.3.0"
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
class ArgDefinition
|
4
|
+
NO_DEFAULT_ASSIGNED = '__fuelcell_no_default_assigned__'
|
5
|
+
attr_reader :name, :type, :default, :required, :banner
|
6
|
+
|
7
|
+
alias_method :required?, :required
|
8
|
+
|
9
|
+
def initialize(text, config = {})
|
10
|
+
@name = text.to_s
|
11
|
+
@required = config[:required] == true ? true : false
|
12
|
+
@type = validate_type(config[:type])
|
13
|
+
@default = config.fetch(:default) { NO_DEFAULT_ASSIGNED }
|
14
|
+
@banner = config[:banner] || ''
|
15
|
+
end
|
16
|
+
|
17
|
+
def default?
|
18
|
+
@default == NO_DEFAULT_ASSIGNED ? false : true
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def validate_type(value)
|
24
|
+
case value.to_s.to_sym
|
25
|
+
when :string, :text, :"" then :string
|
26
|
+
when :number, :numeric then :numeric
|
27
|
+
when :boolean, :bool then :bool
|
28
|
+
when :hash then :hash
|
29
|
+
when :array then :array
|
30
|
+
else
|
31
|
+
fail ArgumentError, "invalid type #{value}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
class ArgResults
|
4
|
+
extend Forwardable
|
5
|
+
attr_reader :raw
|
6
|
+
def_delegators :@raw, :empty?, :each, :join
|
7
|
+
|
8
|
+
def initialize(array, hash)
|
9
|
+
@raw, @map = validate_results(array, hash)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
return raw[key] if key.is_a?(Integer)
|
15
|
+
value(key)
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
attr_reader :map
|
20
|
+
|
21
|
+
def method_missing(method, *args, &_block)
|
22
|
+
method = method.to_s
|
23
|
+
return map.key?(method.chomp('?')) if method[-1] == '?'
|
24
|
+
value(method)
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(key)
|
28
|
+
return nil unless index?(key)
|
29
|
+
raw[index(key)]
|
30
|
+
end
|
31
|
+
|
32
|
+
def index?(key)
|
33
|
+
map.key?(key)
|
34
|
+
end
|
35
|
+
|
36
|
+
def index(key)
|
37
|
+
map[key]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def validate_results(array, hash)
|
43
|
+
hash.each do |(key, value)|
|
44
|
+
unless value.is_a?(Integer)
|
45
|
+
fail ArgumentError, "hash value for #{key} must be an integer"
|
46
|
+
end
|
47
|
+
|
48
|
+
unless array.at(value)
|
49
|
+
fail ArgumentError,
|
50
|
+
"arg definition #{key} does not point to value in args array"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
[array, hash]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
# Used by the Command to manage adding args from its dsl. It is also
|
4
|
+
# used during arg parsing to find args, or check if any required args
|
5
|
+
# have been missed
|
6
|
+
class ArgsManager
|
7
|
+
attr_reader :args
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@args, :each, :each_with_index, :empty?
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@args = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def args?
|
17
|
+
!empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def required
|
21
|
+
args.select { |arg| arg.required? }
|
22
|
+
end
|
23
|
+
alias_method :required_args, :required
|
24
|
+
|
25
|
+
def missing(names)
|
26
|
+
list = []
|
27
|
+
args.select do |arg|
|
28
|
+
list << arg if arg.required? && !names.include?(arg.name)
|
29
|
+
end
|
30
|
+
return [] if list.empty?
|
31
|
+
|
32
|
+
yield list if block_given?
|
33
|
+
list
|
34
|
+
end
|
35
|
+
|
36
|
+
def add(arg, config = {})
|
37
|
+
arg = create(arg, config) if arg.is_a?(String)
|
38
|
+
args.each do |item|
|
39
|
+
if item.name == arg.name
|
40
|
+
fail "can not add arg: duplicate exists with name #{arg.name}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
args << arg
|
44
|
+
end
|
45
|
+
alias_method :arg, :add
|
46
|
+
|
47
|
+
def remove(name)
|
48
|
+
# looks like an arg definition so lets use it's name
|
49
|
+
name = name.name if name.respond_to?(:name)
|
50
|
+
args.delete_if {|arg| arg.name == name}
|
51
|
+
end
|
52
|
+
|
53
|
+
def find(name)
|
54
|
+
target = false
|
55
|
+
args.each {|arg| target = arg if arg.name == name }
|
56
|
+
target
|
57
|
+
end
|
58
|
+
alias_method :find_arg, :find
|
59
|
+
alias_method :[], :find
|
60
|
+
|
61
|
+
def create(name, config = {})
|
62
|
+
ArgDefinition.new(name, config)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
module Callable
|
4
|
+
# Both getter and setting for callable object. This what the cli will
|
5
|
+
# execute when this command is found.
|
6
|
+
#
|
7
|
+
# @param value [Object] any object that implements :call
|
8
|
+
# @return [Object]
|
9
|
+
def callable(value = nil)
|
10
|
+
return @callable if value.nil?
|
11
|
+
|
12
|
+
unless value.respond_to?(:call)
|
13
|
+
fail ArgumentError, 'callable must implement :call'
|
14
|
+
end
|
15
|
+
|
16
|
+
@callable = value
|
17
|
+
end
|
18
|
+
alias_method :run, :callable
|
19
|
+
|
20
|
+
def callable?
|
21
|
+
callable.respond_to?(:call)
|
22
|
+
end
|
23
|
+
alias_method :runnable?, :callable?
|
24
|
+
|
25
|
+
# Executes this command.
|
26
|
+
#
|
27
|
+
# @param opts [Hash] processed opts from the cli parser
|
28
|
+
# @param args [Array] processed args from the cli parser
|
29
|
+
# @param shell [Fuelcell::Shell] IO abstraction
|
30
|
+
# @return [Integer] the exit code
|
31
|
+
def call(opts, args, shell)
|
32
|
+
fail 'command is not be called, callable not assigned' unless callable?
|
33
|
+
begin
|
34
|
+
cast_exit_code callable.call(opts, args, shell)
|
35
|
+
rescue StandardError => e
|
36
|
+
shell.exception e
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def cast_exit_code(code)
|
43
|
+
case code
|
44
|
+
when 0...255 then code
|
45
|
+
when nil, 0, '0', true then 0
|
46
|
+
when false, 1, '1' then 1
|
47
|
+
else
|
48
|
+
fail "command #{name} execution return code #{code} could " \
|
49
|
+
'not be interpreted as an exit code'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
# Represents the action you want to perform. It also holds a list of option
|
4
|
+
# definitions used by the parser, along with meta data used by the help
|
5
|
+
# system and callable which is any object that implements call, this is
|
6
|
+
# your action.
|
7
|
+
class Command
|
8
|
+
attr_reader :name, :opts, :args
|
9
|
+
extend Forwardable
|
10
|
+
include Subcommands
|
11
|
+
include Callable
|
12
|
+
|
13
|
+
delegate empty?: :subcommands
|
14
|
+
def_delegators :@opts, :find_opt, :opt, :missing_opts
|
15
|
+
def_delegators :@args, :find_arg, :arg, :missing_args
|
16
|
+
|
17
|
+
# Every command initializes with only its name set expecting the dsl
|
18
|
+
# to finish assigning the rest of its properties
|
19
|
+
#
|
20
|
+
# @param name [String]
|
21
|
+
def initialize(name)
|
22
|
+
@name = name.to_s
|
23
|
+
@usage = nil
|
24
|
+
@desc = nil
|
25
|
+
@opts = OptsManager.new
|
26
|
+
@args = ArgsManager.new
|
27
|
+
end
|
28
|
+
|
29
|
+
# This will assign both usage text and description, but when called
|
30
|
+
# with no args it will get the usage
|
31
|
+
#
|
32
|
+
# @param text [String]
|
33
|
+
# @param desc_text [String]
|
34
|
+
# @return [String]
|
35
|
+
def usage(text = nil, desc_text = nil)
|
36
|
+
return @usage if text.nil?
|
37
|
+
desc(desc_text) unless desc_text.nil?
|
38
|
+
@usage = text
|
39
|
+
end
|
40
|
+
|
41
|
+
# Both getting and setter for description
|
42
|
+
#
|
43
|
+
# @param text [String]
|
44
|
+
# @return [String]
|
45
|
+
def desc(text = nil)
|
46
|
+
return @desc if text.nil?
|
47
|
+
@desc = text
|
48
|
+
end
|
49
|
+
|
50
|
+
# Allows a command to add subcommands to itself
|
51
|
+
#
|
52
|
+
# @param key [String]
|
53
|
+
# @yield instance_eval into Fuelcell::Command
|
54
|
+
# @return Fuelcell::Command
|
55
|
+
def command(key, &block)
|
56
|
+
cmd = Command.new(key)
|
57
|
+
cmd.instance_eval(&block)
|
58
|
+
add cmd
|
59
|
+
end
|
60
|
+
|
61
|
+
# Allows all the global option definitions from this command hierarchy
|
62
|
+
# to be add to the command given
|
63
|
+
#
|
64
|
+
# @param cmd [Fuelcell::Action::Command]
|
65
|
+
# @return [Fuelcell::Action::Command]
|
66
|
+
def add_global_options(cmd)
|
67
|
+
global_options.each { |_key, opt_definition| cmd.opt opt_definition }
|
68
|
+
cmd
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Fuelcell
|
2
|
+
module Action
|
3
|
+
# Null Object used to indicate a command has not been found
|
4
|
+
class NotFound < Command
|
5
|
+
def initialize(cmd_args)
|
6
|
+
super(validate_cmd_args(cmd_args))
|
7
|
+
|
8
|
+
@callable = command_not_found_action
|
9
|
+
@usage = ''
|
10
|
+
@desc = 'command not found command'
|
11
|
+
end
|
12
|
+
|
13
|
+
def usage(_value = nil)
|
14
|
+
@usage
|
15
|
+
end
|
16
|
+
|
17
|
+
def desc(_value = nil)
|
18
|
+
@desc
|
19
|
+
end
|
20
|
+
|
21
|
+
def callable(_value = nil)
|
22
|
+
@callable
|
23
|
+
end
|
24
|
+
|
25
|
+
def command(key, &block)
|
26
|
+
cmd = Command.new(key)
|
27
|
+
cmd.instance_eval(&block)
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def opt(_name, _config = {})
|
32
|
+
end
|
33
|
+
|
34
|
+
def <<(_cmd)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def validate_cmd_args(args)
|
40
|
+
args = args.is_a?(String) ? [args] : args
|
41
|
+
unless args.respond_to?(:join)
|
42
|
+
fail ArgumentError, 'cmd_args must implement join'
|
43
|
+
end
|
44
|
+
args.join(' ')
|
45
|
+
end
|
46
|
+
|
47
|
+
def command_not_found_action
|
48
|
+
lambda do |_opts, _args, shell|
|
49
|
+
shell.error "command #{name} not found"
|
50
|
+
return 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'fuelcell/parser/opt_name_handler'
|
2
|
+
|
3
|
+
module Fuelcell
|
4
|
+
module Action
|
5
|
+
# Responsible for defining all the information about an option so that the
|
6
|
+
# parser will be able to find it and its values when processing the command
|
7
|
+
# line.
|
8
|
+
class OptDefinition
|
9
|
+
|
10
|
+
include Action::Callable
|
11
|
+
NO_DEFAULT_ASSIGNED = '__fuelcell_no_default_assigned__'
|
12
|
+
attr_reader :name, :long, :short, :type, :default, :names, :required,
|
13
|
+
:cli_labels, :cli_names, :flag, :banner, :global, :cmd_path
|
14
|
+
|
15
|
+
alias_method :flag?, :flag
|
16
|
+
alias_method :required?, :required
|
17
|
+
alias_method :global?, :global
|
18
|
+
|
19
|
+
def initialize(text, config = {})
|
20
|
+
initialize_names(text)
|
21
|
+
@flag = config[:flag] == true ? true : false
|
22
|
+
@required = config[:required] == true ? true : false
|
23
|
+
@global = config[:global] == true ? true : false
|
24
|
+
@type = validate_type(config[:type])
|
25
|
+
@default = config.fetch(:default) { NO_DEFAULT_ASSIGNED }
|
26
|
+
@banner = config[:banner] || ''
|
27
|
+
@cmd_path = config[:cmd_path]
|
28
|
+
callable config[:run] if config.key?(:run)
|
29
|
+
end
|
30
|
+
|
31
|
+
def default?
|
32
|
+
@default == NO_DEFAULT_ASSIGNED ? false : true
|
33
|
+
end
|
34
|
+
|
35
|
+
def name?(value)
|
36
|
+
names.include?(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
def cmd_path?
|
40
|
+
!cmd_path.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def initialize_names(text)
|
46
|
+
@name, @long, @short = Parser::OptNameHandler.new.call(text)
|
47
|
+
@cli_labels = []
|
48
|
+
@names = [name]
|
49
|
+
initialize_short_name(short) if short
|
50
|
+
initialize_long_name(long) if long
|
51
|
+
@cli_names = cli_labels.join(' ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def initialize_long_name(long_name)
|
55
|
+
label = "--#{long_name}"
|
56
|
+
@cli_labels << label
|
57
|
+
@names += [long_name, label]
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize_short_name(short_name)
|
61
|
+
label = "-#{short_name}"
|
62
|
+
@cli_labels << label
|
63
|
+
@names += [short_name, label]
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_type(value)
|
67
|
+
case value.to_s.to_sym
|
68
|
+
when :string, :text, :"" then :string
|
69
|
+
when :number, :numeric then :numeric
|
70
|
+
when :boolean, :bool then :bool
|
71
|
+
when :hash then :hash
|
72
|
+
when :array then :array
|
73
|
+
else
|
74
|
+
fail ArgumentError, "invalid type #{value}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|