class Egalite::Handler
Attributes
routes[RW]
Public Class Methods
new(opts = {})
click to toggle source
# File lib/egalite.rb, line 406 def initialize(opts = {}) @routes = opts[:routes] || Route.default_routes db = opts[:db] @db = db @opts = opts @env = Environment.new(db, opts) opts[:static_root] ||= "static/" @template_path = opts[:template_path] || 'pages/' @template_path << '/' if @template_path[-1..-1] != '/' @template_engine = opts[:template_engine] || HTMLTemplate @profile_logger = opts[:profile_logger] @notfound_template = opts[:notfound_template] if opts[:error_template_file] @error_template = File.open(opts[:error_template_file]).read else @error_template = opts[:error_template] end @admin_emails = opts[:admin_emails] @email_from = opts[:email_from] @exception_log_table = opts[:exception_log_table] if @exception_log_table Egalite::ErrorLogger.table = db[@exception_log_table] Egalite::ErrorLogger.admin_emails = @admin_emails Egalite::ErrorLogger.email_from = @email_from end end
Public Instance Methods
call(rack_env)
click to toggle source
# File lib/egalite.rb, line 702 def call(rack_env) # set up logging res = nil req = Rack::Request.new(rack_env) begin ereq = Request.new( :rack_request => req, :rack_env => rack_env, :handler => self ) # parameter handling params = stringify_hash(req.params) puts "before-cookie: #{req.cookies.inspect}" if @opts[:cookie_debug] ereq.params = params ereq.cookies = req.cookies authorization_keys = ['HTTP_AUTHORIZATION', 'X-HTTP_AUTHORIZATION', 'X_HTTP_AUTHORIZATION'] key = authorization_keys.detect { |key| rack_env.has_key?(key) } ereq.authorization = rack_env[key] if key if @opts[:session_handler] ereq.session = @opts[:session_handler].new(@env,ereq.cookies, @opts[:session_opts] || {}) ereq.session.load end res = dispatch(req.path_info, params, req.request_method, ereq, true) res = res.to_a puts "after-cookie: #{res[1]['Set-Cookie'].inspect}" if @opts[:cookie_debug] if res[0] == 200 if res[1]['Content-Type'] !~ /charset/i and res[1]['Content-Type'] =~ /text\/html/i res[1]["Content-Type"] = @opts[:charset] || 'text/html; charset=utf-8' end end rescue Exception => e raise e if $raise_exception begin # write error log logid = nil if @exception_log_table and not e.is_a?(UserError) logid = ErrorLogger.write_exception(e,{:ipaddress => req.ip, :url => req.url}) end # show exception if @error_template values = {} values[:logid] = logid if logid values[:exception] = e.to_s values[:backtrace] = e.backtrace values[:message] = e.message if e.is_a?(UserError) or e.is_a?(SystemError) values[:usererror] = true if e.is_a?(UserError) html = @template_engine.new.handleTemplate(@error_template.dup,values) res = [500, {"Content-type"=>"text/html; charset=utf-8"}, [html]] else res = display_internal_server_error(e) end rescue Exception => e2 res = display_internal_server_error(e2) end end res = res.to_a p res if @opts[:response_debug] res end
dispatch(path, params, method, req, first_call = false)
click to toggle source
# File lib/egalite.rb, line 645 def dispatch(path, params, method, req, first_call = false) # routing (controller_name, action_name, path_params, prmhash) = nil (controller, action) = nil route = @routes.find { |route| puts "Routing: matching: #{route.inspect}" if RouteDebug route_result = route.match(path) (controller_name, action_name, path_params, prmhash) = route_result next if route_result == nil puts "Routing: pre-matched: #{route_result.inspect}" if RouteDebug (controller, action) = get_controller(controller_name, action_name, method) true if controller } return display_notfound unless controller puts "Routing: matched: #{controller.class} #{action}" if RouteDebug params = prmhash.merge(params) req.route = route req.controller = controller_name req.controller_class = controller req.action = action_name req.action_method = action req.inner_path = path req.path_params = path_params req.path_info = path_params.join('/') res = run_controller(controller, action, req) if first_call controller.after_filter(res.to_a) # access log t = Time.now - req.time log = [req.time.iso8601, req.ipaddr, t, req.url, req.referrer] log += controller.log_values.to_a line = log.map {|s| s.to_s.gsub(/\t/,'')}.join("\t").gsub(/\n/,'') AccessLogger.write(line) end res end
inner_dispatch(req, values)
click to toggle source
# File lib/egalite.rb, line 544 def inner_dispatch(req, values) # recursive controller call to handle include tag or delegate. stringified = StringifyHash.create(values) (path, params) = req.route.get_path_and_params_from_params(stringified) newreq = req.clone newreq.params = params method = 'GET' method = values[:http_method] if values[:http_method] dispatch(path, params, method, newreq) end
run_controller(controller, action, req)
click to toggle source
# File lib/egalite.rb, line 554 def run_controller(controller, action, req) # invoke controller controller.env = @env controller.req = req controller.params = req.params before_filter_result = controller.before_filter if before_filter_result != true return before_filter_result if before_filter_result.is_a?(Array) return [200,{'Content-Type' => "text/html"},[before_filter_result]] if before_filter_result.is_a?(String) return forbidden unless before_filter_result.respond_to?(:command) response = case before_filter_result.command when :delegate inner_dispatch(req, before_filter_result.param) when :redirect redirect(before_filter_result.param) when :notfound display_notfound else forbidden end set_cookies_to_response(response,req) return response end nargs = controller.method(action).arity args = req.path_params[0,nargs.abs] || [] if nargs > 0 args.size.upto(nargs-1) { args << nil } end raise SecurityError unless controller.respond_to?(action, false) s = Time.now values = controller.send(action,*args) t = Time.now - s @profile_logger.puts "#{Time.now}: ctrl #{t}sec #{controller.class.name}.#{action} (#{req.path_info})" if @profile_logger values = controller.after_filter_return_value(values) # result handling result = if values.respond_to?(:command) case values.command when :delegate inner_dispatch(req, values.param) when :redirect redirect(values.param) when :notfound display_notfound end elsif values.is_a?(Array) values elsif values.is_a?(String) html = controller.after_filter_html(values) [200,{'Content-Type' => "text/html"},[html]] elsif values.is_a?(Rack::Response) values.to_a elsif values == nil raise "egalite error: controller returned nil as a response." else htmlfile = controller.template_file unless htmlfile htmlfile = [req.controller,req.action].compact.join('_') htmlfile = 'index' if htmlfile.blank? htmlfile += '.html' end html = load_template(@template_path + htmlfile) return [404, {"Content-Type" => "text/plain"}, ["Template not found: #{htmlfile}\n"]] unless html # apply on_html_load filter html = controller.filter_on_html_load(html, htmlfile) # apply html template template = @template_engine.new template.controller = controller s = Time.now template.handleTemplate(html,values) { |values| inner_dispatch(req,values)[2].join("") } t = Time.now - s html = controller.after_filter_html(html) @profile_logger.puts "#{Time.now}: view #{t}sec #{controller.class.name}.#{action} (#{req.path_info})" if @profile_logger [200,{"Content-Type"=>"text/html"},[html]] end set_cookies_to_response(result,req) return result end
Private Instance Methods
display_internal_server_error(e)
click to toggle source
# File lib/egalite.rb, line 503 def display_internal_server_error(e) html = [ "<html><body>", "<h1>Internal server error.</h1>" ] if ShowException html += [ "<p>Exception: #{escape_html(e.to_s)}</p>", "<h2>Back trace</h2>", "<p>#{e.backtrace.map{|s|escape_html(s)}.join("<br/>\n")}</p>" ] end html << "</body></html>" [500, {'Content-Type' => 'text/html'}, [html.join("\n")]] end
display_notfound()
click to toggle source
# File lib/egalite.rb, line 450 def display_notfound if @notfound_template [404, {"Content-Type" => "text/html"}, [load_template(@notfound_template)]] else [404, {"Content-Type" => "text/plain"}, ['404 not found']] end end
escape_html(s)
click to toggle source
# File lib/egalite.rb, line 446 def escape_html(s) Rack::Utils.escape_html(s) end
forbidden()
click to toggle source
# File lib/egalite.rb, line 499 def forbidden [403, {'Content-Type' => 'text/plain'}, ['Forbidden']] end
get_controller(controllername,action, method)
click to toggle source
# File lib/egalite.rb, line 461 def get_controller(controllername,action, method) # HTTPメソッドと一致するアクション名は却下する(メソッド名で受け付けるべき) # /controller/get みたいな名前でgetがパスパラメータとして渡るべき # といいつつ後方互換性を考慮してとりあえずget/post/headだけ却下 return nil if %w[get post head].include?(action&.downcase) action = method if action.blank? action.downcase! action.gsub!(/[^0-9a-z_]/,'') return nil if action == "" Controller.new.methods.each { |s| return nil if action.downcase == s.to_s } controllername ||= '' controllername = controllername.split('/').map { |c| c.downcase! # 英数字以外が含まれるものはコントローラーとはみなさない。 return nil if c =~ /[^0-9a-z]/ c.capitalize }.join controllername = 'Default' if controllername.blank? kontroller = Object.const_get(controllername+'Controller') rescue nil return nil unless kontroller controller = kontroller.new method = method.downcase unless controller.respond_to?(action) if controller.respond_to?("#{action}_#{method}") action = "#{action}_#{method}" else return nil end end [controller, action] end
load_template(tmpl)
click to toggle source
# File lib/egalite.rb, line 437 def load_template(tmpl) # to expand: template caching if File.file?(tmpl) && File.readable?(tmpl) open(tmpl) { |f| f.read } else nil end end
redirect(url)
click to toggle source
# File lib/egalite.rb, line 457 def redirect(url) url = url.gsub(/\r/, "%0D").gsub(/\n/, "%0A") [302,{'Location' => url}, [url]] end
stringify_hash(params)
click to toggle source
# File lib/egalite.rb, line 689 def stringify_hash(params) sh = StringifyHash.new params.each { |k,v| if v.is_a?(Hash) sh[k] = stringify_hash(v) else sh[k] = v end } sh end