- [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-resulttoSolid::Result.Solid::ResultreplacesBCDD::Result.Solid::OutputreplacesBCDD::Context.
- Add some Hash's methods to
BCDD::Context. They are:#sliceto extract only the desired keys.#[],#dig,#fetchto access the values.#values_atand#fetch_valuesto get the values of the desired keys.
- Add the
BCDD::SuccessandBCDD::Failuremodules. They are key to checking whether a result is a success or a failure independently of whether it is aBCDD::Resultor 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_missingto 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::Contextas well.
-
(BREAKING) Replace transitions with event_logs concept.
- The
BCDD::Result::Transitionsmodule was renamed toBCDD::Result::EventLogs - The
BCDD::Result.transitionstoBCDD::Result.event_logs.
- The
-
(BREAKING) Change
BCDD::Result#deconstruct_keysto return a hash with the keys:typeand:valuewhen one of these keys is present. Otherwise, it will return the value itself. -
(BREAKING) Replace trasitions metadata
:ids_tree, and:ids_matrixwith:idsproperty. 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:treeproperty.
-
Transform
BCDD::Result::ContextintoBCDD::Context. But a constant alias was added to keep the old name. You can useBCDD::Result::ContextorBCDD::Contextto 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_treeproperty. 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:givento:_given_. -
(BREAKING) Rename
Continue()type from:continuedto:_continue_. -
(BREAKING) Move transition
:sourcefrom:and_thento:resultproperty. -
(BREAKING) Rename transitions metadata property
:tree_mapto: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_thenand#and_then!is that the latter does not check the result source.- Attention: to ensure the correct behavior, do not mix
#and_thenand#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
:continueaddon.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_exposehalted 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.transitionsfeature.
- (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_aliasoptions.ContextandBCDD::Contextare 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:continueaddon.
-
(BREAKING) Change the
:continueaddon 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#nametoBCDD::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/resultfile. Now you can defineResultas an alias forBCDD::ResultusingBCDD::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_thencall 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::Contexthas the#and_exposemethod to expose only the desired keys from the accumulated result. -
Add
BCDD::Result::Context::Expectations.newandBCDD::Result::Context::Expectations.mixin. Both are similar toBCDD::Result::Expectations.newandBCDD::Result::Expectations.mixin, but they are forBCDD::Result::Contextinstead ofBCDD::Result.- The
BCDD::Result::Context.mixinandBCDD::Result::Context::Expectations.mixinsupport thewith: :Continueoption.
- The
-
Enhance Pattern Matching support. When a
NoMatchingPatternErroroccurs inside a value checking, theBCDD::Result::Contract::Error::UnexpectedValuemessage will include the value and the expected patterns. -
Add
BCDD::Result::Success::Methodsto be share common methods betweenBCDD::Result::SuccessandBCDD::Result::Context::Success. -
Add
BCDD::Result::Failure::Methodsto be share common methods betweenBCDD::Failure::SuccessandBCDD::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::Contextmixin 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::WrongResultSubjecttoBCDD::Result::Error::InvalidResultSubject. - (BREAKING) Rename
BCDD::Result::WrongSubjectMethodAritytoBCDD::Result::Error::InvalidSubjectMethodArity. - (BREAKING) Rename the constant produced by
BCDD::Result::Expectations.mixinsfromExpectedtoResult. - Extract the major part of the
BCDD::Result::Expectationscomponents/features toBCDD::Result::Contract.- (BREAKING)
BCDD::Result::Expectations::ErrorbecameBCDD::Result::Contract::Error. So,BCDD::Result::Expectations::Error::UnexpectedTypeandBCDD::Result::Expectations::Error::UnexpectedValueare nowBCDD::Result::Contract::Error::UnexpectedTypeandBCDD::Result::Contract::Error::UnexpectedValue.
- (BREAKING)
-
Add
BCDD::Result.mixinto 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::Mixinbe a private constant. TheBCDD::Result.mixinmethod is the new way to use it.
- Add
BCDD::Result::Expectationsto 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
endMixin 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 defineResultas an alias forBCDD::Result. -
Add support to pattern matching (Ruby 2.7+).
-
Add
BCDD::Result#on_unknownto 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#unknownto execute a block if no other handler (#[],#type,#failure,#success) has been executed. Attention: always use it as the last handler.
-
(BREAKING) Rename
BCDD::ResultabletoBCDD::Result::Mixin. -
(BREAKING) Change
BCDD::Result#datato return aBCDD::Result::Datainstead 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
endModule (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#initializeenabled to receive a subject. -
Make the
BCDD::Result#and_thenmethod 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::UnexpectedOutcometo represent an unexpected outcome. -
Add
BCDD::Result::Error::WrongResultSubjectto represent a wrong result subject. When usingBCDD::Resultable, the result subject must be the same as the target object. -
Add
BCDD::Result::Error::WrongSubjectMethodArityto 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::Resultto represent a result. -
Add
BCDD::Result#typeto get the result type. The type must be a symbol. -
Add
BCDD::Result#valueto 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_orto 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#hashto get the hash of a result. -
Add
BCDD::Result#inspectto get the string representation of a result. -
Add
BCDD::Result#onto 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:okor:error. -
Add
BCDD::Result#on_successto execute a block if the result is a success. It works likeBCDD::Result#onbut only for success results. -
Add
BCDD::Result#on_failureto execute a block if the result is a failure. It works likeBCDD::Result#onbut only for failure results. -
Add
BCDD::Result#and_thento 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_thencalls after it, the next blocks will be skipped. -
Add
BCDD::Result#dataas an alias forBCDD::Result#value. -
Add
BCDD::Result#data_oras an alias forBCDD::Result#value_or. -
Add
BCDD::Result#on_typeas an alias forBCDD::Result#on. -
Add
BCDD::Result::Success()to factory a success result. -
Add
BCDD::Result::Failure()to factory a failure result.