Module: CliTool::OptionParser::ClassMethods

Defined in:
lib/cli_tool/option_parser.rb

Constant Summary collapse

GOL_MAP =

Map for symbol types

{
  none:     GetoptLong::NO_ARGUMENT,
  optional: GetoptLong::OPTIONAL_ARGUMENT,
  required: GetoptLong::REQUIRED_ARGUMENT
}

Instance Method Summary collapse

Instance Method Details

#help(message = nil, missing_options = []) ⇒ Object



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/cli_tool/option_parser.rb', line 239

def help(message = nil, missing_options = [])
  if message.nil?
    help_text = __get_options(:all_options).map do |option, option_args|

      # Show the argument with the default value (if applicable)
      case option_args[:argument]
      when :required
        long_dep = "=<#{option_args[:default] || 'value'}>"
        short_dep = " <#{option_args[:default] || 'value'}>"
      when :optional
        long_dep = "=[#{option_args[:default] || 'value'}]"
        short_dep = " [#{option_args[:default] || 'value'}]"
      when :none
        long_dep = ''
        short_dep = ''
      end

      # Set up the options list
      message = "\t" + option_args[:aliases].map{ |x| "--#{x}#{long_dep}"}.join(', ')
      message << ", -#{option_args[:short]}#{short_dep}" if option_args[:short]
      message << %{ :: Default: "#{option_args[:default]}"} if option_args[:default]

      # Highlight missing options
      unless missing_options.empty?
        missing_required_option = ! (missing_options & option_args[:aliases]).empty?
        message = colorize(message, missing_required_option ? :red : :default)
      end

      # Prepare the option documentation
      if option_args[:documentation]
        doc = option_args[:documentation]
        if doc.is_a?(Array) \
           && (doc.last.is_a?(Symbol) \
           || (doc.last.is_a?(Array) \
               && doc.last.reduce(true) { |o, d| o && d.is_a?(Symbol) }
           ))

          colors = doc.pop
          len = doc.reduce(0) { |o, s| s.length > o ? s.length : o }
          doc = doc.map{ |s| colorize(s.ljust(len, ' '), colors) }.join("\n\t\t")
        elsif doc.is_a?(Array)
          doc = doc.join("\n\t\t")
        end

        message << %{\n\t\t#{doc}}
        message << "\n"
      end
    end

    # Print and format the message
    %{\nHelp: #{$0}\n\n### Options ###\n\n#{help_text.join("\n")}\n### Additional Details ###\n\n#{@@help_message || "No additional documentation"}\n}
  else
    @@help_message = message.split(/\n/).map{ |x| "\t#{x.strip}" }.join("\n")
  end
end

#optionify(option, retval = false) ⇒ Object

Ensure the right format of the options (primarily dashes and casing)



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
# File 'lib/cli_tool/option_parser.rb', line 46

def optionify(option, retval = false)
  if option.is_a?(Array)
    optionify_all(option)
  else
    option = "#{option}".gsub(/^[\-]+/, '').gsub(/(-| )/, '_').to_sym

    # Help us get the primary option over the alias
    option =
      case retval
      when :set, :setter
        "#{option}="
      when :dash, :hyphen, :clize
        "#{option}".gsub(/=$/, '').gsub('_', '-')
      when :primary
        all_opts = __get_options(:all_options)
        hyphenated = optionify(option, :hyphen)
        option = all_opts.has_key?(hyphenated) ? option : Proc.new {
          all_opts.reduce(nil) do |result, (opt, opt_args)|
            [opt].concat(opt_args[:aliases]).include?(hyphenated) ? opt : result
          end
        }.call
        optionify(option)
      else
        option
      end

    option.to_sym
  end
end

#optionify_all(option, retval = false) ⇒ Object



76
77
78
# File 'lib/cli_tool/option_parser.rb', line 76

def optionify_all(option, retval = false)
  [option].compact.flatten.map { |x| optionify(x, retval) }
end

#options(opts = nil) ⇒ Object

Create the options array



39
40
41
42
43
# File 'lib/cli_tool/option_parser.rb', line 39

def options(opts = nil)
  return (__get_options(:gol_options) || []).uniq unless opts
  __process_options(opts)
  true
end

#run(entrypoint = false, *args, &block) ⇒ Object

Handle running options



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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/cli_tool/option_parser.rb', line 98

def run(entrypoint = false, *args, &block)

  # Get the object to work with
  object =
    if args.last.class <= self
      args.pop
    elsif self < Singleton
      instance
    else
      new
    end

  # Get class variable hash
  class_vars = __get_options

  # Cache variables
  exit_code = 0
  max_puts_length = 0
  processed_options = []
  missing_arguments = []

  # Option setter proc
  option_setter = Proc.new do |option, value|
    option_attr = optionify(option, :primary)
    option = optionify(option_attr, :dash)
    processed_options << option

    # Help process values
    value =
      case value
      when ''
        true
      when 'true'
        true
      when 'false'
        false
      when 'nil', 'null'
        nil
      else
        value
      end

    # Run preprocessor on the data (if applicable)
    preprocessor = class_vars[:preprocessors][option]
    if preprocessor
      value = object.__send__(:instance_exec, value, &preprocessor)
    end

    # Show notice of the setting being set on the instance
    if class_vars[:private_options].include?(option) && ! object.debug
      value_hidden = value.respond_to?(:length) ? ('*' * value.length).inspect : colorize('<hidden>', :italic)
      m = "Setting @#{option_attr} = #{value_hidden} :: Value hidden for privacy"
      max_puts_length = m.length if m.length > max_puts_length
      puts m, :blue
    else
      m = "Setting @#{option_attr} = #{value.inspect}"
      max_puts_length = m.length if m.length > max_puts_length
      puts m, :green
    end

    # Do the actual set for us
    object.__send__(optionify(option_attr, :set), value)
  end

  # Actually grab the options from GetoptLong and process them
  puts "\nCliTool... Loading Options...\n", :blue
  puts '#' * 29, [:red_bg, :red]
  class_vars[:default_options].to_a.each(&option_setter)
  begin
    GetoptLong.new(*options).each(&option_setter)
  rescue GetoptLong::MissingArgument => e
    missing_arguments << e.message
  end
  puts '#' * 29, [:red_bg, :red]
  puts ''

  # If we wanted help in the first place then don't do any option dependency validations
  unless object.help

    # Handle any missing arguments that are required!
    unless missing_arguments.empty?
      missing_arguments.each { |m| puts "The required #{m}", :red }
      puts ''

      object.help = true
      exit_code   = 1
    end

    # Get a array of all the options that are required and were not provided
    missing_options = class_vars[:required_options].select{ |opt, arg| arg[:require] == true }.keys - processed_options

    # If a option was marked as missing; verify if an alternative was provided
    __slice_hash(class_vars[:required_options], *missing_options).each do |option, option_args|
      if (option_args[:alternatives] & processed_options).empty?
        object.help = true
        exit_code   = 1
      else
        missing_options.delete(option)
      end
    end

    # Find any missing dependencies that may have been required by an option
    missing_dependencies = __slice_hash(class_vars[:required_options], *processed_options).reduce({}) do |out, (option, option_args)|
      missing = option_args[:dependencies] - processed_options
      missing.reduce(out) { |o, missed| ((o[missed] ||= []) << option); o }
    end

    # Let the user know that a dependency has not been met.
    missing_dependencies.each do |dependency, options|
      options = (options.length > 1 ? options.reduce('') { |s, o| s << "\n\t--#{o}" } : "--#{options.first}") << "\n"
      puts "The option --#{dependency} is required by the following options: #{options}", :red
      missing_options << dependency
      object.help = true
      exit_code   = 1
    end

    # Tell user that required options were not provided
    unless missing_options.empty?
      options = missing_options.uniq
      options = (options.length > 1 ? options.reduce('') { |s, o| s << "\n\t--#{o}" } : "--#{options.first}") << "\n"
      puts "The following required options were not provided: #{options}", :red
      object.help = true
      exit_code   = 1
    end
  end

  # Show the help text
  if object.help || exit_code == 1
    puts help(nil, missing_options || [])
    exit(exit_code || 0)
  end

  # Handle the entrypoint
  if entrypoint
    entrypoint = optionify(entrypoint)
    object.__send__(entrypoint, *args, &block)
  else
    object
  end
end

#trap!(signal = "INT", &block) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/cli_tool/option_parser.rb', line 80

def trap!(signal = "INT", &block)
  Signal.trap(signal) do |signo|
    return block.call(singal) if block.is_a?(Proc)

    case signal
    when 'SIGQUIT'
      puts %{Received Signal "#{signal}"... Exiting...}
      exit 0
    when 'SIGKILL'
      puts %{Received Signal "#{signal}"... Killing...}
      exit 1
    end

    puts %{Received Signal "#{signal}"... Don't know what to do...}
  end
end