lib/airbrake-ruby/promise.rb
module Airbrake
# Represents a simplified promise object (similar to promises found in
# JavaScript), which allows chaining callbacks that are executed when the
# promise is either resolved or rejected.
#
# @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise
# @see https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent/promise.rb
# @since v1.7.0
class Promise
def initialize
@on_resolved = []
@on_rejected = []
@value = {}
@mutex = Mutex.new
end
# Attaches a callback to be executed when the promise is resolved.
#
# @example
# Airbrake::Promise.new.then { |response| puts response }
# #=> {"id"=>"00054415-8201-e9c6-65d6-fc4d231d2871",
# # "url"=>"http://localhost/locate/00054415-8201-e9c6-65d6-fc4d231d2871"}
#
# @yield [response]
# @yieldparam response [Hash<String,String>] Contains the `id` & `url` keys
# @return [self]
def then(&block)
@mutex.synchronize do
if @value.key?('ok')
yield(@value['ok'])
return self
end
@on_resolved << block
end
self
end
# Attaches a callback to be executed when the promise is rejected.
#
# @example
# Airbrake::Promise.new.rescue { |error| raise error }
#
# @yield [error] The error message from the API
# @yieldparam error [String]
# @return [self]
def rescue(&block)
@mutex.synchronize do
if @value.key?('error')
yield(@value['error'])
return self
end
@on_rejected << block
end
self
end
# @example
# Airbrake::Promise.new.resolve('id' => '123')
#
# @param reason [Object]
# @return [self]
def resolve(reason = 'resolved')
@mutex.synchronize do
@value['ok'] = reason
@on_resolved.each { |callback| callback.call(reason) }
end
self
end
# @example
# Airbrake::Promise.new.reject('Something went wrong')
#
# @param reason [String]
# @return [self]
def reject(reason = 'rejected')
@mutex.synchronize do
@value['error'] = reason
@on_rejected.each { |callback| callback.call(reason) }
end
self
end
# @return [Boolean]
def rejected?
@value.key?('error')
end
# @return [Boolean]
def resolved?
@value.key?('ok')
end
# @return [Hash<String,String>] either successful response containing the
# +id+ key or unsuccessful response containing the +error+ key
# @note This is a non-blocking call!
# @todo Get rid of this method and use an accessor. The resolved guard is
# needed for compatibility but it shouldn't exist in the future
def value
return @value['ok'] if resolved?
@value
end
end
end