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