Module: FalkorLib::Common Abstract

Defined in:
lib/falkorlib/common.rb

Overview

This module is abstract.

Recipe for all my toolbox and versatile Ruby functions I’m using everywhere. You’ll typically want to include the ‘FalkorLib::Common` module to bring the corresponding definitions into yoru scope.

@example:

require 'falkorlib'
include FalkorLib::Common

info 'exemple of information text'
really_continue?
run %{ echo 'this is an executed command' }

Falkor.config.debug = true
run %{ echo 'this is a simulated command that *will not* be executed' }
error "that's an error text, let's exit with status code 1"

Class Method Summary collapse

Class Method Details

.ask(question, default_answer = '') ⇒ Object

Ask a question



85
86
87
88
89
90
91
92
# File 'lib/falkorlib/common.rb', line 85

def ask(question, default_answer='')
    print "#{question} "
    print "[Default: #{default_answer}]" unless default_answer == ''
    print ": "
    STDOUT.flush
    answer = STDIN.gets.chomp
    return answer.empty?() ? default_answer : answer
end

.bold(str) ⇒ Object

Default printing functions ###

Print a text in bold



38
39
40
# File 'lib/falkorlib/common.rb', line 38

def bold(str)
    COLOR == true ? Term::ANSIColor.bold(str) : str
end

.command?(name) ⇒ Boolean

Check for the presence of a given command

Returns:

  • (Boolean)


106
107
108
109
# File 'lib/falkorlib/common.rb', line 106

def command?(name)
    `which #{name}`
    $?.success?
end

.cyan(str) ⇒ Object

Print a text in cyan



53
54
55
# File 'lib/falkorlib/common.rb', line 53

def cyan(str)
    COLOR == true ? Term::ANSIColor.cyan(str) : str
end

.error(str) ⇒ Object

Print an error message and abort



69
70
71
72
73
# File 'lib/falkorlib/common.rb', line 69

def error(str)
    #abort red("*** ERROR *** " + str)
    $stderr.puts red("*** ERROR *** " + str)
    exit 1
end

.exec_or_exit(cmd) ⇒ Object

Execute a given command - exit if status != 0



147
148
149
150
151
152
153
# File 'lib/falkorlib/common.rb', line 147

def exec_or_exit(cmd)
    status = execute(cmd)
    if (status.to_i != 0)
        error("The command '#{cmd}' failed with exit status #{status.to_i}")
    end
    status
end

.execute(cmd) ⇒ Object

Simpler version that use the system call



131
132
133
134
135
# File 'lib/falkorlib/common.rb', line 131

def execute(cmd)
    puts bold("[Running] #{cmd.gsub(/^\s*/, ' ')}")
    system(cmd)
    $?
end

.execute_in_dir(path, cmd) ⇒ Object

Execute in a given directory



138
139
140
141
142
143
144
# File 'lib/falkorlib/common.rb', line 138

def execute_in_dir(path, cmd)
    exit_status = 0
    Dir.chdir(path) do
        exit_status = run %{ #{cmd} }
    end
    exit_status
end

.green(str) ⇒ Object

Print a text in green



43
44
45
# File 'lib/falkorlib/common.rb', line 43

def green(str)
    COLOR == true ? Term::ANSIColor.green(str) : str
end

.info(str) ⇒ Object

Print an info message



58
59
60
# File 'lib/falkorlib/common.rb', line 58

def info(str)
    puts green("[INFO] " + str)
end

.init_from_template(templatedir, rootdir, config = {}, options = { :erb_exclude => [], :no_interaction => false }) ⇒ Object

Bootstrap the destination directory ‘rootdir` using the template directory `templatedir`. the hash table `config` hosts the elements to feed ERB files which should have the extension .erb. The initialization is performed as follows:

  • a rsync process is initiated to duplicate the directory structure and the symlinks, and exclude .erb files

  • each erb files (thus with extension .erb) is interpreted, the corresponding file is generated without the .erb extension

Supported options:

:erb_exclude [array of strings]: pattern(s) to exclude from erb file
                                 interpretation and thus to copy 'as is'
:no_interaction [boolean]: do not interact


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
# File 'lib/falkorlib/common.rb', line 275

def init_from_template(templatedir, rootdir, config = {}, 
                       options = {
                        :erb_exclude    => [],
                        :no_interaction => false
                       })
    error "Unable to find the template directory" unless File.directory?(templatedir)
    warning "about to initialize/update the directory #{rootdir}"
    really_continue?
    run %{ mkdir -p #{rootdir} } unless File.directory?( rootdir )
    run %{ rsync --exclude '*.erb' -avzu #{templatedir}/ #{rootdir}/ }
    Dir["#{templatedir}/**/*.erb"].each do |erbfile|
        relative_outdir = Pathname.new( File.realpath( File.dirname(erbfile) )).relative_path_from Pathname.new(templatedir)
        filename = File.basename(erbfile, '.erb')
        outdir   = File.realpath( File.join(rootdir, relative_outdir.to_s) )
        outfile  = File.join(outdir, filename)
        unless options[:erb_exclude].nil?
            exclude_entry = false
            options[:erb_exclude].each do |pattern|
                exclude_entry |= erbfile =~ /#{pattern}/
            end
            if exclude_entry
                info "copying non-interpreted ERB file"
                # copy this file since it has been probably excluded from teh rsync process
                run %{ cp #{erbfile} #{outdir}/ }
                next
            end
        end
        # Let's go
        info "updating '#{relative_outdir.to_s}/#{filename}'"
        puts "  using ERB template '#{erbfile}'"
        write_from_erb_template(erbfile, outfile, config, options)
    end
end

.init_rvm(rootdir = Dir.pwd, gemset = '') ⇒ Object

RVM init



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/falkorlib/common.rb', line 387

def init_rvm(rootdir = Dir.pwd, gemset = '')
    rvm_files = {
        :version => File.join(rootdir, '.ruby-version'),
        :gemset  => File.join(rootdir, '.ruby-gemset')
    }
    unless File.exists?( "#{rvm_files[:version]}")
        v = select_from(FalkorLib.config[:rvm][:rubies],
                        "Select RVM ruby to configure for this directory",
                        3)
        File.open( rvm_files[:version], 'w') do |f|
            f.puts v
        end
    end
    unless File.exists?( "#{rvm_files[:gemset]}")
        g = gemset.empty? ? ask("Enter RVM gemset name for this directory", File.basename(rootdir)) : gemset
        File.open( rvm_files[:gemset], 'w') do |f|
            f.puts g
        end
    end

end

.list_items(glob_pattern, options = {}) ⇒ Object

List items from a glob pattern to ask for a unique choice Supported options:

:only_files      [boolean]: list only files in the glob
:only_dirs       [boolean]: list only directories in the glob
:pattern_include [array of strings]: pattern(s) to include for listing
:pattern_exclude [array of strings]: pattern(s) to exclude for listing
:text            [string]: text to put

Raises:

  • (SystemExit)


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
# File 'lib/falkorlib/common.rb', line 177

def list_items(glob_pattern, options = {})
    list  = { 0 => 'Exit' }
    index = 1
    raw_list = { 0 => 'Exit' }

    Dir["#{glob_pattern}"].each do |elem|
        #puts "=> element '#{elem}' - dir = #{File.directory?(elem)}; file = #{File.file?(elem)}"
        next if (! options[:only_files].nil?) && options[:only_files] && File.directory?(elem)
        next if (! options[:only_dirs].nil?)  && options[:only_dirs]  && File.file?(elem)
        entry = File.basename(elem)
        # unless options[:pattern_include].nil?
        #     select_entry = false
        #     options[:pattern_include].each do |pattern|
        #         #puts "considering pattern '#{pattern}' on entry '#{entry}'"
        #         select_entry |= entry =~ /#{pattern}/
        #     end
        #     next unless select_entry
        # end
        unless options[:pattern_exclude].nil?
            select_entry = false
            options[:pattern_exclude].each do |pattern|
                #puts "considering pattern '#{pattern}' on entry '#{entry}'"
                select_entry |= entry =~ /#{pattern}/
            end
            next if select_entry
        end
        #puts "selected entry = '#{entry}'"
        list[index]     = entry
        raw_list[index] = elem
        index += 1
    end
    text        = options[:text].nil?    ? "select the index" : options[:text]
    default_idx = options[:default].nil? ? 0 : options[:default]
    raise SystemExit.new('Empty list') if index == 1
    #ap list
    #ap raw_list
    # puts list.to_yaml
    # answer = ask("=> #{text}", "#{default_idx}")
    # raise SystemExit.new('exiting selection') if answer == '0'
    # raise RangeError.new('Undefined index')   if Integer(answer) >= list.length
    # raw_list[Integer(answer)]
    select_from(list, text, default_idx, raw_list)
end

.load_config(filepath) ⇒ Object

Return the yaml content as a Hash object



246
247
248
# File 'lib/falkorlib/common.rb', line 246

def load_config(filepath)
    YAML::load_file(filepath)
end

.nice_execute(cmd) ⇒ Object

Execute a given command, return exit code and print nicely stdout and stderr



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/falkorlib/common.rb', line 112

def nice_execute(cmd)
    puts bold("[Running] #{cmd.gsub(/^\s*/, ' ')}")
    stdout, stderr, exit_status = Open3.capture3( cmd )
    unless stdout.empty?
        stdout.each_line do |line|
            print "** [out] #{line}"
            $stdout.flush
        end
    end
    unless stderr.empty?
        stderr.each_line do |line|
            $stderr.print red("** [err] #{line}")
            $stderr.flush
        end
    end
    exit_status
end

.not_implementedObject

simple helper text to mention a non-implemented feature



76
77
78
# File 'lib/falkorlib/common.rb', line 76

def not_implemented()
    error("NOT YET IMPLEMENTED")
end

.really_continue?(default_answer = 'Yes') ⇒ Boolean

Ask whether or not to really continue

Returns:

  • (Boolean)


95
96
97
98
99
# File 'lib/falkorlib/common.rb', line 95

def really_continue?(default_answer = 'Yes')
    pattern = (default_answer =~ /yes/i) ? '(Y|n)' : '(y|N)'
    answer = ask( cyan("=> Do you really want to continue #{pattern}?"), default_answer)
    exit 0 if answer =~ /n.*/i
end

.red(str) ⇒ Object

Print a text in red



48
49
50
# File 'lib/falkorlib/common.rb', line 48

def red(str)
    COLOR == true ? Term::ANSIColor.red(str) : str
end

.run(cmds) ⇒ Object

“Nice” way to present run commands Ex: run %{ hostname -f }



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/falkorlib/common.rb', line 157

def run(cmds)
    exit_status = 0
    puts bold("[Running]\n#{cmds.gsub(/^\s*/, '   ')}")
    $stdout.flush
    #puts cmds.split(/\n */).inspect
    cmds.split(/\n */).each do |cmd|
        next if cmd.empty?
        system("#{cmd}") unless FalkorLib.config.debug
        exit_status = $?
    end
    exit_status
end

.select_from(list, text = 'Select the index', default_idx = 0, raw_list = list) ⇒ Object

Display a indexed list to select an i

Raises:

  • (SystemExit)


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/falkorlib/common.rb', line 222

def select_from(list, text = 'Select the index', default_idx = 0, raw_list = list)
    error "list and raw_list differs in size" if list.size != raw_list.size
    l     = list
    raw_l = raw_list
    if list.kind_of?(Array)
        l = raw_l = { 0 => 'Exit' }
        list.each_with_index do |e, idx|
            l[idx+1] = e
            raw_l[idx+1] = raw_list[idx]
        end
    end
    puts l.to_yaml
    answer = ask("=> #{text}", "#{default_idx}")
    raise SystemExit.new('exiting selection') if answer == '0'
    raise RangeError.new('Undefined index')   if Integer(answer) >= l.length
    raw_l[Integer(answer)]
end

.show_diff_and_write(content, outfile, options = { :no_interaction => false, :json_pretty_format => false, }) ⇒ Object

Show the difference between a ‘content` string and an destination file (using Diff algorithm). Obviosuly, if the outfile does not exists, no difference is proposed. Supported options:

:no_interaction [boolean]: do not interact
:json_pretty_format [boolean]: write a json content, in pretty format

return 0 if nothing happened, 1 if a write has been done



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'lib/falkorlib/common.rb', line 331

def show_diff_and_write(content, outfile, options = {
                         :no_interaction     => false,
                         :json_pretty_format => false,
                        })
 if File.exists?( outfile )
  ref = File.read( outfile )
  if options[:json_pretty_format]
   ref = JSON.pretty_generate (JSON.parse( IO.read( outfile ) ))
  end 
  if ref == content
   warn "Nothing to update"
   return 0
  end 
  warn "the file '#{outfile}' already exists and will be overwritten."
  warn "Expected difference: \n------"
  Diffy::Diff.default_format = :color
  puts Diffy::Diff.new(ref, content, :context => 1)
 else
  watch =  options[:no_interaction] ? 'no' : ask( cyan("  ==> Do you want to see the generated file before commiting the writing (y|N)"), 'No')
  puts content if watch =~ /y.*/i
 end
 proceed = options[:no_interaction] ? 'yes' : ask( cyan("  ==> proceed with the writing (Y|n)"), 'Yes')
    return 0 if proceed =~ /n.*/i
    info("=> writing #{outfile}")
    File.open("#{outfile}", "w+") do |f|
  f.write content
    end
 if FalkorLib::Git.init?(File.dirname(outfile))
  do_commit = options[:no_interaction] ? 'yes' : ask( cyan("  ==> commit the changes (Y|n)"), 'Yes')
  FalkorLib::Git.add(outfile, "update content of '#{File.basename(outfile)}'") if do_commit =~ /y.*/i
 end
 return 1
end

.store_config(filepath, hash) ⇒ Object

Store the Hash object as a Yaml file



251
252
253
254
255
256
257
# File 'lib/falkorlib/common.rb', line 251

def store_config(filepath, hash)
    File.open( filepath, 'w') do |f|
        f.print "# ", File.basename(filepath), "\n"
        f.puts "# /!\\ DO NOT EDIT THIS FILE: it has been automatically generated"
        f.puts hash.to_yaml
    end
end

.warning(str) ⇒ Object Also known as: warn

Print an warning message



63
64
65
# File 'lib/falkorlib/common.rb', line 63

def warning(str)
    puts cyan("/!\\ WARNING: " + str)
end

.write_from_erb_template(erbfile, outfile, config = {}, options = { :no_interaction => false }) ⇒ Object

ERB generation of the file ‘outfile` using the source template file `erbfile` Supported options:

:no_interaction [boolean]: do not interact


313
314
315
316
317
318
319
320
321
322
# File 'lib/falkorlib/common.rb', line 313

def write_from_erb_template(erbfile, outfile, config = {}, 
                            options = {
                                :no_interaction => false
                            })
    error "Unable to find the template file #{erbfile}" unless File.exists? (erbfile )
    template = File.read("#{erbfile}")
    output   = ERB.new(template, nil, '<>')
    content  = output.result(binding)
 show_diff_and_write(content, outfile, options)
end

.write_from_template(src, dstdir, options = { :no_interaction => false, :srcdir => '', :outfile => '' }) ⇒ Object

Blind copy of a source file ‘src` into its destination directory `dstdir` Supported options:

:no_interaction [boolean]: do not interact
:srcdir [string]: source directory, make the `src` file relative to that directory
:outfile [string]: alter the outfile name (File.basename(src) by default)


371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/falkorlib/common.rb', line 371

def write_from_template(src,dstdir,options = {
                        :no_interaction => false,
                        :srcdir         => '', 
                        :outfile        => ''
                       })
 srcfile = options[:srcdir].nil? ? src : File.join(options[:srcdir], src)
 error "Unable to find the source file #{srcfile}" unless File.exists? ( srcfile )
 error "The destination directory '#{dstdir}' do not exist" unless File.directory?( dstdir )
 dstfile = options[:outfile].nil? ? File.basename(srcfile) : options[:outfile]
 outfile = File.join(dstdir, dstfile)
 content = File.read( srcfile )
 show_diff_and_write(content, outfile, options)
end