brigadier 0.1.1
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 +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +20 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +83 -0
- data/Rakefile +8 -0
- data/brigadier.gemspec +25 -0
- data/examples/advanced.rb +109 -0
- data/examples/basic.rb +19 -0
- data/examples/intermediate.rb +28 -0
- data/lib/brigadier/commands/base.rb +117 -0
- data/lib/brigadier/commands/command.rb +40 -0
- data/lib/brigadier/commands/sub_command.rb +63 -0
- data/lib/brigadier/commands.rb +8 -0
- data/lib/brigadier/exceptions/base.rb +21 -0
- data/lib/brigadier/exceptions/execute_block_missing.rb +5 -0
- data/lib/brigadier/exceptions/value_invalid.rb +5 -0
- data/lib/brigadier/exceptions/value_missing.rb +5 -0
- data/lib/brigadier/exceptions.rb +10 -0
- data/lib/brigadier/helper.rb +55 -0
- data/lib/brigadier/parameters/argument.rb +57 -0
- data/lib/brigadier/parameters/base.rb +75 -0
- data/lib/brigadier/parameters/option.rb +51 -0
- data/lib/brigadier/parameters/toggle.rb +63 -0
- data/lib/brigadier/parameters.rb +9 -0
- data/lib/brigadier/runner.rb +71 -0
- data/lib/brigadier/validators/base.rb +25 -0
- data/lib/brigadier/validators/email.rb +15 -0
- data/lib/brigadier/validators.rb +7 -0
- data/lib/brigadier/version.rb +3 -0
- data/lib/brigadier.rb +123 -0
- metadata +149 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Helper
|
3
|
+
def display_help_for(item, label)
|
4
|
+
opts = item.values.uniq(&:name)
|
5
|
+
return if opts.empty?
|
6
|
+
$stderr.puts label
|
7
|
+
opts.each do |opt|
|
8
|
+
next if opt.hidden?
|
9
|
+
$stderr.puts '%s %-28s %s' % [ indent, opt.display_name, opt.display_description ]
|
10
|
+
end
|
11
|
+
$stderr.puts
|
12
|
+
end
|
13
|
+
|
14
|
+
def help_requested?(args)
|
15
|
+
!(%w( --help -help -h ) & args).empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def display_help_if_requested(klasses, args)
|
19
|
+
return false unless help_requested?(args)
|
20
|
+
$stderr.puts
|
21
|
+
help(klasses)
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def help(klasses)
|
26
|
+
klasses = [ *klasses ]
|
27
|
+
display_help_for(available_params_for(klasses, :toggles), 'Toggle(s)')
|
28
|
+
display_help_for(available_params_for(klasses, :options), 'Option(s)')
|
29
|
+
display_help_for(available_params_for(klasses, :arguments), 'Argument(s)')
|
30
|
+
display_help_for(available_params_for(klasses, :sub_commands), 'Subcommand(s)') unless sub_command?
|
31
|
+
end
|
32
|
+
|
33
|
+
def available_params_for(klasses, param)
|
34
|
+
klasses.each_with_object({}) { |k, a| a.merge!(k.public_send(param)) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def inverse_toggle_arg?(arg)
|
38
|
+
arg =~ /^-{1,}no.+$/ ? true : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def sub_command?
|
42
|
+
is_a?(Brigadier::Commands::SubCommand)
|
43
|
+
end
|
44
|
+
|
45
|
+
def option_or_toggle?(arg)
|
46
|
+
arg.match(/^-[-\w+]/)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def indent
|
52
|
+
@indent ||= (' ' * 2)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Parameters
|
3
|
+
class Argument
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def initialize(name, description, args, block)
|
7
|
+
@name = name
|
8
|
+
@description = description
|
9
|
+
@args = args
|
10
|
+
@value = nil
|
11
|
+
@block = block
|
12
|
+
assign_value_from_env_variable
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_name
|
16
|
+
name
|
17
|
+
end
|
18
|
+
|
19
|
+
def display_description
|
20
|
+
opts = []
|
21
|
+
opts << 'valid_values: %s' % [ valid_values.inspect ] if valid_values?
|
22
|
+
opts << 'current: %s' % [ value.inspect ]
|
23
|
+
'%s (%s)' % [ description, opts.join(', ') ]
|
24
|
+
end
|
25
|
+
|
26
|
+
def valid_values
|
27
|
+
args.fetch(:valid_values, [])
|
28
|
+
end
|
29
|
+
|
30
|
+
def validate!
|
31
|
+
validate_presence! if required?
|
32
|
+
validate_valid_value! if valid_values?
|
33
|
+
validate_using_klass! if validator_klasses
|
34
|
+
end
|
35
|
+
|
36
|
+
def required?
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_values?
|
41
|
+
!valid_values.empty?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :args
|
47
|
+
|
48
|
+
def validate_valid_value!
|
49
|
+
raise Exceptions::ValueInvalid.new(self), 'Value is invalid. Valid values are %s' % [ valid_values] if valid_values? && !valid_values.include?(value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_presence!
|
53
|
+
raise Exceptions::ValueMissing.new(self), 'Value is empty' if [ nil, '' ].include?(value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Parameters
|
3
|
+
module Base
|
4
|
+
attr_reader :name, :description, :value
|
5
|
+
|
6
|
+
def forbidden_parameters
|
7
|
+
[ :name, :description, :value ]
|
8
|
+
end
|
9
|
+
|
10
|
+
def value=(value)
|
11
|
+
@value = block_defined? ? block.call(value) : value
|
12
|
+
ENV[env_variable_value_key_name] = @value.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def normalised_attribute_name
|
16
|
+
attribute_name.gsub(/[ -]/, '_')
|
17
|
+
end
|
18
|
+
|
19
|
+
def attribute_name
|
20
|
+
@attribute_name ||= args.fetch(:attribute_name, name).to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def hidden?
|
24
|
+
args.fetch(:hidden, false)
|
25
|
+
end
|
26
|
+
|
27
|
+
def value?
|
28
|
+
!value.nil?
|
29
|
+
end
|
30
|
+
|
31
|
+
def valid?
|
32
|
+
validate! && true
|
33
|
+
rescue Exceptions::Base
|
34
|
+
false
|
35
|
+
end
|
36
|
+
|
37
|
+
def required?
|
38
|
+
args.fetch(:required, false)
|
39
|
+
end
|
40
|
+
|
41
|
+
def block_defined?
|
42
|
+
!block.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
attr_reader :block
|
48
|
+
|
49
|
+
def default_value
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_using_klass!
|
54
|
+
validator_klasses.each { |klass| klass.new(self).validate! }
|
55
|
+
end
|
56
|
+
|
57
|
+
def validator_klasses
|
58
|
+
args.fetch(:validators, nil)
|
59
|
+
end
|
60
|
+
|
61
|
+
def assign_value_from_env_variable
|
62
|
+
value = ENV.fetch(env_variable_value_key_name, default_value)
|
63
|
+
self.value = value if value
|
64
|
+
end
|
65
|
+
|
66
|
+
def normalised_name
|
67
|
+
name.gsub(/[ -]/, '_')
|
68
|
+
end
|
69
|
+
|
70
|
+
def env_variable_value_key_name
|
71
|
+
normalised_name.upcase
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Parameters
|
3
|
+
class Option
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def initialize(name, description, args, block)
|
7
|
+
@name = name
|
8
|
+
@description = description
|
9
|
+
@args = args
|
10
|
+
@value = default_value
|
11
|
+
@block = block
|
12
|
+
assign_value_from_env_variable
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_name
|
16
|
+
str = [ "--#{name}" ]
|
17
|
+
str << (required? ? '<value>' : '[<value>]')
|
18
|
+
str.join(' ')
|
19
|
+
end
|
20
|
+
|
21
|
+
def display_description
|
22
|
+
opts = []
|
23
|
+
opts << 'default: %s' % [ default_value ] if default_value
|
24
|
+
opts << 'required: %s' % [ required? ]
|
25
|
+
opts << 'current: %s' % [ value.inspect ]
|
26
|
+
'%s (%s)' % [ description, opts.join(', ') ]
|
27
|
+
end
|
28
|
+
|
29
|
+
def validate!
|
30
|
+
validate_presence! if required?
|
31
|
+
validate_using_klass! if validator_klasses
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :args
|
37
|
+
|
38
|
+
def aliases
|
39
|
+
args.fetch(:aliases, [])
|
40
|
+
end
|
41
|
+
|
42
|
+
def default_value
|
43
|
+
args.fetch(:default, nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_presence!
|
47
|
+
raise Exceptions::ValueMissing.new(self), 'Value is empty' if [ nil, '' ].include?(value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Parameters
|
3
|
+
class Toggle
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def initialize(name, description, args, block)
|
7
|
+
@name = name
|
8
|
+
@description = description
|
9
|
+
@args = args
|
10
|
+
@block = block
|
11
|
+
@value = default_value
|
12
|
+
assign_value_from_env_variable
|
13
|
+
end
|
14
|
+
|
15
|
+
def display_name
|
16
|
+
"--#{name}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def display_description
|
20
|
+
'%s (%s)' % [ description, description_detail ]
|
21
|
+
end
|
22
|
+
|
23
|
+
def enable!
|
24
|
+
assign_value(true)
|
25
|
+
end
|
26
|
+
|
27
|
+
def disable!
|
28
|
+
assign_value(false)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_reader :args
|
34
|
+
|
35
|
+
def value_set?
|
36
|
+
value != default_value
|
37
|
+
end
|
38
|
+
|
39
|
+
def description_detail
|
40
|
+
detail = [ 'default: %s' % [ default_value ] ]
|
41
|
+
detail << 'current: %s' % [ value ] if value_set?
|
42
|
+
detail.join(', ')
|
43
|
+
end
|
44
|
+
|
45
|
+
def aliases
|
46
|
+
args.fetch(:aliases, [])
|
47
|
+
end
|
48
|
+
|
49
|
+
def klass_only
|
50
|
+
self.class.to_s.to_s.gsub(/^.*::/, '')
|
51
|
+
end
|
52
|
+
|
53
|
+
def default_value
|
54
|
+
args.fetch(:default, false)
|
55
|
+
end
|
56
|
+
|
57
|
+
def assign_value(value)
|
58
|
+
@value = value
|
59
|
+
ENV[env_variable_value_key_name] = value.to_s.downcase
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Brigadier
|
4
|
+
class Runner
|
5
|
+
include Helper
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@args = args
|
9
|
+
end
|
10
|
+
|
11
|
+
def run(*klasses)
|
12
|
+
remaining_args = []
|
13
|
+
args_to_process = []
|
14
|
+
sub_command_to_execute = false
|
15
|
+
full_args = args.dup
|
16
|
+
|
17
|
+
collect_execute_blocks(klasses)
|
18
|
+
|
19
|
+
klasses.each do |klass|
|
20
|
+
remaining_args = args.dup
|
21
|
+
|
22
|
+
until remaining_args.empty?
|
23
|
+
arg = remaining_args.shift
|
24
|
+
|
25
|
+
klass.sub_commands.each do |names, sub_command|
|
26
|
+
next unless names.include?(arg)
|
27
|
+
sub_command_to_execute = sub_command
|
28
|
+
break
|
29
|
+
end
|
30
|
+
|
31
|
+
break if sub_command_to_execute
|
32
|
+
|
33
|
+
args_to_process << arg
|
34
|
+
end
|
35
|
+
|
36
|
+
break if sub_command_to_execute
|
37
|
+
end
|
38
|
+
|
39
|
+
if sub_command_to_execute
|
40
|
+
args_to_process += remaining_args
|
41
|
+
sub_command_to_execute.execute(args_to_process, full_args)
|
42
|
+
elsif default_command
|
43
|
+
default_command.execute(args, full_args, klasses)
|
44
|
+
elsif help_requested?(args)
|
45
|
+
$stderr.puts
|
46
|
+
help(klasses)
|
47
|
+
else
|
48
|
+
raise Exceptions::ExecuteBlockMissing.new(self), 'There is no execute {} block defined'
|
49
|
+
end
|
50
|
+
rescue Exceptions::ExecuteBlockMissing, Exceptions::Base => e
|
51
|
+
$stderr.puts "ERROR: #{e.message}"
|
52
|
+
exit(Exceptions::ERROR_EXIT_CODE)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
attr_reader :args
|
58
|
+
|
59
|
+
def default_command
|
60
|
+
Brigadier.default_command
|
61
|
+
end
|
62
|
+
|
63
|
+
def collect_execute_blocks(klasses)
|
64
|
+
klasses.each do |klass|
|
65
|
+
execute_proc = klass.instance_variable_get('@execute_proc')
|
66
|
+
next unless execute_proc
|
67
|
+
Brigadier.set_default_command(klass, execute_proc)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Validators
|
3
|
+
module Base
|
4
|
+
def initialize(obj)
|
5
|
+
@obj = obj
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate!
|
9
|
+
raise Brigadier::Exceptions::Base.new(obj), failure_message unless valid?
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :obj
|
15
|
+
|
16
|
+
def value
|
17
|
+
@value ||= obj.value
|
18
|
+
end
|
19
|
+
|
20
|
+
def valid?
|
21
|
+
!obj.value.match(/poop/i)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Brigadier
|
2
|
+
module Validators
|
3
|
+
class Email
|
4
|
+
include Base
|
5
|
+
|
6
|
+
def failure_message
|
7
|
+
%('%s' is not a valid email address.) % [ value ]
|
8
|
+
end
|
9
|
+
|
10
|
+
def valid?
|
11
|
+
value.match(/\A([\w+\-].?)+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/brigadier.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'brigadier/version'
|
2
|
+
require 'brigadier/exceptions'
|
3
|
+
require 'brigadier/validators'
|
4
|
+
require 'brigadier/helper'
|
5
|
+
require 'brigadier/parameters'
|
6
|
+
require 'brigadier/commands'
|
7
|
+
require 'brigadier/runner'
|
8
|
+
|
9
|
+
module Brigadier
|
10
|
+
@@arguments = {}
|
11
|
+
@@options = {}
|
12
|
+
@@toggles = {}
|
13
|
+
@@sub_commands = {}
|
14
|
+
@@default_command = nil
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def arguments
|
18
|
+
@@arguments
|
19
|
+
end
|
20
|
+
|
21
|
+
def options
|
22
|
+
@@options
|
23
|
+
end
|
24
|
+
|
25
|
+
def toggles
|
26
|
+
@@toggles
|
27
|
+
end
|
28
|
+
|
29
|
+
def sub_commands
|
30
|
+
@@sub_commands
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_command
|
34
|
+
@@default_command
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_default_command(klass, block)
|
38
|
+
@@default_command = Commands::Command.new(klass, block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def options
|
43
|
+
@options ||= {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def toggles
|
47
|
+
@toggles ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def arguments
|
51
|
+
@arguments ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def sub_commands
|
55
|
+
@sub_commands ||= {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def execute(&block)
|
59
|
+
@execute_proc = block
|
60
|
+
end
|
61
|
+
|
62
|
+
def sub_command?
|
63
|
+
@sub_command || false
|
64
|
+
end
|
65
|
+
|
66
|
+
def sub_command(name, description, args = {}, &block)
|
67
|
+
@sub_command = true
|
68
|
+
instance = args.fetch(:instance, new)
|
69
|
+
aliases = args.fetch(:aliases, [])
|
70
|
+
all_aliases = ([ *name ] + aliases).uniq
|
71
|
+
args[:aliases] = all_aliases
|
72
|
+
|
73
|
+
sub_commands[all_aliases] = Commands::SubCommand.new(name, description, instance, args, block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def argument(name, description, args = {}, &block)
|
77
|
+
arguments[name] = Parameters::Argument.new(name, description, args, block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def option(name, description, args = {}, &block)
|
81
|
+
new_aliases = []
|
82
|
+
aliases = args.fetch(:aliases, [])
|
83
|
+
|
84
|
+
([ *name ] + aliases).each do |n|
|
85
|
+
new_aliases << "--#{n}"
|
86
|
+
new_aliases << "-#{n}"
|
87
|
+
end
|
88
|
+
|
89
|
+
new_aliases.uniq!
|
90
|
+
args[:aliases] = new_aliases
|
91
|
+
options[new_aliases] = Parameters::Option.new(name, description, args, block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def toggle(name, description, args = {}, &block)
|
95
|
+
new_aliases = []
|
96
|
+
new_inverse_aliases = []
|
97
|
+
aliases = args.fetch(:aliases, [])
|
98
|
+
modifiers = %w( -- - )
|
99
|
+
inverse_modifiers = %w( -no- --no- -not- --not- -no_ --no_ -not_ --not_ )
|
100
|
+
|
101
|
+
([ *name ] + aliases).each do |n|
|
102
|
+
alt_name = n.gsub(/[ -]/, '_')
|
103
|
+
|
104
|
+
modifiers.each do |modifier|
|
105
|
+
new_aliases << '%s%s' % [ modifier, n]
|
106
|
+
new_aliases << '%s%s' % [ modifier, alt_name ]
|
107
|
+
end
|
108
|
+
|
109
|
+
inverse_modifiers.each do |modifier|
|
110
|
+
new_inverse_aliases << '%s%s' % [ modifier, n]
|
111
|
+
new_inverse_aliases << '%s%s' % [ modifier, alt_name ]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
new_aliases.uniq!
|
116
|
+
new_inverse_aliases.uniq!
|
117
|
+
all_aliases = [ new_aliases + new_inverse_aliases ].uniq.flatten
|
118
|
+
args[:aliases] = new_aliases
|
119
|
+
args[:inverse_aliases] = new_inverse_aliases
|
120
|
+
|
121
|
+
toggles[all_aliases] = Parameters::Toggle.new(name, description, args, block)
|
122
|
+
end
|
123
|
+
end
|