./lib/lux/error/error.rb in lux-fw-0.5.37 vs ./lib/lux/error/error.rb in lux-fw-0.6.2
- old
+ new
@@ -8,186 +8,241 @@
# e.message => 'Not Found'
#
# e = Lux::Error.not_found('foo')
# e.code => 404
# e.message => foo
+module Lux
+ class Error < StandardError
+ class AutoRaise < Lux::Error
+ end
-class Lux::Error < StandardError
- class AutoRaise < Lux::Error
- end
+ # https://httpstatuses.com/
+ CODE_LIST ||= {
+ # 1×× Informational
+ 100 => { name: 'Continue' },
+ 101 => { name: 'Switching Protocols' },
+ 102 => { name: 'Processing' },
- # https://httpstatuses.com/
- CODE_LIST ||= {
- # 1×× Informational
- 100 => { name: 'Continue' },
- 101 => { name: 'Switching Protocols' },
- 102 => { name: 'Processing' },
+ # 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' },
- # 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' },
+ # 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' },
- # 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' },
+ # 4×× Client Error
+ 400 => { name: 'Bad Request', short: :bad_request },
+ 401 => { name: 'Unauthorized', short: :unauthorized },
+ 402 => { name: 'Payment Required', short: :payment_required },
+ 403 => { name: 'Forbidden', short: :forbidden },
+ 404 => { name: 'Document Not Found', short: :not_found },
+ 405 => { name: 'Method Not Allowed', short: :method_not_allowed },
+ 406 => { name: 'Not Acceptable', short: :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' },
- # 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' },
+ # 5×× Server Error
+ 500 => { name: 'Internal Server Error', short: :internal_server_error },
+ 501 => { name: 'Not Implemented', short: :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' },
+ }
- # 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' },
- }
+ # e = Lux::Error.not_found('foo')
+ CODE_LIST.each do |status, data|
+ if data[:short]
+ define_singleton_method(data[:short]) do |message=nil|
+ error = Lux::Error.new status, message
- # 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
+ if self == Lux::Error::AutoRaise
+ Lux.current.response.status status
+ Lux.log " error.#{data[:code]} in #{Lux.app_caller}"
+ raise error
+ end
+
+ error
+ end
end
end
- 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
+ class << self
+ # template to show full error page
+ def render error
+ error = StandardError.new(error) if error.is_a?(String)
- # render error inline or break in production
- def inline name, error=nil
- error ||= $!
+ code = error.respond_to?(:code) ? error.code : 500
+ Lux.current.response.status code
- unless Lux.config(:dump_errors)
- key = log error
- render "Lux inline error: %s\n\nkey: %s" % [error.message, key]
+ Lux.current.response.body(
+ HtmlTag.html do |n|
+ n.head do |n|
+ n.title 'Lux error'
+ end
+ n.body style: "margin: 20px 20px 20px 140px; background-color:#fdd; font-size: 14pt; font-family: sans-serif;" do |n|
+ n.img src: "https://i.imgur.com/Zy7DLXU.png", style: "width: 100px; position: absolute; margin-left: -120px;"
+ n.h4 do |n|
+ n.push %[HTTP Error — <a href="https://httpstatuses.com/#{code}" target="http_error">#{code}</a>]
+ n.push %[ ‐ #{error.name}] if error.respond_to?(:name)
+ end
+ n.push inline error
+ end
+ end
+ )
end
- name ||= 'Undefined name'
- msg = error.message.to_s.gsub('","',%[",\n "]).gsub('<','<')
+ # render error inline
+ def inline object, msg = nil
+ error, message = if object.is_a?(String)
+ [nil, object]
+ else
+ [object, object.message]
+ end
- dmp = split_backtrace error
+ error_key = error ? log(error) : nil
+ message = message.to_s.gsub('","',%[",\n "]).gsub('<','<')
- dmp[0] = dmp[0].map { |_| _ = _.split(':', 3); '<b>%s</b> - %s - %s' % _ }
+ HtmlTag.pre(class: 'lux-inline-error', style: 'background: #fff; margin-top: 10px; padding: 10px; font-size: 14px; border: 2px solid #600; line-height: 20px;') do |n|
+ n.h3 '%s : %s' % [error.class, message]
+ n.p msg if msg
+ n.p 'Key: %s' % error_key if error_key
+ n.p 'Description: %s' % error.description if error && error.respond_to?(:description) && error.description
- log error
+ if error && Lux.env.show_errors?
+ n.hr
+ n.push mark_backtrace(error, html: true).join("\n")
+ end
+ end
+ end
- <<~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>
+ # clear osx screen :)
+ def clear_screen time = 0
+ last = (Thread.current[:_lux_clear_screen] ||= 1.day.ago)
+ if last < Time.now - time
+ Thread.current[:_lux_clear_screen] = Time.now
+ print "\e[H\e[2J\e[3J"
+ end
+ end
- <b>#{error}: #{msg}</b>
+ # prepare backtrace for better render
+ def split_backtrace error
+ # split app log rest of the log
+ dmp = [[error.class, error.message], [], []]
- #{dmp[0].join("\n")}
+ root = Lux.root.to_s
- #{dmp[1].join("\n")}
- </pre>
- TEXT
- end
+ (error.backtrace || caller).each do |line|
+ line = line.sub(root, '.')
+ dmp[line[0,1] == '.' ? 1 : 2].push line
+ end
- def report code, msg=nil
- e = Integer === code ? Lux::Error.new(code) : Lux::Error.send(code)
- e.message = msg if msg
- raise e
- end
+ dmp
+ end
- def log error
- Lux.config.error_logger.call error
- end
+ def mark_backtrace error, html: false
+ return ['no backtrace present'] unless error && error.backtrace
- def split_backtrace error
- # split app log rest of the log
- dmp = [[], []]
+ root = Lux.root.to_s
- root = Lux.root.to_s
+ error
+ .backtrace
+ .map do |line|
+ path = line.split(':in').first
+ path = path.sub(/^\.\//, root+'/')
- error.backtrace.each do |line|
- line = line.sub(root, '.')
- dmp[line[0,1] == '.' ? 0 : 1].push line
+ edit = html ? %[ • <a href="subl://open?url=file:/#{path}">edit</a>] : ''
+ line = line.sub(root, '.')
+ (line[0,1] != '/' ? (html ? line.tag(:b) : line) : line) + edit
+ end
end
- dmp
+ # show in stdout
+ def screen error
+ return unless Lux.env.show_errors?
+
+ data = split_backtrace(error)
+ if error.class == Lux::Error
+ Lux.info "Lux error: #{error.message} (#{error.code}) - #{data[1][0]}"
+ else
+ data[2] = data[2][0,5]
+ ap data
+ end
+ end
+
+ def log err
+ if Lux.config.error_logger
+ Lux.config.error_logger.call err
+ end
+ end
end
- end
- ###
+ ###
- attr_accessor :message
+ attr_accessor :message
- def initialize code_num, message=nil
- self.code = code_num
- @message = message || CODE_LIST[code_num][:name]
- end
+ def initialize *args
+ if args.first.is_a?(Integer)
+ self.code = args.shift
+ end
- def code
- # 400 is a default
- @code || 400
- end
+ self.message = args.shift || name
+ end
- def code= num
- @code = num.to_i
+ def code
+ # 400 is a default
+ @code || 400
+ end
- raise 'Status code %s not found' % @code unless CODE_LIST[@code]
- end
+ def name
+ CODE_LIST[code][:name]
+ end
- def render
- self.class.render message, code
+ def code= num
+ @code = num.to_i
+
+ raise 'Status code %s not found' % @code unless CODE_LIST[@code]
+ end
end
end
-