./lib/lux/error/error.rb in lux-fw-0.2.3 vs ./lib/lux/error/error.rb in lux-fw-0.5.32

- old
+ new

@@ -1,91 +1,193 @@ # frozen_string_literal: true -# 400: for bad parameter request or similar -BadRequestError ||= Class.new(StandardError) +# https://en.wikipedia.org/wiki/List_of_HTTP_status_codes -# 401: for unauthorized access -UnauthorizedError ||= Class.new(StandardError) +# default error handler for lux +# e = Lux::Error.new 404 +# e.code => 404 +# e.message => 'Not Found' +# +# e = Lux::Error.not_found('foo') +# e.code => 404 +# e.message => foo -# 403: for unalloed access -ForbidenError ||= Class.new(StandardError) +class Lux::Error < StandardError + class AutoRaise < Lux::Error + end -# 404: for not found pages -NotFoundError ||= Class.new(StandardError) + # https://httpstatuses.com/ + CODE_LIST ||= { + # 1×× Informational + 100 => { name: 'Continue' }, + 101 => { name: 'Switching Protocols' }, + 102 => { name: 'Processing' }, -# 503: for too many requests at the same time -RateLimitError ||= Class.new(StandardError) + # 2×× Success + 200 => { name: 'OK' }, + 201 => { name: 'Created' }, + 202 => { name: 'Accepted' }, + 203 => { name: 'Non-authoritative Information' }, + 204 => { name: 'No Content' }, + 205 => { name: 'Reset Content' }, + 206 => { name: 'Partial Content' }, + 207 => { name: 'Multi-Status' }, + 208 => { name: 'Already Reported' }, + 226 => { name: 'IM Used' }, -module Lux::Error - extend self + # 3×× Redirection + 300 => { name: 'Multiple Choices' }, + 301 => { name: 'Moved Permanently' }, + 302 => { name: 'Found' }, + 303 => { name: 'See Other' }, + 304 => { name: 'Not Modified' }, + 305 => { name: 'Use Proxy' }, + 307 => { name: 'Temporary Redirect' }, + 308 => { name: 'Permanent Redirect' }, - def try name=nil - begin - yield - rescue Exception => e - Lux.current.response.status 500 + # 4×× Client Error + 400 => { name: 'Bad Request', code: :bad_request }, + 401 => { name: 'Unauthorized', code: :unauthorized }, + 402 => { name: 'Payment Required', code: :payment_required }, + 403 => { name: 'Forbidden', code: :forbidden }, + 404 => { name: 'Document Not Found', code: :not_found }, + 405 => { name: 'Method Not Allowed', code: :method_not_allowed }, + 406 => { name: 'Not Acceptable', code: :not_acceptable }, + 407 => { name: 'Proxy Authentication Required' }, + 408 => { name: 'Request Timeout' }, + 409 => { name: 'Conflict' }, + 410 => { name: 'Gone' }, + 411 => { name: 'Length Required' }, + 412 => { name: 'Precondition Failed' }, + 413 => { name: 'Payload Too Large' }, + 414 => { name: 'Request-URI Too Long' }, + 415 => { name: 'Unsupported Media Type' }, + 416 => { name: 'Requested Range Not Satisfiable' }, + 417 => { name: 'Expectation Failed' }, + 418 => { name: 'I\'m a teapot' }, + 421 => { name: 'Misdirected Request' }, + 422 => { name: 'Unprocessable Entity' }, + 423 => { name: 'Locked' }, + 424 => { name: 'Failed Dependency' }, + 426 => { name: 'Upgrade Required' }, + 428 => { name: 'Precondition Required' }, + 429 => { name: 'Too Many Requests' }, + 431 => { name: 'Request Header Fields Too Large' }, + 444 => { name: 'Connection Closed Without Response' }, + 451 => { name: 'Unavailable For Legal Reasons' }, + 499 => { name: 'Client Closed Request' }, - key = log e + # 5×× Server Error + 500 => { name: 'Internal Server Error', code: :internal_server_error }, + 501 => { name: 'Not Implemented', code: :not_implemented }, + 502 => { name: 'Bad Gateway' }, + 503 => { name: 'Service Unavailable' }, + 504 => { name: 'Gateway Timeout' }, + 505 => { name: 'HTTP Version Not Supported' }, + 506 => { name: 'Variant Also Negotiates' }, + 507 => { name: 'Insufficient Storage' }, + 508 => { name: 'Loop Detected' }, + 510 => { name: 'Not Extended' }, + 511 => { name: 'Network Authentication Required' }, + 599 => { name: 'Network Connect Timeout Error' }, + } - if Lux.config(:show_server_errors) - inline name - else - name ||= 'Server error occured' - name += "\n\nkey: %s" % key - - Lux.error(name) + # e = Lux::Error.not_found('foo') + CODE_LIST.each do |status, data| + if data[:code] + define_singleton_method(data[:code]) do |message=nil| + error = new status, message + raise error if Lux::Error::AutoRaise === error + error end end end - def render data - %[<html><head><title>Server error (#{Lux.current.response.status})</title></head><body style="background:#fdd;"><pre style="color:red; padding:10px; font-size:14pt;">#{data.gsub('<','&lt;')}</pre></body></html>] - end + class << self + # template to show full error page + def render text, status=500 + Lux.current.response.status status + Lux.current.response.body Lux.config.server_error_template.call(text) + throw :done + end - def show desc=nil - Lux.current.response.status 500 - data = "Lux #{Lux.current.response.status} error\n\n#{desc}" - Lux.current.response.body! render(data) - end + # render error inline or break in production + def inline name, error=nil + error ||= $! - def inline name=nil, o=nil - o ||= $! + unless Lux.config(:dump_errors) + key = log error + render "Lux inline error: %s\n\nkey: %s" % [error.message, key] + end - dmp = [[], []] + name ||= 'Undefined name' + msg = error.message.to_s.gsub('","',%[",\n "]).gsub('<','&lt;') - o.backtrace.each do |line| - line = line.sub(Lux.root.to_s, '.') - dmp[line.include?('/app/') ? 0 : 1].push line + dmp = split_backtrace error + + dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ } + + log error + + <<~TEXT + <pre style="color:red; background:#eee; padding:10px; font-family:'Lucida Console'; line-height:15pt; font-size:11pt;"> + <b style="font-size:110%;">#{name}</b> + + <b>#{error}: #{msg}</b> + + #{dmp[0].join("\n")} + + #{dmp[1].join("\n")} + </pre> + TEXT end - dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ } + def report code, msg=nil + e = Integer === code ? Lux::Error.new(code) : Lux::Error.send(code) + e.message = msg if msg + raise e + end - name ||= 'Undefined name' - msg = $!.to_s.gsub('","',%[",\n "]).gsub('<','&lt;') + def log error + Lux.config.error_logger.call error + end - %[<pre style="color:red; background:#eee; padding:10px; font-family:'Lucida Console'; line-height:15pt; font-size:11pt;"><b style="font-size:110%;">#{name}</b>\n\n<b>#{msg}</b>\n\n#{dmp[0].join("\n")}\n\n#{dmp[1].join("\n")}</pre>] + def split_backtrace error + # split app log rest of the log + dmp = [[], []] + + root = Lux.root.to_s + + error.backtrace.each do |line| + line = line.sub(root, '.') + dmp[line[0,1] == '.' ? 0 : 1].push line + end + + dmp + end end - def log exception - return if Lux.env == 'test' - return if exception.class == LocalRaiseError - return unless Lux.current + ### - # .backtrace.reject{ |el| el.index('/gems/') } - history = exception.backtrace - .map{ |el| el.sub(Lux.root.to_s, '') } - .join("\n") + attr_accessor :message - data = '%s in %s (user: %s)' % [exception.class, Lux.current.request.url, (Lux.current.var.user.email rescue 'guest')] - data = [data, exception.message, history].join("\n\n") - key = Digest::SHA1.hexdigest exception.backtrace.first.split(' ').first + def initialize code_num, message=nil + self.code = code_num + @message = message || CODE_LIST[code_num][:name] + end - folder = Lux.root.join('log/exceptions').to_s - Dir.mkdir(folder) unless Dir.exists?(folder) - folder += "/#{exception.class.to_s.tableize.gsub('/','-')}" - Dir.mkdir(folder) unless Dir.exists?(folder) + def code + # 400 is a default + @code || 400 + end - File.write("#{folder}/#{key}.txt", data) + def code= num + @code = num.to_i - key + raise 'Status code %s not found' % @code unless CODE_LIST[@code] end + + def render + self.class.render message, code + end end +