Class: Tap::Tasks::Prompt

Inherits:
Stream show all
Includes:
Utils
Defined in:
lib/tap/tasks/prompt.rb

Overview

:startdoc::task open a prompt

Prompt reads signals from the input until a signal that returns the app is reached (ex run/stop) or the source io is closed.

% tap prompt
/set 0 load
/set 1 dump
/build join 0 1
/enq 0 'goodnight moon'
/run
goodnight moon

Prompts can be registered to a control signal (ex INT) so that that a running app may be interrupted, interrogated, or modified. This infinite loop can be stopped using ctl-C and a prompt.

% tap dump '.' - join 0 0 -q - prompt --on INT
.
.
.
(ctl-C)
/stop

Instance Attribute Summary

Attributes inherited from Tap::Task

#joins

Attributes inherited from App::Api

#app

Instance Method Summary collapse

Methods included from Utils

set_env, sh, sh_escape, shellsplit, with_env

Methods inherited from Stream

#reque

Methods inherited from Load

#close, #open, #open_file

Methods inherited from Tap::Task

#associations, #call, #enq, #exe, #log, #on_complete, parser

Methods inherited from App::Api

#associations, build, help, inherited, #inspect, parse, parse!, parser, #to_spec

Methods included from Signals

#sig, #signal?, #signals

Methods included from Signals::ModuleMethods

included

Constructor Details

#initialize(*args) ⇒ Prompt

Returns a new instance of Prompt.



38
39
40
41
# File 'lib/tap/tasks/prompt.rb', line 38

def initialize(*args)
  super
  trap(on) if on
end

Instance Method Details

#complete?(io, result) ⇒ Boolean

Returns:

  • (Boolean)


123
124
125
# File 'lib/tap/tasks/prompt.rb', line 123

def complete?(io, result)
  result == app
end

#load(io) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/tap/tasks/prompt.rb', line 97

def load(io)
  line = readline(io)
  return nil if line.empty?
  
  begin
    sig, *args = shellsplit(line)
    app.call('sig' => sig, 'args' => args)
  rescue
    $!
  end
end

#process(io = $stdin) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/tap/tasks/prompt.rb', line 83

def process(io=$stdin)
  app.set(variable, self) if variable
  
  result = super(io)
  unless file || result.nil? || result == app
    open_io(terminal) do |terminal|
      terminal.puts result
    end
  end
  
  app.set(variable, nil) if variable
  result
end

#readline(io) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/tap/tasks/prompt.rb', line 109

def readline(io)
  if io == $stdin && terminal == $stdout
    return Readline.readline(prompt, true)
  end
  
  if prompt && !file
    open_io(terminal) do |terminal|
      terminal.print prompt
    end
  end
  
  io.eof? ? '' : io.readline.strip!
end

#signal(sig) ⇒ Object



75
76
77
78
79
80
81
# File 'lib/tap/tasks/prompt.rb', line 75

def signal(sig)
  lambda do |spec|
    app.build('class' => sig, 'spec' => spec) do |obj, args|
      obj.call(args)
    end
  end
end

#trap(sig) ⇒ Object

Traps interrupt the normal flow of the program and so I assume thread safety is an issue (ex if the INT occurs during an enque and a signal specifies another enque). A safer way to go is to enque the prompt… when the prompt is executed the app won’t be be doing anything else so thread safety shouldn’t be an issue.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/tap/tasks/prompt.rb', line 48

def trap(sig)
  ::Signal.trap(sig) do
    puts
    puts "Interrupt! Signals from an interruption are not thread-safe."
    
    call_prompt = true
    3.times do
      print "Wait for thread-safe break? (y/n): "

      case gets.strip
      when /^y(es)?$/i
        puts "waiting for break..."
        app.pq(self, [])
        call_prompt = false
        break

      when /^no?$/i
        break
      end
    end

    if call_prompt
      call([])
    end
  end
end