class Knj::Eruby attr_reader :connects, :error, :headers, :cookies, :fcgi def initialize(args = {}) @args = args @tmpdir = "#{Knj::Os.tmpdir}/knj_erb" Dir.mkdir(@tmpdir, 0777) if !File.exists?(@tmpdir) #This argument can be used if a shared cache should be used to speed up performance. if @args[:cache_hash] @cache = @args[:cache_hash] else @cache = {} end if RUBY_PLATFORM == "java" or RUBY_ENGINE == "rbx" @cache_mode = :code_eval #@cache_mode = :compile_knj elsif RUBY_VERSION.slice(0..2) == "1.9" and RubyVM::InstructionSequence.respond_to?(:compile_file) @cache_mode = :code_eval #@cache_mode = :inseq #@cache_mode = :compile_knj end if @cache_mode == :compile_knj require "#{$knjpath}compiler" @compiler = Knj::Compiler.new(:cache_hash => @cache) end self.reset_headers self.reset_connects end def import(filename) @error = false Dir.mkdir(@tmpdir) if !File.exists?(@tmpdir) filename = File.expand_path(filename) raise "File does not exist: #{filename}" if !File.exists?(filename) filetime = File.mtime(filename) cachename = "#{@tmpdir}/#{filename.gsub("/", "_").gsub(".", "_")}.cache" cachetime = File.mtime(cachename) if File.exists?(cachename) begin if !File.exists?(cachename) or filetime > cachetime Knj::Eruby::Handler.load_file(filename, {:cachename => cachename}) File.chmod(0777, cachename) cachetime = File.mtime(cachename) reload_cache = true elsif !@cache.key?(cachename) reload_cache = true end case @cache_mode when :compile_knj @compiler.eval_file(:filepath => cachename, :fileident => filename) when :code_eval if @args[:binding_callback] binding_use = @args[:binding_callback].call else eruby_binding = Knj::Eruby::Binding.new binding_use = eruby_binding.get_binding end @cache[cachename] = File.read(cachename) if reload_cache eval(@cache[cachename], binding_use, filename) when :inseq if reload_cache or @cache[cachename][:time] < cachetime @cache[cachename] = { :inseq => RubyVM::InstructionSequence.compile(File.read(cachename), filename, nil, 1), :time => Time.now } end @cache[cachename][:inseq].eval else loaded_content = Knj::Eruby::Handler.load_file(filename, {:cachename => cachename}) print loaded_content.evaluate end rescue SystemExit #do nothing. rescue Exception => e @error = true self.handle_error(e) end end def destroy @connects.clear if @connects.is_a?(Hash) @headers.clear if @headers.is_a?(Array) @cookies.clear if @cookies.is_a?(Array) @cache.clear if @cache.is_a?(Hash) and @args and !@args.key?(:cache_hash) @args.clear if @args.is_a?(Hash) @args = nil @cache = nil @connects = nil @headers = nil @cookies = nil end def print_headers(args = {}) header_str = "" @headers.each do |header| header_str << "#{header[0]}: #{header[1]}\n" end @cookies.each do |cookie| header_str << "Set-Cookie: #{Knj::Web.cookie_str(cookie)}\n" end header_str << "\n" self.reset_headers if @fcgi return header_str end def has_status_header? @headers.each do |header| return true if header[0] == "Status" end return false end def reset_connects @connects = {} end def reset_headers @headers = [] @cookies = [] end def header(key, value) @headers << [key, value] end def cookie(cookie_data) @cookies << cookie_data end def connect(signal, &block) @connects[signal] = [] if !@connects.key?(signal) @connects[signal] << block end def printcont(tmp_out, args = {}) if @fcgi @fcgi.print self.print_headers tmp_out.rewind @fcgi.print tmp_out.read.to_s else if args[:io] and !args[:custom_io] old_out = $stdout $stdout = args[:io] elsif !args[:custom_io] $stdout = STDOUT end if !args[:custom_io] print self.print_headers if !args.key?(:with_headers) or args[:with_headers] tmp_out.rewind print tmp_out.read end end end def load_return(filename, args = {}) if !args[:io] retio = StringIO.new args[:io] = retio end self.load_filename(filename, args) if !args[:custom_io] retio.rewind return retio.read end end def load_filename(filename, args = {}) begin if !args[:custom_io] tmp_out = StringIO.new $stdout = tmp_out end self.import(filename) if @connects["exit"] @connects["exit"].each do |block| block.call end end self.printcont(tmp_out, args) rescue SystemExit => e self.printcont(tmp_out, args) rescue Exception => e self.handle_error(e) self.printcont(tmp_out, args) end end #This method will handle an error without crashing simply adding the error to the print-queue. def handle_error(e) begin if @connects and @connects.key?("error") @connects["error"].each do |block| block.call(e) end end rescue SystemExit => e exit rescue Exception => e #An error occurred while trying to run the on-error-block - show this as an normal error. print "\n\n<pre>\n\n" print "<b>#{Knj::Web.html(e.class.name)}: #{Knj::Web.html(e.message)}</b>\n\n" e.backtrace.each do |line| print "#{Knj::Web.html(line)}\n" end print "</pre>" end print "\n\n<pre>\n\n" print "<b>#{Knj::Web.html(e.class.name)}: #{Knj::Web.html(e.message)}</b>\n\n" e.backtrace.each do |line| print "#{Knj::Web.html(line)}\n" end print "</pre>" end end class Knj::Eruby::Handler < Erubis::Eruby include Erubis::StdoutEnhancer end class Knj::Eruby::Binding def get_binding return binding end end