./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('<','<')}</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('<','<')
- 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('<','<')
+ 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
+