15

I want make interactive application where user launches it and can do various task by typing commands (some kind of shell)

example:

./myapp.rb  
App says Hi  
Commands:   
  help - display help about command
  open - open task
  do - do action
Start>help open
  open <TaskName>
  opens specified task
Start>open Something  
Something>do SomeAction
  Success!
Something> (blinking cursor here)

I searched but couldn't find any ruby gems that I could use specially for console interaction, so I'm about to my make my own...

I looked at Thor, but that's not exactly as I want, maybe I could use it, but not sure...

it could look something like:

class Tasks
  attr_reader :opened_task

  desc "open <TaskName>", "opens specified task"
  def open(params)
  end

  desc "do <ActionName>", "do specified action"
  def do(params)
  end
end

tasks = Tasks.new
# theoretical Console class
console = Console.new
console.addCommand("open",tasks.method(:open),"open task")
console.addCommand("do",tasks.method(:do),"do action")
console.start("%s>",[*tasks.opened_task])

so my question is, what gems I could use to make such console class? maybe someone have already made something similar? I plan using HighLine for input/output, but any other suggestion what could I use?

6
  • Should it be ruby or your own syntax? Commented Mar 24, 2012 at 19:02
  • what you mean by ruby or own syntax? :| if you mean by using IRB, then that's not an option... Commented Mar 24, 2012 at 19:11
  • 1
    Why not? It gives you Turing-completeness for free. Commented Mar 24, 2012 at 19:15
  • 1
    because it executes ruby code, but in my case, there's allowed only some specific "Commands", typing incorrect syntax shouldn't throw exceptions and also there shouldn't be way to redefine or override those commands... Commented Mar 24, 2012 at 19:26
  • Is it a security issue? And you can hook into pry for syntax errors. And redefining shouldn't be a problem, as you need to know how it works. As you expect your users to use a command line instead of a GUI in the current time of touchy feely stuff, I would trust them with a bit of power. But it's your choice, have fun reimplementing a lot of stuff instead of calling pry on an object. And you get an API for free. Commented Mar 24, 2012 at 20:44

6 Answers 6

20

What you want is a REPLRead → 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.

Sign up to request clarification or add additional context in comments.

5 Comments

okay thanks for showing one of way how I could implement console class method "start" :)
@davispuh I forgot about the prompt – updated answer with an improved implementation. Your start method should just contain the loop.
I accepted this answer, because it recommends making it myself by showing part of implementation so I'll make my own CLI, specially for all my needs...
Is Console a class that comes with Ruby? If so, what do I require to get it?
@LironYahdav no, that class is part of an API I made up just for this answer. Imagine that Console contains all your application's code related to interfacing with a terminal emulator and that prompt is a reader method which returns the REPL's configured prompt.
5

You could also try ripl. (from the documentation): Creating and starting a custom shell is as simple as:

require 'ripl'
# Define plugins, load files, etc...
Ripl.start

There is a comprehensive list of plugins for ripl as well as list of console applications using ripl on the projects website.

4 Comments

this looks more promising, but still need do some configuration, and again it have same "features" I don't need as pry/irb, also I think there need pretty much changes/configuration...
To be honest, I didn't use ripl so far - but writing your own solution from the ground up doesn't seem to be the best idea. Just consider movement with arrow key - without readline support users will be swearing.
in windows, command history is working (Up/Down arrows), I just need to make auto-completion with tab, but at start I might even live without that...
@davispuh It is not just about history and completion. Readline provides you with emacs/vi-like bindings in the shell which a lot of users are very habituated to and take for granted because they are available in Bash/Zsh by default.
5

ok, so I made this library for creating console applications in ruby. Actually it was some while ago, but only just decided to release it. It does support auto-completion if used with HighLine and Readline.

When I wrote it there wasn't any documentation nor tests/specs, but now I made some. Still not much but for beginning should be ok.

So gem cli-console and code is at GitHub, here's usage example

Comments

5

TTY is a really good gem for easily doing this sort of things. You have plenty of tools which can work alone or with the full toolKit. You can use colors, prompts, execute shell natives, interact with the screen, print tables, progressbars and many other useful elements of command lines whith the easy of a goop api.

Particularly tty-prompt is really useful for asking for user input.

A brief example for the case you proposed:

require 'tty-prompt'
require 'pastel'

prompt = TTY::Prompt.new
loop do
  cmd, parms* = prompt.ask('user@machine$ ').split /\s/
  case cmd
    when "hola"
      puts "Hola amigo " parms
    when "exit"
      break if prompt.yes?('Do you really want to exit?')
  end
end

1 Comment

It's not exactly what I was looking for, this is a library which could be used to implement such console as you showed. Also by the way when I asked this question then it didn't exist yet then :D I implemented my CLI-Console using github.com/JEG2/highline which does something very similar.
4

Take a look at cliqr ruby gem. It looks like exactly what you need. Here is the github link with a descriptive readme: https://github.com/anshulverma/cliqr

It can execute the commands directly or within a inbuilt shell.

Here is a test case from its git repo:

    it 'can execute a sub action from shell' do
      cli = Cliqr.interface do
        name 'my-command'
        handler do
          puts 'base command executed'
        end

        action :foo do
          handler do
            puts 'foo executed'
          end

          action :bar do
            handler do
              puts 'bar executed'
            end
          end
        end
      end

      with_input(['', 'my-command', 'foo', 'foo bar', 'foo bar help']) do
        result = cli.execute %w(my-command shell), output: :buffer
        expect(result[:stdout]).to eq <<-EOS
Starting shell for command "my-command"
my-command > .
base command executed
my-command > my-command.
base command executed
my-command > foo.
foo executed
my-command > foo bar.
bar executed
my-command > foo bar help.
my-command foo bar

USAGE:
    my-command foo bar [actions] [options] [arguments]

Available options:

    --help, -h  :  Get helpful information for action "my-command foo bar" along with its usage information.

Available actions:
[ Type "my-command foo bar help [action-name]" to get more information about that action ]

    help -- The help action for command "my-command foo bar" which provides details and usage information on how to use the command.
my-command > exit.
shell exited with code 0
        EOS
      end
    end

Comments

2
class MyAPI
  def self.__is__(text)
    @__is__ = text
  end

  def self.method_added(method)
    @__help__ ||= {}
    @__help__[method.to_s] = @__is__
    @__is__ = nil
  end

  def self.help(of)
    @__help__[of]
  end

  __is__ "open file <file>"
  def open(file)
    #...
  end

  __is__ "do X"
  def do(*params)
    #...
  end

  __is__ "calls help, use help <command>"
  def help(*args, &block)
    self.class.help(*args, &block)
  end
end

MyAPI.new(...).pry

Or you could use pry commands, but that defeats the turing-completeness. Help might be implemented using commands, as I'm not sure how well my approach works out. Those methods need to be coded defensive. I can't remember how to use class variables :-/

3 Comments

it requires a lot of changes/configuration to get it working as I need... also I would be using it for task that it wasn't made for and it have "features" I don't even need...
@davispuh, have you given cliqr (above) a shot? It does not require much configuration and pretty much works out of the box.
@nuaavee when I wrote this question it didn't exsist then yet, but yea it looks something very similar to what I needed then only 3 years earlier than it was created :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.