# code: # * George Moschovitis # # (c) 2004 Navel, all rights reserved. # $Id: handlers.rb 99 2004-10-22 09:50:28Z gmosx $ require "n/utils/cache" require "n/server/filter" module N; module App # = Handler # # App server handlers handle requests to specific resources. # Handlers are a special kinf of filters so the can be chained # in the rendering pipeline. # # === Design: # # Handlers are NOT singleton classes. This way we can assign one # handler class to multiple resources, and keep statistics and # metrics for each resource. # class Handler < N::ServerFilter # Perform the actual work of the handler # Typically calls the next filter in the pipeline afterwards. # def process(request) # nop # walk the filter pipeline @next_filter.process(request) if @next_filter end end # class # = Handler Error # # Raise this if an error happens when handling a request # class HandlerError < StandardError; end # = Script Handler # # Base handler for scripts. All othere script handlers are extensions # from this class. # class ScriptHandler < Handler # cache the compiled page scripts to optimize future references. # use a thread safe cache. @@compiled_script_cache = N::SafeHash.new() # dont allow 2 threads to compile the same script. In fact dont allow # two threads to compile in parallel. @@compile_sync = Sync.new # Overload the path. # FIXME: better name and much better documentation. # def overload_path(path) path.gsub!(/\/\//, '/') if ::File.exists?("#$root_dir/#{path}") return path else $log.debug "OVERLOAD: '#{path}' -> 'p/#{path}'" if $DBG return "p/#{path}" end end # the compiler returns a singleton class customized for rendering the # input script. The original idea was to define render() as a static # method and return the class, but we will use a singleton object to # keep custom data structures (sub-page-graph, metrics, etc) # # script_path is used as key in the Dynamic class creation and for # caching # # === Design: # we use __ for out too to avoid nasty collisions. # def compile_script(script) compiled_script = nil # dont allow 2 threads to compile the same script. In fact dont # allow two threads to compile in parallel. # gmosx: SOS this eval assigns the variable compiled_script! @@compile_sync.synchronize { eval(script) } return compiled_script end def compiled_script_cache return @@compiled_script_cache end # Log a rendering error. The accumulated log is available # for rendering in the offending page when in Debug mode. # def log_error(request, ex) request.log_error "--------" request.log_error "#{request.path}:" request.log_error "#{ex.class}: #{ex}" request.log_error ex.backtrace() raise ScriptHandlerError.new(0, "error") end end # =ScriptHandlerError # # Raise this if an error happens when handling a script. # class ScriptHandlerError < HandlerError attr_reader :error_line def initialize(error_line, message = nil) @error_line, @message = error_line, message end end end; end # module