Skip to content

Commit

Permalink
Updated documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebayes committed Dec 23, 2010
1 parent 195ed63 commit ad379f8
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 43 deletions.
66 changes: 39 additions & 27 deletions lib/sprout/daemon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
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.
#
# class Foo
# include Sprout::Executable
# include Sprout::Daemon
# ##
# # The Foo class extends Sprout::Daemon
# class Foo < Sprout::Daemon
#
# ##
# # Keep in mind that we're still working
Expand All @@ -32,27 +28,21 @@ module Sprout
# 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
#
class Daemon < Executable::Base

class << self
Expand All @@ -64,20 +54,39 @@ class << self
# 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
Expand Down Expand Up @@ -127,16 +136,13 @@ def accessor_can_be_defined_at name
#
# set :prompt, /^\(fdb\) |\(y or n\) /
#
# @return [Regexp]
attr_accessor :prompt

##
# This is the array of actions that have
# been provided at the class level to this instance.
attr_reader :action_stack

def initialize
super
@action_stack = []
# @return [Array<Hash>] Return or create a new array.
def action_stack
@action_stack ||= []
end

##
Expand All @@ -161,6 +167,8 @@ def execute
# 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
Expand All @@ -172,6 +180,8 @@ def create_outer_task *args
# 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
Expand All @@ -181,6 +191,8 @@ def update_rake_task_name_from_args *args
# 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
Expand Down
79 changes: 63 additions & 16 deletions lib/sprout/process_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,48 @@ module Sprout
# The ProcessRunner is a cross-platform wrapper for executing
# external executable processes.
#
# As it turns out, Ruby handle differences very well, and
# other process libraries (like win32-open3 and open4.popen4),
# do make the experience more consistent on a given platform,
# but they don't hide the differences introduced by the
# continuing beligerence of Windows or *nix (depending on
# which side of the fence you're on).
# This class is typically accesses via the concrete Sprout::System
# classes in order to avoid ugly branching logic in the application
# layer.
#
# An example of this kind of usage might be:
#
# Sprout.current_system.execute './some.exe', '--foo=bar --baz=buz'
#
# To use this class directly, you need to know if you're on
# a unix-like system or a dos platform as these two deal with
# processes very differently.
#
# Assuming you know you're on a unix-like system, you could
# execute the previous example with:
#
# runner = Sprout::ProcessRunner.new
# runner.execute_open4 './some.exe --foo-bar --baz=buz'
# puts runner.read
#
class ProcessRunner

attr_reader :pid
attr_reader :ruby_version

##
# Read IO (readable)
# @return [IO] Read only
attr_reader :r

##
# Write IO (writeable)
# @return [IO] Write only
attr_reader :w

##
# Error IO (readable)
# @return [IO] Error output
attr_reader :e

def initialize
super
@ruby_version = RUBY_VERSION
end

##
# Execute the provided command using the open4.popen4
# library. This is generally only used by Cygwin and
# *nix variants (including OS X).
Expand All @@ -44,27 +58,36 @@ def execute_open4 *command
end
end

##
# Execute the provided command using the win32-open3
# library. This is generally used even by 64-bit
# Windows installations.
# library. This is generally used only only Windows
# systems (even 64 bit).
def execute_win32(*command)
execute_with_block *command do
@pid, @w, @r, @e = io_popen_block *command.join(' ')
end
end

##
# @return [Boolean] whether the process is still running.
def alive?
@alive = update_status
end

##
# Kill the process.
def kill
Process.kill(9, pid)
end

##
# Close the process
def close
update_status
end

##
# Send an update signal to the process.
def update_status
pid_int = Integer("#{ @pid }")
begin
Expand All @@ -75,38 +98,62 @@ def update_status
end
end

def readpartial(count)
@r.readpartial(count)
##
# Read +count+ characters from the process standard out.
#
# @param count [Integer] Number of characters to read.
# @return [String]
def readpartial count
@r.readpartial count
end

def readlines
@r.readlines
##
# Read +count+ lines from the process standard out.
#
# @param count [Integer] Number of lines to read.
# @return [String]
def readlines count
@r.readlines count
end

##
# Flush the write IO to the process.
def flush
@w.flush
end

##
# Get user input on the read stream from the process.
def getc
@r.getc
end

def print(msg)
##
# Print some characters to process without an end of line character.
def print msg
@w.print msg
end

##
# Print characters to the process followed by an end of line.
def puts(msg)
@w.puts(msg)
end

##
# Close the write stream - usually terminates the process.
def close_write
@w.close_write
end

##
# Wait for the process to end and return the entire standard output.
def read
return @r.read
end

##
# Wait for the process to end and return the entire standard error.
def read_err
return @e.read
end
Expand Down

0 comments on commit ad379f8

Please sign in to comment.