lib/sprout/daemon.rb in sprout-1.0.35.pre vs lib/sprout/daemon.rb in sprout-1.1.2.pre
- old
+ new
@@ -1,22 +1,18 @@
module Sprout
##
- # The Sprout::Daemon module exposes the Domain Specific Language
- # provided by the Sprout::Executable module, but with
+ # 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).
#
- # NOTE: Any class that includes Sprout::Daemon should first include
- # Sprout::Executable. This is an admittedly smelly constraint, but it works
- # for now.
+ # ##
+ # # The Foo class extends Sprout::Daemon
+ # class Foo < Sprout::Daemon
#
- # class Foo
- # include Sprout::Executable
- # include 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
@@ -30,61 +26,69 @@
# # 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 expose
- # your new Daemon instance to Rake by creating a method like the following:
+ # 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
#
- # Note: The aforementioned rake helper is usually written at the bottom of
- # the class file that is referenced.
- #
- # The aforementioned Rake task could be used like:
+ # The previous Rake task could be used like:
#
- # foo :bar do |t|
+ # foo 'Bar.txt' do |t|
# t.do_something
# t.do_something_else
# end
#
- # @see: Sprout::Daemon::ClassMethods
- # @see: Sprout::Daemon::InstanceMethods
- #
- module Daemon
+ class Daemon < Executable::Base
- extend Concern
+ class << self
- ##
- # These are the methods that will be available
- # on any class that includes Sprout::Daemon.
- module ClassMethods
-
##
# 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.
+ #
+ # @param name [Symbol, String] The name of the method.
+ # @param arguments [Array<Object>] An array of arguments that the method accepts.
+ # @param options [Hash] The options hash is reserved for future use.
+ #
+ # class Foo < Sprout::Daemon
+ #
+ # add_action :continue
+ #
+ # add_action :quit
+ # end
+ #
+ # @return [nil]
def add_action name, arguments=nil, options=nil
options ||= {}
options[:name] = name
options[:arguments] = arguments
create_action_method options
+ nil
end
##
- # Create an (often shorter) alias to another
+ # Create an (often shorter) alias to an existing
# action name.
+ #
+ # @return [nil]
+ #
+ # @see add_action
def add_action_alias alias_name, source_name
define_method(alias_name) do |*params|
self.send(source_name, params)
end
+ nil
end
private
##
@@ -100,10 +104,11 @@
define_method(name) do |*params|
action = name.to_s
action = "y" if name == :confirm # Convert affirmation
action << " #{params.join(' ')}" unless params.nil?
action_stack << action
+ execute_actions if process_launched?
end
end
##
# TODO: Raise an exception if the name is
@@ -111,146 +116,181 @@
def accessor_can_be_defined_at name
end
end
+
##
- # Instance methods that will be available on any
- # class that includes Sprout::Daemon.
- module InstanceMethods
+ # 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\) /
+ #
+ # @return [Regexp]
+ attr_accessor :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\) /
- #
- attr_accessor :prompt
- ##
- # This is the array of actions that have
- # been provided at the class level to this instance.
- attr_reader :action_stack
+ ##
+ # The Sprout::ProcessRunner that delegates to the long-running process,
+ # via stdin, stdout and stderr.
+ attr_reader :process_runner
- def initialize
- super
- @action_stack = []
- end
+ ##
+ # @return [Array<Hash>] Return or create a new array.
+ def action_stack
+ @action_stack ||= []
+ end
- ##
- # 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.
- def execute
- runner = super
- execute_actions runner
- handle_user_session runner
- Process.wait runner.pid
- end
+ ##
+ # 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
+ #
+ # @param wait [Boolean] default true. Send false to
+ # connect to a daemon from Ruby code.
+ #
+ def execute should_wait=true
+ @process_runner = super()
+ @process_launched = true
+ wait_for_prompt
+ execute_actions
+ handle_user_session if should_wait
+ wait if should_wait
+ end
- protected
+ def wait
+ Process.wait process_runner.pid
+ end
- ##
- # This is the override of the underlying
- # Sprout::Executable template method so that we
- # create a 'task' instead of a 'file' task.
- def create_outer_task *args
- task *args do
- execute
+ ##
+ # Wait for the underlying process to present
+ # an input prompt, so that another action
+ # can be submitted, or user input can be
+ # collected.
+ def wait_for_prompt expected_prompt=nil
+ expected_prompt = expected_prompt || prompt
+ line = ''
+
+ while process_runner.alive? do
+ #puts ">> ERROR #{process_runner.readpartial_err 1}"
+
+ return false if process_runner.r.eof?
+ char = process_runner.readpartial 1
+ line << char
+ if char == "\n"
+ line = ''
end
+ Sprout.stdout.printf char
+ Sprout.stdout.flush
+ return true unless line.match(expected_prompt).nil?
end
+ end
- ##
- # 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)
- def update_rake_task_name_from_args *args
- self.rake_task_name = parse_rake_task_arg args.last
+ ##
+ # Expose the running process to manual
+ # input on the terminal, and write stdout
+ # back to the user.
+ def handle_user_session
+ while !process_runner.r.eof?
+ input = $stdin.gets.chomp!
+ execute_action input, true
+ wait_for_prompt
end
+ end
- ##
- # 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.
- def system_execute binary, params
- Sprout.current_system.execute_thread binary, params
- end
+ protected
- private
+ def process_launched?
+ @process_launched
+ end
- ##
- # Execute the collection of provided actions.
- def execute_actions runner
- action_stack.each do |action|
- if wait_for_prompt runner
- Sprout::Log.puts action
- execute_action runner, action
- end
- end
+ ##
+ # This is the override of the underlying
+ # Sprout::Executable template method so that we
+ # create a 'task' instead of a 'file' task.
+ #
+ # @return [Rake::Task]
+ def create_outer_task *args
+ task *args do
+ execute
end
+ end
- ##
- # Execute a single action.
- def execute_action runner, action
- runner.puts action.strip
- end
+ ##
+ # 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)
+ #
+ # @return [String]
+ def update_rake_task_name_from_args *args
+ self.rake_task_name = parse_rake_task_arg args.last
+ end
- ##
- # Expose the running process to manual
- # input on the terminal, and write stdout
- # back to the user.
- def handle_user_session runner
- while !runner.r.eof?
- if wait_for_prompt runner
- input = $stdin.gets.chomp!
- execute_action runner, input
- end
- end
- end
+ ##
+ # 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.
+ #
+ # @return [Thread]
+ def system_execute binary, params
+ Sprout.current_system.execute_thread binary, params
+ end
- ##
- # Wait for the underlying process to present
- # an input prompt, so that another action
- # can be submitted, or user input can be
- # collected.
- def wait_for_prompt runner, expected_prompt=nil
- ##
- # TODO: This should also check for a variety of prompts...
- expected_prompt = expected_prompt || prompt
- line = ''
+ private
- while runner.alive? do
- Sprout::Log.flush
- return false if runner.r.eof?
- char = runner.readpartial 1
- line << char
- if char == "\n"
- line = ''
- end
- Sprout::Log.printf char
- return true if line.match expected_prompt
- end
+ ##
+ # Execute the collection of provided actions.
+ def execute_actions
+ action_stack.each do |action|
+ break unless execute_action(action)
end
+ @action_stack = []
+ end
+ ##
+ # Execute a single action.
+ def execute_action action, silence=false
+ action = action.strip
+ Sprout.stdout.puts(action) unless silence
+ process_runner.puts action
+ wait_for_prompt
end
+
end
end