{"id":21958671,"url":"https://github.com/solid-process/solid-result","last_synced_at":"2026-04-02T01:21:22.621Z","repository":{"id":196314491,"uuid":"695376205","full_name":"solid-process/solid-result","owner":"solid-process","description":"Unleash a pragmatic and observable use of Result Pattern and Railway-Oriented Programming in Ruby.","archived":false,"fork":false,"pushed_at":"2024-05-05T21:57:14.000Z","size":697,"stargazers_count":58,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-03-14T22:03:47.970Z","etag":null,"topics":["pattern-matching","railway-oriented-programming","result-monad","result-pattern","rop","ruby","rubygem","solid-process"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/solid-result","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/solid-process.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-09-23T02:01:33.000Z","updated_at":"2026-01-24T21:10:59.000Z","dependencies_parsed_at":"2023-09-29T03:37:22.107Z","dependency_job_id":"8d286d02-d9cd-45bc-bc94-1772d388c77b","html_url":"https://github.com/solid-process/solid-result","commit_stats":null,"previous_names":["b-cdd/result","solid-process/solid-result"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/solid-process/solid-result","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solid-process%2Fsolid-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solid-process%2Fsolid-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solid-process%2Fsolid-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solid-process%2Fsolid-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/solid-process","download_url":"https://codeload.github.com/solid-process/solid-result/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/solid-process%2Fsolid-result/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31293788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T01:05:07.454Z","status":"ssl_error","status_checked_at":"2026-04-02T00:56:46.496Z","response_time":53,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pattern-matching","railway-oriented-programming","result-monad","result-pattern","rop","ruby","rubygem","solid-process"],"created_at":"2024-11-29T09:16:41.354Z","updated_at":"2026-04-02T01:21:22.597Z","avatar_url":"https://github.com/solid-process.png","language":"Ruby","readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\" id=\"-solidresult\"\u003e🔀 Solid::Result\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\u003ci\u003eUnleash a pragmatic and observable use of Result Pattern and Railway-Oriented Programming in Ruby.\u003c/i\u003e\u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Ruby%20%3E%3D%202.7%2C%20%3C%3D%20Head-ruby.svg?colorA=444\u0026colorB=333\" alt=\"Ruby\"\u003e\n    \u003ca href=\"https://rubygems.org/gems/solid-result\"\u003e\u003cimg src=\"https://badge.fury.io/rb/solid-result.svg\" alt=\"solid-result gem version\" height=\"18\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://codeclimate.com/github/solid-process/solid-result/maintainability\"\u003e\u003cimg src=\"https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/maintainability\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://codeclimate.com/github/solid-process/solid-result/test_coverage\"\u003e\u003cimg src=\"https://api.codeclimate.com/v1/badges/aa8360f8f012d7dedd62/test_coverage\" /\u003e\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\nIt's a general-purpose result monad that allows you to create objects representing a success (`Solid::Result::Success`) or failure (`Solid::Result::Failure`).\n\n**What problem does it solve?**\n\nIt allows you to consistently represent the concept of success and failure throughout your codebase.\n\nFurthermore, this abstraction exposes several features that will be useful to make the application flow react cleanly and securely to the result represented by these objects.\n\nUse it to enable the [Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) pattern (superpower) in your code.\n\n- [Supported Ruby](#supported-ruby)\n- [Installation](#installation)\n- [Usage](#usage)\n    - [`Solid::Result` *versus* `Result`](#solidresult-versus-result)\n- [Reference](#reference)\n  - [Basic methods](#basic-methods)\n    - [Checking types with `result.is?` or `method missing`](#checking-types-with-resultis-or-method-missing)\n    - [Checking types with `result.success?` or `result.failure?`](#checking-types-with-resultsuccess-or-resultfailure)\n  - [Result Hooks](#result-hooks)\n    - [`result.on`](#resulton)\n    - [`result.on_type`](#resulton_type)\n    - [`result.on_success`](#resulton_success)\n    - [`result.on_failure`](#resulton_failure)\n    - [`result.on_unknown`](#resulton_unknown)\n    - [`result.handle`](#resulthandle)\n  - [Result Value](#result-value)\n    - [`result.value_or`](#resultvalue_or)\n  - [Result Data](#result-data)\n    - [`result.data`](#resultdata)\n  - [Railway Oriented Programming](#railway-oriented-programming)\n    - [`result.and_then`](#resultand_then)\n    - [`Solid::Result.mixin`](#solidresultmixin)\n      - [Class example (Instance Methods)](#class-example-instance-methods)\n      - [Module example (Singleton Methods)](#module-example-singleton-methods)\n      - [Important Requirement](#important-requirement)\n      - [Dependency Injection](#dependency-injection)\n      - [Add-ons](#add-ons)\n  - [`Solid::Result::Expectations`](#solidresultexpectations)\n    - [Standalone *versus* Mixin mode](#standalone-versus-mixin-mode)\n    - [Type checking - Result Hooks](#type-checking---result-hooks)\n      - [`#success?` and `#failure?`](#success-and-failure)\n      - [`#on` and `#on_type`](#on-and-on_type)\n      - [`#on_success` and `#on_failure`](#on_success-and-on_failure)\n      - [`#handle`](#handle)\n    - [Type checking - Result Creation](#type-checking---result-creation)\n      - [Mixin mode](#mixin-mode)\n      - [Standalone mode](#standalone-mode)\n    - [Value checking - Result Creation](#value-checking---result-creation)\n      - [Success()](#success)\n      - [Failure()](#failure)\n      - [Pattern Matching Support](#pattern-matching-support)\n    - [`Solid::Result::Expectations.mixin` add-ons](#solidresultexpectationsmixin-add-ons)\n  - [`Solid::Output`](#solidoutput)\n    - [Defining successes and failures](#defining-successes-and-failures)\n    - [Hash methods](#hash-methods)\n    - [`Solid::Output.mixin`](#solidoutputmixin)\n      - [Class example (Instance Methods)](#class-example-instance-methods-1)\n      - [`and_expose`](#and_expose)\n      - [Module example (Singleton Methods)](#module-example-singleton-methods-1)\n    - [`Solid::Output::Expectations`](#solidoutputexpectations)\n    - [Mixin add-ons](#mixin-add-ons)\n- [Pattern Matching](#pattern-matching)\n  - [`Solid::Result`](#solidresult)\n    - [`Array`/`Find` patterns](#arrayfind-patterns)\n    - [`Hash` patterns](#hash-patterns)\n  - [`Solid::Output`](#solidoutput-1)\n    - [`Array`/`Find` patterns](#arrayfind-patterns-1)\n    - [`Hash` patterns](#hash-patterns-1)\n  - [How to pattern match without the concept of success and failure](#how-to-pattern-match-without-the-concept-of-success-and-failure)\n- [`Solid::Result.event_logs`](#solidresultevent_logs)\n  - [`metadata: {ids:}`](#metadata-ids)\n  - [Configuration](#configuration)\n    - [Turning on/off](#turning-onoff)\n    - [Setting a `trace_id` fetcher](#setting-a-trace_id-fetcher)\n    - [Setting a `listener`](#setting-a-listener)\n    - [Setting multiple `listeners`](#setting-multiple-listeners)\n- [`Solid::Result.configuration`](#solidresultconfiguration)\n  - [`Solid::Result.config`](#solidresultconfig)\n- [`Solid::Result#and_then!`](#solidresultand_then)\n    - [Dependency Injection](#dependency-injection-1)\n    - [Configuration](#configuration-1)\n    - [Analysis: Why is `and_then!` an Anti-pattern?](#analysis-why-is-and_then-an-anti-pattern)\n    - [`#and_then` versus `#and_then!`](#and_then-versus-and_then)\n    - [Analysis: Why is `#and_then` the antidote/standard?](#analysis-why-is-and_then-the-antidotestandard)\n- [About](#about)\n- [Development](#development)\n- [Contributing](#contributing)\n- [License](#license)\n- [Code of Conduct](#code-of-conduct)\n\n## Supported Ruby\n\nThis library is tested against:\n\nVersion | 2.7 | 3.0 | 3.1 | 3.2 | 3.3 | Head\n---- | --- | --- | --- | --- | --- | ---\n100% Coverage | ✅  | ✅  | ✅  | ✅  | ✅  | ✅\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'solid-result'\n```\n\nAnd then execute:\n\n    $ bundle install\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n    $ gem install solid-result\n\nAnd require it in your code:\n\n    require 'solid/result'\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Usage\n\nTo create a result, you must define a type (symbol) and its value (any kind of object). e.g.,\n\n```ruby\nSolid::Result::Success(:ok, :1)           #\n                                          # The value can be any kind of object\nSolid::Result::Failure(:err, 'the value') #\n```\n\nThe reason for defining a `type` is that it is very common for a method/operation to return different types of successes or failures. Because of this, the `type` will always be required. e,g.,\n\n```ruby\nSolid::Result::Success(:ok)  #\n                             # The type is mandatory and the value is optional\nSolid::Result::Failure(:err) #\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Solid::Result` *versus* `Result`\n\nThis gem provides a way to create constant aliases for `Solid::Result` and other classes/modules.\n\nTo enable it, you must call the `Solid::Result.configuration` method and pass a block to it. You can turn the aliases you want on/off in this block.\n\n```ruby\nSolid::Result.configuration do |config|\n  config.constant_alias.enable!('Result')\nend\n```\n\nSo, instead of using `Solid::Result` everywhere, you can use `Result` as an alias/shortcut.\n\n```ruby\nResult::Success(:ok) # \u003cSolid::Result::Success type=:ok value=nil\u003e\n\nResult::Failure(:err) # \u003cSolid::Result::Failure type=:err value=nil\u003e\n```\n\nIf you have enabled constant aliasing, all examples in this README that use `Solid::Result` can be implemented using `Result`.\n\nThere are other aliases and configurations available. Check the [Solid::Result.configuration]() section for more information.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Reference\n\n### Basic methods\n\nBoth `Solid::Result::Success` and `Solid::Result::Failure` are composed of the same methods. Look at the basic ones:\n\n**Solid::Result::Success**\n\n```ruby\n################\n# With a value #\n################\nresult = Solid::Result::Success(:ok, my: 'value')\n\nresult.success?   # true\nresult.failure?   # false\nresult.type?(:ok) # true\nresult.type       # :ok\nresult.value      # {:my =\u003e \"value\"}\n\n###################\n# Without a value #\n###################\nresult = Solid::Result::Success(:yes)\n\nresult.success?    # true\nresult.failure?    # false\nresult.type?(:yes) # true\nresult.type        # :yes\nresult.value       # nil\n```\n\n**Solid::Result::Failure**\n\n```ruby\n################\n# With a value #\n################\nresult = Solid::Result::Failure(:err, 'my_value')\n\nresult.success?    # false\nresult.failure?    # true\nresult.type?(:err) # true\nresult.type        # :err\nresult.value       # \"my_value\"\n\n###################\n# Without a value #\n###################\nresult = Solid::Result::Failure(:no)\n\nresult.success?   # false\nresult.failure?   # true\nresult.type?(:no) # true\nresult.type       # :no\nresult.value      # nil\n```\n\nIn both cases, the `type` must be a symbol, and the `value` can be any kind of object.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Checking types with `result.is?` or `method missing`\n\nBeyond the `type?` method, you can also use the `is?` method to check the result type. If you want to check the type directly, you can write the type using a method that ends with a question mark.\n\n```ruby\nresult = Solid::Result::Success(:ok)\n\nresult.is?(:ok) # true\nresult.ok?      # true\n\nresult = Solid::Result::Failure(:err)\n\nresult.is?(:err) # true\nresult.err?      # true\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Checking types with `result.success?` or `result.failure?`\n\n`Solid::Result#success?` and `Solid::Result#failure?` are methods that allow you to check if the result is a success or a failure.\n\nYou 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`.\n\n```ruby\nresult = Solid::Result::Success(:ok)\n\nresult.success?(:ok)\n\n# This is the same as:\n\nresult.success? \u0026\u0026 result.type == :ok\n```\n\nThe same is valid for `Solid::Result#failure?`.\n\n```ruby\nresult = Solid::Result::Failure(:err)\n\nresult.failure?(:err)\n\n# This is the same as:\n\nresult.failure? \u0026\u0026 result.type == :err\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### Result Hooks\n\nResult hooks are methods that allow you to execute a block of code based on the type of result obtained.\nTo demonstrate their use, I will implement a method that can divide two numbers.\n\n```ruby\ndef divide(arg1, arg2)\n  arg1.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg1 must be numeric')\n  arg2.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n  return Solid::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?\n\n  Solid::Result::Success(:division_completed, arg1 / arg2)\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.on`\n\nWhen you use `Solid::Result#on`, the block will be executed only when the type matches the result type.\n\nHowever, even if the block is executed, the method will always return the result itself.\n\nThe value of the result will be available as the first argument of the block.\n\n```ruby\nresult = divide(nil, 2)\n#\u003cSolid::Result::Failure type=:invalid_arg data='arg1 must be numeric'\u003e\n\noutput =\n  result\n    .on(:invalid_arg) { |msg| puts msg }\n    .on(:division_by_zero) { |msg| puts msg }\n    .on(:division_completed) { |number| puts number }\n\n# The code above will print 'arg1 must be numeric' and return the result itself.\n\nresult.object_id == output.object_id # true\n```\n\nYou can define multiple types to be handled by the same hook/block\n```ruby\nresult = divide(4, 0)\n\noutput =\n  result.on(:invalid_arg, :division_by_zero, :division_completed) { |value| puts value }\n\n# The code above will print 'arg2 must not be zero' and return the result itself.\n\nresult.object_id == output.object_id # true\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.on_type`\n\n`Solid::Result#on_type` is an alias of `Solid::Result#on`.\n\n```ruby\nresult = divide(nil, 2)\n#\u003cSolid::Result::Failure type=:invalid_arg data='arg1 must be numeric'\u003e\n\noutput =\n  result\n    .on_type(:invalid_arg, :division_by_zero) { |msg| puts msg }\n    .on_type(:division_completed) { |number| puts number }\n\n# The code above will print 'arg1 must be numeric' and return the result itself.\n\nresult.object_id == output.object_id # true\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.on_success`\n\nThe `Solid::Result#on_success` method is quite similar to the `Solid::Result#on` hook, but with a few key differences:\n\n1. It will only execute the block of code if the result is a success.\n2. If the type declaration is not included, the method will execute the block for any successful result, regardless of its type.\n\n```ruby\n# In both examples, it executes the block and returns the result itself.\n\ndivide(4, 2).on_success { |number| puts number }\n\ndivide(4, 2).on_success(:division_completed) { |number| puts number }\n\n# It doesn't execute the block as the type is different.\n\ndivide(4, 4).on_success(:ok) { |value| puts value }\n\n# It doesn't execute the block, as the result is a success, but the hook expects a failure.\n\ndivide(4, 4).on_failure { |error| puts error }\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.on_failure`\n\nIt is the opposite of `Result#on_success`:\n\n1. It will only execute the block of code if the result is a failure.\n2. If the type declaration is not included, the method will execute the block for any failed result, regardless of its type.\n\n```ruby\n# In both examples, it executes the block and returns the result itself.\n\ndivide(nil, 2).on_failure { |error| puts error }\n\ndivide(4, 0).on_failure(:division_by_zero) { |error| puts error }\n\n# It doesn't execute the block as the type is different.\n\ndivide(4, 0).on_failure(:invalid_arg) { |error| puts error }\n\n# It doesn't execute the block, as the result is a failure, but the hook expects a success.\n\ndivide(4, 0).on_success { |number| puts number }\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.on_unknown`\n\n`Solid::Result#on_unknown` will execute the block when no other hook (`#on`, `#on_type`, `#on_failure`, `#on_success`) has been executed.\n\nRegardless of the block being executed, the method will always return the result itself.\n\nThe value of the result will be available as the first argument of the block.\n\n```ruby\ndivide(4, 2)\n  .on(:invalid_arg) { |msg| puts msg }\n  .on(:division_by_zero) { |msg| puts msg }\n  .on_unknown { |value, type| puts [type, value].inspect }\n\n# The code above will print '[:division_completed, 2]' and return the result itself.\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `result.handle`\n\nThis method lets you define blocks for each hook (type, failure, or success), but instead of returning itself, it will return the output of the first match/block execution.\n\n```ruby\ndivide(4, 2).handle do |result|\n  result.success { |number| number }\n  result.failure(:invalid_arg) { |err| puts err }\n  result.type(:division_by_zero) { raise ZeroDivisionError }\n  result.unknown { raise NotImplementedError }\nend\n\n#or\n\ndivide(4, 2).handle do |on|\n  on.success { |number| number }\n  on.failure { |err| puts err }\n  on.unknown { raise NotImplementedError }\nend\n\n#or\n\ndivide(4, 2).handle do |on|\n  on.type(:invalid_arg) { |err| puts err }\n  on.type(:division_by_zero) { raise ZeroDivisionError }\n  on.type(:division_completed) { |number| number }\n  on.unknown { raise NotImplementedError }\nend\n\n# or\n\ndivide(4, 2).handle do |on|\n  on[:invalid_arg] { |err| puts err }\n  on[:division_by_zero] { raise ZeroDivisionError }\n  on[:division_completed] { |number| number }\n  on.unknown { raise NotImplementedError }\nend\n\n# The [] syntax 👆 is an alias of #type.\n```\n\n**Notes:**\n* You can define multiple types to be handled by the same hook/block\n* If the type is missing, it will execute the block for any success or failure handler.\n* The `#type` and `#[]` handlers require at least one type/argument.\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### Result Value\n\nTo access the result value, you can simply call `Solid::Result#value`.\n\nHowever, there may be instances where you need to retrieve the value of a successful result or a default value if the result is a failure. In such cases, you can make use of `Solid::Result#value_or`.\n\n#### `result.value_or`\n\n`Solid::Result#value_or` returns the value when the result is a success. However, if it is a failure, the given block will be executed, and its outcome will be returned.\n\n```ruby\ndef divide(arg1, arg2)\n  arg1.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg)\n  arg2.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg)\n\n  return Solid::Result::Failure(:division_by_zero) if arg2.zero?\n\n  Solid::Result::Success(:division_completed, arg1 / arg2)\nend\n\n# When the result is success\ndivide(4, 2).value_or { 0 } # 2\n\n# When the result is failure\ndivide('4', 2).value_or { 0 } # 0\ndivide(4, '2').value_or { 0 } # 0\ndivide(100, 0).value_or { 0 } # 0\n```\n\n*PS: The `divide()` implementation is [here](#result-hooks).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### Result Data\n\n#### `result.data`\n\nThe `Solid::Result#data` exposes the result attributes (kind, type, value) directly and as a hash (`to_h`/`to_hash`) and array (`to_a`/`to_ary`).\n\nThis is helpful if you need to access the result attributes generically or want to use Ruby features like splat (`*`) and double splat (`**`) operators.\n\nSee the examples below to understand how to use it.\n\n```ruby\nresult = Solid::Result::Success(:ok, 1)\n\nsuccess_data = result.data # #\u003cSolid::Result::Data kind=:success type=:ok value=1\u003e\n\nsuccess_data.kind  # :success\nsuccess_data.type  # :ok\nsuccess_data.value # 1\n\nsuccess_data.to_h  # {:kind=\u003e:success, :type=\u003e:ok, :value=\u003e1}\nsuccess_data.to_a  # [:success, :ok, 1]\n\nkind, type, value = success_data\n\n[kind, type, value] # [:success, :ok, 1]\n\ndef print_to_ary(kind, type, value)\n  puts [kind, type, value].inspect\nend\n\ndef print_to_hash(kind:, type:, value:)\n  puts [kind, type, value].inspect\nend\n\nprint_to_ary(*success_data)   # [:success, :ok, 1]\n\nprint_to_hash(**success_data) # [:success, :ok, 1]\n```\n\n\u003e **NOTE:** The example above uses a success result, but the same is valid for a failure result.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### Railway Oriented Programming\n\n[\"Railway Oriented Programming (ROP)\"](https://fsharpforfunandprofit.com/rop/)  is a programming technique that involves linking blocks together to form a sequence of operations, also known as a pipeline.\nIf a failure occurs in any of the blocks, the pipeline is interrupted and subsequent blocks are skipped.\n\nThe ROP technique allows you to structure your code in a way that expresses your logic as a series of operations, with the added benefit of stopping the process at the first detection of failure.\n\nIf all blocks successfully execute, the final result of the pipeline will be a success.\n\n#### `result.and_then`\n\n```ruby\nmodule Divide\n  extend self\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then { |numbers| validate_nonzero(numbers) }\n      .and_then { |numbers| divide(numbers) }\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Solid::Result::Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    return Solid::Result::Success(:ok, numbers) if numbers.last.nonzero?\n\n    Solid::Result::Failure(:division_by_zero, 'arg2 must not be zero')\n  end\n\n  def divide((number1, number2))\n    Solid::Result::Success(:division_completed, number1 / number2)\n  end\nend\n```\n\nExample of outputs:\n\n```ruby\nDivide.call('4', 2)\n#\u003cSolid::Result::Failure type=:invalid_arg data=\"arg1 must be numeric\"\u003e\n\nDivide.call(2, '2')\n#\u003cSolid::Result::Failure type=:invalid_arg data=\"arg2 must be numeric\"\u003e\n\nDivide.call(2, 0)\n#\u003cSolid::Result::Failure type=:division_by_zero data=\"arg2 must not be zero\"\u003e\n\nDivide.call(2, 2)\n#\u003cSolid::Result::Success type=:division_completed data=1\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Solid::Result.mixin`\n\nThis method generates a module that any object can include or extend. It adds two methods to the target object: `Success()` and `Failure()`.\n\nThe main difference between these methods and `Solid::Result::Success()`/`Solid::Result::Failure()` is that the former will utilize the target object (which has received the include/extend) as the result's source.\n\nBecause the result has a source, the `#and_then` method can call methods from it.\n\n##### Class example (Instance Methods)\n\n```ruby\nclass Divide\n  include Solid::Result.mixin\n\n  attr_reader :arg1, :arg2\n\n  def initialize(arg1, arg2)\n    @arg1 = arg1\n    @arg2 = arg2\n  end\n\n  def call\n    validate_numbers\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    # As arg1 and arg2 are instance methods, they will be available in the instance scope.\n    # So, in this case, I'm passing them as an array to show how the next method can receive the value as its argument.\n    Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    return Success(:ok, numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n\nDivide.new(4, 2).call #\u003cSolid::Result::Success type=:division_completed value=2\u003e\n\nDivide.new(4, 0).call   #\u003cSolid::Result::Failure type=:division_by_zero value=\"arg2 must not be zero\"\u003e\nDivide.new('4', 2).call #\u003cSolid::Result::Failure type=:invalid_arg value=\"arg1 must be numeric\"\u003e\nDivide.new(4, '2').call #\u003cSolid::Result::Failure type=:invalid_arg value=\"arg2 must be numeric\"\u003e\n```\n\n##### Module example (Singleton Methods)\n\n```ruby\nmodule Divide\n  extend self, Solid::Result.mixin\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    return Success(:ok, numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n\nDivide.call(4, 2) #\u003cSolid::Result::Success type=:division_completed value=2\u003e\n\nDivide.call(4, 0)   #\u003cSolid::Result::Failure type=:division_by_zero value=\"arg2 must not be zero\"\u003e\nDivide.call('4', 2) #\u003cSolid::Result::Failure type=:invalid_arg value=\"arg1 must be numeric\"\u003e\nDivide.call(4, '2') #\u003cSolid::Result::Failure type=:invalid_arg value=\"arg2 must be numeric\"\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Important Requirement\n\nTo use the `#and_then` method to call methods, they must use `Success()` and `Failure()` to produce the results.\n\nIf you try to use `Solid::Result::Success()`/`Solid::Result::Failure()`, or results from another `Solid::Result.mixin` instance with `#and_then`, it will raise an error because the sources are different.\n\n**Note:** You can still use the block syntax, but all the results must be produced by the source's `Success()` and `Failure()` methods.\n\n```ruby\nmodule ValidateNonzero\n  extend self, Solid::Result.mixin\n\n  def call(numbers)\n    return Success(:ok, numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\nend\n\nmodule Divide\n  extend self, Solid::Result.mixin\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    ValidateNonzero.call(numbers) # This will raise an error\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n```\n\nLook at the error produced by the code above:\n\n```ruby\nDivide.call(2, 0)\n\n# You cannot call #and_then and return a result that does not belong to the same source! (Solid::Result::Error::InvalidResultSource)\n# Expected source: Divide\n# Given source: ValidateNonzero\n# Given result: #\u003cSolid::Result::Failure type=:division_by_zero value=\"arg2 must not be zero\"\u003e\n```\n\nIn order to fix this, you must handle the result produced by `ValidateNonzero.call()` and return a result that belongs to the same source.\n\n```ruby\nmodule ValidateNonzero\n  extend self, Solid::Result.mixin\n\n  def call(numbers)\n    return Success(:ok, numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\nend\n\nmodule Divide\n  extend self, Solid::Result.mixin\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    # In this case we are handling the result from other source\n    # and returning our own\n    ValidateNonzero.call(numbers).handle do |on|\n      on.success { |numbers| Success(:ok, numbers) }\n\n      on.failure { |err| Failure(:division_by_zero, err) }\n    end\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n```\n\nLook at the output of the code above:\n\n```ruby\nDivide.call(2, 0)\n\n#\u003cSolid::Result::Failure type=:division_by_zero value=\"arg2 must not be zero\"\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Dependency Injection\n\nThe `Solid::Result#and_then` accepts a second argument that will be used to share a value with the source's method.\nTo receive this argument, the source's method must have an arity of two, where the first argument will be the result value and the second will be the injected value.\n\n```ruby\nrequire 'logger'\n\nmodule Divide\n  extend self, Solid::Result.mixin\n\n  def call(arg1, arg2, logger: ::Logger.new(STDOUT))\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero, logger)\n      .and_then(:divide, logger)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Success(:ok, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers, logger)\n    if numbers.last.zero?\n      logger.error('arg2 must not be zero')\n\n      Failure(:division_by_zero, 'arg2 must not be zero')\n    else\n      logger.info('The numbers are valid')\n\n      Success(:ok, numbers)\n    end\n  end\n\n  def divide((number1, number2), logger)\n    division = number1 / number2\n\n    logger.info(\"The division result is #{division}\")\n\n    Success(:division_completed, division)\n  end\nend\n\nDivide.call(4, 2)\n# I, [2023-10-11T00:08:05.546237 #18139]  INFO -- : The numbers are valid\n# I, [2023-10-11T00:08:05.546337 #18139]  INFO -- : The division result is 2\n#=\u003e #\u003cSolid::Result::Success type=:division_completed value=2\u003e\n\nDivide.call(4, 2, logger: Logger.new(IO::NULL))\n#=\u003e #\u003cSolid::Result::Success type=:division_completed value=2\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Add-ons\n\nThe `Solid::Result.mixin` also accepts the `config:` argument. It is a hash that will be used to define custom behaviors for the mixin.\n\n**given**\n\nThis addon is enabled by default. It will create the `Given(value)` method. Use it to add a value to the result chain and invoke the next step (through `and_then`).\n\nYou can turn it off by passing `given: false` to the `config:` argument or using the `Solid::Result.configuration`.\n\n**continue**\n\nThis addon will create the `Continue(value)` method and change the `Success()` behavior to terminate the step chain.\n\nSo, if you want to advance to the next step, you must use `Continue(value)` instead of `Success(type, value)`. Otherwise, the step chain will be terminated.\n\nIn this example below, the `validate_nonzero` will return a `Success(:division_completed, 0)` and terminate the chain if the first number is zero.\n\n```ruby\nmodule Divide\n  extend self, Solid::Result.mixin(config: { addon: { continue: true } })\n\n  def call(arg1, arg2)\n    Given([arg1, arg2])\n      .and_then(:validate_numbers)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(numbers)\n    number1, number2 = numbers\n\n    number1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    number2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Continue(numbers)\n  end\n\n  def validate_nonzero(numbers)\n    return Failure(:division_by_zero, 'arg2 must not be zero') if numbers.last.zero?\n\n    return Success(:division_completed, 0) if numbers.first.zero?\n\n    Continue(numbers)\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### `Solid::Result::Expectations`\n\nThis feature lets you define contracts for your results' types and values. There are two ways to use it: the standalone (`Solid::Result::Expectations.new`) and the mixin (`Solid::Result::Expectations.mixin`) mode.\n\nIt was designed to ensure all the aspects of the result's type and value. So, an error will be raised if you try to create or handle a result with an unexpected type or value.\n\n#### Standalone *versus* Mixin mode\n\nThe _**standalone mode**_ creates an object that knows how to produce and validate results based on the defined expectations. Look at the example below:\n\n```ruby\nmodule Divide\n  Result = Solid::Result::Expectations.new(\n    success: %i[numbers division_completed],\n    failure: %i[invalid_arg division_by_zero]\n  )\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Result::Success(:division_completed, arg1 / arg2)\n  end\nend\n```\n\nIn the code above, we define a constant `Divide::Result`. And because of this (it is a constant), we can use it inside and outside the module.\n\nLook what happens if you try to create a result without one of the expected types.\n\n```ruby\nDivide::Result::Success(:ok)\n# type :ok is not allowed. Allowed types: :numbers, :division_completed\n# (Solid::Result::Contract::Error::UnexpectedType)\n\nDivide::Result::Failure(:err)\n# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\nThe _**mixin mode**_ is similar to `Solid::Result::Mixin`, but it also defines the expectations for the result's types and values.\n\n```ruby\nclass Divide\n  include Solid::Result::Expectations.mixin(\n    success: %i[numbers division_completed],\n    failure: %i[invalid_arg division_by_zero]\n  )\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Success(:numbers, [arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    return Success(:numbers, numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n```\n\nThis mode also defines an `Result` constant to be used inside and outside the module.\n\n\u003e **PROTIP:**\n\u003e You can use the `Result` constant to mock the result's type and value in your tests. As they will have the exact expectations, your tests will check if the result clients are handling the result correctly.\n\nNow that you know the two modes, let's understand how expectations can be beneficial and powerful for defining contracts.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Type checking - Result Hooks\n\nThe `Solid::Result::Expectations` will check if the type of the result is valid. This checking will be performed in all methods that depend on the result’s type, such as `#success?`, `#failure?`, `#on`, `#on_type`, `#on_success`, `#on_failure`, and `#handle`.\n\n##### `#success?` and `#failure?`\n\nWhen checking whether a result is a success or failure, `Solid::Result::Expectations` will also verify if the result type is valid/expected. In case of an invalid type, an error will be raised.\n\n**Success example:**\n\n```ruby\nresult = Divide.new.call(10, 2)\n\nresult.success?                      # true\nresult.success?(:numbers)            # false\nresult.success?(:division_completed) # true\n\nresult.success?(:ok)\n# type :ok is not allowed. Allowed types: :numbers, :division_completed\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n**Failure example:**\n\n```ruby\nresult = Divide.new.call(10, '2')\n\nresult.failure?                    # true\nresult.failure?(:invalid_arg)      # true\nresult.failure?(:division_by_zero) # false\n\nresult.failure?(:err)\n# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### `#on` and `#on_type`\n\nIf you use `#on` or `#on_type` to execute a block, `Solid::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.\n\n```ruby\nresult = Divide.new.call(10, 2)\n\nresult\n  .on(:invalid_arg, :division_by_zero) { |msg| puts msg }\n  .on(:division_completed) { |number| puts \"The result is #{number}\" }\n\n# The code above will print 'The result is 5'\n\nresult.on(:number) { |_| :this_type_does_not_exist }\n# type :number is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### `#on_success` and `#on_failure`\n\nIf you use `#on_success` or `#on_failure` to execute a block, `Solid::Result::Expectations` will check whether the result type is valid/expected. Otherwise, an error will be raised.\n\n```ruby\nresult = Divide.new.call(10, '2')\n\nresult\n  .on_failure(:invalid_arg, :division_by_zero) { |msg| puts msg }\n  .on_success(:division_completed) { |number| puts \"The result is #{number}\" }\n\nresult\n  .on_success { |number| puts \"The result is #{number}\" }\n  .on_failure { |msg| puts msg }\n\n# Both codes above will print 'arg2 must be numeric'\n\nresult.on_success(:ok) { |_| :this_type_does_not_exist }\n# type :ok is not allowed. Allowed types: :numbers, :division_completed\n# (Solid::Result::Contract::Error::UnexpectedType)\n\nresult.on_failure(:err) { |_| :this_type_does_not_exist }\n# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### `#handle`\n\nThe `Solid::Result::Expectations` will also be applied on all the handlers defined by the `#handle` method/block.\n\n```ruby\nresult = Divide.call(10, 2)\n\nresult.handle do |on|\n  on.type(:ok) { |_| :this_type_does_not_exist }\nend\n# type :ok is not allowed. Allowed types: :numbers, :division_completed, :invalid_arg, :division_by_zero (Solid::Result::Contract::Error::UnexpectedType)\n\nresult.handle do |on|\n  on.success(:ok) { |_| :this_type_does_not_exist }\nend\n# type :ok is not allowed. Allowed types: :numbers, :division_completed (Solid::Result::Contract::Error::UnexpectedType)\n\nresult.handle do |on|\n  on.failure(:err) { |_| :this_type_does_not_exist }\nend\n# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n*PS: The `Divide` implementation is [here](#standalone-versus-mixin-mode).*\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Type checking - Result Creation\n\nThe `Solid::Result::Expectations` will be used on the result creation `Success()` and `Failure()` methods. So, when the result type is valid/expected, the result will be created. Otherwise, an error will be raised.\n\nThis works for both modes (standalone and mixin).\n\n##### Mixin mode\n\n```ruby\nmodule Divide\n  extend Solid::Result::Expectations.mixin(success: :ok, failure: :err)\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Success(:division_completed, arg1 / arg2)\n  end\nend\n\nDivide.call('4', 2)\n# type :invalid_arg is not allowed. Allowed types: :err\n# (Solid::Result::Contract::Error::UnexpectedType)\n\nDivide.call(4, 2)\n# type :division_completed is not allowed. Allowed types: :ok\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Standalone mode\n\n```ruby\nmodule Divide\n  Result = Solid::Result::Expectations.new(success: :ok, failure: :err)\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Result::Success(:division_completed, arg1 / arg2)\n  end\nend\n\nDivide.call('4', 2)\n# type :invalid_arg is not allowed. Allowed types: :err\n# (Solid::Result::Contract::Error::UnexpectedType)\n\nDivide.call(4, 2)\n# type :division_completed is not allowed. Allowed types: :ok\n# (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Value checking - Result Creation\n\nThe `Result::Expectations` supports two types of validations. The first is the type checking only, and the second is the type and value checking.\n\nTo define expectations for your result's values, you must declare a Hash with the type as the key and the value as the value. A value validator is any object that responds to `#===` (case equality operator).\n\n**Mixin mode:**\n\n```ruby\nmodule Divide\n  extend Solid::Result::Expectations.mixin(\n    success: {\n      numbers: -\u003e(value) { value.is_a?(Array) \u0026\u0026 value.size == 2 \u0026\u0026 value.all?(Numeric) },\n      division_completed: Numeric\n    },\n    failure: {\n      invalid_arg: String,\n      division_by_zero: String\n    }\n  )\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Success(:division_completed, arg1 / arg2)\n  end\nend\n```\n\n**Standalone mode:**\n\n```ruby\nmodule Divide\n  Result = Solid::Result::Expectations.new(\n    success: {\n      numbers: -\u003e(value) { value.is_a?(Array) \u0026\u0026 value.size == 2 \u0026\u0026 value.all?(Numeric) },\n      division_completed: Numeric\n    },\n    failure: {\n      invalid_arg: String,\n      division_by_zero: String\n    }\n  )\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Result::Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Result::Success(:division_completed, arg1 / arg2)\n  end\nend\n```\n\nThe value validation will only be performed through the methods `Success()` and `Failure()`.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Success()\n\n```ruby\nDivide::Result::Success(:ok)\n# type :ok is not allowed. Allowed types: :numbers, :division_completed (Solid::Result::Contract::Error::UnexpectedType)\n\nDivide::Result::Success(:numbers, [1])\n# value [1] is not allowed for :numbers type (Solid::Result::Contract::Error::UnexpectedValue)\n\nDivide::Result::Success(:division_completed, '2')\n# value \"2\" is not allowed for :division_completed type (Solid::Result::Contract::Error::UnexpectedValue)\n```\n\n##### Failure()\n\n```ruby\nDivide::Result::Failure(:err)\n# type :err is not allowed. Allowed types: :invalid_arg, :division_by_zero (Solid::Result::Contract::Error::UnexpectedType)\n\nDivide::Result::Failure(:invalid_arg, :arg1_must_be_numeric)\n# value :arg1_must_be_numeric is not allowed for :invalid_arg type (Solid::Result::Contract::Error::UnexpectedValue)\n\nDivide::Result::Failure(:division_by_zero, msg: 'arg2 must not be zero')\n# value {:msg=\u003e\"arg2 must not be zero\"} is not allowed for :division_by_zero type (Solid::Result::Contract::Error::UnexpectedValue)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Pattern Matching Support\n\nThe value checking has support for handling pattern-matching errors, and the cleanest way to do it is using the one-line pattern matching operators (`=\u003e` since Ruby 3.0) and (`in` Ruby 2.7).\n\nHow does this operator work? They raise an error when the pattern does not match but returns nil when it matches.\n\nBecause of this, you will need to enable `nil` as a valid value checking. You can do it through the `Solid::Result.configuration` or by allowing it directly on the mixin config.\n\n```ruby\nmodule Divide\n  extend Solid::Result::Expectations.mixin(\n    config: {\n      pattern_matching: { nil_as_valid_value_checking: true }\n    },\n    success: {\n      division_completed: -\u003e(value) { value =\u003e (Integer | Float) }\n    },\n    failure: { invalid_arg: String, division_by_zero: String }\n  )\n\n  def self.call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    arg2.zero? and return Failure(:division_by_zero, 'arg2 must not be zero')\n\n    Success(:division_completed, String(arg1 / arg2))\n  end\nend\n\nDivide.call(10, 5)\n# value \"2\" is not allowed for :division_completed type (\"2\": Float === \"2\" does not return true) (Solid::Result::Contract::Error::UnexpectedValue)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Solid::Result::Expectations.mixin` add-ons\n\nThe `Solid::Result::Expectations.mixin` also accepts the `config:` argument. It is a hash that can be used to define custom behaviors for the mixin.\n\n**Continue**\n\nIt is similar to `Solid::Result.mixin(config: { addon: { continue: true } })`. The key difference is that the expectations will ignore the `Continue(value)`.\n\nBased on this, use the `Success()` to produce a terminal result and `Continue()` to produce a result that will be used in the next step.\n\n```ruby\nclass Divide\n  include Solid::Result::Expectations.mixin(\n    config: { addon: { continue: true } },\n    success: :division_completed,\n    failure: %i[invalid_arg division_by_zero]\n  )\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:invalid_arg, 'arg2 must be numeric')\n\n    Continue([arg1, arg2])\n  end\n\n  def validate_nonzero(numbers)\n    return Continue(numbers) unless numbers.last.zero?\n\n    Failure(:division_by_zero, 'arg2 must not be zero')\n  end\n\n  def divide((number1, number2))\n    Success(:division_completed, number1 / number2)\n  end\nend\n\nresult = Divide.new.call(4, 2)\n# =\u003e #\u003cSolid::Result::Success type=:division_completed value=2\u003e\n\n# The example below shows an error because the :ok type is not allowed.\n# But look at the allowed types have only one type (:division_completed).\n# This is because the :_continue_ type is ignored by the expectations.\n#\nresult.success?(:ok)\n# type :ok is not allowed. Allowed types: :division_completed (Solid::Result::Contract::Error::UnexpectedType)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### `Solid::Output`\n\nThe `Solid::Output` is a `Solid::Result`, meaning it has all the features of the `Solid::Result`. The main difference is that it only accepts keyword arguments as a value, which applies to the `and_then`: The called methods must receive keyword arguments, and the dependency injection will be performed through keyword arguments.\n\nAs the input/output are hashes, the results of each `and_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, the `Solid::Output` has the `#and_expose` method to expose only the desired keys from the accumulated result.\n\n#### Defining successes and failures\n\nAs the `Solid::Result`, you can declare success and failures directly from `Solid::Output`.\n\n```ruby\nSolid::Output::Success(:ok, a: 1, b: 2)\n#\u003cSolid::Output::Success type=:ok value={:a=\u003e1, :b=\u003e2}\u003e\n\nSolid::Output::Failure(:err, message: 'something went wrong')\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"something went wrong\"}\u003e\n```\n\nBut different from `Solid::Result` that accepts any value, the `Solid::Output` only takes keyword arguments.\n\n```ruby\nSolid::Output::Success(:ok, [1, 2])\n# wrong number of arguments (given 2, expected 1) (ArgumentError)\n\nSolid::Output::Failure(:err, { message: 'something went wrong' })\n# wrong number of arguments (given 2, expected 1) (ArgumentError)\n\n#\n# Use ** to convert a hash to keyword arguments\n#\nSolid::Output::Success(:ok, **{ message: 'hashes can be converted to keyword arguments' })\n#\u003cSolid::Output::Success type=:ok value={:message=\u003e\"hashes can be converted to keyword arguments\"}\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Hash methods\n\nThe `Solid::Output` only accepts hashes as its values. Because of this, its instances have some Hash's methods to query/access the values. The available methods are:\n\n- `#slice` to extract only the desired keys.\n- `#[]`, `#dig`, `#fetch` to access the values.\n- `#values_at` and `#fetch_values` to get the values of the desired keys.\n\n```ruby\nresult = Solid::Output::Success(:ok, a: 1, b: 2, c: {d: 4})\n\nresult[:a]         # 1\nresult.fetch(:a)   # 1\nresult.dig(:c, :d) # 4\n\nresult.slice(:a, :b) # {:a=\u003e1, :b=\u003e2}\n\nresult.values_at(:a, :b) # [1, 2]\nresult.fetch_values(:a, :b) # [1, 2]\n```\n\nThese methods are available for `Solid::Output::Success` and `Solid::Output::Failure` instances.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Solid::Output.mixin`\n\nAs in the `Solid::Result`, you can use the `Solid::Output.mixin` to add the `Success()` and `Failure()` methods to your classes/modules.\n\nLet's see this feature and the data accumulation in action:\n\n##### Class example (Instance Methods)\n\n```ruby\nrequire 'logger'\n\nclass Divide\n  include Solid::Output.mixin\n\n  def call(arg1, arg2, logger: ::Logger.new(STDOUT))\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide, logger: logger)\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')\n\n    Success(:ok, number1: arg1, number2: arg2)\n  end\n\n  def validate_nonzero(number2:, **)\n    return Success(:ok) if number2.nonzero?\n\n    Failure(:err, message: 'arg2 must not be zero')\n  end\n\n  #\n  # The logger was injected via #and_then and keyword arguments\n  #\n  def divide(number1:, number2:, logger:)\n    result = number1 / number2\n\n    logger.info(\"The division result is #{result}\")\n\n    Success(:ok, number: result)\n  end\nend\n\nDivide.new.call(10, 5)\n# I, [2023-10-27T01:51:46.905004 #76915]  INFO -- : The division result is 2\n#\u003cSolid::Output::Success type=:ok value={:number=\u003e2}\u003e\n\nDivide.new.call('10', 5)\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg1 must be numeric\"}\u003e\n\nDivide.new.call(10, '5')\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg2 must be numeric\"}\u003e\n\nDivide.new.call(10, 0)\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg2 must not be zero\"}\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### `and_expose`\n\nThis allows you to expose only the desired keys from the accumulated result. It can be used with any `Solid::Output` object.\n\nLet's add it to the previous example:\n\n```ruby\nclass Divide\n  include Solid::Output.mixin\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n      .and_expose(:division_completed, [:number])\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')\n\n    Success(:ok, number1: arg1, number2: arg2)\n  end\n\n  def validate_nonzero(number2:, **)\n    return Success(:ok) if number2.nonzero?\n\n    Failure(:err, message: 'arg2 must not be zero')\n  end\n\n  def divide(**input)\n    Success(:ok, number: input.values.reduce(:/), **input)\n  end\nend\n\nDivide.new.call(10, 5)\n#\u003cSolid::Output::Success type=:division_completed value={:number=\u003e2}\u003e\n```\n\nAs you can see, even with `divide` success exposing the division number with all the accumulated data (`**input`), the `#and_expose` could generate a new success with a new type and only with the desired keys.\n\nRemove the `#and_expose` call to see the difference. This will be the outcome:\n\n```ruby\nDivide.new.call(10, 5)\n#\u003cSolid::Output::Success type=:ok value={:number=\u003e2, :number1=\u003e10, :number2=\u003e5}\u003e\n```\n\n\u003e PS: The `#and_expose` produces a terminal success by default. This means the next step will not be executed even if you call `#and_then` after `#and_expose`. To change this behavior, you can pass `terminal: false` to `#and_expose`.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n##### Module example (Singleton Methods)\n\n`Solid::Output.mixin` can also produce singleton methods. Below is an example using a module (but it could be a class, too).\n\n```ruby\nmodule Divide\n  extend self, Solid::Output.mixin\n\n  def call(arg1, arg2)\n    validate_numbers(arg1, arg2)\n      .and_then(:validate_nonzero)\n      .and_then(:divide)\n      .and_expose(:division_completed, [:number])\n  end\n\n  private\n\n  def validate_numbers(arg1, arg2)\n    arg1.is_a?(::Numeric) or return Failure(:err, message: 'arg1 must be numeric')\n    arg2.is_a?(::Numeric) or return Failure(:err, message: 'arg2 must be numeric')\n\n    Success(:ok, number1: arg1, number2: arg2)\n  end\n\n  def validate_nonzero(number2:, **)\n    return Success(:ok) if number2.nonzero?\n\n    Failure(:err, message: 'arg2 must not be zero')\n  end\n\n  def divide(number1:, number2:)\n    Success(:ok, number: number1 / number2)\n  end\nend\n\nDivide.call(10, 5)\n#\u003cSolid::Output::Success type=:division_completed value={:number=\u003e2}\u003e\n\nDivide.call('10', 5)\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg1 must be numeric\"}\u003e\n\nDivide.call(10, '5')\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg2 must be numeric\"}\u003e\n\nDivide.call(10, 0)\n#\u003cSolid::Output::Failure type=:err value={:message=\u003e\"arg2 must not be zero\"}\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Solid::Output::Expectations`\n\nThe `Solid::Output::Expectations` is a `Solid::Result::Expectations` with the `Solid::Output` features.\n\nThis is an example using the mixin mode, but the standalone mode is also supported.\n\n```ruby\nclass Divide\n  include Solid::Output::Expectations.mixin(\n    config: {\n      pattern_matching: { nil_as_valid_value_checking: true }\n    },\n    success: {\n      division_completed: -\u003e(value) { value =\u003e { number: Numeric } }\n    },\n    failure: {\n      invalid_arg:      -\u003e(value) { value =\u003e { message: String } },\n      division_by_zero: -\u003e(value) { value =\u003e { message: String } }\n    }\n  )\n\n  def call(arg1, arg2)\n    arg1.is_a?(Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')\n    arg2.is_a?(Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')\n\n    arg2.zero? and return Failure(:division_by_zero, message: 'arg2 must not be zero')\n\n    Success(:division_completed, number: (arg1 / arg2))\n  end\nend\n\nDivide.new.call(10, 5)\n#\u003cSolid::Output::Success type=:division_completed value={:number=\u003e2}\u003e\n```\n\nAs in the `Solid::Result::Expectations.mixin`, the `Solid::Output::Expectations.mixin` will add a Result constant in the target class. It can generate success/failure results, which ensure the mixin expectations.\n\nLet's see this using the previous example:\n\n```ruby\nDivide::Result::Success(:division_completed, number: 2)\n#\u003cSolid::Output::Success type=:division_completed value={:number=\u003e2}\u003e\n\nDivide::Result::Success(:division_completed, number: '2')\n# value {:number=\u003e\"2\"} is not allowed for :division_completed type ({:number=\u003e\"2\"}: Numeric === \"2\" does not return true) (Solid::Result::Contract::Error::UnexpectedValue)\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Mixin add-ons\n\nThe `Solid::Output.mixin` and `Solid::Output::Expectations.mixin` also accepts the `config:` argument. And it works the same way as the `Solid::Result` mixins.\n\n**given**\n\nThis addon is enabled by default. It will create the `Given(*value)` method. Use it to add a value to the result chain and invoke the next step (through `and_then`).\n\nYou can turn it off by passing `given: false` to the `config:` argument or using the `Solid::Result.configuration`.\n\nThe `Given()` addon for a Solid::Output can be called with one or more arguments. The arguments will be converted to a hash (`to_h`) and merged to define the first value of the result chain.\n\n**continue**\n\nThe `Solid::Output.mixin(config: { addon: { continue: true } })` or `Solid::Output::Expectations.mixin(config: { addon: { continue: true } })` creates the `Continue(value)` method and change the `Success()` behavior to terminate the step chain.\n\nSo, if you want to advance to the next step, you must use `Continue(**value)` instead of `Success(type, **value)`. Otherwise, the step chain will be terminated.\n\nLet's use a mix of `Solid::Output` features to see in action with this add-on:\n\n```ruby\nmodule Division\n  require 'logger'\n\n  extend self, Solid::Output::Expectations.mixin(\n    config: {\n      addon:            { continue: true },\n      pattern_matching: { nil_as_valid_value_checking: true }\n    },\n    success: {\n      division_completed: -\u003e(value) { value =\u003e { number: Numeric } }\n    },\n    failure: {\n      invalid_arg:      -\u003e(value) { value =\u003e { message: String } },\n      division_by_zero: -\u003e(value) { value =\u003e { message: String } }\n    }\n  )\n\n  def call(arg1, arg2, logger: ::Logger.new(STDOUT))\n    Given(number1: arg1, number2: arg2)\n      .and_then(:require_numbers)\n      .and_then(:check_for_zeros)\n      .and_then(:divide, logger: logger)\n      .and_expose(:division_completed, [:number])\n  end\n\n  private\n\n  def require_numbers(number1:, number2:)\n    number1.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg1 must be numeric')\n    number2.is_a?(::Numeric) or return Failure(:invalid_arg, message: 'arg2 must be numeric')\n\n    Continue()\n  end\n\n  def check_for_zeros(number1:, number2:)\n    return Failure(:division_by_zero, message: 'arg2 must not be zero') if number2.zero?\n\n    return Success(:division_completed, number: 0) if number1.zero?\n\n    Continue()\n  end\n\n  def divide(number1:, number2:, logger:)\n    result = number1 / number2\n\n    logger.info(\"The division result is #{result}\")\n\n    Continue(number: result)\n  end\nend\n\nDivision.call(14, 2)\n# I, [2023-10-27T02:01:05.812388 #77823]  INFO -- : The division result is 7\n#\u003cSolid::Output::Success type=:division_completed value={:number=\u003e7}\u003e\n\nDivision.call(0, 2)\n##\u003cSolid::Output::Success type=:division_completed value={:number=\u003e0}\u003e\n\nDivision.call('14', 2)\n#\u003cSolid::Output::Failure type=:invalid_arg value={:message=\u003e\"arg1 must be numeric\"}\u003e\n\nDivision.call(14, '2')\n#\u003cSolid::Output::Failure type=:invalid_arg value={:message=\u003e\"arg2 must be numeric\"}\u003e\n\nDivision.call(14, 0)\n#\u003cSolid::Output::Failure type=:division_by_zero value={:message=\u003e\"arg2 must not be zero\"}\u003e\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Pattern Matching\n\nThe `Solid::Result` and `Solid::Output` also provides support to pattern matching.\n\n### `Solid::Result`\n\nIn the further examples, I will use the `Divide` lambda to exemplify its usage.\n\n```ruby\nDivide = lambda do |arg1, arg2|\n  arg1.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg1 must be numeric')\n  arg2.is_a?(::Numeric) or return Solid::Result::Failure(:invalid_arg, 'arg2 must be numeric')\n\n  return Solid::Result::Failure(:division_by_zero, 'arg2 must not be zero') if arg2.zero?\n\n  Solid::Result::Success(:division_completed, arg1 / arg2)\nend\n```\n\n#### `Array`/`Find` patterns\n\n```ruby\ncase Divide.call(4, 2)\nin Solid::Failure[:invalid_arg, msg] then puts msg\nin Solid::Failure[:division_by_zero, msg] then puts msg\nin Solid::Success[:division_completed, num] then puts num\nend\n\n# The code above will print: 2\n\ncase Divide.call(4, 0)\nin Solid::Failure[:invalid_arg, msg] then puts msg\nin Solid::Failure[:division_by_zero, msg] then puts msg\nin Solid::Success[:division_completed, num] then puts num\nend\n\n# The code above will print: arg2 must not be zero\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Hash` patterns\n\n```ruby\ncase Divide.call(10, 2)\nin Solid::Failure(type: :invalid_arg, value: msg) then puts msg\nin Solid::Failure(type: :division_by_zero, value: msg) then puts msg\nin Solid::Success(type: :division_completed, value: num) then puts num\nend\n\n# The code above will print: 5\n\ncase Divide.call('10', 2)\nin Solid::Failure(type: :invalid_arg, value: msg) then puts msg\nin Solid::Failure(type: :division_by_zero, value: msg) then puts msg\nin Solid::Success(type: :division_completed, value: num) then puts num\nend\n\n# The code above will print: arg1 must be numeric\n```\n\nYou can also use `Solid::Result::Success` and `Solid::Result::Failure` as patterns.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n\n### `Solid::Output`\n\nIn the further examples, I will use the `Divide` lambda to exemplify its usage.\n\n```ruby\nDivide = lambda do |arg1, arg2|\n  arg1.is_a?(::Numeric) or return Solid::Output::Failure(:invalid_arg, err: 'arg1 must be numeric')\n  arg2.is_a?(::Numeric) or return Solid::Output::Failure(:invalid_arg, err: 'arg2 must be numeric')\n\n  return Solid::Output::Failure(:division_by_zero, err: 'arg2 must not be zero') if arg2.zero?\n\n  Solid::Output::Success(:division_completed, num: arg1 / arg2)\nend\n```\n\n#### `Array`/`Find` patterns\n\n```ruby\ncase Divide.call(4, 2)\nin Solid::Failure[:invalid_arg, {msg:}] then puts msg\nin Solid::Failure[:division_by_zero, {msg:}] then puts msg\nin Solid::Success[:division_completed, {num:}] then puts num\nend\n\n# The code above will print: 2\n\ncase Divide.call(4, 0)\nin Solid::Failure[:invalid_arg, {msg:}] then puts msg\nin Solid::Failure[:division_by_zero, {msg:}] then puts msg\nin Solid::Success[:division_completed, {num:}] then puts num\nend\n\n# The code above will print: arg2 must not be zero\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `Hash` patterns\n\nIf you don't provide the keys :type and :value, the pattern will match the result value.\n\n```ruby\ncase Divide.call(10, 2)\nin Solid::Failure({msg:}) then puts msg\nin Solid::Success({num:}) then puts num\nend\n```\n\n```ruby\ncase Divide.call(10, 2)\nin Solid::Failure(type: :invalid_arg, value: {msg:}) then puts msg\nin Solid::Failure(type: :division_by_zero, value: {msg:}) then puts msg\nin Solid::Success(type: :division_completed, value: {num:}) then puts num\nend\n\n# The code above will print: 5\n\ncase Divide.call('10', 2)\nin Solid::Failure(type: :invalid_arg, value: {msg:}) then puts {msg:}\nin Solid::Failure(type: :division_by_zero, value: {msg:}) then puts msg\nin Solid::Success(type: :division_completed, value: {num:}) then puts num\nend\n\n# The code above will print: arg1 must be numeric\n```\n\nYou can also use `Solid::Output::Success` and `Solid::Output::Failure` as patterns.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### How to pattern match without the concept of success and failure\n\nYou can use the classes `Solid::Result` and `Solid::Output` as patterns, and the pattern matching will work without the concept of success and failure.\n\n```ruby\ncase Divide.call(10, 2)\nin Solid::Output(:invalid_arg, {msg:}) then puts msg\nin Solid::Output(:division_by_zero, {msg:}) then puts msg\nin Solid::Output(:division_completed, {num:}) then puts num\nend\n\ncase Divide.call(10, 2)\nin Solid::Result(:invalid_arg, msg) then puts msg\nin Solid::Result(:division_by_zero, msg) then puts msg\nin Solid::Result(:division_completed, num) then puts num\nend\n```\n\nThe `Solid::Result` will also work with the `Solid::Output`, but the opposite won't.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## `Solid::Result.event_logs`\n\nUse `Solid::Result.event_logs(\u0026block)` to track all the results produced in the same or between different operations (it works with `Solid::Result` and `Solid::Output`). When there is a nesting of `event_logs` blocks, this mechanism will be able to correlate parent and child blocks and present the duration of all operations in milliseconds.\n\nWhen you wrap the creation of the result with `Solid::Result.event_logs`, the final one will expose all the event log records through the `Solid::Result#event_logs` method.\n\n```ruby\nclass Division\n  include Solid::Result.mixin(config: { addon: { continue: true } })\n\n  def call(arg1, arg2)\n    Solid::Result.event_logs(name: 'Division', desc: 'divide two numbers') do\n      Given([arg1, arg2])\n        .and_then(:require_numbers)\n        .and_then(:check_for_zeros)\n        .and_then(:divide)\n    end\n  end\n\n  private\n\n  ValidNumber = -\u003e(arg) { arg.is_a?(Numeric) \u0026\u0026 (!arg.respond_to?(:finite?) || arg.finite?) }\n\n  def require_numbers((arg1, arg2))\n    ValidNumber[arg1] or return Failure(:invalid_arg, 'arg1 must be a valid number')\n    ValidNumber[arg2] or return Failure(:invalid_arg, 'arg2 must be a valid number')\n\n    Continue([arg1, arg2])\n  end\n\n  def check_for_zeros(numbers)\n    num1, num2 = numbers\n\n    return Failure(:division_by_zero, 'num2 cannot be zero') if num2.zero?\n\n    num1.zero? ? Success(:division_completed, 0) : Continue(numbers)\n  end\n\n  def divide((num1, num2))\n    Success(:division_completed, num1 / num2)\n  end\nend\n\nmodule SumDivisionsByTwo\n  extend self, Solid::Result.mixin\n\n  def call(*numbers)\n    Solid::Result.event_logs(name: 'SumDivisionsByTwo') do\n      divisions = numbers.map { |number| Division.new.call(number, 2) }\n\n      if divisions.any?(\u0026:failure?)\n        Failure(:errors, divisions.select(\u0026:failure?).map(\u0026:value))\n      else\n        Success(:sum, divisions.sum(\u0026:value))\n      end\n    end\n  end\nend\n```\n\nLet's see the result of the `SumDivisionsByTwo` call:\n\n```ruby\nresult = SumDivisionsByTwo.call(20, 10)\n# =\u003e #\u003cSolid::Result::Success type=:sum value=15\u003e\n\nresult.event_logs\n{\n  :version =\u003e 1,\n  :metadata =\u003e {\n    :duration =\u003e 0,   # milliseconds\n    :trace_id =\u003e nil, # can be set through configuration\n    :ids =\u003e {\n      :tree =\u003e [0, [[1, []], [2, []]]],\n      :matrix =\u003e { 0 =\u003e [0, 0], 1 =\u003e [1, 1], 2 =\u003e [2, 1]},\n      :level_parent =\u003e { 0 =\u003e [0, 0], 1 =\u003e [1, 0], 2 =\u003e [1, 0]}\n    }\n  },\n  :records=\u003e [\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e1, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_given_, :value=\u003e[20, 2], :source=\u003e\u003cDivision:0x0000000102fd7ed0\u003e},\n      :and_then =\u003e {},\n      :time =\u003e 2024-01-26 02:53:11.310346 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e1, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_continue_, :value=\u003e[20, 2], :source=\u003e\u003cDivision:0x0000000102fd7ed0\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:require_numbers},\n      :time =\u003e 2024-01-26 02:53:11.310392 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e1, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_continue_, :value=\u003e[20, 2], :source=\u003e\u003cDivision:0x0000000102fd7ed0\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:check_for_zeros},\n      :time=\u003e2024-01-26 02:53:11.310403 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e1, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:division_completed, :value=\u003e10, :source=\u003e\u003cDivision:0x0000000102fd7ed0\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:divide},\n      :time =\u003e 2024-01-26 02:53:11.310409 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e2, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_given_, :value=\u003e[10, 2], :source=\u003e\u003cDivision:0x0000000102fd6378\u003e},\n      :and_then =\u003e {},\n      :time =\u003e 2024-01-26 02:53:11.310424 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e2, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_continue_, :value=\u003e[10, 2], :source=\u003e\u003cDivision:0x0000000102fd6378\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:require_numbers},\n      :time =\u003e 2024-01-26 02:53:11.310428 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e2, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:_continue_, :value=\u003e[10, 2], :source=\u003e\u003cDivision:0x0000000102fd6378\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:check_for_zeros},\n      :time =\u003e 2024-01-26 02:53:11.310431 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e2, :name=\u003e\"Division\", :desc=\u003e\"divide two numbers\"},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:division_completed, :value=\u003e5, :source=\u003e\u003cDivision:0x0000000102fd6378\u003e},\n      :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:divide},\n      :time =\u003e 2024-01-26 02:53:11.310434 UTC\n    },\n    {\n      :root =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :parent =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :current =\u003e {:id=\u003e0, :name=\u003e\"SumDivisionsByTwo\", :desc=\u003enil},\n      :result =\u003e {:kind=\u003e:success, :type=\u003e:sum, :value=\u003e15, :source=\u003eSumDivisionsByTwo},\n      :and_then =\u003e {},\n      :time =\u003e 2024-01-26 02:53:11.310444 UTC\n    }\n  ]\n}\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### `metadata: {ids:}`\n\nThe `:ids` metadata property is a hash with three properties:\n- `:tree`, a graph/tree representation of the id of each `event_logs` block.\n- `:level_parent`, a hash with the level (depth) of each block and its parent id.\n- `:matrix`, a matrix representation of the event logs ids. It is a simplification of the `:tree` property.\n\nUse these data structures to build your own visualization.\n\n\u003e Check out [Event Logs Listener example](examples/single_listener/lib/single_event_logs_listener.rb) to see how a listener can be used to build a STDOUT visualization, using these properties.\n\n```ruby\n# tree:\n# A graph representation (array of arrays) of the each event logs block id.\n#\n0                  # [0, [\n|- 1               #   [1, [[2, []]]],\n|  |- 2            #   [3, []],\n|- 3               #   [4, [\n|- 4               #     [5, []],\n|  |- 5            #     [6, [[7, []]]]\n|  |- 6            #   ]],\n|     |- 7         #   [8, []]\n|- 8               # ]]\n\n# level_parent:\n# The event logs ids are the keys, and the level (depth) and parent id the values.\n                   # {\n0                  #   0 =\u003e [0, 0],\n|- 1               #   1 =\u003e [1, 0],\n|  |- 2            #   2 =\u003e [2, 1],\n|- 3               #   3 =\u003e [1, 0],\n|- 4               #   4 =\u003e [1, 0],\n|  |- 5            #   5 =\u003e [2, 4],\n|  |- 6            #   6 =\u003e [2, 4],\n|     |- 7         #   7 =\u003e [3, 6],\n|- 8               #   8 =\u003e [1, 0]\n                   # }\n\n# matrix:\n# The rows are the direct blocks from the root block,\n# and the columns are the nested blocks from the direct ones.\n                   # {\n0 | 1 | 2 | 3 | 4  #   0 =\u003e [0, 0],\n- | - | - | - | -  #   1 =\u003e [1, 1],\n0 |   |   |   |    #   2 =\u003e [1, 2],\n1 | 1 | 2 |   |    #   3 =\u003e [2, 1],\n2 | 3 |   |   |    #   4 =\u003e [3, 1],\n3 | 4 | 5 | 6 | 7  #   5 =\u003e [3, 2],\n4 | 8 |   |   |    #   6 =\u003e [3, 3],\n                   #   7 =\u003e [3, 4],\n                   #   8 =\u003e [4, 1]\n                   # }\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### Configuration\n\n#### Turning on/off\n\nYou can use `Solid::Result.config.feature.disable!(event_logs)` and `Solid::Result.config.feature.enable!(event_logs)` to turn on/off the `Solid::Result.event_logs` feature.\n\n```ruby\nSolid::Result.configuration do |config|\n  config.feature.disable!(event_logs)\nend\n\nresult = SumDivisionsByTwo.call(20, 10)\n# =\u003e #\u003cSolid::Result::Success type=:sum value=15\u003e\n\nresult.event_logs\n{\n  :version=\u003e1,\n  :records=\u003e[],\n  :metadata=\u003e{\n    :duration=\u003e0,\n    :ids=\u003e{:tree=\u003e[], :matrix=\u003e{}, :level_parent=\u003e{}}, :trace_id=\u003enil\n  }\n}\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Setting a `trace_id` fetcher\n\nYou can define a lambda (arity 0) to fetch the trace_id. This lambda will be called before the first event logs block and will be used to set the `:trace_id` in the `:metadata` property.\n\nUse to correlate different or the same operation (executed multiple times).\n\n```ruby\nSolid::Result.config.event_logs.trace_id = -\u003e { Thread.current[:solid_result_event_logs_trace_id] }\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Setting a `listener`\n\nYou can define a listener to be called during the event logs tracking (check out [this example](examples/single_listener/lib/single_event_logs_listener.rb)). It must be a class that includes `Solid::Result::EventLogs::Listener`.\n\nUse it to build your additional logic on top of the tracking. Examples:\n  - Log the event  logs.\n  - Perform the tracing.\n  - Instrument the event logs (measure/report).\n  - Build a visualization (Diagrams, using the `records:` + `metadata: {ids:}` properties).\n\nAfter implementing your listener, you can set it to the `Solid::Result.config.event_logs.listener=`:\n\n```ruby\nSolid::Result.config.event_logs.listener = MyEventLogsListener\n```\n\nSee the example below to understand how to implement one:\n\n```ruby\nclass MyEventLogsListener\n  include Solid::Result::EventLogs::Listener\n\n  # A listener will be initialized before the first event logs block, and it is discarded after the last one.\n  def initialize\n  end\n\n  # This method will be called before each event logs block.\n  # The parent block will be called first in the case of nested ones.\n  #\n  # @param scope: {:id=\u003e1, :name=\u003e\"SomeOperation\", :desc=\u003e\"Optional description\"}\n  def on_start(scope:)\n  end\n\n  # This method will wrap all the event logs in the same block.\n  # It can be used to perform an instrumentation (measure/report).\n  #\n  # @param scope: {:id=\u003e1, :name=\u003e\"SomeOperation\", :desc=\u003e\"Optional description\"}\n  def around_event_logs(scope:)\n    yield\n  end\n\n  # This method will wrap each and_then call.\n  # It can be used to perform an instrumentation of the and_then calls.\n  #\n  # @param scope: {:id=\u003e1, :name=\u003e\"SomeOperation\", :desc=\u003e\"Optional description\"}\n  # @param and_then:\n  #  {:type=\u003e:block, :arg=\u003e:some_injected_value}\n  #  {:type=\u003e:method, :arg=\u003e:some_injected_value, :method_name=\u003e:some_method_name}\n  def around_and_then(scope:, and_then:)\n    yield\n  end\n\n  # This method will be called after each result recording/tracking.\n  #\n  # @param record:\n  # {\n  #   :root =\u003e {:id=\u003e0, :name=\u003e\"RootOperation\", :desc=\u003enil},\n  #   :parent =\u003e {:id=\u003e0, :name=\u003e\"RootOperation\", :desc=\u003enil},\n  #   :current =\u003e {:id=\u003e1, :name=\u003e\"SomeOperation\", :desc=\u003enil},\n  #   :result =\u003e {:kind=\u003e:success, :type=\u003e:_continue_, :value=\u003e{some: :thing}, :source=\u003e\u003cMyProcess:0x0000000102fd6378\u003e},\n  #   :and_then =\u003e {:type=\u003e:method, :arg=\u003enil, :method_name=\u003e:some_method},\n  #   :time =\u003e 2024-01-26 02:53:11.310431 UTC\n  # }\n  def on_record(record:)\n  end\n\n  # This method will be called at the end of the event logs tracking.\n  #\n  # @param event_logs:\n  # {\n  #   :version =\u003e 1,\n  #   :metadata =\u003e {\n  #     :duration =\u003e 0,\n  #     :trace_id =\u003e nil,\n  #     :ids =\u003e {\n  #       :tree =\u003e [0, [[1, []], [2, []]]],\n  #       :matrix =\u003e { 0 =\u003e [0, 0], 1 =\u003e [1, 1], 2 =\u003e [2, 1]},\n  #       :level_parent =\u003e { 0 =\u003e [0, 0], 1 =\u003e [1, 0], 2 =\u003e [1, 0]}\n  #     }\n  #   },\n  #   :records =\u003e [\n  #     # ...\n  #   ]\n  # }\n  def on_finish(event_logs:)\n  end\n\n  # This method will be called when an exception is raised during the event logs tracking.\n  #\n  # @param exception: Exception\n  # @param event_logs: Hash\n  def before_interruption(exception:, event_logs:)\n  end\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Setting multiple `listeners`\n\nYou can use `Solid::Result::EventLogs::Listeners[]` to creates a listener of listeners (check out [this example](examples/multiple_listeners/Rakefile)), which will be called in the order they were added.\n\n**Attention:** It only allows one listener to handle `around_and_then` and another `around_event_logs` records.\n\n\u003e The example below defines different listeners to handle `around_and_then` and `around_event_logs,` but it is also possible to define a listener to handle both.\n\n```ruby\nclass AroundAndThenListener\n  include Solid::Result::EventLogs::Listener\n\n  # It must be a static/singleton method.\n  def self.around_and_then?\n    true\n  end\n\n  def around_and_then(scope:, and_then:)\n    #...\n  end\nend\n\nclass AroundEventLogsListener\n  include Solid::Result::EventLogs::Listener\n\n  # It must be a static/singleton method.\n  def self.around_event_logs?\n    true\n  end\n\n  def around_event_logs(scope:)\n    #...\n  end\nend\n\nclass MyEventLogsListener\n  include Solid::Result::EventLogs::Listener\nend\n```\n\nHow to use it:\n\n```ruby\n# The listeners will be called in the order they were added.\nSolid::Result.config.event_logs.listener = Solid::Result::EventLogs::Listeners[\n  MyEventLogsListener,\n  AroundAndThenListener,\n  AroundEventLogsListener\n]\n```\n\n\u003e Check out [this example](examples/multiple_listeners) to see a listener to print the event logs and another to store them in the database.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## `Solid::Result.configuration`\n\nThe `Solid::Result.configuration` allows you to configure default behaviors for `Solid::Result` and `Solid::Output` through a configuration block. After using it, the configuration is frozen, ensuring the expected behaviors for your application.\n\n\u003e Note: You can use `Solid::Result.configuration(freeze: false) {}` to avoid the freezing. This can be useful in tests. Please be sure to use it with caution.\n\n```ruby\nSolid::Result.configuration do |config|\n  config.addon.enable!(:given, :continue)\n\n  config.constant_alias.enable!('Result', 'Solid::Output')\n\n  config.pattern_matching.disable!(:nil_as_valid_value_checking)\n\n  # config.feature.disable!(:expectations) if ::Rails.env.production?\nend\n```\n\nUse `disable!` to disable a feature and `enable!` to enable it.\n\nLet's see what each configuration in the example above does:\n\n### `config.addon.enable!(:given, :continue)` \u003c!-- omit in toc --\u003e\n\nThis configuration enables the `Continue()` method for `Solid::Result.mixin`, `Solid::Output.mixin`, `Solid::Result::Expectation.mixin`, and `Solid::Output::Expectation.mixin`. Link to documentations: [(1)](#add-ons) [(2)](#mixin-add-ons).\n\nIt is also enabling the `Given()` which is already enabled by default. Link to documentation: [(1)](#add-ons) [(2)](#mixin-add-ons).\n\n### `config.constant_alias.enable!('Result', 'Solid::Output')` \u003c!-- omit in toc --\u003e\n\nThis configuration make `Result` a constant alias for `Solid::Result`, and `Solid::Output` a constant alias for `Solid::Output`.\n\nLink to documentations:\n- [Result alias](#solidresult-versus-result)\n\n### `config.pattern_matching.disable!(:nil_as_valid_value_checking)` \u003c!-- omit in toc --\u003e\n\nThis configuration disables the `nil_as_valid_value_checking` for `Solid::Result` and `Solid::Output`. Link to [documentation](#pattern-matching-support).\n\n### `config.feature.disable!(:expectations)` \u003c!-- omit in toc --\u003e\n\nThis configuration turns off the expectations for `Solid::Result` and `Solid::Output`. The expectations are helpful in development and test environments, but they can be disabled in production environments for performance gain.\n\nPS: I'm using `::Rails.env.production?` to check the environment, but you can use any logic you want.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n### `Solid::Result.config`\n\nThe `Solid::Result.config` allows you to access the current configuration.\n\n#### **Solid::Result.config.addon** \u003c!-- omit in toc --\u003e\n\n```ruby\nSolid::Result.config.addon.enabled?(:continue)\nSolid::Result.config.addon.enabled?(:given)\n\nSolid::Result.config.addon.options\n# {\n#   :continue=\u003e{\n#     :enabled=\u003efalse,\n#     :affects=\u003e[\n#       \"Solid::Result.mixin\",\n#       \"Solid::Output.mixin\",\n#       \"Solid::Result::Expectations.mixin\",\n#       \"Solid::Output::Expectations.mixin\"\n#     ]\n#   },\n#   :given=\u003e{\n#     :enabled=\u003etrue,\n#     :affects=\u003e[\n#       \"Solid::Result.mixin\",\n#       \"Solid::Output.mixin\",\n#       \"Solid::Result::Expectations.mixin\",\n#       \"Solid::Output::Expectations.mixin\"\n#     ]\n#   }\n# }\n```\n\n#### **Solid::Result.config.constant_alias** \u003c!-- omit in toc --\u003e\n\n```ruby\nSolid::Result.config.constant_alias.enabled?('Result')\n\nSolid::Result.config.constant_alias.options\n# {\n#   \"Result\"=\u003e{:enabled=\u003efalse, :affects=\u003e[\"Object\"]}\n# }\n```\n\n#### **Solid::Result.config.pattern_matching** \u003c!-- omit in toc --\u003e\n\n```ruby\nSolid::Result.config.pattern_matching.enabled?(:nil_as_valid_value_checking)\n\nSolid::Result.config.pattern_matching.options\n# {\n#   :nil_as_valid_value_checking=\u003e{\n#     :enabled=\u003efalse,\n#     :affects=\u003e[\n#       \"Solid::Result::Expectations,\n#       \"Solid::Output::Expectations\"\n#     ]\n#   }\n# }\n```\n\n#### **Solid::Result.config.feature** \u003c!-- omit in toc --\u003e\n\n```ruby\nSolid::Result.config.feature.enabled?(:expectations)\n\nSolid::Result.config.feature.options\n# {\n#   :expectations=\u003e{\n#     :enabled=\u003etrue,\n#     :affects=\u003e[\n#       \"Solid::Result::Expectations,\n#       \"Solid::Output::Expectations\"\n#     ]\n#   },\n#   event_logs=\u003e{\n#     :enabled=\u003etrue,\n#     :affects=\u003e[\n#       \"Solid::Result\",\n#       \"Solid::Output\"\n#     ]\n#   },\n#   :and_then!=\u003e{\n#     :enabled=\u003efalse,\n#     :affects=\u003e[\n#       \"Solid::Result\",\n#       \"Solid::Output\"\n#     ]\n#   },\n# }\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## `Solid::Result#and_then!`\n\nIn the Ruby ecosystem, several gems facilitate operation composition using classes and modules. Two notable examples are the `interactor` gem and the `u-case` gem.\n\n**`interactor` gem example**\n\n```ruby\nclass PlaceOrder\n  include Interactor::Organizer\n\n  organize CreateOrder,\n           PayOrder,\n           SendOrderConfirmation,\n           NotifyAdmins\nend\n```\n\n**`u-case` gem example**\n\n```ruby\nclass PlaceOrder \u003c Micro::Case\n  flow CreateOrder, PayOrder, SendOrderConfirmation, NotifyAdmins\nend\n\n# Alternative approach\nclass PlaceOrder \u003c Micro::Case\n  def call!\n    call(CreateOrder)\n      .then(PayOrder)\n      .then(SendOrderConfirmation)\n      .then(NotifyAdmins)\n  end\nend\n```\n\nTo facilitate migration for users accustomed to the above approaches, `solid-result` includes the `Solid::Result#and_then!`/`Solid::Output#and_then!` methods, which will invoke the method `call` of the given operation and expect it to return a `Solid::Result`/`Solid::Output` object.\n\n```ruby\nSolid::Result.configure do |config|\n  config.feature.enable!(:and_then!)\nend\n\nclass PlaceOrder\n  include Solid::Output.mixin\n\n  def call(**input)\n    Given(input)\n      .and_then!(CreateOrder.new)\n      .and_then!(PayOrder.new)\n      .and_then!(SendOrderConfirmation.new)\n      .and_then!(NotifyAdmins.new)\n  end\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Dependency Injection\n\nLike `#and_then`, `#and_then!` also supports an additional argument for dependency injection.\n\n**In Solid::Result**\n\n```ruby\nclass PlaceOrder\n  include Solid::Result.mixin\n\n  def call(input, logger:)\n    Given(input)\n      .and_then!(CreateOrder.new, logger)\n      # Further method chaining...\n  end\nend\n```\n\n**In Solid::Output**\n\n```ruby\nclass PlaceOrder\n  include Solid::Output.mixin\n\n  def call(logger:, **input)\n    Given(input)\n      .and_then!(CreateOrder.new, logger:)\n      # Further method chaining...\n  end\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Configuration\n\n```ruby\nSolid::Result.configure do |config|\n  config.feature.enable!(:and_then!)\n\n  config.and_then!.default_method_name_to_call = :perform\nend\n```\n\n**Explanation:**\n\n- `enable!(:and_then!)`: Activates the `and_then!` feature.\n\n- `default_method_name_to_call`: Sets a default method other than `:call` for `and_then!`.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Analysis: Why is `and_then!` an Anti-pattern?\n\nThe `and_then!` approach, despite its brevity, introduces several issues:\n\n- **Lack of Clarity:** The input/output relationship between the steps is not apparent.\n\n- **Steps Coupling:** Each operation becomes interdependent (high coupling), complicating implementation and compromising the reusability of these operations.\n\nWe recommend cautious use of `#and_then!`. Due to these issues, it is turned off by default and considered an antipattern.\n\nIt should be a temporary solution, primarily for assisting in migration from another to gem to `solid-result`.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### `#and_then` versus `#and_then!`\n\nThe main difference between the `#and_then` and `#and_then!` is that the latter does not check the result source. However, as a drawback, the result source will change.\n\nAttention: to ensure the correct behavior, do not mix `#and_then` and `#and_then!` in the same result chain.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n#### Analysis: Why is `#and_then` the antidote/standard?\n\nThe `Solid::Result#and_then`/`Solid::Output#and_then` methods diverge from the above approach by requiring explicit invocation and mapping of the outcomes at each process step. This approach has the following advantages:\n\n- **Clarity:** The input/output relationship between the steps is apparent and highly understandable.\n\n- **Steps uncoupling:** Each operation becomes independent (low coupling). You can even map a failure result to a success (and vice versa).\n\nSee this example to understand what your code should look like:\n\n```ruby\nclass PlaceOrder\n  include Solid::Output.mixin(config: { addon: { continue: true } })\n\n  def call(**input)\n    Given(input)\n      .and_then(:create_order)\n      .and_then(:pay_order)\n      .and_then(:send_order_confirmation)\n      .and_then(:notify_admins)\n      .and_expose(:order_placed, %i[order])\n  end\n\n  private\n\n  def create_order(customer:, products:)\n    CreateOrder.new.call(customer:, products:).handle do |on|\n      on.success { |output| Continue(order: output[:order]) }\n      on.failure { |error| Failure(:order_creation_failed, error:) }\n    end\n  end\n\n  def pay_order(customer:, order:, payment_method:, **)\n    PayOrder.new.call(customer:, payment_method:, order:).handle do |on|\n      on.success { |output| Continue(payment: output[:payment]) }\n      on.failure { |error| Failure(:order_payment_failed, error:) }\n    end\n  end\n\n  def send_order_confirmation(customer:, order:, payment:, **)\n    SendOrderConfirmation.new.call(customer:, order:, payment:).handle do |on|\n      on.success { Continue() }\n      on.failure { |error| Failure(:order_confirmation_failed, error:) }\n    end\n  end\n\n  def notify_admins(customer:, order:, payment:, **)\n    NotifyAdmins.new.call(customer:, order:, payment:)\n\n    Continue()\n  end\nend\n```\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## About\n\n[Rodrigo Serradura](https://github.com/serradura) created this project. He is the Solid Process creator and has already made similar gems like the [u-case](https://github.com/serradura/u-case) and [kind](https://github.com/serradura/kind/blob/main/lib/kind/result.rb). This gem can be used independently, but it also contains essential features that facilitate the adoption of Solid Process (the method) in code.\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/solid-process/solid-result. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/solid-process/solid-result/blob/master/CODE_OF_CONDUCT.md).\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n\u003cp align=\"right\"\u003e\u003ca href=\"#-solidresult\"\u003e⬆️ \u0026nbsp;back to top\u003c/a\u003e\u003c/p\u003e\n\n## Code of Conduct\n\nEveryone interacting in the Solid::Result project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/solid-process/solid-result/blob/master/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolid-process%2Fsolid-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsolid-process%2Fsolid-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsolid-process%2Fsolid-result/lists"}