Module: CliHelper

Defined in:
lib/cli_helper.rb

Overview

module Slop

Constant Summary collapse

DEFAULTS =
{
  version:        '0.0.1',# the version of this program
  arguments:      [],     # whats left after options and parameters are extracted
  verbose:        false,
  debug:          false,
  help:           false,
  enable_config_files: false,
  disable_help:   false,
  disable_debug:  false,
  disable_verbose: false,
  disable_version: false,
  suppress_errors: true,   # suppress the exceptions generated by Slop
  ini_comment:    '#',
  ini_seperator:  '=',
  ini_encoding:   'UTF-8',
  user_name:      Nenv.user || Nenv.user_name || Nenv.logname || 'The Unknown User',
  home_path:      Pathname.new(Nenv.home),
  cli:            'a place holder for the Slop object',
  errors:         [],
  warnings:       []
}

Instance Method Summary collapse

Instance Method Details

#abort_if_errorsObject

Display global warnings and errors arrays and exit if necessary



369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/cli_helper.rb', line 369

def abort_if_errors
  unless configatron.warnings.empty?
    STDERR.puts
    STDERR.puts "The following warnings were generated:"
    STDERR.puts
    configatron.warnings.each do |w|
      STDERR.puts "\tWarning: #{w}"
    end
    STDERR.print "\nAbort program? (y/N) "
    answer = (STDIN.gets).chomp.strip.downcase
    configatron.errors << "Aborted by user" if answer.size>0 && 'y' == answer[0]
    configatron.warnings = []
  end
  unless configatron.errors.empty?
    STDERR.puts
    STDERR.puts "Correct the following errors and try again:"
    STDERR.puts
    configatron.errors.each do |e|
      STDERR.puts "\t#{e}"
    end
    STDERR.puts
    exit(-1)
  end
end

#cli_helper(my_banner = '', slop_options_config = {}) {|param| ... } ⇒ Object

Invoke Slop with common CLI parameters and custom parameters provided via a block. Create ‘?’ for all boolean parameters that have a ‘–name’ flag form. Returns a Slop::Options object

Yields:

  • (param)


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
238
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'lib/cli_helper.rb', line 198

def cli_helper( my_banner='',
                slop_options_config={}
              )
  default_config  = { suppress_errors: configatron.suppress_errors }
  options_hash    = default_config.merge(slop_options_config)

  param = Slop::Options.new( **options_hash )

  if my_banner.empty?
    param.banner = "Usage: #{my_name} [options] ..."
  else
    param.banner = my_banner
    param.separator "\nUsage: #{my_name} [options] ..."
  end

  param.separator "\nWhere:"

  if  configatron.disable_help      &&
      configatron.disable_verbose   &&
      configatron.disable_debug     &&
      configatron.disable_version
    # NOOP
  else
    param.separator "\n  Common Options Are:"
  end

  unless configatron.disable_help
    param.bool '-h', '--help',    'show this message'
  end

  unless configatron.disable_verbose
    param.bool '-v', '--verbose', 'enable verbose mode'
  end

  unless configatron.disable_debug
    param.bool '-d', '--debug',   'enable debug mode'
  end

  unless configatron.disable_version
    param.on '--version', "print the version: #{configatron.version}" do
      puts configatron.version
      exit
    end
  end

  param.separator "\n  Program Options Are:"

  yield(param) if block_given?

  if configatron.enable_config_files
    param.paths '--config',    'read config file(s) [*.rb, *.yml, *.ini, *.erb]'
  end

  parser = Slop::Parser.new(param, suppress_errors: configatron.suppress_errors)

  configatron.cli = parser.parse(ARGV)

  # Lets do some error checking ...
  configatron.cli.options.options.each do |opt|
    if opt.count > 1
      warning "#{opt.desc} #{opt.flags.inspect} entered more than once; only last value is used"
    end
    if opt.value.nil?
      if opt.config.default.nil?
        # NOTE: why is this a warning and not an error?
        #       while it may be required, it might be set in a config file
        #       so we let the user decide to continue or abort.
        # SMELL: see below, past the loading of the config file values.
        warning "Required parameter is missing: #{opt.desc} #{opt.flags.inspect}"
      end
    end
  end

  unless configatron.cli.arguments.empty?
    bad_options = configatron.cli.arguments.select {|o| o.start_with?('-')}
    unless bad_options.empty?
      error "Invalid parameters: #{bad_options.inspect}"
      bad_options.each do |o|
        x = configatron.cli.arguments.index(o)
        configatron.cli.arguments[x] = nil
      end
      configatron.cli.arguments.compact!
    end
  end


  # NOTE: The config files are being process before the
  #       command line options in order for the command
  #       line options to over-write the values from the
  #       config files.
  if configatron.enable_config_files
    configatron.cli[:config].each do |cf|
      cli_helper_process_config_file(cf)
    end # configatron.cli.config.each do |cf|
  end # if configatron.enable_config_files

  # SMELL: a required parameter that is not entered on the command line
  #        will have a value of nil.  IF THAT parameter had been
  #        set in a config file, this will overlay the valid value
  #        with an invalid nil.
  configatron.configure_from_hash(configatron.cli.to_hash)
  configatron.arguments = configatron.cli.arguments

  bools = param.options.select do |o|
    o.is_a? Slop::BoolOption
  end.select{|o| o.flags.select{|f|f.start_with?('--')}}.
      map{|o| o.flags.last.gsub('--','')} # SMELL: depends on convention

  bools.each do |m|
    s = m.to_sym
    define_method(m+'?') do
      configatron[s]
    end unless self.respond_to?(m+'?')
    define_method((m+'!').to_sym) do
      configatron[s] = true
    end unless self.respond_to?(m+'!')
  end


  if self.respond_to?(:help?) && help?
    show_usage
    exit
  end

  return param
end

#cli_helper_process_config_file(a_cf) ⇒ Object

Obtain options and values from a config file



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
# File 'lib/cli_helper.rb', line 134

def cli_helper_process_config_file(a_cf)
  cf = String == a_cf.class ? Pathname.new(a_cf) : a_cf
  if cf.directory?
    cf.children.each do |ccf|
      cli_helper_process_config_file(ccf)
    end
    return
  end
  unless cf.exist?
    error "Config file is missing: #{cf}"
  else
    file_type = case cf.extname.downcase
      when '.rb'
        :ruby
      when '.yml', '.yaml'
        :yaml
      when '.ini', '.txt'
        :ini
      when '.erb'
        extname = cf.basename.to_s.downcase.gsub('.erb','').split('.').last
        if %w[ yml yaml].include? extname
          :yaml
        elsif %w[ ini txt ].include? extname
          :ini
        elsif 'rb' == extname
          warning 'MakesNoSenseToMe: *.rb.erb is not supported'
          :unknown
        else
          :unknown
        end
    else
      :unknown
    end

    case file_type
      when :ruby
        load cf
      when :yaml
        configatron.configure_from_hash(
          config_file_hash = configatron.configure_from_hash(
            cli_helper_process_yaml(
              cli_helper_process_erb(cf.read)
            )
          )
        )
      when :ini
        configatron.configure_from_hash(
          configatron.configure_from_hash(
            cli_helper_process_ini(
              cli_helper_process_erb(cf.read)
            )
          )
        )
      else
        error "Do not know how to parse this file: #{cf}"
    end # case type_type
  end # unless cf.exist? || cf.directory?
end

#cli_helper_process_erb(file_contents) ⇒ Object

Extract options and values from an ERB formatted config file



108
109
110
111
# File 'lib/cli_helper.rb', line 108

def cli_helper_process_erb(file_contents)
  erb_contents = ERB.new(file_contents).result
  return erb_contents
end

#cli_helper_process_ini(file_contents = '') ⇒ Object

Extract options and values from an INI formated config file



122
123
124
125
126
127
128
129
130
# File 'lib/cli_helper.rb', line 122

def cli_helper_process_ini(file_contents='')
  an_ini_object = IniFile.new(
                    content: file_contents,
                    comment: configatron.ini_comment,
                    parameter: configatron.ini_seperator,
                    encoding: configatron.ini_encoding
                  )
  return an_ini_object.to_h
end

#cli_helper_process_yaml(file_contents = '') ⇒ Object

Extract options and values from a YAML formatted config file



115
116
117
118
# File 'lib/cli_helper.rb', line 115

def cli_helper_process_yaml(file_contents='')
  a_hash = YAML.load file_contents
  return a_hash
end

#error(a_string) ⇒ Object

Adds a string to the global $errors array



396
397
398
# File 'lib/cli_helper.rb', line 396

def error(a_string)
  configatron.errors << a_string
end

#errorsObject

Returns an Array of errors



402
403
404
# File 'lib/cli_helper.rb', line 402

def errors
  configatron.errors
end

#get_pathnames_from(an_array, extnames = ['.json', '.txt', '.docx']) ⇒ Object

Returns an array of valid files of valid type(s) Creates warnings for files not matching the valid extensions. Generates errors fpr files that do not exist



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/cli_helper.rb', line 343

def get_pathnames_from(an_array, extnames=['.json', '.txt', '.docx'])
  an_array = [an_array] unless an_array.is_a? Array
  extnames = [extnames] unless extnames.is_a? Array
  extnames = extnames.map{|e| e.downcase}
  file_array = []
  an_array.each do |a|
    pfn = Pathname.new(a)
    if pfn.directory?
      file_array << get_pathnames_from(pfn.children, extnames)
    else
      if pfn.exist?
        if extnames.include?(pfn.extname.downcase)
          file_array << pfn
        else
          warning "File ignored because extension is not #{extnames.join(' or ')} file: #{pfn}"
        end
      else
        error "File does not exist: #{pfn}"
      end
    end
  end
  return file_array.flatten
end

#meObject

Return full pathname of program



83
84
85
# File 'lib/cli_helper.rb', line 83

def me
  configatron.me
end

#my_dirObject Also known as: root

Return full pathname of program



89
90
91
# File 'lib/cli_helper.rb', line 89

def my_dir
  configatron.my_dir
end

#my_nameObject

Returns the basename of the program as a string



96
97
98
# File 'lib/cli_helper.rb', line 96

def my_name
  configatron.my_name
end

#show_usageObject

Prints to STDOUT the usage/help string



335
336
337
# File 'lib/cli_helper.rb', line 335

def show_usage
  puts usage()
end

#usageObject

Returns the usage/help information as a string



327
328
329
330
331
# File 'lib/cli_helper.rb', line 327

def usage
  a_string = configatron.cli.to_s + "\n"
  a_string += HELP + "\n" if defined?(HELP)
  return a_string
end

#versionObject

Returns the version of the program as a string



102
103
104
# File 'lib/cli_helper.rb', line 102

def version
  configatron.version
end

#warning(a_string) ⇒ Object

Adds a string to the global $warnings array



408
409
410
# File 'lib/cli_helper.rb', line 408

def warning(a_string)
  configatron.warnings << a_string
end

#warningsObject

Returns an Array of warnings



414
415
416
# File 'lib/cli_helper.rb', line 414

def warnings
  configatron.warnings
end