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
+++++++++++++++++
Public Class Methods
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
# 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
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
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 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
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
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
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
# File lib/fiksu-af/application.rb, line 293 def cleanup_after_fork ActiveRecord::Base.connection.reconnect! end
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
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
# 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
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
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 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
Returns a list of OS signals.
# File lib/fiksu-af/application.rb, line 298 def signal_list return Signal.list.keys end
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 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