How to Write a Ruby Command Line App That Supports Tab Completion

How to write a Ruby command line app that supports tab completion?

Ah, it seems the standard library is my friend after all. What I was looking for is the Readline library.

Doc and examples here: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/readline/rdoc/Readline.html

In particular, this is a good example from that page to show how completion works:

require 'readline'

LIST = [
'search', 'download', 'open',
'help', 'history', 'quit',
'url', 'next', 'clear',
'prev', 'past'
].sort

comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }

Readline.completion_append_character = " "
Readline.completion_proc = comp

while line = Readline.readline('> ', true)
p line
end

NOTE: The proc receives only the last word entered. If you want the whole line typed so far (because you want to do context-specific completion), add the following line to the above code:

Readline.completer_word_break_characters = "" #Pass whole line to proc each time

(This is by default set to a list of characters that represent word boundaries and causes only the last word to be passed into your proc).

When using `gets` in Ruby for input, is there any way to get command history?

I started looking into tab completion and found this post which recommends using readline from stdlib.

I discovered that it also supports command history.

Capistrano 3 execute arbitrary command on remote server

I suggest to write your own little rake task to do it. Use readline gem
First of all thanks to follow materials:

  1. https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition

  2. How to write a Ruby command line app that supports tab completion?


desc "Remote console"
task :console do
require 'readline'
# https://thoughtbot.com/blog/tab-completion-in-gnu-readline-ruby-edition

host_args = (ENV['HOSTS'] || '').split(',').map { |r| r.to_sym }
role_args = (ENV['ROLES'] || '').split(',').map { |r| r.to_sym }

LIST = `ls /usr/bin`.split("\n").sort + `ls /bin`.split("\n").sort

comp = proc { |s| LIST.grep(/^#{Regexp.escape(s)}/) }

Readline.completion_append_character = " "
Readline.completion_proc = comp

while line = Readline.readline('cap> ', true)
begin
next if line.strip.empty?
exec_cmd(line, host_args, role_args)
rescue StandardError => e
puts e
puts e.backtrace
end
end
end

def exec_cmd(line, host_args, role_args)
line = "RAILS_ENV=#{fetch(:stage)} #{line}" if fetch(:stage)
cmd = "bash -lc '#{line}'"
puts "Final command: #{cmd}"
if host_args.any?
on hosts host_args do
execute cmd
end
elsif role_args.any?
on roles role_args do
execute cmd
end
else
on roles :all do
execute cmd
end
end
end


And do what you want with it, cheers! =))

Creating interactive ruby console application

What you want is a REPL – Read → Evaluate → Print Loop.

IRB, for example, implements a REPL for the Ruby language.

Here's a very simple implementation of your application's REPL:

loop do
Application::Console.prompt.display
input = gets.chomp
command, *params = input.split /\s/

case command
when /\Ahelp\z/i
puts Application::Console.help_text
when /\Aopen\z/i
Application::Task.open params.first
when /\Ado\z/i
Application::Action.perform *params
else puts 'Invalid command'
end
end

\A and \z match the start of the string and the end of the string, respectively.

Auto-complete command line arguments

This is an example of BASH's smart completion. A basic description is here, a guide to writing your own extensions is here and another (Debian-based) guide is here. And here's a fuller featured introduction to the complete command (the command that facilitates this behaviour).

Which testing technology to use for a command-line Ruby app?

Perhaps it might help to address HOW to write tests. There are lots of test frameworks, and lots of philosophies of how we should write tests but I try to keep it simple. I generally start with these:

  • Test to see I got back nil or an object first.
  • Test to see if the object is the right type.
  • Test to see if mandatory attributes are set, then if they're the right types.

Once I've got those out of the way I'll start antagonizing the code, throwing out-of-bounds and evil values at it, forcing it to raise its exceptions if it's supposed to do that.

Then, as further use/testing reveal bugs I'd add specific tests to check to see those don't reappear as I screw around with the code. ("Code screwing-around" is gonna happen, so its important I know I didn't make the program go out in flames.)

ZenTest has the autotest command which looks for a change in your test files and runs the tests automatically. It makes it really easy to make sure I haven't borked things, because in a separate console window autotest will be doing its thing each time I save. It's a great-big safety net you'll get used to having very quickly. From the docs:

autotest is a continous testing facility meant to be used during
development. As soon as you save a file, autotest will run the
corresponding dependent tests.

Writing tests are a necessary evil. They'll double your code-writing load, but they're very important to start early and continue maintaining. Trying to add them later to a large code base is a major problem, causing too many apps to never have unit tests. Icky.

Rookie - Ruby: Run file in terminal

Ruby programs generally use the '.rb' extension, so in order to run a ruby file that you've written, you need to save it somewhere with that extension first- eg. 'my-app.rb'.

It's a good idea when starting out to save it in a folder inside your "Home" directory (/Users/your user name/). You can find that in the mac "Finder" by clicking on the folder on the left hand list that's named "your username". In your terminal, your home directory is shortened to '~/' - and you can easily change directory into it with that shortcut:

cd ~

While I've been learning, I've stuck to a quick, short directory to store my files- '~/code/'. Anything will do, but it's much quicker to type 'cd ~/code/my-app.rb' than to type something long like 'cd ~/Documents/Programming/Ruby/my-app.rb' every time. So when you're deciding on where to save, think about how much you'll have to type in terminal! :)

Once you've saved your file, and used 'cd' to change into the directory you've saved it in, you use the command 'ruby' to run it.

ruby my-app.rb

That's about all there is to actually running your file! There's so much more to using the terminal, and writing code- but there's plenty of info out there on how to start.

I found Chris Pine's "Learn To Program" really simple and easy to follow. There are plenty of other resources out there, too! Try out Try Ruby to get going straight in your browser.

How can I do readline arguments completion?

After thinking a while, the solution was very simple:

comp = proc do |s| 
if Readline.line_buffer =~ /^.* /
COLLECT.grep( /^#{Regexp.escape(s)}/ )
else
COMMANDS.grep( /^#{Regexp.escape(s)}/ )
end
end

Now I just need to turn it into something more flexible/usable.

How to turn off snippets in Atom?

Sadly, there's currently no built-in feature for this kind of thing.

Until some filter feature is added to the snippets package, the only way to access the snippets is to monkey-patch the package from your init script.

For instance something like that will allow you to filter the snippets returned for a given editor at runtime:

# we need a reference to the snippets package
snippetsPackage = require(atom.packages.getLoadedPackage('snippets').path)

# we need a reference to the original method we'll monkey patch
__oldGetSnippets = snippetsPackage.getSnippets

snippetsPackage.getSnippets = (editor) ->
snippets = __oldGetSnippets.call(this, editor)

# we're only concerned by ruby files
return snippets unless editor.getGrammar().scopeName is 'source.ruby'

# snippets is an object where keys are the snippets's prefixes and the values
# the snippets objects
console.log snippets

newSnippets = {}
excludedPrefixes = ['your','prefixes','exclusion','list']

for prefix, snippet of snippets
newSippets[prefix] = snippet unless prefix in excludedPrefixes

newSnippets


Related Topics



Leave a reply



Submit