require 'singleton' module Perennial class Loader include Singleton include Perennial::Hookable cattr_accessor :controllers, :current_type, :default_type @@controllers = {} define_hook :before_run, :after_stop def self.register_controller(name, controller) return if name.blank? || controller.blank? name = name.to_sym @@controllers[name] = controller metaclass.class_eval(<<-RUBY, __FILE__, __LINE__ + 1) def #{name}? # def client? @@current_type == :#{name} # @@current_type == :client end # end RUBY end def self.run!(type = self.default_type, options = {}) @@current_type = type.to_sym self.instance.run!(options) end def self.stop!(force = false) self.instance.stop!(force) end def run!(options = {}) self.register_signals self.class.invoke_hooks! :before_setup Daemon.daemonize! if Settings.daemon? Logger.log_name = "#{@@current_type.to_s}.log" Logger.setup Settings.setup self.load_custom_code self.class.invoke_hooks! :before_run self.attempt_controller_action! :run, options end def stop!(force = false) if force || !@attempted_stop self.class.invoke_hooks! :before_stop self.attempt_controller_action! :stop self.class.invoke_hooks! :after_stop @attempted_stop = true end Daemon.cleanup! if Settings.daemon? end def current_controller @current_controller ||= begin c = @@controllers[@@current_type.to_sym] c.is_a?(String) ? eval(c) : c end end protected def load_custom_code # Attempt to load a setup file given it exists. begin config_dir = Settings.root / "config" setup_file = config_dir / "setup.rb" require(setup_file) if File.directory?(config_dir) && File.exist?(setup_file) rescue LoadError end # Load any existing handlers assuming we can find the folder handler_directory = Settings.root / "handlers" if File.directory?(handler_directory) Dir[handler_directory / "**" / "*.rb"].each do |handler| Perennial::Reloading.watch(handler, handler_directory) require handler end end end def register_signals loader = self.class %w(INT TERM).each do |signal| trap(signal) do loader.stop! exit end end end def attempt_controller_action!(action, *args) action = action.to_sym unless current_controller.blank? || !current_controller.respond_to?(action) method = current_controller.method(action) if method.arity == 0 method.call elsif method.arity < 0 || method.arity == args.size method.call(*args) else raise ArgumentError, "controller action #{action} requires #{method.arity} arguments, provided #{args.size}" end end end end end