# = Code handler (.sx scripts) # # code: # George Moschovitis # # (c) 2004 Navel, all rights reserved. # $Id: code-handler.rb 71 2004-10-18 10:50:22Z gmosx $ require "cgi" require "singleton" require "sync" require "n/utils/cache" require "n/utils/uri" require "n/app/script" require "n/app/fragment" require "n/app/handlers" module N; module App # = CodeHandler # # web server handler that executes ruby code (logic) # Caching is NOT SUPPORTED (and not needed anyway) # # TODO: # # probably extend PageHandler from CodeHandler and not vice versa. # class CodeHandler < ScriptHandler # process is called ONLY for top level scripts. # # no need for synchronization, the page-script is thread safe. # if you use thread-unsafe code in your script you are responsible # for synchronization. # # TODO: # add timing code here # def process(request) # gmosx, FIXME: temporarily here, move somewhere ELSE! request.locale = $lc_map[request.user.locale] || $lc_en script = get_compiled_script(request.path) fragment = eval_script(script, request) # walk the filter pipeline super return fragment, script end # evaluate the request script in the context of the current request. # returns the output of the script, ie the fragment body. # # === Design: # # I think that the script caching logic should be here, because it nicelly # encapsulates transform and compile. # def eval_script(script, request) $log.debug "Evaluating, #{request.path}" if $DBG fragment = Fragment.new(:key) # try to render the script, report errors. begin fragment.body = script.__render(request) rescue N::ScriptExitException => see # the script raised a ScripteExitException to force a premature # end. Surpress this error! rescue => ex log_error(request, ex) end return fragment end # === Output: # defcode: the code that customizes the base script class. # pagecode: the code that renders the fragment. # # === REMARKS: # - this method is evaluated in compile time, so we cannot pass # or use the request/request pair. gmosx: NO, we do pass request, # some data can and SHOULD be used to prepare the transform. # def transform_script(path, hash, shader = nil) path = overload_path(path) # load the page text from the script pagecode = "" pagecode = File.read("#$root_dir/#{path}") # convert to ruby code pagecode.gsub!(/<<>>/, "}") script = %{ class CodeScript_#{hash} < CodeScript def initialize(path) super @pagecode = <<-'PAGECODE' #{pagecode} PAGECODE @defcode = nil end def __render(request) __out = "" lc = request.locale #{pagecode} return __out end end return CodeScript_#{hash}.new("#{path}") } return script end # try to get the script from the cache. invalidates the compiled version and return nil # if the script is modified since compile time. Also takes active shader and localization # into account. If script is not compiled, transform and compile it. # # Output: # the compiled script. Throws exception if the script does not exist. # def get_compiled_script(path, hash = nil) compiled_script = nil key = calc_script_key(path, hash) # gmosx: $reload_scripts is typically set to true when debugging # statically included files (.ss) unless $reload_scripts compiled_script = @@compiled_script_cache[key] # gmosx: monitor scripts should be explicit! if $srv_monitor_scripts and compiled_script and (File.mtime(compiled_script.path) > compiled_script.__create_time) $log.debug "Script '#{path}' externaly modified, recompiling" if $DBG compiled_script = nil end end unless compiled_script # the script is not cached, so load, transform and compile it! $log.debug "Compiling script '#{key}'" if $DBG script = transform_script(path, key) compiled_script = compile_script(script) @@compiled_script_cache[key] = compiled_script end return compiled_script end def calc_script_key(path, hash) return "#{path}#{hash}".gsub(/[^\w]/, "") end end # CodeHandler # = Codescript # # encapsulates the script defining Ruby Code to execute. # effectively acts as a Generator. # # === Requirements: # # - the generated class should be a singleton, no need to stress # the garbage collector. # - the render method should be thread safe. # class CodeScript < Script end # CodeScript end; end # module