Code Error
A code-based customized error.
raise MyError.new(10002)
Why?
It is a standard error but it provides more.
- All error cases are defined in the same place. Easy to find and reference.
- Just raising an error with a code, then the corresponding status and message are ready to use.
- Highly customizable and extendable, you can even re-define the default messages to what you want.
Usage
Basic
Inherit CodeError::Base to create your own code-based error. You need to implement the "error_codes" method which provides a hash to define your own error code map.
class MyError < CodeError::Base
private
def error_codes
{
20001 => {
status: :failed,
msg: 'Purchase information format is incorrect.'
},
20002 => {
status: :failed,
msg: 'Device information format is incorrect.'
},
20003 => {
status: :failed,
msg: 'Unknown store.'
},
20100 => {
status: :duplicated,
msg: 'A duplicated IAP request is sent.'
},
20200 => {
status: :retry,
msg: 'Client should send the IAP request again.'
}
}
end
end
Raise an error with a code when you need.
raise MyError.new(20001)
Rescue and handle it.
begin
#...
raise MyError.new(20001)
#...
rescue MyError => e
raise e if e.internal?
msg = e.msg
code = e.code
info = e.info
data = e.data
case(e.status)
when :failed
#...
when :duplicated
#...
else
#...
end
#...
end
Customize error code hash
A customized code-based error class need to implement the "error_codes" method which provides a hash like:
def error_codes
{
20001 => {
status: :failed,
msg: 'Purchase information format is incorrect.'
},
20002 => {
status: :failed,
msg: 'Device information format is incorrect.'
},
20003 => {
status: :failed,
msg: 'Unknown store.'
},
20100 => {
status: :duplicated,
msg: 'A duplicated IAP request is sent.',
masked: true
},
20200 => {
status: :retry,
msg: 'Client should send the IAP request again.'
}
}
end
where keys are the supported codes and each value is an another hash to store the code information corresponding each codes. The code information hash contains the following keys:
- :status - [any type you like] The error status to define the error handling flow. You can get it by calling the "status" method.
- :msg - [String] The error message. You can get it by calling the "msg" method.
- :masked - (optional)[Boolean] To define the error message is masked by default or not. The default value is false if no
:maskedis given.
Raise a code-based error
Once you define the error_codes method, raising a code-based error is easy.
raise MyError.new(20001)
There are three argments in the new method, MyError.new(code, msg, info). If the given code is defined in the error_codes, the error would contain the corresponding status and msg.
e = MyError.new(20001)
e.code # 20001
e.status # :failed
e.msg # "Purchase information format is incorrect.(20001)"
You can give another string in "msg" arg to overwrite the original message:
e = MyError.new(20001, "The another message which would override the original one.")
e.code # 20001
e.status # :failed
e.msg # "The another message which would override the original one.(20001)"
If the given code is unknown, the error would become an internal error, i.e., the status is the :internal status.
e = MyError.new(1111)
e.code # 1111
e.status # :internal
e.msg # "An internal error occurs.(1111)"
e.internal? # true
If a string is given, the error is also an internal error, but the "msg" would keep this string.
e = MyError.new("Invalid input!!")
e.code # 99999
e.status # :internal
e.msg # "Invalid input!!"(99999)
e.internal? # true
You can pass anything to the error handling you want through the "info" arg.
something = 'what you want...'
e = MyError.new(20001, nil, something)
e.info # 'what you want...'
There is a special code :success which used in the success case.
e = MyError.new(:success)
e.code # 0
e.status # :success
e.msg # ""
e.internal? # false
Handle the code-base error
When you rescue a code-based error, some useful methods are ready to use.
- code - The error code
- status - The error status
- msg - The error message
- info - The error information
- data - A hash contains the values of code, status, msg, info.
- internal? - To show this error is an internal error or not.
Here is an example:
e = MyError.new(20001, nil, 'some information')
e.code # 20001
e.status # :failed
e.msg # "Purchase information format is incorrect.(20001)"
e.info # "some information"
e.data # { code: 20001, status: :failed, msg: "Purchase information format is incorrect.(20001)", info: 'some information' }
e.internal? # false
Configure your code-base error
You can override the config method in your code-based error to customize the default behavior. Here is an example:
class MyError < CodeError::Base
private
def error_codes
#...
end
def config
super.merge({
success: {
code: CodeError::SUCCESS_CODE,
status: CodeError::SUCCESS_STATUS,
msg: "My own success message."
},
internal: {
code: CodeError::INTERNAL_CODE,
status: CodeError::INTERNAL_STATUS,
msg: "My own internal error message."
},
masked_msg: "My own masked message",
append_code_in_msg: :none
})
end
end
where the config hash contains the following keys:
- :success - Define the success code, status and message.
- :internal - Define the internal error code, status and message.
- :masked_msg - [String] Define the message to replace the masked one.
- :code_in_msg - [:append/:prepend/:none] Define how to embed code in the message. The default option is :append.
Mask the error message
You can mask an error message if you don't want to show it. A masked message would be replaced with the default masked message. There are several ways to do it, they are listed below sorted by the precedence.
Pass
trueto the "masked" arg in the "msg" or "data" method.e = MyError.new(20001) e.msg(ture) # "An error occurs.(20001)" e.data(true) # { code: 20001, status: :failed, msg: "An error occurs.(20001)", info: {} }
Pass the
:maskedto the "msg" arg in the "new" method.e = MyError.new(20001, :masked) e.msg # "An error occurs.(20001)"
Add
masked: truein the error_codes hash for particular error. Please see [Customize error code hash].
The default masked message can be customized to your own message by overwriting the :masked_msg in your config hash. Please see [Configure your code-base error].
I18n
We don't support the i18n for simpleness but you can do it yourself. Here is an example:
class MyError < CodeError::Base
private
def error_codes
{
20001 => {
status: :failed,
msg: 'code_error.my_error.purchase_info_incorrect'
},
20002 => {
status: :failed,
msg: 'code_error.my_error.device_info_incorrect'
},
20003 => {
status: :failed,
msg: 'code_error.my_error.unknown_store'
},
20100 => {
status: :duplicated,
msg: 'code_error.my_error.duplicated_request'
},
20200 => {
status: :retry,
msg: 'code_error.my_error.retry'
}
}
end
def config
super.merge({
success: {
code: CodeError::SUCCESS_CODE,
status: CodeError::SUCCESS_STATUS,
msg: 'code_error_my_error.success_msg'
},
internal: {
code: CodeError::INTERNAL_CODE,
status: CodeError::INTERNAL_STATUS,
msg: 'code_error_my_error.internal_msg'
},
masked_msg: 'code_error_my_error.masked_msg',
append_code_in_msg: :append
})
end
end
def (code, msg, masked = false)
masked = masked || @masked || false
m = masked ? I18n.t(config[:masked_msg]) : I18n.t(msg)
code_in_msg = config[:code_in_msg]
if code != config[:success][:code]
if == :append
m = I18n.t('code_error.my_error.append_code_msg', msg: m, code: code)
elsif == :prepand
m = I18n.t('code_error.my_error.prepend_code_msg', msg: m, code: code)
end
end
m
end
And the i18n yml file is:
en:
code_error:
my_error:
append_code_msg: "%{msg}(%{code})"
prepend_code_msg: "(%{code})%{msg}"
purchase_info_invalid: "Purchase information format is incorrect."
device_info_invalid: "Device information format is incorrect."
unknown_store: "Unknown Store."
duplicated_request: "A duplicated IAP request is sent."
retry: "Client should send the IAP request again."
Test
Go to code_error gem folder and run
ruby ./test/test_all.rb
Authors
Sibevin Wang
Copyright
Copyright (c) 2013 Sibevin Wang. Released under the MIT license.