choosy 0.1.0
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.
- data/Gemfile +7 -0
- data/Gemfile.lock +25 -0
- data/LICENSE +23 -0
- data/README.markdown +393 -0
- data/Rakefile +57 -0
- data/lib/VERSION +1 -0
- data/lib/choosy/base_command.rb +59 -0
- data/lib/choosy/command.rb +32 -0
- data/lib/choosy/converter.rb +115 -0
- data/lib/choosy/dsl/base_command_builder.rb +172 -0
- data/lib/choosy/dsl/command_builder.rb +43 -0
- data/lib/choosy/dsl/option_builder.rb +155 -0
- data/lib/choosy/dsl/super_command_builder.rb +41 -0
- data/lib/choosy/errors.rb +11 -0
- data/lib/choosy/option.rb +22 -0
- data/lib/choosy/parse_result.rb +64 -0
- data/lib/choosy/parser.rb +184 -0
- data/lib/choosy/printing/color.rb +101 -0
- data/lib/choosy/printing/erb_printer.rb +23 -0
- data/lib/choosy/printing/help_printer.rb +174 -0
- data/lib/choosy/super_command.rb +77 -0
- data/lib/choosy/super_parser.rb +81 -0
- data/lib/choosy/verifier.rb +62 -0
- data/lib/choosy/version.rb +12 -0
- data/lib/choosy.rb +16 -0
- data/spec/choosy/base_command_spec.rb +11 -0
- data/spec/choosy/command_spec.rb +51 -0
- data/spec/choosy/converter_spec.rb +145 -0
- data/spec/choosy/dsl/base_command_builder_spec.rb +328 -0
- data/spec/choosy/dsl/commmand_builder_spec.rb +80 -0
- data/spec/choosy/dsl/option_builder_spec.rb +386 -0
- data/spec/choosy/dsl/super_command_builder_spec.rb +83 -0
- data/spec/choosy/parser_spec.rb +275 -0
- data/spec/choosy/printing/color_spec.rb +74 -0
- data/spec/choosy/printing/help_printer_spec.rb +117 -0
- data/spec/choosy/super_command_spec.rb +80 -0
- data/spec/choosy/super_parser_spec.rb +106 -0
- data/spec/choosy/verifier_spec.rb +180 -0
- data/spec/integration/command-A_spec.rb +37 -0
- data/spec/integration/supercommand-A_spec.rb +61 -0
- data/spec/spec_helpers.rb +30 -0
- metadata +150 -0
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
require 'time'
|
3
|
+
require 'date'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Choosy
|
7
|
+
class Converter
|
8
|
+
CONVERSIONS = {
|
9
|
+
:integer => nil,
|
10
|
+
:int => :integer,
|
11
|
+
:float => nil,
|
12
|
+
:symbol => nil,
|
13
|
+
:file => nil, # Succeeds only if a file is present
|
14
|
+
:yaml => nil, # Loads a YAML file, if present
|
15
|
+
:date => nil,
|
16
|
+
:time => nil,
|
17
|
+
:datetime => nil,
|
18
|
+
:string => nil,
|
19
|
+
:boolean => nil,
|
20
|
+
:bool => :boolean
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.for(ty)
|
24
|
+
if ty.is_a?(Symbol) && CONVERSIONS.has_key?(ty)
|
25
|
+
val = CONVERSIONS[ty]
|
26
|
+
if val
|
27
|
+
return val
|
28
|
+
else
|
29
|
+
return ty
|
30
|
+
end
|
31
|
+
elsif ty.respond_to?(:convert)
|
32
|
+
return ty
|
33
|
+
end
|
34
|
+
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.convert(ty, value)
|
39
|
+
if CONVERSIONS.has_key?(ty)
|
40
|
+
send(ty, value)
|
41
|
+
else
|
42
|
+
ty.convert(value)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.boolean(value)
|
47
|
+
value # already set
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.string(value)
|
51
|
+
value # already set
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.integer(value)
|
55
|
+
begin
|
56
|
+
return Integer(value)
|
57
|
+
rescue ArgumentError
|
58
|
+
raise Choosy::ConversionError.new("Unable to convert '#{value}' into an integer")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.float(value)
|
63
|
+
begin
|
64
|
+
return Float(value)
|
65
|
+
rescue ArgumentError
|
66
|
+
raise Choosy::ConversionError.new("Unable to convert '#{value}' into a float")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.symbol(value)
|
71
|
+
value.to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.file(value)
|
75
|
+
if File.exist?(value)
|
76
|
+
File.new(value)
|
77
|
+
else
|
78
|
+
raise Choosy::ValidationError.new("Unable to locate file: '#{value}'")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.yaml(value)
|
83
|
+
fd = file(value)
|
84
|
+
begin
|
85
|
+
return YAML::load_file(fd.path)
|
86
|
+
rescue Error
|
87
|
+
raise Choosy::ConversionError.new("Unable to load YAML from file: '#{value}'")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.date(value)
|
92
|
+
begin
|
93
|
+
return Date.parse(value)
|
94
|
+
rescue ArgumentError
|
95
|
+
raise Choosy::ConversionError.new("Unable to convert '#{value}' into a date")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.time(value)
|
100
|
+
begin
|
101
|
+
return Time.parse(value)
|
102
|
+
rescue ArgumentError
|
103
|
+
raise Choosy::ConversionError.new("Unable to convert '#{value}' into a time")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.datetime(value)
|
108
|
+
begin
|
109
|
+
return DateTime.parse(value)
|
110
|
+
rescue ArgumentError
|
111
|
+
raise Choosy::ConversionError.new("Unable to convert '#{value}' into datetime")
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
require 'choosy/dsl/option_builder'
|
3
|
+
require 'choosy/printing/erb_printer'
|
4
|
+
|
5
|
+
module Choosy::DSL
|
6
|
+
class BaseCommandBuilder
|
7
|
+
attr_reader :command
|
8
|
+
|
9
|
+
def initialize(command)
|
10
|
+
@command = command
|
11
|
+
end
|
12
|
+
|
13
|
+
def summary(msg)
|
14
|
+
@command.summary = msg
|
15
|
+
end
|
16
|
+
|
17
|
+
def printer(kind, options=nil)
|
18
|
+
return if kind.nil?
|
19
|
+
|
20
|
+
p = nil
|
21
|
+
if kind == :standard
|
22
|
+
p = Choosy::Printing::HelpPrinter.new
|
23
|
+
elsif kind == :erb
|
24
|
+
p = Choosy::Printing::ERBPrinter.new
|
25
|
+
if options.nil? || options[:template].nil?
|
26
|
+
raise Choosy::ConfigurationError.new("no template file given to ERBPrinter")
|
27
|
+
elsif !File.exist?(options[:template])
|
28
|
+
raise Choosy::ConfigurationError.new("the template file doesn't exist: #{options[:template]}")
|
29
|
+
end
|
30
|
+
p.template = options[:template]
|
31
|
+
elsif kind.respond_to?(:print!)
|
32
|
+
p = kind
|
33
|
+
else
|
34
|
+
raise Choosy::ConfigurationError.new("Unknown printing method for help: #{kind}")
|
35
|
+
end
|
36
|
+
|
37
|
+
if p.respond_to?(:color) && options && options.has_key?(:color)
|
38
|
+
p.color.disable! if !options[:color]
|
39
|
+
end
|
40
|
+
|
41
|
+
@command.printer = p
|
42
|
+
end
|
43
|
+
|
44
|
+
def desc(msg)
|
45
|
+
@command.description = msg
|
46
|
+
end
|
47
|
+
|
48
|
+
def separator(msg=nil)
|
49
|
+
@command.listing << (msg.nil? ? "" : msg)
|
50
|
+
end
|
51
|
+
|
52
|
+
def option(arg)
|
53
|
+
raise Choosy::ConfigurationError.new("The option name was nil") if arg.nil?
|
54
|
+
|
55
|
+
builder = nil
|
56
|
+
|
57
|
+
if arg.is_a?(Hash)
|
58
|
+
raise Choosy::ConfigurationError.new("Malformed option hash") if arg.count != 1
|
59
|
+
name = arg.keys[0]
|
60
|
+
builder = OptionBuilder.new(name)
|
61
|
+
|
62
|
+
to_process = arg[name]
|
63
|
+
if to_process.is_a?(Array)
|
64
|
+
builder.dependencies to_process
|
65
|
+
elsif to_process.is_a?(Hash)
|
66
|
+
builder.from_hash to_process
|
67
|
+
else
|
68
|
+
raise Choosy::ConfigurationError.new("Unable to process option hash")
|
69
|
+
end
|
70
|
+
else
|
71
|
+
builder = OptionBuilder.new(arg)
|
72
|
+
raise Choosy::ConfigurationError.new("No configuration block was given") if !block_given?
|
73
|
+
end
|
74
|
+
|
75
|
+
yield builder if block_given?
|
76
|
+
finalize_option_builder builder
|
77
|
+
end
|
78
|
+
|
79
|
+
# Option types
|
80
|
+
def self.create_conversions
|
81
|
+
Choosy::Converter::CONVERSIONS.keys.each do |method|
|
82
|
+
next if method == :boolean || method == :bool
|
83
|
+
|
84
|
+
define_method method do |sym, desc, config=nil, &block|
|
85
|
+
simple_option(sym, desc, true, :one, method, config, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
plural = "#{method}s".to_sym
|
89
|
+
define_method plural do |sym, desc, config=nil, &block|
|
90
|
+
simple_option(sym, desc, true, :many, method, config, &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
underscore = "#{method}_"
|
94
|
+
define_method underscore do |sym, desc, config=nil, &block|
|
95
|
+
simple_option(sym, desc, false, :one, method, config, &block)
|
96
|
+
end
|
97
|
+
|
98
|
+
plural_underscore = "#{plural}_".to_sym
|
99
|
+
define_method plural_underscore do |sym, desc, config=nil, &block|
|
100
|
+
simple_option(sym, desc, false, :many, method, config, &block)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
create_conversions
|
106
|
+
alias :single :string
|
107
|
+
alias :single_ :string_
|
108
|
+
|
109
|
+
alias :multiple :strings
|
110
|
+
alias :multiple_ :strings_
|
111
|
+
|
112
|
+
def boolean(sym, desc, config=nil, &block)
|
113
|
+
simple_option(sym, desc, true, :zero, :boolean, config, &block)
|
114
|
+
end
|
115
|
+
def boolean_(sym, desc, config=nil, &block)
|
116
|
+
simple_option(sym, desc, false, :zero, :boolean, config, &block)
|
117
|
+
end
|
118
|
+
alias :bool :boolean
|
119
|
+
alias :bool_ :boolean_
|
120
|
+
|
121
|
+
def version(msg)
|
122
|
+
v = OptionBuilder.new(OptionBuilder::VERSION)
|
123
|
+
v.long '--version'
|
124
|
+
v.desc "The version number"
|
125
|
+
|
126
|
+
v.validate do
|
127
|
+
raise Choosy::VersionCalled.new(msg)
|
128
|
+
end
|
129
|
+
|
130
|
+
yield v if block_given?
|
131
|
+
finalize_option_builder v
|
132
|
+
end
|
133
|
+
|
134
|
+
def finalize!
|
135
|
+
if @command.printer.nil?
|
136
|
+
printer :standard
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
def finalize_option_builder(option_builder)
|
142
|
+
option_builder.finalize!
|
143
|
+
@command.option_builders[option_builder.option.name] = option_builder
|
144
|
+
@command.listing << option_builder.option
|
145
|
+
|
146
|
+
option_builder.option
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def simple_option(sym, desc, allow_short, param, cast, config, &block)
|
151
|
+
name = sym.to_s
|
152
|
+
builder = OptionBuilder.new sym
|
153
|
+
builder.desc desc
|
154
|
+
builder.short "-#{name[0]}" if allow_short
|
155
|
+
builder.long "--#{name.downcase.gsub(/_/, '-')}"
|
156
|
+
builder.param format_param(name, param)
|
157
|
+
builder.cast cast
|
158
|
+
builder.from_hash config if config
|
159
|
+
|
160
|
+
yield builder if block_given?
|
161
|
+
finalize_option_builder builder
|
162
|
+
end
|
163
|
+
|
164
|
+
def format_param(name, count)
|
165
|
+
case count
|
166
|
+
when :zero then nil
|
167
|
+
when :one then name.upcase
|
168
|
+
when :many then "#{name.upcase}+"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
require 'choosy/dsl/base_command_builder'
|
3
|
+
require 'choosy/dsl/option_builder'
|
4
|
+
require 'choosy/printing/help_printer'
|
5
|
+
|
6
|
+
module Choosy::DSL
|
7
|
+
class CommandBuilder < BaseCommandBuilder
|
8
|
+
def executor(exec=nil, &block)
|
9
|
+
if exec.nil?
|
10
|
+
if block_given?
|
11
|
+
@command.executor = block
|
12
|
+
else
|
13
|
+
raise Choosy::ConfigurationError.new("The executor was nil")
|
14
|
+
end
|
15
|
+
else
|
16
|
+
if !exec.respond_to?(:execute!)
|
17
|
+
raise Choosy::ConfigurationError.new("Execution class doesn't implement 'execute!'")
|
18
|
+
end
|
19
|
+
@command.executor = exec
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def help(msg=nil)
|
24
|
+
h = OptionBuilder.new(OptionBuilder::HELP)
|
25
|
+
h.short '-h'
|
26
|
+
h.long '--help'
|
27
|
+
msg ||= "Show this help message"
|
28
|
+
h.desc msg
|
29
|
+
|
30
|
+
h.validate do
|
31
|
+
raise Choosy::HelpCalled.new(@name)
|
32
|
+
end
|
33
|
+
|
34
|
+
finalize_option_builder h
|
35
|
+
end
|
36
|
+
|
37
|
+
def arguments(&block)
|
38
|
+
raise Choosy::ConfigurationError.new("No block to arguments call") if !block_given?
|
39
|
+
|
40
|
+
command.argument_validation = block
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'choosy/option'
|
2
|
+
require 'choosy/errors'
|
3
|
+
require 'choosy/converter'
|
4
|
+
|
5
|
+
module Choosy::DSL
|
6
|
+
class OptionBuilder
|
7
|
+
HELP = :__help__
|
8
|
+
VERSION = :__version__
|
9
|
+
|
10
|
+
ZERO_ARITY = (0 .. 0)
|
11
|
+
ONE_ARITY = (1 .. 1)
|
12
|
+
MANY_ARITY = (1 .. 1000)
|
13
|
+
|
14
|
+
attr_reader :option
|
15
|
+
|
16
|
+
def initialize(name)
|
17
|
+
@option = Choosy::Option.new(name)
|
18
|
+
@count_called = false
|
19
|
+
end
|
20
|
+
|
21
|
+
def short(flag, param=nil)
|
22
|
+
option.short_flag = flag
|
23
|
+
param(param)
|
24
|
+
end
|
25
|
+
|
26
|
+
def long(flag, param=nil)
|
27
|
+
option.long_flag = flag
|
28
|
+
param(param)
|
29
|
+
end
|
30
|
+
|
31
|
+
def flags(shorter, longer=nil, parameter=nil)
|
32
|
+
short(shorter)
|
33
|
+
long(longer) if longer
|
34
|
+
param(parameter) if parameter
|
35
|
+
end
|
36
|
+
|
37
|
+
def desc(description)
|
38
|
+
option.description = description
|
39
|
+
end
|
40
|
+
|
41
|
+
def default(value)
|
42
|
+
option.default_value = value
|
43
|
+
end
|
44
|
+
|
45
|
+
def required(value=nil)
|
46
|
+
option.required = if value.nil? || value == true
|
47
|
+
true
|
48
|
+
else
|
49
|
+
false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def param(param)
|
54
|
+
return if param.nil?
|
55
|
+
option.flag_parameter = param
|
56
|
+
return if @count_called
|
57
|
+
|
58
|
+
if param =~ /\+$/
|
59
|
+
option.arity = MANY_ARITY
|
60
|
+
else
|
61
|
+
option.arity = ONE_ARITY
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def count(restriction)
|
66
|
+
@count_called = true
|
67
|
+
if restriction.is_a?(Hash)
|
68
|
+
lower_bound = restriction[:at_least] || restriction[:exactly] || 1
|
69
|
+
upper_bound = restriction[:at_most] || restriction[:exactly] || 1000
|
70
|
+
|
71
|
+
check_count(lower_bound)
|
72
|
+
check_count(upper_bound)
|
73
|
+
if lower_bound > upper_bound
|
74
|
+
raise Choosy::ConfigurationError.new("The upper bound (#{upper_bound}) is less than the lower bound (#{lower_bound}).")
|
75
|
+
end
|
76
|
+
|
77
|
+
option.arity = (lower_bound .. upper_bound)
|
78
|
+
elsif restriction == :zero || restriction == :none
|
79
|
+
option.arity = ZERO_ARITY
|
80
|
+
elsif restriction == :once
|
81
|
+
option.arity = ONE_ARITY
|
82
|
+
else
|
83
|
+
check_count(restriction)
|
84
|
+
option.arity = (restriction .. restriction)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def cast(ty)
|
89
|
+
option.cast_to = Choosy::Converter.for(ty)
|
90
|
+
if option.cast_to.nil?
|
91
|
+
raise Choosy::ConfigurationError.new("Unknown conversion cast: #{ty}")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def validate(&block)
|
96
|
+
option.validation_step = block
|
97
|
+
end
|
98
|
+
|
99
|
+
def fail(msg)
|
100
|
+
flag_fmt = if option.short_flag && option.long_flag
|
101
|
+
"#{option.short_flag}/#{option.long_flag}"
|
102
|
+
end
|
103
|
+
flag_fmt ||= option.short_flag || option.long_flag
|
104
|
+
flag_param = if option.flag_parameter
|
105
|
+
" #{option.flag_parameter}"
|
106
|
+
end
|
107
|
+
raise Choosy::ValidationError.new("#{flag_fmt}#{flag_param}: #{msg}")
|
108
|
+
end
|
109
|
+
|
110
|
+
def dependencies(*args)
|
111
|
+
if args.count == 1 && args[0].is_a?(Array)
|
112
|
+
option.dependent_options = args[0]
|
113
|
+
else
|
114
|
+
option.dependent_options = args
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def from_hash(hash)
|
119
|
+
raise Choosy::ConfigurationError.new("Only hash arguments allowed") if !hash.is_a?(Hash)
|
120
|
+
|
121
|
+
hash.each do |k, v|
|
122
|
+
if respond_to?(k)
|
123
|
+
if v.is_a?(Array)
|
124
|
+
self.send(k, *v)
|
125
|
+
else
|
126
|
+
self.send(k, v)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
raise Choosy::ConfigurationError.new("Not a recognized option: #{k}")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def finalize!
|
135
|
+
if option.arity.nil?
|
136
|
+
option.arity = ZERO_ARITY
|
137
|
+
end
|
138
|
+
|
139
|
+
if option.cast_to.nil?
|
140
|
+
if option.arity == ZERO_ARITY
|
141
|
+
option.cast_to = :boolean
|
142
|
+
else
|
143
|
+
option.cast_to = :string
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
def check_count(count)
|
150
|
+
if !count.is_a?(Integer)
|
151
|
+
raise Choosy::ConfigurationError.new("Expected a number to count, got '#{count}'")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'choosy/errors'
|
2
|
+
require 'choosy/dsl/base_command_builder'
|
3
|
+
require 'choosy/command'
|
4
|
+
|
5
|
+
module Choosy::DSL
|
6
|
+
class SuperCommandBuilder < BaseCommandBuilder
|
7
|
+
def command(cmd)
|
8
|
+
subcommand = if cmd.is_a?(Choosy::Command)
|
9
|
+
cmd
|
10
|
+
else
|
11
|
+
Choosy::Command.new(cmd)
|
12
|
+
end
|
13
|
+
yield subcommand.builder if block_given?
|
14
|
+
finalize_subcommand(subcommand)
|
15
|
+
end
|
16
|
+
|
17
|
+
def help(msg=nil)
|
18
|
+
msg ||= "Show the info for a command, or this message"
|
19
|
+
help = Choosy::Command.new :help do |help|
|
20
|
+
help.summary msg
|
21
|
+
|
22
|
+
help.arguments do |args|
|
23
|
+
if args.nil? || args.length == 0
|
24
|
+
raise Choosy::HelpCalled.new(@command.name)
|
25
|
+
else
|
26
|
+
raise Choosy::HelpCalled.new(args[0].to_sym)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
finalize_subcommand(help)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def finalize_subcommand(subcommand)
|
35
|
+
subcommand.builder.finalize!
|
36
|
+
@command.command_builders[subcommand.name] = subcommand.builder
|
37
|
+
@command.listing << subcommand
|
38
|
+
subcommand
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Choosy
|
2
|
+
Error = Class.new(RuntimeError)
|
3
|
+
|
4
|
+
ConfigurationError = Class.new(Choosy::Error)
|
5
|
+
ValidationError = Class.new(Choosy::Error)
|
6
|
+
HelpCalled = Class.new(Choosy::Error)
|
7
|
+
VersionCalled = Class.new(Choosy::Error)
|
8
|
+
ConversionError = Class.new(Choosy::Error)
|
9
|
+
ParseError = Class.new(Choosy::Error)
|
10
|
+
SuperParseError = Class.new(Choosy::Error)
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Choosy
|
2
|
+
class Option
|
3
|
+
attr_accessor :name, :description
|
4
|
+
attr_accessor :short_flag, :long_flag, :flag_parameter
|
5
|
+
attr_accessor :cast_to, :default_value
|
6
|
+
attr_accessor :validation_step
|
7
|
+
attr_accessor :arity
|
8
|
+
attr_accessor :dependent_options
|
9
|
+
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
@required = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def required=(req)
|
16
|
+
@required = req
|
17
|
+
end
|
18
|
+
def required?
|
19
|
+
@required == true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'choosy/verifier'
|
2
|
+
|
3
|
+
module Choosy
|
4
|
+
class BaseParseResult
|
5
|
+
attr_reader :command, :options, :unparsed
|
6
|
+
|
7
|
+
def initialize(command)
|
8
|
+
@command = command
|
9
|
+
@options = {}
|
10
|
+
@unparsed = []
|
11
|
+
@verified = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](opt)
|
15
|
+
@options[opt]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(opt, val)
|
19
|
+
@options[opt] = val
|
20
|
+
end
|
21
|
+
|
22
|
+
def verified?
|
23
|
+
@verified
|
24
|
+
end
|
25
|
+
|
26
|
+
def verify!
|
27
|
+
basic_verification
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
def basic_verification(&block)
|
32
|
+
verifier = Verifier.new
|
33
|
+
verifier.verify_options!(self)
|
34
|
+
yield verifier if block_given?
|
35
|
+
@verified = true
|
36
|
+
self
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class ParseResult < BaseParseResult
|
41
|
+
attr_reader :args
|
42
|
+
|
43
|
+
def initialize(command)
|
44
|
+
super(command)
|
45
|
+
@args = []
|
46
|
+
end
|
47
|
+
|
48
|
+
def verify!
|
49
|
+
return self if verified?
|
50
|
+
basic_verification do |verifier|
|
51
|
+
verifier.verify_arguments!(self)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class SuperParseResult < BaseParseResult
|
57
|
+
attr_reader :subresults
|
58
|
+
|
59
|
+
def initialize(command)
|
60
|
+
super(command)
|
61
|
+
@subresults = []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|