class Af::Application

Abstract superclass for implementing command line applications.

Provides:

* Command line option parsing.
* Logging with Log4r.
* Pre and post option processes hooks.
* Proxy support access to application frame functionality from other classes

Subclasses must implement:

* work

Subclasses can implement:

* pre_work

Attributes

has_errors[RW]

+++++++++++++++++

Public Class Methods

_run(*arguments) click to toggle source

Run this application with the provided arguments that must adhere to configured command line switches. It rewrites ARGV with these values.

Example

instance._run("-v", "--file", "foo.log")

Arguments

* arguments - list of command line option strings
# File lib/fiksu-af/application.rb, line 125
def self._run(*arguments)
  # this ARGV hack is here for test specs to add script arguments
  ARGV[0..-1] = arguments if arguments.length > 0
  self.new._run
end
new() click to toggle source
Calls superclass method
# File lib/fiksu-af/application.rb, line 201
def initialize
  super
  # sort of a singleton -- if forced to it really means "last instantiated and current"
  @@singleton = self
  set_connection_application_name(startup_database_application_name)
  $stdout.sync = true
  $stderr.sync = true
  opt :log_configuration_search_path, :default => [".", Rails.root + "config/logging"]
  opt :log_configuration_files, :default => ["af.yml", "#{af_name}.yml"]
  opt :log_stdout, :default => Rails.root + "log/runner.log"
  opt :log_stderr, :default => Rails.root + "log/runner-errors.log"
end
run(*arguments) click to toggle source

Instantiate and run the application.

Arguments

- arguments - ????
# File lib/fiksu-af/application.rb, line 96
def self.run(*arguments)
  application = self.new._run(*arguments)
  application._work
end
singleton(safe = false) click to toggle source

Return the single allowable instance of this class.

Arguments

* safe - defaults to false, instantiates instance if it doesn't exist
# File lib/fiksu-af/application.rb, line 105
def self.singleton(safe = false)
  if @@singleton.nil?
    if safe
      @@singleton = new
    else
      fail("Application @@singleton not initialized! Maybe you are using a Proxy before creating an instance? or use SafeProxy")
    end
  end
  return @@singleton
end

Public Instance Methods

_run() click to toggle source

Run the application, fetching and parsing options from the command line.

Arguments

* usage - string describing usage (optional)
* options - hash of options, containing ???
# File lib/fiksu-af/application.rb, line 141
def _run
  process_command_line_options(af_opt_class_path)
  post_command_line_parsing
  pre_work
  return self
end
_work() click to toggle source

Execute the actual work of the application upon execution.

This method is used to wrap the actual run code with whatever specific code we are looking to maintain the execution context.

one can imagine overlaoding this function with something call initiates a profiler or debugger

# File lib/fiksu-af/application.rb, line 156
def _work
  begin
    work
  rescue SystemExit => se
    # we do nothing here
    if se.status != 0
      logger.error "exit called with error: #{se.message}"
      logger.warn se
      exit se.status
    end
  rescue SignalException => e
    logger.error "receiving signal: #{e.message}"
    logger.warn e
    @has_errors = true
  rescue Exception => e
    # RSpec occasionally uses a special exception when running tests to verify test
    # results. To ensure that exception passes through to RSpec, we catch it
    # specifically and re-raise it if needed.
    raise e if e.class.name == "RSpec::Mocks::MockExpectationError"

    # catching Exception cause some programs and libraries suck
    logger.error "fatal error during work: #{e.message}"
    logger.warn e
    @has_errors = true
  end

  if @gc_profiler
    logger("GC::Profiler").info GC::Profiler.result
  end

  exit @has_errors ? 1 : 0
end
af_name() click to toggle source

Accessor for the af name set on the instance’s class.

# File lib/fiksu-af/application.rb, line 190
def af_name
  return self.class.name
end
af_opt_class_path() click to toggle source

override if you wish to include other class’s opt/opt_group

# File lib/fiksu-af/application.rb, line 195
def af_opt_class_path
  return [self.class]
end

Protected Instance Methods

cleanup_after_fork() click to toggle source
# File lib/fiksu-af/application.rb, line 293
def cleanup_after_fork
  ActiveRecord::Base.connection.reconnect!
end
database_application_name() click to toggle source

Accessor for the application name set on the ActiveRecord database connection.

# File lib/fiksu-af/application.rb, line 229
def database_application_name
  return self.class.startup_database_application_name
end
periodic_application_checkpoint() click to toggle source

call this every once in a while

# File lib/fiksu-af/application.rb, line 316
def periodic_application_checkpoint
  if @gc_profiler
    if (Time.zone.now - @last_gc_profiler_dump) > @gc_profiler_interval_minutes.minutes
      @last_gc_profiler_dump = Time.zone.now
      logger("GC::Profiler").info GC::Profiler.result
    end
  end
end
post_command_line_parsing() click to toggle source
# File lib/fiksu-af/application.rb, line 238
def post_command_line_parsing
  # Iterate through all option_checks and option_selects in the class hierarchy.
  # Create instance variables and accessor methods for each.
  option_finder = OptionFinder.new(af_opt_class_path)

  option_checks = option_finder.all_option_checks
  option_selects = option_finder.all_option_selects

  begin
    option_checks.each do |option_check|
      option_check.validate
    end

    option_selects.each do |option_select|
      option_select.validate
    end
  rescue GetoptLong::Error, Error => e
    opt_error e.message
  end
end
pre_work() click to toggle source

Overload to do any operations that need to be handled before work is called. Call exit if needed. Always call super.

# File lib/fiksu-af/application.rb, line 261
def pre_work
  logging_configurator.configurate

  if @gc_profiler
    logger("GC::Profiler").info "Enabling GC:Profilier"
    logger("GC::Profiler").info "Signal USR1 will dump results"
    logger("GC::Profiler").info "Will dump every #{@gc_profiler_interval_minutes} minutes"
    GC::Profiler.enable
    @last_gc_profiler_dump = Time.zone.now
    Signal.trap("USR1") do
      logger("GC::Profiler").info GC::Profiler.result
    end
  end

  if @daemon
    $stdout.reopen(Af::Logging::Configurator.log_stdout, "a")
    $stderr.reopen(Af::Logging::Configurator.log_stderr, "a")
    $stdout.sync = true
    $stderr.sync = true
    logger.info "Daemonizing"
    pid = fork
    if pid
      exit 0
    else
      logger.info "forked"
      Process.setsid
      trap 'SIGHUP', 'IGNORE'
      cleanup_after_fork
    end
  end
end
protect_from_signals() { || ... } click to toggle source

Utility method to wrap code in a protective sheen use with “signal_list”

# File lib/fiksu-af/application.rb, line 304
def protect_from_signals
  # we are indiscriminate with the signals we block -- too bad ruby doesn't have some
  # reasonable signal management system
  signals = Hash[signal_list.map {|signal| [signal, Signal.trap(signal, "IGNORE")] }]
  begin
    yield
  ensure
    signals.each {|signal, saved_value| Signal.trap(signal, saved_value)}
  end
end
set_connection_application_name(name) click to toggle source

Set the application name on the ActiveRecord connection. It is truncated to 64 characters.

Arguments

* name - application name to set on the connection
# File lib/fiksu-af/application.rb, line 219
def set_connection_application_name(name)
  ActiveRecord::ConnectionAdapters::ConnectionPool.initialize_connection_application_name(name[0...63])
end
signal_list() click to toggle source

Returns a list of OS signals.

# File lib/fiksu-af/application.rb, line 298
def signal_list
  return Signal.list.keys
end
startup_database_application_name() click to toggle source

Application name consisting of process PID and af name.

# File lib/fiksu-af/application.rb, line 224
def startup_database_application_name
  return "//pid=#{Process.pid}/#{af_name}"
end
work() click to toggle source

Work performed by this application. MUST be implemented by subclasses.

# File lib/fiksu-af/application.rb, line 234
def work
  raise NotImplemented.new("#{self.class.name}#work must be implemented to use the Application framework")
end