Module: HQ::Tools::Getopt

Defined in:
lib/hq/tools/getopt.rb

Class Method Summary collapse

Class Method Details

.process(argv, easy_specs) ⇒ Object



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
# File 'lib/hq/tools/getopt.rb', line 9

def self.process argv, easy_specs

	argv = argv.flatten

	# convert easy_specs into specs
	specs = {}
	ret = {}
	easy_specs.each do |easy_spec|
		if easy_spec[:options]
			options = easy_spec[:options].clone
			options << easy_spec[:default] if easy_spec[:default]
			options.each do |option|
				spec = {}
				spec[:long_name] = to_long option
				spec[:type] = option == easy_spec[:default] ? :switch_default : :switch
				spec[:key] = easy_spec[:name]
				spec[:arg_value] = option
				specs[spec[:long_name]] = spec
			end
			ret[easy_spec[:name]] = easy_spec[:default]
		else
			easy_spec[:long_name] = to_long easy_spec[:name]
			spec = {}
			spec[:long_name] = to_long easy_spec[:name]
			spec[:type] = case
				when easy_spec[:boolean] then :boolean
				when easy_spec[:required] then :required
				else :optional
			end
			spec[:key] = easy_spec[:name]
			spec[:arg_value] = easy_spec[:default]
			spec[:verify] = easy_spec[:regex]
			spec[:convert] = easy_spec[:convert]
			spec[:multi] = easy_spec[:multi]
			specs[spec[:long_name]] = spec
			if easy_spec[:multi]
				ret[easy_spec[:name]] = []
			elsif easy_spec[:boolean]
				ret[easy_spec[:name]] = false
			elsif easy_spec[:required]
				# do nothing
			else
				ret[easy_spec[:name]] = easy_spec[:default]
			end
		end
	end

	# save main argv value because we clobber it
	old_argv = []
	ARGV.each { |arg| old_argv << arg }
	new_argv = []
	argv.each { |arg| new_argv << arg }
	begin

		require "getoptlong"
		getopt_args = []
		specs.each do |long_name, spec|
			getopt_flags = case spec[:type]
				when :required then GetoptLong::REQUIRED_ARGUMENT
				when :optional then GetoptLong::REQUIRED_ARGUMENT
				when :boolean then GetoptLong::NO_ARGUMENT
				when :switch then GetoptLong::NO_ARGUMENT
				when :switch_default then GetoptLong::NO_ARGUMENT
				else raise "Invalid getopt argument type: #{spec[:type]}"
			end
			getopt_args << [ spec[:long_name], getopt_flags ]
			#ret[spec[:key]] = spec[:arg_value] if [ :optional, :switch_default ].include? spec[:type]
			#ret[spec[:key]] = [] if spec[:multi]
			#ret[spec[:key]] = false if spec[:type] == :boolean
		end
		ARGV.clear
		new_argv.each { |arg| ARGV << arg }
		GetoptLong.new(*getopt_args).each do |opt, arg|
			spec = specs[opt]
			case spec[:type]
				when :required, :optional
					ret[spec[:key]] << arg if spec[:multi]
					ret[spec[:key]] = arg unless spec[:multi]
				when :switch, :switch_default
					ret[spec[:key]] = spec[:arg_value]
				when :boolean
					ret[spec[:key]] = true
				else
					raise "Error"
			end
		end

		# check for missing required arguments
		specs.values.each do |spec|
			next unless spec[:type] == :required
			next if ! spec[:multi] && ret.include?(spec[:key])
			next if spec[:multi] && ! ret[spec[:key]].empty?
			msg = "#{$0}: option '#{spec[:long_name]}' is required"
			$stderr.puts msg
			raise GetoptError.new msg
		end

		# check for mismatched regex arguments
		easy_specs.each do |easy_spec|
			next unless easy_spec[:regex]
			if easy_spec[:multi]
				ret[easy_spec[:name]].each do |value|
					next if value =~ /^#{easy_spec[:regex]}$/
					msg = "#{$0}: option '#{easy_spec[:long_name]}' is invalid: #{value}"
					$stderr.puts msg
					raise GetoptError.new msg
				end
			else
				next if ret[easy_spec[:name]] == easy_spec[:default]
				next if ret[easy_spec[:name]] =~ /^#{easy_spec[:regex]}$/
				msg = "#{$0}: option '#{easy_spec[:long_name]}' is invalid: #{ret[easy_spec[:name]]}"
				$stderr.puts msg
				raise GetoptError.new msg
			end
		end

		# perform conversions
		specs.values.each do |spec|
			next unless ret[spec[:key]].is_a? String
			case spec[:convert]
			when nil
				# do nothing
			when Symbol
				ret[spec[:key]] = ret[spec[:key]].send spec[:convert]
			when Method
				ret[spec[:key]] = spec[:convert].call ret[spec[:key]]
			else
				raise "Don't know what to do with #{spec[:convert].class}"
			end
		end

		rest = []
		ARGV.each { |arg| rest << arg }
		return ret, rest

	rescue GetoptLong::MissingArgument
		raise GetoptError

	ensure
		ARGV.clear
		old_argv.each { |arg| ARGV << arg }
	end
end

.to_long(name) ⇒ Object



5
6
7
# File 'lib/hq/tools/getopt.rb', line 5

def self.to_long name
	return "--#{name.to_s.gsub "_", "-"}"
end