brigadier 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|