Class: Sprout::Executable::Session

Inherits:
Base
  • Object
show all
Defined in:
lib/sprout/executable/session.rb

Overview

The Sprout::Daemon class exposes the Domain Specific Language provided by the Sprout::Executable, along with enhancements (and modifications) to support long-lived processes (like FDB and FCSH).


  ##
  # The Foo class extends Sprout::Daemon
  class Foo < Sprout::Daemon

    ##
    # Keep in mind that we're still working
    # with Executable, so add_param is available
    # for the initialization of the process.
    add_param :input, File

    ##
    # Expose the do_something action after
    # the process is started.
    add_action :do_something

    ##
    # Expose the do_something_else action after
    # the process is started.
    add_action :do_something_else
  end

You can also create a globally-accessible rake task to use your new Daemon instance by creating a method like the following:


  def foo *args, &block
    foo_tool = Foo.new
    foo_tool.to_rake *args, &block
  end

The previous Rake task could be used like:


  foo 'Bar.txt' do |t|
    t.do_something
    t.do_something_else
  end

Constant Summary

Constant Summary

Constants included from Sprout::Executable

DEFAULT_FILE_EXPRESSION, DEFAULT_PREFIX, DEFAULT_SHORT_PREFIX

Instance Attribute Summary (collapse)

Attributes inherited from Base

#abort_on_failure, #default_prefix, #default_short_prefix, #executable, #param_hash, #params, #pkg_name, #pkg_version, #prerequisites, #rake_task_name

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods inherited from Base

add_param, add_param_alias, #binary_path, #binary_path=, #default_file_expression, #execute_delegate, #from_hash, #initialize, #initialize_defaults, #library_added, #parse!, #parse_extra_options!, #parse_rake_task_arg, #prepare, set, static_default_value_collection, static_parameter_collection, #stderr, #stderr=, #stdout, #stdout=, #to_hash, #to_help, #to_rake, #to_shell

Methods included from Concern

#append_features, extended, #included

Constructor Details

This class inherits a constructor from Sprout::Executable::Base

Instance Attribute Details

- (Object) process_runner (readonly)

The Sprout::ProcessRunner that delegates to the long-running process, via stdin, stdout and stderr.



148
149
150
# File 'lib/sprout/executable/session.rb', line 148

def process_runner
  @process_runner
end

- (Object) process_thread (readonly)

The Thread that contains the forked running process.



152
153
154
# File 'lib/sprout/executable/session.rb', line 152

def process_thread
  @process_thread
end

- (Regexp) prompt

The prompt expression for this daemon process.

When executing a series of commands, the wrapper will wait until it matches this expression on stdout before continuing the series.

For FDB, this value is set like:


  set :prompt, /^\(fdb\) /

Most processes can trigger a variety of different prompts, these can be expressed here using the | (or) operator.

FDB actually uses the following:


  set :prompt, /^\(fdb\) |\(y or n\) /

Returns:

  • (Regexp)


142
143
144
# File 'lib/sprout/executable/session.rb', line 142

def prompt
  @prompt
end

Class Method Details

+ (nil) add_action(name, arguments = nil, options = nil)

Add an action that can be called while the long-lived process is active.

This method should raise a Sprout::Errors::UsageError if the provided action name is already defined for the provided instance.

Parameters:

  • name (Symbol, String)

    The name of the method.

  • arguments (Array<Object>) (defaults to: nil)

    An array of arguments that the method accepts.

  • options (Hash) (defaults to: nil)

    The options hash is reserved for future use.

    class Foo < Sprout::Daemon

    
      add_action :continue
    
      add_action :quit
    

    end

Returns:

  • (nil)


70
71
72
73
74
75
76
# File 'lib/sprout/executable/session.rb', line 70

def add_action name, arguments=nil, options=nil
  options ||= {}
  options[:name] = name
  options[:arguments] = arguments
  create_action_method options
  nil
end

+ (nil) add_action_alias(alias_name, source_name)

Create an (often shorter) alias to an existing action name.

Returns:

  • (nil)

See Also:



85
86
87
88
89
90
# File 'lib/sprout/executable/session.rb', line 85

def add_action_alias alias_name, source_name
  define_method(alias_name) do |*params|
    self.send(source_name, params)
  end
  nil
end

Instance Method Details

- (Array<Hash>) action_stack

Return or create a new array.

Returns:

  • (Array<Hash>)

    Return or create a new array.



156
157
158
# File 'lib/sprout/executable/session.rb', line 156

def action_stack
  @action_stack ||= []
end

- (Rake::Task) create_outer_task(*args) (protected)

This is the override of the underlying Sprout::Executable template method so that we create a ‘task’ instead of a ‘file’ task.

Returns:



250
251
252
253
254
# File 'lib/sprout/executable/session.rb', line 250

def create_outer_task *args
  Rake::Task.define_task *args do
    execute
  end
end

- (Object) execute(should_wait = true)

Execute the Daemon executable, followed by the collection of stored actions in the order they were called.

If none of the stored actions result in terminating the process, the underlying daemon will be connected to the terminal for user (manual) input.

You can also send wait=false to connect to a daemon process from Ruby and execute actions over time. This might look like:


   fdb = FlashSDK::FDB.new
   fdb.execute false

   # Do something else while FDB
   # is open, then:
   
   fdb.run
   fdb.break "AsUnitRunner:12"
   fdb.continue
   fdb.kill
   fdb.confirm
   fdb.quit

Parameters:

  • wait (Boolean)

    default true. Send false to connect to a daemon from Ruby code.



196
197
198
199
200
201
202
# File 'lib/sprout/executable/session.rb', line 196

def execute should_wait=true
  @process_runner = super()
  @process_launched = true
  wait_for_prompt
  execute_actions
  handle_user_input if should_wait
end

- (Object) handle_user_input

Expose the running process to manual input on the terminal, and write stdout back to the user.



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/sprout/executable/session.rb', line 225

def handle_user_input
  while true
    begin
      break if !wait_for_prompt
      input = $stdin.gets.chomp!
      execute_action(input, true)
    rescue SignalException => e
      return false
    end
  end
  wait
end

- (Boolean) process_launched? (protected)

Returns:



240
241
242
# File 'lib/sprout/executable/session.rb', line 240

def process_launched?
  @process_launched
end

- (Boolean) prompted?

If executable is awaiting input.

Returns:

  • (Boolean)

    If executable is awaiting input.



162
163
164
# File 'lib/sprout/executable/session.rb', line 162

def prompted?
  @prompted
end

- (Thread) system_execute(binary, params) (protected)

This is the override of the underlying Sprout::Executable template method so that we create the process in a thread in order to read and write to it.

Returns:

  • (Thread)


274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/sprout/executable/session.rb', line 274

def system_execute binary, params
  # Combine the stderr and stdout for long-lived
  # processes so that they are both written to
  # stdout, this allows us to collect these streams
  # without threads or blocking eternally.
  #
  # Thanks to https://github.com/apinstein for this
  # solution.
  #params = "#{params} " + '2>&1'
  @process_thread = Sprout.current_system.execute_thread binary, params, prompt do |message|
    yield message if block_given?
    Sprout.stdout.printf message
    @prompted = true if prompt.match message
  end
  @process_runner = process_thread['runner']
end

- (String) update_rake_task_name_from_args(*args) (protected)

This is the override of the underlying Sprout::Executable template method so that we are NOT added to the CLEAN collection. (Work performed in the Executable)

Returns:



263
264
265
# File 'lib/sprout/executable/session.rb', line 263

def update_rake_task_name_from_args *args
  self.rake_task_name = parse_rake_task_arg args.last
end

- (Object) wait



204
205
206
207
# File 'lib/sprout/executable/session.rb', line 204

def wait
  Process.wait process_runner.pid
rescue Errno::ECHILD
end

- (Object) wait_for_prompt

Wait for the underlying process to present an input prompt, so that another action can be submitted, or user input can be collected.



214
215
216
217
218
219
# File 'lib/sprout/executable/session.rb', line 214

def wait_for_prompt
  while process_thread.alive? && !prompted?
    sleep 0.2
  end
  process_thread.alive?
end