-
Notifications
You must be signed in to change notification settings - Fork 79
/
parser.rb
155 lines (123 loc) · 3.7 KB
/
parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
require 'optparse'
require 'cane/default_checks'
require 'cane/cli/options'
require 'cane/version'
module Cane
module CLI
# Provides a specification for the command line interface that drives
# documentation, parsing, and default values.
class Parser
# Exception to indicate that no further processing is required and the
# program can exit. This is used to handle --help and --version flags.
class OptionsHandled < RuntimeError; end
def self.parse(*args)
new.parse(*args)
end
def initialize(stdout = $stdout)
@stdout = stdout
add_banner
add_user_defined_checks
Cane.default_checks.each do |check|
add_check_options(check)
end
add_cane_options
add_version
add_help
end
def parse(args, ret = true)
parser.parse!(get_default_options + args)
Cane::CLI.default_options.merge(options)
rescue OptionParser::InvalidOption
args = %w(--help)
ret = false
retry
rescue OptionsHandled
ret
end
def get_default_options
if Cane::File.exists?('./.cane')
Cane::File.contents('./.cane').split(/\s+/m)
else
[]
end
end
def add_banner
parser.banner = <<-BANNER
Usage: cane [options]
Default options are loaded from a .cane file in the current directory.
BANNER
end
def add_user_defined_checks
description = "Load a Ruby file containing user-defined checks"
parser.on("-r", "--require FILE", description) do |f|
load(f)
end
description = "Use the given user-defined check"
parser.on("-c", "--check CLASS", description) do |c|
check = Kernel.const_get(c)
options[:checks] << check
add_check_options(check)
end
parser.separator ""
end
def add_check_options(check)
check.options.each do |key, data|
cli_key = key.to_s.tr('_', '-')
opts = data[1] || {}
variable = opts[:variable] || "VALUE"
defaults = opts[:default] || []
if opts[:type] == Array
parser.on("--#{cli_key} #{variable}", Array, data[0]) do |opts|
(options[key.to_sym] ||= []) << opts
end
else
if [*defaults].length > 0
add_option ["--#{cli_key}", variable], *data
else
add_option ["--#{cli_key}"], *data
end
end
end
parser.separator ""
end
def add_cane_options
add_option %w(--max-violations VALUE),
"Max allowed violations", default: 0, cast: :to_i
parser.separator ""
end
def add_version
parser.on_tail("-v", "--version", "Show version") do
stdout.puts Cane::VERSION
raise OptionsHandled
end
end
def add_help
parser.on_tail("-h", "--help", "Show this message") do
stdout.puts parser
raise OptionsHandled
end
end
def add_option(option, description, opts={})
option_key = option[0].gsub('--', '').tr('-', '_').to_sym
default = opts[:default]
cast = opts[:cast] || ->(x) { x }
if default
description += " (default: %s)" % default
end
parser.on(option.join(' '), description) do |v|
options[option_key] = cast.to_proc.call(v)
options.delete(opts[:clobber])
end
end
def options
@options ||= {
checks: Cane.default_checks
}
end
def parser
@parser ||= OptionParser.new
end
attr_reader :stdout
end
end
end