module PadUtils

Main namespace for PadUtils.

Each method is implemented as a module method. Prefix them with PadUtils to call them. @example

PadUtils.some_method(param)

Constants

VERSION

PadUtils version number

Public Class Methods

append_to_file(filepath, content, new_line = true) click to toggle source

Appends content to a file.

@note Will create the destination file if it doesn't exist.

It also prepends a newline character. Set new_line to false to change this.

Will log errors using {PadUtils.log PadUtils.log}.

@param filepath [String] the file path and name @param content [String] the content to append @param new_line [Boolean] prepends a newline character @return [Void] nothing @example

PadUtils.append_to_file("path/to/file.txt", "Append this!", false)
# File lib/pad_utils/pad_files.rb, line 162
def self.append_to_file(filepath, content, new_line = true)
  self.create_directory(filepath)
  content = "\n#{content}" if new_line
  File.open(filepath, 'a') { |f| f.write("#{content}")}
rescue Exception => e
  PadUtils.log("Error in append_to_file", e)
end
camel_case(value) click to toggle source

Converts a string to a CamelCase.

@note The string will first be sanitized using {PadUtils.sanitize PadUtils.sanitize}.

@param value [String] the string to CamelCase @return [String] the CamelCased string @example

s = "hello_you"
s2 = "hello you"
s3 = "hello   $you#"
PadUtils.convert_to_ruby_name(s) # => 'HelloYou'
PadUtils.convert_to_ruby_name(s2) # => 'HelloYou'
PadUtils.convert_to_ruby_name(s3) # => 'HelloYou'
# File lib/pad_utils/pad_text.rb, line 34
def self.camel_case(value)
  value = self.sanitize(value)
  if value.scan(/\_|\-/).size > 0
    value.split(/\_|\-/).map(&:capitalize).join
  else
    value.slice(0,1).capitalize + value.slice(1..-1)
  end
end
choice_menu(question: "Question?", choices: {}, default: nil) click to toggle source

Builds a multiple choice menu.

Will log errors using {PadUtils.log PadUtils.log}.

@param question [String] the question to ask @param choices [Hash] the possible choices @param default [Symbol, nil] the default answer hash key. If nil, the last choice will be used. @return [Symbol] the chosen option @example

options = {r: "red", b: "blue", g: "green"}
PadUtils.choice_menu(question: "Which color?", choices: options, default: :b)
# File lib/pad_utils/pad_menu.rb, line 46
def self.choice_menu(question: "Question?", choices: {}, default: nil)
  STDOUT.puts
  PadUtils.puts_c "- #{question}", :green
  default ||= choices.keys.last
  default = default.to_sym

  i = 0
  choices.each do |key, value|
    i += 1
    STDOUT.print "#{i}. #{value}"
    STDOUT.print " (default)" if key.to_s == default.to_s
    STDOUT.print "\n"
  end

  STDOUT.print "Your choice (1-#{choices.length}): "
  answer = STDIN.gets.chomp.strip.to_i
  STDOUT.puts
  if answer == 0 || answer > choices.length
    return default
  else
    return choices.keys[answer - 1].to_sym
  end
rescue Exception => e
  PadUtils.log("Error in choice menu", e)
end
convert_to_ruby_name(value) click to toggle source

Converts a string to a Rubified name.

@deprecated Use {PadUtils.camel_case PadUtils.camel_case} instead as it will

properly sanitize the string as well.

@param value [String] the string to rubify @return [String] the rubified string @example

s = "hello_you"
PadUtils.convert_to_ruby_name(s) # => 'HelloYou'
# File lib/pad_utils/pad_text.rb, line 13
def self.convert_to_ruby_name(value)
  if value.scan(/\_|\-/).size > 0
    value.split(/\_|\-/).map(&:capitalize).join
  else
    value.slice(0,1).capitalize + value.slice(1..-1)
  end
end
copy_all_files(orig_dir, dest_dir) click to toggle source

Copies all files from a directory.

@note Will create the destination if it doesn't exist.

Files existing on destination but not on source won't be overwritten.

@param orig_dir [String] the path to source directory @return [Void] nothing @example

PadUtils.copy_all_files("path/to/source", "path/to/destination")
# File lib/pad_utils/pad_files.rb, line 86
def self.copy_all_files(orig_dir, dest_dir)
  FileUtils.copy_entry(orig_dir, dest_dir)
end
copy_file(file_path, dest_dir) click to toggle source

Copies a file.

@param file_path [String] the source file name and path @param dest_dir [String] the destination directory @return [Void] nothing @example

PadUtils.copy_file("test.txt", "path/to/destination/directory")
# File lib/pad_utils/pad_files.rb, line 47
def self.copy_file(file_path, dest_dir)
  FileUtils.cp(file_path, dest_dir)
end
copy_files(files, dest_dir) click to toggle source

Copies an array of files.

@param files [Array<String>] the array of files paths and names to copy @param dest_dir [String] the destination path @return [Void] nothing @example

files = ["file1.txt", "path/to/file2.txt", "path/to/files/file3.txt"]
PadUtils.copy_files(files, "path/to/destination")
# File lib/pad_utils/pad_files.rb, line 71
def self.copy_files(files, dest_dir)
  files.each do |f|
    copy_file(f, dest_dir)
  end
end
create_directory(dir_name) click to toggle source

Creates a directory.

@note Won't override directory content if it already exists.

@param dir_name [String] the directory path and name (add `/.`) @return [Void] nothing @example

PadUtils.create_directory("path/to/dir/.")
# File lib/pad_utils/pad_files.rb, line 98
def self.create_directory(dir_name)
  dirname = File.dirname(dir_name)
  unless File.directory?(dirname)
    FileUtils.mkdir_p(dirname)
  end
end
decrypt(content: "", key: "") click to toggle source

Decrypts a string.

@param content [String] the encrypted string @param key [String] the encryption key @return [String] the decrypted string @example

PadUtils.decrypt("DKBJVYG69YsAWT%0A__1f346a8eeedc7", "s3cr3t") # => 'Hello'
# File lib/pad_utils/pad_security.rb, line 44
def self.decrypt(content: "", key: "")
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.decrypt

  key = Digest::SHA1.hexdigest(key)

  iv = content.split("__")[1]
  content = content.split("*+*")[0]

  cipher.key = key
  cipher.iv = iv

  content = URI.unescape(content)
  content = Base64.decode64(content)

  decrypted = '' << cipher.update(content) << cipher.final

  decrypted

rescue
  decrypted = "invalid"
end
deep_symbolize_hash_keys(hash) click to toggle source

Converts keys and sub-keys to symbols in a hash.

Emulates Rails {apidock.com/rails/Hash/deep_symbolize_keys deep_symbolize_keys} method.

@param hash [Hash] the hash to symbolize @return [Hash] the symbolized hash @example

h = {"name": "Nico", "age": 37, "computers": [{"model": "Mac Pro"}, {"model": "MacBook"}]}
PadUtils.deep_symbolize_hash_keys(h)
# => {:name => "Nico", :age => 37, :computers => [{:model => "Mac Pro"}, {:model => "MacBook"}]}
# File lib/pad_utils/pad_json.rb, line 15
def self.deep_symbolize_hash_keys(hash)
  return hash.collect { |e| deep_symbolize_hash_keys(e) } if hash.is_a?(Array)
  return hash.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_symbolize_hash_keys(v); sh } if hash.is_a?(Hash)
  hash
end
delete_directory(dir_name) click to toggle source

Deletes a directory and its content

@param dir_name [String] the directory path and name @return [Void] nothing @example

PadUtils.delete_directory("path/to/dir")
# File lib/pad_utils/pad_files.rb, line 111
def self.delete_directory(dir_name)
  FileUtils.rm_r(dir_name)
end
delete_file(file_path) click to toggle source

Deletes a file.

@param file_path [String] the file path and name @return [Void] nothing @example

PadUtils.delete("path/to/file.txt")
# File lib/pad_utils/pad_files.rb, line 15
def self.delete_file(file_path)
  FileUtils.rm(file_path, force: true)
end
delete_recursive(path, filename) click to toggle source

Deletes a file recursively.

@param path [String] the path to search recurively @param filename [String] the filename to search and delete. `*` wildcard accepted @return [Void] nothing @example

PadUtils.delete_recursive("images/photos", ".DS_Store")
# File lib/pad_utils/pad_files.rb, line 26
def self.delete_recursive(path, filename)
  Dir.glob("#{path}/**/#{filename}").each { |f| File.delete(f) }
end
encrypt(content: "", key: "") click to toggle source

Encrypts a string.

@param content [String] the content to encrypt @param key [String] the encryption key @return [String] the encrypted string @example

PadUtils.encrypt("Hello", "s3cr3t") # => 'DKBJVYG69YsAWT%0A__1f346a8eeedc7'
# File lib/pad_utils/pad_security.rb, line 15
def self.encrypt(content: "", key: "")
  cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
  cipher.encrypt

  iv = PadUtils.uuid.gsub("-", "")

  key = Digest::SHA1.hexdigest(key)

  cipher.key = key
  cipher.iv = iv

  encrypted = '' << cipher.update(content) << cipher.final

  encrypted = Base64.encode64(encrypted)
  encrypted = URI.escape(encrypted, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))

  encrypted = "#{encrypted}__#{iv}"

rescue
  encrypted = "invalid"
end
extract_zip(filename, target_path) click to toggle source

Extracts a zip file.

@note If the target path doesn't exist, it will be created. Existing files

will **not** be overwritten.

@param filename [String] the path and name of the zip file @param target_path [String] the target directory where to extract the file

# File lib/pad_utils/pad_compression.rb, line 13
def self.extract_zip(filename, target_path)
  # Unzip the content
  Zip::File.open(filename) do |zip_file|
    zip_file.each do |f|
      f_path=File.join(target_path, f.name)
      FileUtils.mkdir_p(File.dirname(f_path))
      zip_file.extract(f, f_path) unless File.exist?(f_path)
    end
  end
  # Get rid of these filthy OS X files
  PadUtils.delete_directory "#{target_path}/__MACOSX" if PadUtils.file_exist?("#{target_path}/__MACOSX")
  PadUtils.delete_recursive("#{target_path}", ".DS_Store")
end
file_exist?(file_path) click to toggle source

Checks if a file exists.

@param file_path [String] the file path and name @return [Boolean] @example

PadUtils.file_exist?("path/to/file.txt")
# File lib/pad_utils/pad_files.rb, line 36
def self.file_exist?(file_path)
  File.exist?(file_path)
end
filename_to_class(file) click to toggle source

Create a class from a filename.

**Don't forget to `require_relative` the file**

@param file [String] the file path and name. Must be a Ruby (`.rb`) file. @return [Object] the class generated from the file @example

require_relative 'somepath/foo_bar'
foobar = PadUtils.filename_to_class("somepath/foo_bar.rb")
f = foobar.new
# File lib/pad_utils/pad_code.rb, line 27
def self.filename_to_class(file)
  class_name = PadUtils.filename_to_classname(file)
  Object.const_get(class_name)
end
filename_to_classname(file) click to toggle source

Converts a Ruby filename to a class name

@param file [String] the file path and name. Must be a Ruby (`.rb`) file. @return [String] the class name @example

PadUtils.filename_to_class("foo_bar.rb") # => FooBar
# File lib/pad_utils/pad_code.rb, line 10
def self.filename_to_classname(file)
  filename = File.basename file
  filename_no_ext = filename.gsub(".rb", "")
  class_name = PadUtils.camel_case(filename_no_ext)
  class_name
end
generate_rsa(type, path) click to toggle source
# File lib/pad_utils.rb, line 51
def self.generate_rsa(type, path)
  if type == "private"
    PadUtils.generate_rsa_private_key path: path
  end

  if type == "public"
    PadUtils.generate_rsa_public_key private_key: path, path: "public.pem"
  end
end
generate_rsa_private_key(key_size: 2048, path: nil) click to toggle source

Generates a RSA private key.

@param key_size [Integer] key size (*defaults to 2048*) @param path [String] optional path to save the private key @return [RSA] the RSA private key @example

PadUtils.generate_rsa_private_key 3096 # => <RSA key...>
# File lib/pad_utils/pad_security.rb, line 74
def self.generate_rsa_private_key(key_size: 2048, path: nil)
  key = OpenSSL::PKey::RSA.generate key_size
  if !path.nil?
    File.open(path, 'w') { |file| file.write(key.to_pem) }
  end
  key
end
generate_rsa_public_key(private_key: nil, path: nil) click to toggle source

Generates a RSA public key.

@param private_key [RSA, String] the private key as an RSA key or a pem file path @param path [String] optional path to save the public key @return [RSA] the RSA public key @example

PadUtils.generate_rsa_public_key "private.pem" # => <RSA key...>
# File lib/pad_utils/pad_security.rb, line 89
def self.generate_rsa_public_key(private_key: nil, path: nil)
  key = nil
  if private_key.class == OpenSSL::PKey::RSA
    key = private_key.public_key
  elsif private_key.class == String
    private_key = OpenSSL::PKey::RSA.new(File.read(private_key))
    key = PadUtils.generate_rsa_public_key private_key: private_key
  end

  if !path.nil? && !key.nil?
    File.open(path, 'w') { |file| file.write(key.to_pem) }
  end
  key
end
get_config_value(key, file) click to toggle source

Gets a value from a Ruby config file.

*This method is some kind of UFO but it is heavily used in Padstone.

It is made to retrieve the value inside a Rails config file or initializer
such as getting the value of `config.eager_load` in `production.rb`.
Everything gets returned as a string or as nil if not found.*

Will log errors using {PadUtils.log PadUtils.log}.

@param key [String] the config key to look for @param file [String] the file path and name containing the key @return [String, nil] the value returned as a string or nil @example

PadUtils.get_config_value("config.eager_load", "production.rb") # => 'true'
# File lib/pad_utils/pad_text.rb, line 128
def self.get_config_value(key, file)
  content = PadUtils.get_file_content(file)
  content.each_line do |line|
    if line.strip.start_with? key
      return line.gsub(key, "").gsub("=","").strip
    end
  end
  nil
rescue Exception => e
  PadUtils.log("Error in get_config_value", e)
end
get_file_content(filepath) click to toggle source

Reads the whole content of a file into a string.

Will log errors using {PadUtils.log PadUtils.log}.

@param filepath [String] the file path and name @return [String] the content of the file @example

PadUtils.get_file_content("path/to/file.txt")
# File lib/pad_utils/pad_files.rb, line 123
def self.get_file_content(filepath)
  File.read(filepath)
rescue Exception => e
  PadUtils.log("Error in get_file_content", e)
end
hash_to_json(hash) click to toggle source

Convert a hash to a JSON string.

@param hash [Hash] the hash to convert @return [String] the JSON string @example

h = {name: "Nico", age: 37}
PadUtils.hash_to_json(h) # => '{"name": "Nico", "age": 37}'
# File lib/pad_utils/pad_json.rb, line 55
def self.hash_to_json(hash)
  hash.to_json
end
hash_to_json_file(filename, hash) click to toggle source

Writes a hash to a JSON file.

@note Will overwrite the file if it already exists.

@param filename [String] the file path and name to write to @param hash [Hash] the hash to convert to JSON and write @return [String] the JSON string @example

h = {name: "Nico", age: 37}
PadUtils.hash_to_json_file("path/to/json_file.txt", h) # => '{"name": "Nico", "age": 37}'
# File lib/pad_utils/pad_json.rb, line 69
def self.hash_to_json_file(filename, hash)
  json = hash.to_json
  PadUtils.write_to_file(filename, json)
  json
end
help() click to toggle source

Display version of PadUtils if no switches are passed.

# File lib/pad_utils.rb, line 62
def self.help
  puts
  PadUtils.puts_c "PadUtils v.#{PadUtils::VERSION}", :green
  puts
  puts "Part of the Padstone app builder (http://padstone.io)"
  puts
  puts "Copyright 2016 - Nico Schuele"
  puts "Licensed under the Apache License, Version 2.0"
  puts
end
http_get(url, headers: {'User-Agent' => 'Padstone'}) click to toggle source

Sends a HTTP GET request and receives a hash.

@note Although it can parse any JSON responses, this method is made to work

in conjunction with PadUtils on the server side code.

If the server can't be reached, `{error: “Server unreachable”}` is returned.

If the JSON in the response can't be parsed (or if it's not JSON),

`{error: "Response is not JSON"}` is returned.

@param url [String] the url to call @param headers [Hash] the hash containing the header key/value pairs @return [Hash] the response as a hash @example

url = "http://example.com/api/weather"
PadUtils.http_get(url) # => {Geneva: "Sunny", Lausanne: "Cloudy"}
# File lib/pad_utils/pad_http.rb, line 72
def self.http_get(url, headers: {'User-Agent' => 'Padstone'})
  reply_hash = {}
  reply = HTTParty.get(url, headers: headers).to_s
  reply_hash = PadUtils.json_to_hash reply
  reply_hash
rescue JSON::ParserError => e
  reply_hash = {error: "Response is not JSON"}
rescue Errno::ECONNREFUSED => e
  reply_hash = {error: "Server unreachable"}
rescue Exception => e
  reply_hash = {error: e.message}
end
http_get_file(url, target, headers: {'User-Agent' => 'Padstone'}) click to toggle source

Downloads a file API-frienldy.

@note Warning: If the server answers with an error message, no error will

be raised and the `target` file be created with the content of the error message.

@param url [String] @param target [String] the local target file path and name @param headers [Hash] the hash containing the header key/value pair @return [String] the target path and name or the error message @example

PadUtils.http_get_file("http:/example.com/v1/get_file", "image.jpg") # => "image.jpg"
# File lib/pad_utils/pad_http.rb, line 96
def self.http_get_file(url, target, headers: {'User-Agent' => 'Padstone'})
  File.open(target, "wb") do |f|
    f.binmode
    f.write HTTParty.get(url).parsed_response
  end

  if File.size(target) < 1
    PadUtils.delete_file(target)
    "File not found"
  else
    target
  end

rescue Errno::ECONNREFUSED => e
  PadUtils.delete_file(target)
  "Server unreachable"
rescue Exception => e
  PadUtils.delete_file(target)
  e.message
end
http_get_plain(url, headers: {'User-Agent' => 'Padstone'}) click to toggle source

Sends a HTTP GET request and receives plain text.

If the server can't be reached, `“Server unreachable”` is returned.

@param url [String] the url to call @param headers [Hash] the hash containing the header key/value pairs @return [String] the response @example

url = "http://example.com/api/joke"
PadUtils.http_get_plain(url) # => "There are 10 kinds of programmers."
# File lib/pad_utils/pad_http.rb, line 48
def self.http_get_plain(url, headers: {'User-Agent' => 'Padstone'})
  reply = HTTParty.get(url, headers: headers).to_s
rescue Errno::ECONNREFUSED => e
  reply = "Server unreachable"
rescue Exception => e
  reply = e.message
end
http_post(url: "", body: {}, headers: {'User-Agent' => 'Padstone'}) click to toggle source

Sends a HTTP POST request.

@note Although it can parse any JSON responses, this method is made to work

in conjunction with PadUtils on the server side code.

If the server can't be reached, `{error: “Server unreachable”}` is returned.

If the JSON in the response can't be parsed (or if it's not JSON),

`{error: "Response is not JSON"}` is returned.

@param url [String] the url to call @param body [Hash] the hash containing the key/value pairs to send @param headers [Hash] the hash containing the header key/value pairs @return [Hash] the response as a hash @example

url = "http://example.com/api/movies"
body = {year: 2008, director: "Nolan"}
PadUtils.http_post(url: url, body: body) # => {movie: "The Dark Knight"}
# File lib/pad_utils/pad_http.rb, line 23
def self.http_post(url: "", body: {}, headers: {'User-Agent' => 'Padstone'})
  reply_hash = {}
  body = PadUtils.hash_to_json(body)
  reply = HTTParty.post(url, {headers: headers, body: body}).to_s
  reply_hash = PadUtils.json_to_hash reply
  reply_hash
rescue JSON::ParserError => e
  reply_hash = {error: "Response is not JSON"}
rescue Errno::ECONNREFUSED => e
  reply_hash = {error: "Server unreachable"}
rescue Exception => e
  reply_hash = {error: e.message}
end
insert_after_first(original: nil, tag: nil, text: nil, is_file: true) click to toggle source

Inserts text after the first occurence of a string.

Can be used on a string or on a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param original [String] the original file path and name or the original string @param tag [String] the string to find @param text [String] the string to insert @param is_file [Boolean] `true` if `original` is a file, `false` if it is a string @return [String] the new content @example

PadUtils.insert_after_first(original: "file.txt", tag: "end", text: "# hello!")
# File lib/pad_utils/pad_text.rb, line 327
def self.insert_after_first(original: nil, tag: nil, text: nil, is_file: true)
  # The new text will be consolidated in content
  content = ""

  # If coming from a file, read original into content
  if is_file
    content = PadUtils.get_file_content(original)
  else
    content = original
  end

  # Iterate line by line. If a position is found, insert the text
  # and set found to true to prevent further insertions
  found = false
  new_content = ""
  content.each_line do |line|
    position = line.index(/#{tag}/)
    if position && !found
      new_content += "#{line}#{text}"
      found = true
    else
      new_content += line
    end
  end

  # If coming from a file, write result in same file. If not,
  # simply return content
  if is_file
    PadUtils.write_to_file(original, new_content)
    return new_content
  else
    return new_content
  end

rescue Exception => e
  PadUtils.log("Error in insert_after_first", e)
end
insert_after_last(original: nil, tag: nil, text: nil, is_file: true) click to toggle source

Inserts text after the last occurence of a string.

Can be used on a string or on a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param original [String] the original file path and name or the original string @param tag [String] the string to find @param text [String] the string to insert @param is_file [Boolean] `true` if `original` is a file, `false` if it is a string @return [String] the new content @example

PadUtils.insert_after_last(original: "file.txt", tag: "end", text: "# hello!")
# File lib/pad_utils/pad_text.rb, line 378
def self.insert_after_last(original: nil, tag: nil, text: nil, is_file: true)
  # The new text will be consolidated in content
  content = ""

  # If coming from a file, read original into content
  if is_file
    content = PadUtils.get_file_content(original)
  else
    content = original
  end

  # Find the position of tag in the string array and insert the text
  positions = content.enum_for(:scan, /#{tag}/).map { Regexp.last_match.begin(0) }
  content[positions.last + tag.length] = "#{text}"

  # If coming from a file, write result in same file. If not,
  # simply return content
  if is_file
    PadUtils.write_to_file(original, content)
    return content
  else
    return content
  end

rescue Exception => e
  PadUtils.log("Error in insert_after_last", e)
end
insert_before_first(original: nil, tag: nil, text: nil, is_file: true) click to toggle source

Inserts text before the first occurence of a string.

Can be used on a string or on a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param original [String] the original file path and name or the original string @param tag [String] the string to find @param text [String] the string to insert @param is_file [Boolean] `true` if `original` is a file, `false` if it is a string @return [String] the new content @example

PadUtils.insert_before_first(original: "file.txt", tag: "end", text: "# hello!")
# File lib/pad_utils/pad_text.rb, line 235
def self.insert_before_first(original: nil, tag: nil, text: nil, is_file: true)
  # The new text will be consolidated in content
  content = ""

  # If coming from a file, read original into content
  if is_file
    content = PadUtils.get_file_content(original)
  else
    content = original
  end

  # Iterate line by line. If a position is found, insert the text
  # and set found to true to prevent further insertions
  found = false
  new_content = ""
  content.each_line do |line|
    position = line.index(/#{tag}/)
    if position && !found
      new_content += "#{text}#{line}"
      found = true
    else
      new_content += line
    end
  end

  # If coming from a file, write result in same file. If not,
  # simply return content
  if is_file
    PadUtils.write_to_file(original, new_content)
    return new_content
  else
    return new_content
  end

rescue Exception => e
  PadUtils.log("Error in insert_before_first", e)
end
insert_before_last(original: nil, tag: nil, text: nil, is_file: true) click to toggle source

Inserts text before the last occurence of a string.

Can be used on a string or on a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param original [String] the original file path and name or the original string @param tag [String] the string to find @param text [String] the string to insert @param is_file [Boolean] `true` if `original` is a file, `false` if it is a string @return [String] the new content @example

PadUtils.insert_before_last(original: "file.txt", tag: "end", text: "# hello!")
# File lib/pad_utils/pad_text.rb, line 286
def self.insert_before_last(original: nil, tag: nil, text: nil, is_file: true)
  # The new text will be consolidated in content
  content = ""

  # If coming from a file, read original into content
  if is_file
    content = PadUtils.get_file_content(original)
  else
    content = original
  end

  # Find the position of tag in the string array and insert the text
  positions = content.enum_for(:scan, /#{tag}/).map { Regexp.last_match.begin(0) }
  content[positions.last - 1] = "#{text}"

  # If coming from a file, write result in same file. If not,
  # simply return content
  if is_file
    PadUtils.write_to_file(original, content)
    return content
  else
    return content
  end

rescue Exception => e
  PadUtils.log("Error in insert_before_last", e)
end
interval(start_time, end_time, unit = :seconds) click to toggle source

Returns the interval of time between two Time objects.

@param start_time [Time] the starting time @param end_time [Time] the ending time @param unit [Symbol] the unit to use from `:seconds`, `:minutes`, `:hours`, `:days` @return [Float] the interval, rounded at 3 digits @example

start = Time.now - 30
finish = Time.now
PadUtils.interval(start, finish, :minutes)  # => 0.5
# File lib/pad_utils/pad_time.rb, line 63
def self.interval(start_time, end_time, unit = :seconds)
  result = -1
  inter = (end_time - start_time)

  if unit == :minutes
    result = inter / 60.0
  elsif unit == :hours
    result = (inter / 60) / 60
  elsif unit == :days
    result = ((inter / 60) / 60) / 24
  else
    result = inter
  end

  result.round(3)

end
json_file_to_hash(json_file) click to toggle source

Converts a JSON file to a symbolized hash.

Reads a JSON file and calls {PadUtils.deep_symbolize_hash_keys} on its content.

@param json_file [String] the json file path and name @return [Hash] the symbolized hash @example

PadUtils.json_file_to_hash("path/to/file")
# File lib/pad_utils/pad_json.rb, line 42
def self.json_file_to_hash(json_file)
  jfile = PadUtils.get_file_content(json_file)
  h = JSON.parse(jfile)
  self.deep_symbolize_hash_keys(h)
end
json_to_hash(json) click to toggle source

Converts a JSON string to a symbolized hash.

Calls {PadUtils.deep_symbolize_hash_keys} on a JSON formatted string.

@param json [String] the JSON string to convert @return [Hash] the symbolized hash @example

PadUtils.json_to_hash(a_json_string)
# File lib/pad_utils/pad_json.rb, line 29
def self.json_to_hash(json)
  h = JSON.parse(json)
  self.deep_symbolize_hash_keys(h)
end
log(message, e = nil) click to toggle source

Logs a message.

@note If using the method within a rescue block, pass the exception from

`rescue Exception => e`

@param message [String] the message to log @param e [Exception] the raised exception @return [Void] nothing @example

def faulty_method
  a = 0
  b = 3
  c = b / a
rescue Exception => e
  PadUtils.log("Something bad happened!", e)
end
# File lib/pad_utils/pad_logger.rb, line 47
def self.log(message, e = nil)
  # Create the log directory if it doesn't exist
  PadUtils.create_directory(@@log_path)

  # Add a timestamp to the message
  message = "#{PadUtils.time_to_stamp(Time.now)}: #{message}"

  # If an error is added, add its inner message to the message
  # as well as the whole stack
  if e != nil
    message = "#{message}\n\tError: #{e.message} (#{e.class.name})"
    stack = e.backtrace.inspect.split(",")
    stack.each do |s|
      message = "#{message}\n\t\t#{s}"
    end
    message = "#{message}\n"
  end

  # Adds the message to the log file
  PadUtils.append_to_file("#{@@log_path}/#{@@log_file}", message)
end
main(arg) click to toggle source

Entry point for the executable.

# File lib/pad_utils.rb, line 22
def self.main(arg)
  if arg[0] == '-u'
    PadUtils.puts_c PadUtils.uuid, :blue
  elsif arg[0] == '-e'
    if arg[1].nil? || arg[2].nil?
      puts
      PadUtils.puts_c "padutils -e <word to encrypt> <encryption key>"
    else
      PadUtils.puts_c PadUtils.encrypt content: arg[1], key: arg[2]
    end
  elsif arg[0] == '-d'
    if arg[1].nil? || arg[2].nil?
      puts
      PadUtils.puts_c "padutils -d <string to decrypt> <encryption key>"
    else
      PadUtils.puts_c PadUtils.decrypt content: arg[1], key: arg[2]
    end
  elsif arg[0] == '--rsa'
    if arg[1].nil? || arg[2].nil?
      puts
      PadUtils.puts_c "padutils --rsa <private | public> <path/to/key.pem>", :blue
    else
      generate_rsa arg[1], arg[2]
    end
  else
    help
  end
end
move_file(file_path, dest_file) click to toggle source

Moves a file.

@param file_path [String] the source file path and name @param dest_file [String] the destination path and name @return [Void] nothing @example

PadUtils.move_file("source.txt", "path/to/destination/renamed.txt")
# File lib/pad_utils/pad_files.rb, line 59
def self.move_file(file_path, dest_file)
  FileUtils.mv(file_path, dest_file, force: true)
end
puts_c(text, highlight = nil, wrap = true) click to toggle source

Colorizes the cli output.

Colors have aliases:

  • `:blue` => `:note`, `:emphasis`

  • `:red` => `:error`, `:danger`, `:alert`, `:failure`

  • `:green` => `:correct`, `:success`, `:info`

@param text [String] the string to output @param highlight [Symbol, nil] the color to be used (`:red`, `:green`, `:blue`, `nil`) @param wrap [Boolean] if `true`, will wrap the text at 80 characters @return [Void] nothing @example

PadUtils.puts_c "Hello World", :green
# File lib/pad_utils/pad_color.rb, line 17
def self.puts_c(text, highlight = nil, wrap = true)
  # Set color codes
  red = 31
  green = 32
  blue = 36

  # Set color to use
  current_color = nil
  case highlight
  when :red, :error, :danger, :alert, :failure
    current_color = red
  when :green, :correct, :success, :info
    current_color = green
  when :blue, :note, :emphasis
    current_color = blue
  else
    current_color = nil
  end

  # Wrap the text
  if wrap
    text = text.gsub(/\n/, ' ').gsub(/(.{1,#{79}})(\s+|$)/, "\\1\n").strip
  end

  # Just display text if no color chosen. Else, colorize it.
  if current_color == nil
    STDOUT.puts text
  else
    STDOUT.puts "\e[#{current_color}m#{text}\e[0m"
  end

end
question_menu(question) click to toggle source

Builds an open question menu.

Will add `:` and a space after the question.

@param question [String] the question to ask @return [String] the answer to the question @example

PadUtils.question_menu("How are you?") # => 'All good'
# File lib/pad_utils/pad_menu.rb, line 30
def self.question_menu(question)
  STDOUT.print "#{question}: "
  STDIN.gets.chomp.strip
end
readable_stamp_to_time(val) click to toggle source

Returns a Time object from a readable timestamp.

Will log errors using {PadUtils.log PadUtils.log}.

@param val [String] the readable timestamp formatted as `YYYY-MM-DD HH:mm:ss` @return [Time] the Time object

# File lib/pad_utils/pad_time.rb, line 47
def self.readable_stamp_to_time(val)
  Time.parse val
rescue Exception => e
  PadUtils.log("Error in readable_stamp_to_time", e)
end
replace_in_file(file, old_text, new_text) click to toggle source

Replaces text in a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param file [String] the file path and name @param old_text [String, Regexp] the text to find as a string or regex @param new_text [String] the new text @return [Void] nothing @example

PadUtils.replace_in_file("example.txt", /some_text/, "new text")
# File lib/pad_utils/pad_text.rb, line 84
def self.replace_in_file(file, old_text, new_text)
  text_update = PadUtils.get_file_content(file)
  text_update = text_update.gsub(old_text, new_text)

  PadUtils.write_to_file(file, text_update)
rescue Exception => e
  PadUtils.log("Error replacing #{old_text} in #{file} with #{new_text}", e)
end
replace_keys_in_file(file, values) click to toggle source

Replaces multiple strings in a file.

Will log errors using {PadUtils.log PadUtils.log}.

@param file [String] the file path and name @param values [Array] the array containing hashes of `key`, `value` @return [Void] nothing @example

values = [
  {key: /REPLACE_ME/, value: "REPLACED"},
  {key: /REPLACE_ALSO/, value: "MODIFIED AS WELL"}
]
PadUtils.replace_keys_in_file("example.txt", values)
# File lib/pad_utils/pad_text.rb, line 106
def self.replace_keys_in_file(file, values)
  values.each do |value|
    PadUtils.replace_in_file(file, value[:key], value[:value])
  end
rescue Exception => e
  PadUtils.log("Error replacing multiple keys in #{file}", e)
end
replace_line_containing(value, in_file: nil, new_value: nil) click to toggle source

Replaces a line in a file containing a specific value.

Will log errors using {PadUtils.log PadUtils.log}.

@param value [String] the value to search for in a line @param in_file [String] the file path and name where to search @param new_value [String] the value replacing the line @return [Void] nothing @example

PadUtils.replace_line_containing("Config.port", in_file: "config.rb", new_value: "Config.port = 232")
# File lib/pad_utils/pad_text.rb, line 207
def self.replace_line_containing(value, in_file: nil, new_value: nil)
  content = PadUtils.get_file_content(in_file)
  new_content = ""
  content.each_line do |line|
    if line.include? value
      new_content << "#{new_value}\n"
    else
      new_content << line
    end
  end
  PadUtils.write_to_file(in_file, new_content)
rescue Exception => e
  PadUtils.log("Error in replace_line_containing", e)
end
rsa_private_decrypt(content: nil, private_key: nil) click to toggle source

Decrypts a string with a RSA private key.

@param content [String] the string to decrypt @param public_key [RSA, String] the private key as an RSA key or a pem file path @return [String] the decrypted string @example

PadUtils.rsa_private_decrypt "mwRAHtpE9...", "private.pem" # => 'Hello!'
# File lib/pad_utils/pad_security.rb, line 126
def self.rsa_private_decrypt(content: nil, private_key: nil)
  if private_key.class == String
    private_key = OpenSSL::PKey::RSA.new(File.read(private_key))
  end

  private_key.private_decrypt(Base64.decode64(content))
end
rsa_private_encrypt(content: nil, private_key: nil) click to toggle source

Encrypts a string with a RSA private key.

@param content [String] the string to encrypt @param public_key [RSA, String] the private key as an RSA key or a pem file path @return [String] the encrypted string @example

PadUtils.rsa_private_encrypt "Hello!", "private.pem" # => 'mwRAHtpE9...'
# File lib/pad_utils/pad_security.rb, line 141
def self.rsa_private_encrypt(content: nil, private_key: nil)
  if private_key.class == String
    private_key = OpenSSL::PKey::RSA.new(File.read(private_key))
  end

  Base64.encode64(private_key.private_encrypt(content))
end
rsa_public_decrypt(content: nil, public_key: nil) click to toggle source

Decrypts a string with a RSA public key.

@param content [String] the string to decrypt @param public_key [RSA, String] the public key as an RSA key or a pem file path @return [String] the decrypted string @example

PadUtils.rsa_public_decrypt "mwRAHtpE9...", "public.pem" # => 'Hello!'
# File lib/pad_utils/pad_security.rb, line 156
def self.rsa_public_decrypt(content: nil, public_key: nil)
  if public_key.class == String
    public_key = OpenSSL::PKey::RSA.new(File.read(public_key))
  end

  public_key.public_decrypt(Base64.decode64(content))
end
rsa_public_encrypt(content: nil, public_key: nil) click to toggle source

Encrypts a string with a RSA public key.

@param content [String] the string to encrypt @param public_key [RSA, String] the public key as an RSA key or a pem file path @return [String] the encrypted string @example

PadUtils.rsa_public_encrypt "Hello!", "public.pem" # => 'mwRAHtpE9...'
# File lib/pad_utils/pad_security.rb, line 111
def self.rsa_public_encrypt(content: nil, public_key: nil)
  if public_key.class == String
    public_key = OpenSSL::PKey::RSA.new(File.read(public_key))
  end

  Base64.encode64(public_key.public_encrypt(content))
end
sanitize(value) click to toggle source

Sanitizes a string.

Will only allow alphanumeric characters and underscores.

@param value [String] the string to sanitize @return [String] the sanitized string @example

PadUtils.sanitize("Abc Def *34*#yXz") # => 'Abc_Def__34__yXz'
# File lib/pad_utils/pad_text.rb, line 70
def self.sanitize(value)
  value.tr('^A-Za-z0-9', '_')
end
set_config_value(key, value, file, comment = nil) click to toggle source

Sets a value in a Ruby config file.

*This is another method typically used by Padstone to write

new config values in Rails config files such as `production.rb` or
overwrite existing ones.*

Will log errors using {PadUtils.log PadUtils.log}.

@param key [String] the config key to find (or create) @param value [String] the value to set @param file [String] the file path and name of the file to overwrite @param comment [String] the optional comment to add before the key @return [Void] nothing @example

key = "config.assets.digest"
value = "false"
file = "production.rb"
PadUtils.set_config_value(key, value, file, "Overwritten with PadUtils")
# File lib/pad_utils/pad_text.rb, line 158
def self.set_config_value(key, value, file, comment = nil)
  # read the config file
  content = PadUtils.get_file_content(file)

  # set some vars
  found = false
  new_content = ""

  # for each line in the file, check if one contains the key.
  # If the key is found, get its position so we can indent the
  # config line properly.
  content.each_line do |line|
    position = line.index(key)
    if position != nil
      new_line = ""
      (0..position - 1).each do |p|
        new_line << " "
      end
      if comment != nil
        new_content << "#{new_line}# #{comment}\n"
      end
      new_content << "#{new_line}#{key} = #{value}\n"
      found = true
    else
      new_content << line
    end
  end

  # If the config key was not found, we'll insert it before the last end,
  # indented with two spaces
  if !found
    PadUtils.insert_before_last(original: file, tag: 'end', text: "\n\n  # #{comment == nil ? key : comment}\n  #{key} = #{value}\n")
  else
    PadUtils.write_to_file(file, new_content)
  end
rescue Exception => e
  PadUtils.log("Error in set_config_value", e)
end
set_log_file(val) click to toggle source

Sets a different log file.

@param val [String] the log file name @return [String] the log file name @example

PadUtils.set_log_file("my_logs.txt") # => 'my_logs.txt'
# File lib/pad_utils/pad_logger.rb, line 27
def self.set_log_file(val)
  @@log_file = val
end
set_log_path(val) click to toggle source

Sets a different log path.

@param val [String] the path to the directory @return [String] the log directory path @example

PadUtils.set_log_path("path/directory") # => 'path/directory'
# File lib/pad_utils/pad_logger.rb, line 17
def self.set_log_path(val)
  @@log_path = val
end
stamp_to_time(val) click to toggle source

Returns a Time object from a string timestamp.

Will log errors using {PadUtils.log PadUtils.log}.

@param val [String] a string formatted as `YYYYMMDDHHmmss` @return [Time] the Time object @example

PadUtils.stamp_to_time("20160227160122")
# File lib/pad_utils/pad_time.rb, line 25
def self.stamp_to_time(val)
  Time.parse "#{val[0..3]}-#{val[4..5]}-#{val[6..7]} #{val[8..9]}:#{val[10..11]}:#{val[12..13]}"
rescue Exception => e
  PadUtils.log("Error in stamp_to_time", e)
end
time_to_readable_stamp(val) click to toggle source

Returns a readable string timestamp.

Format `YYYY-MM-DD HH:mm:ss` will be used.

@param val [Time] the Time object to convert @return [String] the readable timestamp

# File lib/pad_utils/pad_time.rb, line 37
def self.time_to_readable_stamp(val)
  val.strftime("%Y-%m-%d %H:%M:%S")
end
time_to_stamp(val) click to toggle source

Returns a string timestamp.

Format `YYYYMMDDHHmmss` will be used.

@param val [Time] the Time object to convert @return [String] the timestamp @example

PadUtils.time_to_stamp(Time.now) # => 20160227160122
# File lib/pad_utils/pad_time.rb, line 13
def self.time_to_stamp(val)
  val.strftime("%Y%m%d%H%M%S")
end
underscore(val) click to toggle source

Converts a CamelCase string to an underscore string.

Taken from the Rails {api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-underscore Inflector} class.

@param val [String] the CamelCase string to underscore @return [String] the under_score string @example

PadUtils.underscore("CamelCase") # => camel_case
# File lib/pad_utils/pad_text.rb, line 52
def self.underscore(val)
  word = val.dup
  word.gsub!(/::/, '/')
  word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
  word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
  word.tr!("-", "_")
  word.downcase!
  word
end
uuid(count = 1) click to toggle source

Generate UUID's.

@param count [Int] how many uuid's to generate @return [String, Array<String>] if only one uuid generated, returns a `String`. If not, an `Array`. @example

PadUtils.uuid(2) # => ["96b0a57c-d9ae-453f-b56f-3b154eb10cda", "332a0fa3-7b07-41e1-9fc8-ef804a377e4e"]
# File lib/pad_utils/pad_code.rb, line 38
def self.uuid(count = 1)
  if count == 1
    SecureRandom.uuid
  else
    result = []
    (1..count).each do
      result << SecureRandom.uuid
    end
    result
  end
end
write_to_file(filepath, content) click to toggle source

Writes content to a file.

@note Will create destination file if it doesn't exist.

Will also overwrite the file if it already exists.

Will log errors using {PadUtils.log PadUtils.log}.

@param filepath [String] the file path and name @param content [String] the content to be written @return [Void] nothing @example

PadUtils.write_to_file("path/to/file", "Hello\nThis is a test")
# File lib/pad_utils/pad_files.rb, line 141
def self.write_to_file(filepath, content)
  self.create_directory(filepath)
  File.open(filepath, 'w') { |f| f.write(content)}
rescue Exception => e
  PadUtils.log("Error in write_to_file", e)
end
yes_no_menu(question: "Question?", default: "y") click to toggle source

Builds a `yes/no` cli menu.

Will log errors using {PadUtils.log PadUtils.log}.

@param question [String] the question to ask @param default [String] the default answer as `“y”` or `“n”` - (*default: `“y”`*) @return [Boolean] `true` if `yes`, `false` for anything else @example

PadUtils.yes_no_menu(question: "Is it cold outside?", default: "n")
# File lib/pad_utils/pad_menu.rb, line 12
def self.yes_no_menu(question: "Question?", default: "y")
  default_answer = default == "y" ? "(Y/n)" : "(y/N)"
  STDOUT.print "#{question} #{default_answer}: "
  answer = STDIN.gets.chomp.strip.downcase
  answer = default if answer.length < 1
  answer == "y"
rescue Exception => e
  PadUtils.log("Error in yes/no menu", e)
end