- [Unreleased]
- 2.0.0 - 2024-04-13
- 1.1.0 - 2024-03-25
- 1.0.0 - 2024-03-16
- [0.13.0] - 2024-02-01
- [0.12.0] - 2024-01-07
- [0.11.0] - 2024-01-02
- [0.10.0] - 2023-12-31
- [0.9.1] - 2023-12-12
- [0.9.0] - 2023-12-12
- [0.8.0] - 2023-12-11
- [0.7.0] - 2023-10-27
- [0.6.0] - 2023-10-11
- [0.5.0] - 2023-10-09
- [0.4.0] - 2023-09-28
- [0.3.0] - 2023-09-26
- [0.2.0] - 2023-09-26
- [0.1.0] - 2023-09-25
- (BREAKING) Rebrand the gem from
bcdd-result
toSolid::Result
.Solid::Result
replacesBCDD::Result
.Solid::Output
replacesBCDD::Context
.
- Add some Hash's methods to
BCDD::Context
. They are:#slice
to extract only the desired keys.#[]
,#dig
,#fetch
to access the values.#values_at
and#fetch_values
to get the values of the desired keys.
- Add the
BCDD::Success
andBCDD::Failure
modules. They are key to checking whether a result is a success or a failure independently of whether it is aBCDD::Result
or aBCDD::Context
. - Add
BCDD::Result#type?
to check if the given type is the result type. - Add
BCDD::Result#is?
as an alias forBCDD::Result#type?
. - Add
BCDD::Result#method_missing
to allow the type checking through method calls. For example,result.ok?
will check if the result type is:ok
.
Note: All the methods above are available for the
BCDD::Context
as well.
-
(BREAKING) Replace transitions with event_logs concept.
- The
BCDD::Result::Transitions
module was renamed toBCDD::Result::EventLogs
- The
BCDD::Result.transitions
toBCDD::Result.event_logs
.
- The
-
(BREAKING) Change
BCDD::Result#deconstruct_keys
to return a hash with the keys:type
and:value
when one of these keys is present. Otherwise, it will return the value itself. -
(BREAKING) Replace trasitions metadata
:ids_tree
, and:ids_matrix
with:ids
property. This property is a hash with the following keys::tree
, a graph/tree representation of the transitions ids.:level_parent
, a hash with the level (depth) of each transition and its parent id.:matrix
, a matrix representation of the transitions ids. It is a simplification of the:tree
property.
-
Transform
BCDD::Result::Context
intoBCDD::Context
. But a constant alias was added to keep the old name. You can useBCDD::Result::Context
orBCDD::Context
to access the same class.
-
BCDD::Result::Context#and_expose
- Raise error when trying to expose an invalid key. -
BCDD::Result.configuration
- Accept freeze option (default:true
). When true, the configuration will be frozen after the block execution. -
BCDD::Result.config.transitions
- Add transitions feature configuration.config.transitions.listener =
- Set a listener to be called during the result transitions tracking. It must be a class that includesBCDD::Result::Transitions::Listener
.config.transitions.trace_id =
- Set a lambda (must have arity 0) to be called to get a trace id. Use to correlate different or the same operation (executed multiple times).
-
Add transitions metadata property
:ids_matrix
. It is a simplification of the:ids_tree
property. The matrix rows are the direct transitions from the root transition block, and the columns are the transitions nested from the direct transitions.# ids_matrix # { 0 | 1 | 2 | 3 | 4 # 0 => [0, 0], - | - | - | - | - # 1 => [1, 1], 0 | | | | # 2 => [1, 2], 1 | 1 | 2 | | # 3 => [2, 1], 2 | 3 | | | # 4 => [3, 1], 3 | 4 | 5 | 6 | 7 # 5 => [3, 2], 4 | 8 | | | # 6 => [3, 3], # 7 => [3, 4], # 8 => [4, 1] # }
-
Add
BCDD::Result::Transitions::Listeners[]
- It creates a listener of listeners, which will be called in the order they were added.
-
(BREAKING) Rename
Given()
type from:given
to:_given_
. -
(BREAKING) Rename
Continue()
type from:continued
to:_continue_
. -
(BREAKING) Move transition
:source
from:and_then
to:result
property. -
(BREAKING) Rename transitions metadata property
:tree_map
to:ids_tree
.# ids_tree # 0 # [0, [ |- 1 # [1, [[2, []]]], | |- 2 # [3, []], |- 3 # [4, [ |- 4 # [5, []], | |- 5 # [6, [[7, []]]] | |- 6 # ]], | |- 7 # [8, []] |- 8 # ]]
- Add
BCDD::Result#and_then!
andBCDD::Result::Context#and_then!
to execute a callable object (any object that responds to#call
) to produce a result. The main difference between the#and_then
and#and_then!
is that the latter does not check the result source.- Attention: to ensure the correct behavior, do not mix
#and_then
and#and_then!
in the same result chain. - This feature is turned off by default. You can enable it through the
BCDD::Result.config.feature.enable!(:and_then!)
. - The method called by default (
:call
) can be changed throughBCDD::Result.config.and_then!.default_method_name_to_call=
.
- Attention: to ensure the correct behavior, do not mix
- (BREAKING) Renames the subject concept/term to
source
. When a mixin is included/extended, it defines theSuccess()
andFailure()
methods. Since the results are generated in a context (instance or singleton where the mixin was used), they will have a defined source (instance or singleton itself).Definition of source
From dictionary:
- a place, person, or thing from which something comes or can be obtained.
- Add the
Given()
addon to produce aSuccess(:given, value)
result. As theContinue()
addon, it is ignored by the expectations. Use it to add a value to the result chain and invoke the next step (throughand_then
).
-
(BREAKING) Rename halted concept to terminal. Failures are terminal by default, but you can make a success terminal by enabling the
:continue
addon.Definition of terminal
From dictionary:
- of, forming, or situated at the end or extremity of something.
- the end of a railroad or other transport route, or a station at such a point.
From Wikipedia:
- A "terminus" or "terminal" is a station at the end of a railway line.
-
(BREAKING) Rename
BCDD::Result::Context::Success#and_expose
halted keyword argument toterminal
. -
(BREAKING) Rename
BCDD::Result#halted?
toBCDD::Result#terminal?
.
-
Add
BCDD::Result.transitions(&block)
to track all transitions in the same or between different operations. When there is a nesting of transition blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds. -
Add
BCDD::Result.config.feature.disable!(:transitions)
andBCDD::Result.config.feature.enable!(:transitions)
to turn on/off theBCDD::Result.transitions
feature.
- (BREAKING) Make
BCDD::Result::Context::Success#and_expose()
to produce a terminal success by default. You can turn this off by passinghalted: false
.
- Make
BCDD::Result::Context#and_then(&block)
accumulate the result value.
-
Add new
BCDD::Result.config.constant_alias
options.Context
andBCDD::Context
are now available as aliases forBCDD::Result::Context
.BCDD::Result.config.constant_alias.enable!('Context') BCDD::Result.config.constant_alias.enable!('BCDD::Context')
-
Add
BCDD::Result#halted?
to check if the result is halted. Failure results are halted by default, but you can halt a successful result by enabling the:continue
addon.
-
(BREAKING) Change the
:continue
addon to halt the step chain on the firstSuccess()
result. So, if you want to advance to the next step, you must useContinue(value)
instead ofSuccess(type, value)
. Otherwise, the step chain will be halted. (Implementation of the following proposal: #14) -
(BREAKING) Rename
BCDD::Result::Data#name
toBCDD::Result::Data#kind
. The new word is more appropriate as it represents a result's kind (success or failure).
-
Add
BCDD::Result.config
- Feature
BCDD::Result.config.feature.options BCDD::Result.config.feature.enabled?(:expectations) BCDD::Result.config.feature.enable!(:expectations) BCDD::Result.config.feature.disable!(:expectations)
- Default Add-ons
BCDD::Result.config.addon.options BCDD::Result.config.addon.enabled?(:continue) BCDD::Result.config.addon.enable!(:continue) BCDD::Result.config.addon.disable!(:continue)
- Pattern matching
BCDD::Result.config.pattern_matching.options BCDD::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking) BCDD::Result.config.pattern_matching.enable!(:nil_as_valid_value_checking) BCDD::Result.config.pattern_matching.disable!(:nil_as_valid_value_checking)
- Constant Aliases
BCDD::Result.config.constant_alias.options BCDD::Result.config.constant_alias.enabled?('Result') BCDD::Result.config.constant_alias.enable!('Result') BCDD::Result.config.constant_alias.disable!('Result')
- Feature
-
Add
BCDD::Result::configuration
. It freezes the configuration, disallowing methods that promote changes but allowing the query ones. You can use this feature to ensure integrity in your configuration.BCDD::Result.configuration do |config| config.addon.enable!(:continue) config.constant_alias.enable!('Result') config.pattern_matching.disable!(:nil_as_valid_value_checking) config.feature.disable!(:expectations) if ::Rails.env.production? end BCDD::Result.config.addon.enabled?(:continue) # true BCDD::Result.config.constant_alias.enabled?('Result') # true BCDD::Result.config.addon.disable!(:continue) # raises FrozenError BCDD::Result.config.constant_alias.disable!('Result') # raises FrozenError
-
Allow the pattern matching feature to be turned on/off through the
BCDD::Result::Expectations.mixin
. Now, it can be used without enabling it for the whole project.extend BCDD::Result::Expectations.mixin( config: { addon: { continue: false }, pattern_matching: { nil_as_valid_value_checking: true }, }, success: { numbers: ->(value) { value => [Numeric, Numeric] }, division_completed: Numeric }, failure: { invalid_arg: String, division_by_zero: String } )
-
(BREAKING) Replace
BCDD::Result::Contract.nil_as_valid_value_checking!
withBCDD::Result::Config.pattern_matching.enable!(:nil_as_valid_value_checking)
. -
(BREAKING) Replace
BCDD::Result::Contract.nil_as_valid_value_checking?
withBCDD::Result::Config.pattern_matching.enabled?(:nil_as_valid_value_checking)
. -
(BREAKING) Replace
mixin(with:)
withmixin(config:)
keyword argument. -
(BREAKING) Change the addons definition.
- From
BCDD::Result.mixin(with: :Continue) BCDD::Result.mixin(with: [:Continue])
- To
BCDD::Result.mixin(config: { addon: { continue: true } })
- These examples are valid to all kinds of mixins (
BCDD::Result.mixin
,BCDD::Result::Context.mixin
,BCDD::Result::Expectations.mixin
,BCDD::Result::Context::Expectations.mixin
)
- (BREAKING) Remove the
lib/result
file. Now you can defineResult
as an alias forBCDD::Result
usingBCDD::Result::Config.constant_alias.enable!('Result')
.
-
Add
BCDD::Result::Context
. It is aBCDD::Result
, meaning it has all the features of theBCDD::Result
. The main difference is that it only accepts keyword arguments as a value, which applies to theand_then
: The called methods must receive keyword arguments, and the dependency injection will be performed through keyword arguments.
As the input/output are hashes, the results of eachand_then
call will automatically accumulate. This is useful in operations chaining, as the result of the previous operations will be automatically available for the next one. Because of this behavior, theBCDD::Result::Context
has the#and_expose
method to expose only the desired keys from the accumulated result. -
Add
BCDD::Result::Context::Expectations.new
andBCDD::Result::Context::Expectations.mixin
. Both are similar toBCDD::Result::Expectations.new
andBCDD::Result::Expectations.mixin
, but they are forBCDD::Result::Context
instead ofBCDD::Result
.- The
BCDD::Result::Context.mixin
andBCDD::Result::Context::Expectations.mixin
support thewith: :Continue
option.
- The
-
Enhance Pattern Matching support. When a
NoMatchingPatternError
occurs inside a value checking, theBCDD::Result::Contract::Error::UnexpectedValue
message will include the value and the expected patterns. -
Add
BCDD::Result::Success::Methods
to be share common methods betweenBCDD::Result::Success
andBCDD::Result::Context::Success
. -
Add
BCDD::Result::Failure::Methods
to be share common methods betweenBCDD::Failure::Success
andBCDD::Result::Context::Failure
. -
Make all mixin generators produce a named module. The module name will be added to the target class/module (who included/extended a
BCDD::Result
/BCDD::Result::Context
mixin module). -
Add
BCDD::Result::Contract.nil_as_valid_value_checking!
. Please use this method when using the one-line pattern-matching operators on the result's value expectations.
- (BREAKING) Rename
BCDD::Result::WrongResultSubject
toBCDD::Result::Error::InvalidResultSubject
. - (BREAKING) Rename
BCDD::Result::WrongSubjectMethodArity
toBCDD::Result::Error::InvalidSubjectMethodArity
. - (BREAKING) Rename the constant produced by
BCDD::Result::Expectations.mixins
fromExpected
toResult
. - Extract the major part of the
BCDD::Result::Expectations
components/features toBCDD::Result::Contract
.- (BREAKING)
BCDD::Result::Expectations::Error
becameBCDD::Result::Contract::Error
. So,BCDD::Result::Expectations::Error::UnexpectedType
andBCDD::Result::Expectations::Error::UnexpectedValue
are nowBCDD::Result::Contract::Error::UnexpectedType
andBCDD::Result::Contract::Error::UnexpectedValue
.
- (BREAKING)
-
Add
BCDD::Result.mixin
to be included or extended in any object. It will addSuccess()
andFailure()
to the target object (the object who receives the include/extend). -
Add
BCDD::Result.mixin(with: :Continue)
. This addon will add aContinue(value)
method to the target object to produce aSuccess(:continued, value)
result. -
Add
BCDD::Result::Expectations.mixin(with: :Continue)
, it is similar toBCDD::Result.mixin(with: :Continue)
, the key difference is that theContinue(value)
will be ignored by the expectations. This is extremely useful when you want to useContinue(value)
to chain operations, but you don't want to declare N success types in the expectations. -
Increase the arity of
BCDD::Result#and_then
. Now, it can receive a second argument (a value to be injected and shared with the subject's method). -
Increase the arity (maximum of 2) for the methods called through
BCDD::Result#and_then
. The second argument is the value injected byBCDD::Result#and_then
.
- (BREAKING) Make
BCDD::Result::Mixin
be a private constant. TheBCDD::Result.mixin
method is the new way to use it.
- Add
BCDD::Result::Expectations
to define contracts for your results. There are two ways to use it: the standalone (BCDD::Result::Expectations.new
) and the mixin (BCDD::Result::Expectations.mixin
) mode.
The main difference is that the mixin mode will use the target object (who receives the include/extend) as the result's subject (like the BCDD::Result::Mixin
does), while the standalone mode won't.
Standalone mode:
module Divide
Expected = BCDD::Result::Expectations.new(
success: {
numbers: ->(value) { value.is_a?(Array) && value.size == 2 && value.all?(Numeric) },
division_completed: Numeric
},
failure: {
invalid_arg: String,
division_by_zero: String
}
)
def self.call(arg1, arg2)
arg1.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg1 must be numeric')
arg2.is_a?(Numeric) or return Expected::Failure(:invalid_arg, 'arg2 must be numeric')
arg2.zero? and return Expected::Failure(:division_by_zero, 'arg2 must not be zero')
Expected::Success(:division_completed, arg1 / arg2)
end
end
Mixin mode:
class Divide
include BCDD::Result::Expectations.mixin(
success: {
numbers: ->(value) { value.is_a?(Array) && value.size == 2 && value.all?(Numeric) },
division_completed: Numeric
},
failure: {
invalid_arg: String,
division_by_zero: String
}
)
def call(arg1, arg2)
validate_numbers(arg1, arg2)
.and_then(:validate_non_zero)
.and_then(:divide)
end
private
def validate_numbers(arg1, arg2)
arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
Success(:numbers, [arg1, arg2])
end
def validate_non_zero(numbers)
return Success(:numbers, numbers) unless numbers.last.zero?
Failure(:division_by_zero, 'arg2 must not be zero')
end
def divide((number1, number2))
Success(:division_completed, number1 / number2)
end
end
-
Add
require 'result'
to defineResult
as an alias forBCDD::Result
. -
Add support to pattern matching (Ruby 2.7+).
-
Add
BCDD::Result#on_unknown
to execute a block if no other hook (#on
,#on_type
,#on_failure
,#on_success
) has been executed. Attention: always use it as the last hook. -
Add
BCDD::Result::Handler#unknown
to execute a block if no other handler (#[]
,#type
,#failure
,#success
) has been executed. Attention: always use it as the last handler.
-
(BREAKING) Rename
BCDD::Resultable
toBCDD::Result::Mixin
. -
(BREAKING) Change
BCDD::Result#data
to return aBCDD::Result::Data
instead of the result value. This object exposes the result attributes (name, type, value) directly and as a hash (to_h
/to_hash
) and array (to_a
/to_ary
).
- (BREAKING) Remove
BCDD::Result#data_or
.
- Add
BCDD::Result#handle
. This method allows defining blocks for each hook (type, failure, success), but instead of returning the result itself, it will return the output of the first match/block execution.
- Add
BCDD::Resultable
. This module can addSuccess()
andFailure()
in any object. The target object will be the subject of the result object produced by these methods.
Classes (instance methods)
class Divide
include BCDD::Resultable
attr_reader :arg1, :arg2
def initialize(arg1, arg2)
@arg1 = arg1
@arg2 = arg2
end
def call
validate_numbers
.and_then(:validate_non_zero)
.and_then(:divide)
end
private
def validate_numbers
arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
Success(:ok, [arg1, arg2])
end
def validate_non_zero(numbers)
return Success(:ok, numbers) unless numbers.last.zero?
Failure(:division_by_zero, 'arg2 must not be zero')
end
def divide((number1, number2))
Success(:division_completed, number1 / number2)
end
end
Module (singleton methods)
module Divide
extend self, BCDD::Resultable
def call(arg1, arg2)
validate_numbers(arg1, arg2)
.and_then(:validate_non_zero)
.and_then(:divide)
end
private
def validate_numbers(arg1, arg2)
arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')
arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')
Success(:ok, [arg1, arg2])
end
def validate_non_zero(numbers)
return Success(:ok, numbers) unless numbers.last.zero?
Failure(:division_by_zero, 'arg2 must not be zero')
end
def divide((number1, number2))
Success(:division_completed, number1 / number2)
end
end
-
Make the
BCDD::Result#initialize
enabled to receive a subject. -
Make the
BCDD::Result#and_then
method receive a method name (symbol) and perform it on the result subject (added byBCDD::Resultable
). The called method must return a result; otherwise, an error (BCDD::Result::Error::UnexpectedOutcome
) will be raised. -
Add
BCDD::Result::Error::UnexpectedOutcome
to represent an unexpected outcome. -
Add
BCDD::Result::Error::WrongResultSubject
to represent a wrong result subject. When usingBCDD::Resultable
, the result subject must be the same as the target object. -
Add
BCDD::Result::Error::WrongSubjectMethodArity
to represent a wrong subject method arity. Valid arities are 0 and 1.
- (BREAKING) Remove
BCDD::Result::Error::UnexpectedBlockOutcome
. It was replaced byBCDD::Result::Error::UnexpectedOutcome
.
-
Add
BCDD::Result
to represent a result. -
Add
BCDD::Result#type
to get the result type. The type must be a symbol. -
Add
BCDD::Result#value
to get the result value. The value can be anything. -
Add
BCDD::Result#success?
to check if the result is a success. You can also check the result type by passing an argument to it. For example,result.success?(:ok)
will check if the result is a success and if the type is:ok
. -
Add
BCDD::Result#failure?
to check if the result is a failure. You can also check the result type by passing an argument to it. For example,result.failure?(:error)
will check if the result is a failure and if the type is:error
. -
Add
BCDD::Result#value_or
to get the value of a successful result or a default value (from the block) if it is a failure. -
Add
BCDD::Result#==
to compare two results. -
Add
BCDD::Result#eql?
to compare two results. -
Add
BCDD::Result#hash
to get the hash of a result. -
Add
BCDD::Result#inspect
to get the string representation of a result. -
Add
BCDD::Result#on
to execute a block depending on the result type (independently of the result being a success or a failure). The block will receive the result value as an argument, and the result itself will be returned after (or not) the block execution. The method can be called multiple times and with one or more arguments. For example,result.on(:ok, :error) { |value| # ... }
will execute the block if the result type is:ok
or:error
. -
Add
BCDD::Result#on_success
to execute a block if the result is a success. It works likeBCDD::Result#on
but only for success results. -
Add
BCDD::Result#on_failure
to execute a block if the result is a failure. It works likeBCDD::Result#on
but only for failure results. -
Add
BCDD::Result#and_then
to execute the block if the result is a success. You can use it to chain multiple operations. If the block returns a failure result and there are otherand_then
calls after it, the next blocks will be skipped. -
Add
BCDD::Result#data
as an alias forBCDD::Result#value
. -
Add
BCDD::Result#data_or
as an alias forBCDD::Result#value_or
. -
Add
BCDD::Result#on_type
as an alias forBCDD::Result#on
. -
Add
BCDD::Result::Success()
to factory a success result. -
Add
BCDD::Result::Failure()
to factory a failure result.