class Object

Constants

BLUE
BOLD
BOLD_OFF
CLEAR
CURMARK
CURSOR_COLOR

CURSOR_COLOR = ON_BLUE

GMARK

CONSTANTS

GREEN
KEY_DOWN
KEY_END
KEY_F1
KEY_F10
KEY_F5
KEY_F6
KEY_F7
KEY_F8
KEY_F9
KEY_HOME

I needed to replace the O with a [ for this to work in Vim Home comes as ^[OH whereas on the command line it is correct as ^[[H

KEY_LEFT
KEY_PGDN
KEY_PGUP
KEY_RIGHT
KEY_S_F1
KEY_UP
ON_BLUE
ON_RED
RED
REVERSE
SPACE
UNDERLINE
YELLOW

Public Instance Methods

OLDview_data(db, sql, options) click to toggle source

TODO option of headers run query and put into a temp table and view it using vim If no outputfile name passed, then use temp table What about STDOUT TODO use temp file, format it there and append to given file only after termtable

# File lib/cetusql/cli_sqlite.rb, line 53
def OLDview_data db, sql, options
  outputfile = options[:output_to]
  formatting = options[:formatting]
  headers = options[:headers]
  #str = db.get_data sql
  rs = db.execute_query sql
  str = rs.content
  columns = rs.columns
  #puts "SQL: #{sql}.\nstr: #{str.size}"
  data = []
  if headers
    data << columns.join("\t")
  end
  str.each {|line| data << line.join("\t");  }
  #puts "Rows: #{data.size}"
  require 'tempfile'
  tmpfile = Tempfile.new('SQL.XXXXXX')
  filename = tmpfile.path
  filename = Shellwords.escape(filename)
  #puts "Writing to #{filename}"
  tmpfile.write(data.join("\n"))
  tmpfile.close # need to flush, otherwise write is buffered
  headerstr=nil
  if formatting
    headerstr = "-H" unless headers
    # sometimes this can be slow, and it can fault on UTF-8 chars
    system("cat #{filename} | term-table.rb #{headerstr} | sponge #{filename}")
  end
  if outputfile
    #puts "comes here"
    system("cp #{filename} #{outputfile}")
    filename = outputfile
  end
  system "wc -l #{filename}" if $opt_debug
  
  #system "$EDITOR #{filename}"
  system "vim -c ':set nowrap' #{filename}"
  tmpfile.close
  tmpfile.unlink
end
agree(prompt="") click to toggle source
# File lib/cetusql/cli_utils.rb, line 313
def agree(prompt="")
  x = input(prompt)
  return true if x.upcase == "Y"
  false
end
Also aliased as: confirm
calculate_column_width(content, col, maxrows=99) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 200
def calculate_column_width content, col, maxrows=99
  ret = 1
  ctr = 0
  content.each_with_index { |r, i| 
    break if ctr > maxrows
    ctr += 1
    c = r[col]
    x = c.to_s.length
    ret = x if x > ret
  }
  ret
end
calculate_column_widths(content, maxrows=99) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 195
def calculate_column_widths content, maxrows=99
  widths = []
  content.first.each_with_index {|r,i| widths << calculate_column_width(content, i, maxrows) }
  return widths
end
columnate(ary, sz) click to toggle source

print in columns ary - array of data sz - lines in one column

# File lib/cetusql/cli_utils.rb, line 325
def columnate ary, sz
  buff=Array.new
  return buff if ary.nil? || ary.size == 0
  
  # determine width based on number of files to show
  # if less than sz then 1 col and full width
  #
  wid = 30
  ars = ary.size
  ars = [$pagesize, ary.size].min
  d = 0
  if ars <= sz
    wid = $gcols - d
  else
    tmp = (ars * 1.000/ sz).ceil
    wid = $gcols / tmp - d
  end
  #elsif ars < sz * 2
    #wid = $gcols/2 - d
  #elsif ars < sz * 3
    #wid = $gcols/3 - d
  #else
    #wid = $gcols/$gviscols - d
  #end

  # ix refers to the index in the complete file list, wherease we only show 60 at a time
  ix=0
  while true
    ## ctr refers to the index in the column
    ctr=0
    while ctr < sz

      f = ary[ix]
      fsz = f.size
      if fsz > wid
        f = f[0, wid-2]+"$ "
        ## we do the coloring after trunc so ANSI escpe seq does not get get
        if ix + $sta == $cursor
          f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
        end
      else
        ## we do the coloring before padding so the entire line does not get padded, only file name
        if ix + $sta == $cursor
          f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
        end
        #f = f.ljust(wid)
        f << " " * (wid-fsz)
      end

      if buff[ctr]
        buff[ctr] += f
      else
        buff[ctr] = f
      end

      ctr+=1
      ix+=1
      break if ix >= ary.size
    end
    break if ix >= ary.size
  end
  return buff
end
confirm(prompt="")
Alias for: agree
connect(dbname=nil) click to toggle source

connect to given database, and if no name supplied then allow user to choose

# File lib/cetusql/cli_sqlite.rb, line 27
def connect dbname=nil
  dbname ||= getdbname
  return nil unless dbname
  #$log.debug "XXX:  CONNECT got #{dbname} "
  $current_db = dbname
  $db = SQLite3::Database.new(dbname) if dbname
  return $db
end
ctrlp(arr) click to toggle source

allows user to select from list, returning string if user pressed ENTER

Aborts if user presses Q or C-c or ESCAPE
# File lib/cetusql/cli_utils.rb, line 218
def ctrlp arr
  patt = nil
  curr = 0
  while true
    # clear required otherwise will keep drawing itself
    system("clear")
    if patt and patt != ""
      # need fuzzy match here
      view = arr.grep(/^#{patt}/)
      view = view | arr.grep(/#{patt}/)
      fuzzypatt = patt.split("").join(".*")
      view = view | arr.grep(/#{fuzzypatt}/)
    else
      view = arr
    end
    curr = [view.size-1, curr].min
    # if empty then curr becomes -1
    curr = 0 if curr < 0
    view.each_with_index do |a, i|
      mark = " "
      mark = ">" if curr == i
      print "#{mark}  #{a} \n"
    end
    #puts " "
    print "\r#{patt} >"
    ch = get_char
    if  ch =~ /^[a-z]$/
      patt ||= ""
      patt << ch
    elsif ch == "BACKSPACE"
      if patt && patt.size > 0
        patt = patt[0..-2]
      end
    elsif ch == "Q" or ch == "C-c" or ch == "ESCAPE"
      break
    elsif ch == "UP"
      curr -= 1
      curr = 0 if curr < 0
    elsif ch == "DOWN"
      curr += 1
      curr = [view.size-1, curr].min
      # if empty then curr becomes -1
      curr = 0 if curr < 0
    elsif ch == "ENTER"
      return view[curr]
    else
      # do right and left arrow

      # get arrow keys here
    end

  end
end
editline(array) click to toggle source
# File lib/cetusql/cli_utils.rb, line 206
def editline array
  Readline::HISTORY.push(*array) 
  begin
    command = Readline::readline('>', true)
  rescue Exception => e
    return nil
  end
  return command
end
get_char() click to toggle source
 get a character from user and return as a string
Adapted from:

stackoverflow.com/questions/174933/how-to-get-a-single-character-without-pressing-enter/8274275#8274275

Need to take complex keys and matc against a hash.
# File lib/cetusql/cli_utils.rb, line 70
def get_char
  begin
    system("stty raw -echo 2>/dev/null") # turn raw input on
    c = nil
    #if $stdin.ready?
      c = $stdin.getc
      cn=c.ord
      return "ENTER" if cn == 10 || cn == 13
      return "BACKSPACE" if cn == 127
      return "C-SPACE" if cn == 0
      return "SPACE" if cn == 32
      # next does not seem to work, you need to bind C-i
      return "TAB" if cn == 8
      if cn >= 0 && cn < 27
        x= cn + 96
        return "C-#{x.chr}"
      end
      if c == ''
        buff=c.chr
        while true
          k = nil
          if $stdin.ready?
            k = $stdin.getc
            #puts "got #{k}"
            buff += k.chr
          else
            x=$kh[buff]
            return x if x
            #puts "returning with  #{buff}"
            if buff.size == 2
              ## possibly a meta/alt char
              k = buff[-1]
              return "M-#{k.chr}"
            end
            return buff
          end
        end
      end
    #end
    return c.chr if c
  ensure
    #system "stty -raw echo" # turn raw input off
    system("stty -raw echo 2>/dev/null") # turn raw input on
  end
end
get_column_names(tbname) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 23
def get_column_names tbname
  get_metadata tbname
end
get_data(db, sql) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 35
def get_data db, sql
  #$log.debug "SQL: #{sql} "
  $columns, *rows = db.execute2(sql)
  #$log.debug "XXX COLUMNS #{sql}, #{rows.count}  "
  content = rows
  return nil if content.nil? or content[0].nil?
  $datatypes = content[0].types #if @datatypes.nil?
  return content
end
get_metadata(table) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 44
def get_metadata table
  get_data "select * from #{table} limit 1"
  return $columns
end
get_table_names(db) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 16
def get_table_names db
  #raise "No database file selected." unless $current_db

  tables = get_data "select name from sqlite_master where type='table'"
  tables.collect!{|x| x[0] }  ## 1.9 hack, but will it run on 1.8 ??
  tables
end
input(prompt="", newline=false) click to toggle source

readline version of gets

# File lib/cetusql/cli_utils.rb, line 303
def input(prompt="", newline=false)
  prompt += "\n" if newline
  ret = nil
  begin
    ret = Readline.readline(prompt, true).squeeze(" ").strip
  rescue Exception => e
    return nil
  end
  return ret
end
menu(title, h) click to toggle source

What if we only want to allow the given keys and ignore others. In menu maybe ENTER and other such keys should be ignored, or atleast option should be there, so i don't accidentally hit enter. print in columns, but take into account size so we don't exceed COLS (Some entries like paths can be long)

mktemp() click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 139
def mktemp
  require 'tempfile'
  tmpfile = Tempfile.new('SQL.XXXXXX')
  filename = tmpfile.path
  filename = Shellwords.escape(filename)
  #puts "Writing to #{filename}"
  #tmpfile.write(data.join("\n"))
  #tmpfile.close # need to flush, otherwise write is buffered
  return tmpfile
end
multi_select(title, array) click to toggle source

multiselect from an array using fzf

# File lib/cetusql/cli_utils.rb, line 196
def multi_select title, array
  arr = %x[ echo "#{array.join("\n")}" | fzf --multi --reverse --prompt="#{title} >"]
  return arr.split("\n")
end
pause(text=" Press a key ...") click to toggle source
# File lib/cetusql/cli_utils.rb, line 175
def pause text=" Press a key ..."
  print text
  get_char
end
pbold(text) click to toggle source
# File lib/cetusql/cli_utils.rb, line 162
def pbold text
  puts "#{BOLD}#{text}#{BOLD_OFF}"
end
perror(text) click to toggle source
# File lib/cetusql/cli_utils.rb, line 168
def perror text
  pred text
  get_char
end
pgreen(text) click to toggle source
# File lib/cetusql/cli_utils.rb, line 165
def pgreen text
  puts "#{GREEN}#{text}#{CLEAR}"
end
pred(text) click to toggle source
# File lib/cetusql/cli_utils.rb, line 172
def pred text
  puts "#{RED}#{text}#{CLEAR}"
end
print_in_cols(a, noc=nil) click to toggle source

print an array in columns default number of columns is 3 and can be supplied. I actually much prefer that this be derived, so we can get more columns if possible, and te width should be caclulated too

screen_settings() click to toggle source

— end constants

check screen size and accordingly adjust some variables
# File lib/cetusql/cli_utils.rb, line 293
def screen_settings
  $glines=%x(tput lines).to_i
  $gcols=%x(tput cols).to_i
  $grows = $glines - 3
  $pagesize = 60
  #$gviscols = 3
  $pagesize = $grows * $gviscols
end
select_from(title, array) click to toggle source

alternative of menu that takes an array and uses numbers as indices. Hey wait, if there aer more than 10 then we are screwed since we take one character I have handled this somewhere, should check, maybe we should use characters returns text, can be nil if selection not one of choices How do we communicate to caller, that user pressed C-c

# File lib/cetusql/cli_utils.rb, line 184
def select_from title, array
  h = {}
  array.each_with_index {|e,ix| ix += 1; h[ix.to_s] = e }
  ch, text = menu title, h
  unless text
    if ch == "ENTER"
      return array.first
    end
  end
  return text
end
single_select(title, array) click to toggle source

single select from an array using fzf CAUTION: this messes with single and double quotes, so don't pass a query in

# File lib/cetusql/cli_utils.rb, line 202
def single_select title, array
  str = %x[ echo "#{array.join("\n")}" | fzf --reverse --prompt="#{title} >" -1 -0 ]
  return str
end
tabulate(content, options) click to toggle source

given content returned by get_data, formats and returns in a file

# File lib/cetusql/cli_sqlite.rb, line 150
def tabulate content, options
  data = []
  content.each {|line| data << line.join("\t");  }
  puts "Rows: #{data.size}" if $opt_verbose
  require 'tempfile'
  tmpfile = Tempfile.new('SQL.XXXXXX')
  filename = tmpfile.path
  #filename = Shellwords.escape(filename)
  #puts "Writing to #{filename}"
  tmpfile.write(data.join("\n"))
  tmpfile.close # need to flush, otherwise write is buffered
  if options[:formatting]
    system("term-table.rb < #{filename} | sponge #{filename}")
  end
  return filename
end
tabulate2(content, options) click to toggle source

rather than use external program with dependencies, we generate tabular format for an array with headings returns filename TODO check for headings true

# File lib/cetusql/cli_sqlite.rb, line 170
def tabulate2 content, options
  widths = calculate_column_widths(content, 99)
  str = "| "
  sep = "+"
  widths.each do |w|
    str << "%-#{w}s | "
    sep << ("-"*(w+2)) + "+"
  end
  data = []
  data << sep 
  content.each_with_index {|line, ix| 
    data << str % line 
    data << sep if ix == 0
  }
  data << sep
  require 'tempfile'
  tmpfile = Tempfile.new('SQL.XXXXXX')
  filename = tmpfile.path
  #filename = Shellwords.escape(filename)
  #puts "Writing to #{filename}"
  tmpfile.write(data.join("\n"))
  tmpfile.close # need to flush, otherwise write is buffered
  return filename
end
vared(var, prompt=">") click to toggle source

edit a variable inline like zsh's vared

# File lib/cetusql/cli_utils.rb, line 15
def vared var, prompt=">"
  Readline.pre_input_hook = -> do
    Readline.insert_text var
    Readline.redisplay
    # Remove the hook right away.
    Readline.pre_input_hook = nil
  end
  begin 
    input = Readline.readline(prompt, false)
  rescue Exception => e
    return nil
  end
  input
end
view_data(db, sql, options) click to toggle source
# File lib/cetusql/cli_sqlite.rb, line 93
def view_data db, sql, options
  outputfile = options[:output_to]
  formatting = options[:formatting]
  headers = options[:headers]
  #str = db.get_data sql
  rs = db.execute_query sql
  str = rs.content
  columns = rs.columns
  #puts "SQL: #{sql}.\nstr: #{str.size}"
  #data = []
  #if headers
    #data << columns.join("\t")
  #end
  #str.each {|line| data << line.join("\t");  }
  #puts "Rows: #{data.size}"
  headerstr=nil
  tmpfile = nil
  if formatting
    if headers
      str.unshift(columns)
    end
    filename = tabulate2 str, options
  else
    data = []
    if headers
      data << columns.join("\t")
    end
    str.each {|line| data << line.join("\t");  }
    tmpfile = mktemp()
    tmpfile.write(data.join("\n"))
    tmpfile.close # need to flush, otherwise write is buffered
    filename = tmpfile.path
  end
  if outputfile
    #puts "comes here"
    system("cp #{filename} #{outputfile}")
    filename = outputfile
  end
  
  #system "$EDITOR #{filename}"
  system "vim -c ':set nowrap' #{filename}"
  if tmpfile
    tmpfile.close
    tmpfile.unlink
  end
end