{"id":15471924,"url":"https://github.com/thadeu/zx-result","last_synced_at":"2025-04-13T21:06:22.021Z","repository":{"id":194163070,"uuid":"690367160","full_name":"thadeu/zx-result","owner":"thadeu","description":"Functional result object for Ruby","archived":false,"fork":false,"pushed_at":"2024-12-18T00:22:48.000Z","size":59,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-13T21:06:05.329Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/thadeu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2023-09-12T04:04:33.000Z","updated_at":"2024-12-30T17:32:58.000Z","dependencies_parsed_at":"2024-12-15T20:25:13.617Z","dependency_job_id":"23e174d2-2f29-473a-bc52-f69dda03f4b6","html_url":"https://github.com/thadeu/zx-result","commit_stats":{"total_commits":29,"total_committers":1,"mean_commits":29.0,"dds":0.0,"last_synced_commit":"7008de4fec23b1779c0c20d414de6a388dfa7811"},"previous_names":["thadeu/zx-result"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thadeu%2Fzx-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thadeu%2Fzx-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thadeu%2Fzx-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thadeu%2Fzx-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thadeu","download_url":"https://codeload.github.com/thadeu/zx-result/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248782270,"owners_count":21160717,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-10-02T02:22:30.196Z","updated_at":"2025-04-13T21:06:21.997Z","avatar_url":"https://github.com/thadeu.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ch1 align=\"center\"\u003e🔃 Zx::Result\u003c/h1\u003e\n  \u003cp align=\"center\"\u003e\u003ci\u003eFunctional result object for Ruby\u003c/i\u003e\u003c/p\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://rubygems.org/gems/zx-result\"\u003e\n    \u003cimg alt=\"Gem\" src=\"https://img.shields.io/gem/v/zx-result.svg\"\u003e    \n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/thadeu/zx-result/actions/workflows/ci.yml\"\u003e\n    \u003cimg alt=\"Build Status\" src=\"https://github.com/thadeu/zx-result/actions/workflows/ci.yml/badge.svg\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\n## Motivation\n\nBecause in sometimes, we need to create a safe return for our objects. This gem simplify this work.\n\n## Documentation \u003c!-- omit in toc --\u003e\n\nVersion    | Documentation\n---------- | -------------\nunreleased | https://github.com/thadeu/zx-result/blob/main/README.md\n\n## Table of Contents \u003c!-- omit in toc --\u003e\n  - [Installation](#installation)\n  - [Usage](#usage)\n    - [Success](#success)\n    - [Failure](#failure)\n    - [Try](#try)\n    - [Given](#given)\n\n## Compatibility\n\n| kind           | branch  | ruby               |\n| -------------- | ------- | ------------------ |\n| unreleased     | main    | \u003e= 2.5.8, \u003c= 3 |\n\n## Installation\n\nUse bundle\n\n```ruby\nbundle add zx-result\n```\n\nor add this line to your application's Gemfile.\n\n```ruby\ngem 'zx-result'\n```\n\nand then, require module\n\n```ruby\nrequire 'zx'\n```\n\n## Configuration\n\nWithout configuration, because we use only Ruby. ❤️\n\n## Usage\n\nYou can use with many towards.\n\n### Given\n\n```ruby\nresult = Zx.Given { Zx.Success(5) }\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 5\nresult.value! #=\u003e 5 or raise\nresult.error #=\u003e nil or raises an exception\n```\n\n```ruby\ninput = 5\n\nresult = Zx.Given(input)\n  .then{ |number| number + 5 }\n  .then{ |number| number + 5 }\n  .then{ |number| number + 5 }\n  .on_success{|number| number }\n```\n\nYou can use `Given` to invoke other methods in the class, like this.\n\n```ruby\nclass User\n  def self.create(name:, email:)\n    new(name:, email:).create\n  end\n\n  attr_reader :name, :email\n\n  def initialize(name:, email:)\n    @name = name\n    @email = email\n  end\n\n  def create\n    self\n  end\nend\n\nclass Account\n  def self.create(user:)\n    new(user:).create\n  end\n\n  attr_reader :user\n\n  def initialize(user:)\n    @user = user\n  end\n\n  def create\n    self\n  end\n\n  def send_welcome_email!\n    true\n  end\nend\n\nclass NewsletterMailer\n  def self.subscribe!(account:)\n    new(account:).subscribe!\n  end\n\n  attr_reader :account\n\n  def initialize(account:)\n    @account = account\n  end\n\n  def subscribe!\n    true\n  end\nend\n\nclass AccountCreation\n  include Zx\n\n  def self.deliver(input)\n    new.deliver(input)\n  end\n  \n  def self.deliver_with_sym(input)\n    new.deliver_with_sym(input)\n  end\n\n  def deliver(input)\n    Given(input)\n      .and_then(\u0026method(:create_user))\n      .and_then(\u0026method(:create_account))\n      .and_then(\u0026method(:subscribe_mailer))\n      .and_then(\u0026method(:send_welcome_email!))\n  end\n  \n  def deliver_with_sym(input)\n    Given(input)\n      .and_then(:create_user)\n      .and_then(:create_account)\n      .and_then(:subscribe_mailer)\n      .and_then(:send_welcome_email!)\n  end\n\n  def create_user(input)\n    user = User.create(name: input[:name], email: input[:email])\n    Success(user:, type: :user_created)\n  end\n\n  def create_account(user:, **)\n    account = Account.create(user:)\n    Success(account:, type: :account_created)\n  end\n\n  def subscribe_mailer(account:, **)\n    NewsletterMailer.subscribe!(account:)\n    Success(account:, type: :mailer_subscribed)\n  end\n\n  def send_welcome_email!(account:, **)\n    account.send_welcome_email!\n    Success(account, type: :email_sent)\n  end\nend\n\ninput = { name: 'Thadeu Esteves', email: 'tadeuu@gmail.com' }\n\naccount = AccountCreation.deliver(input)\n  .on(:success, :user_created) { |user| p [:user_created, user] }\n  .on(:success, :account_created) { |acc| p [:account_created, acc] }\n  .on(:success, :mailer_subscribed) { |acc| p [:mailer_subscribed, acc] }\n  .on(:success, :email_sent) { |acc| p [:email_sent, acc] }\n  .on(:failure, :user_not_created) { |error| p [:user_not_created, error] }\n  .otherwise { |error| p [:otherwise, error] }\n\nexpect(account.success?).to be_truthy\nexpect(account.type).to eq(:email_sent)\nexpect(account.unwrap.user.name).to eq('Thadeu Esteves')\nexpect(account.unwrap.user.email).to eq('tadeuu@gmail.com')\n```\n\n### Success\n\n```ruby\nresult = Zx.Success(5)\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 5\nresult.value! #=\u003e 5 or raise\nresult.error #=\u003e nil or raises an exception\n```\n\n```ruby\nresult = Zx.Success(5, type: :integer)\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 5\nresult.value! #=\u003e 5 or raise\nresult.error #=\u003e nil or raises an exception\nresult.type #=\u003e :integer\n```\n\n### Failure\n\n```ruby\nresult = Zx.Failure(:fizz)\nresult.success? #=\u003e false\nresult.failure? #=\u003e true\nresult.value #=\u003e raises an exception\nresult.error #=\u003e :fizz\nresult.type #=\u003e :error\n```\n\n```ruby\nresult = Zx.Failure(:fizz, type: :not_found)\nresult.success? #=\u003e false\nresult.failure? #=\u003e true\nresult.value #=\u003e raises an exception\nresult.error #=\u003e :fizz\nresult.type #=\u003e :not_found\n```\n\n### Map or Then\n\n```ruby\nresult = Zx.Success(5, type: :integer)\n  .fmap{ |number| number + 5 }\n  .fmap{ |number| number + 5 }\n  .fmap{ |number| number + 5 }\n  .on_success(:integer) {|number| puts number } #=\u003e 20\n  .on(:success, :integer) {|number| puts number } #=\u003e 20\n  .on_success {|number| puts number } #=\u003e 20\n\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 20\nresult.value! #=\u003e 20 or raise\nresult.error #=\u003e nil or raises a  n exception\nresult.type #=\u003e :integer\n```\n\n```ruby\nresult = Zx.Success(5, type: :integer)\n  .then{ |number| number + 5 }\n  .then{ |number| number + 5 }\n  .then{ |number| number + 5 }\n  .on_success{|number| puts number } #=\u003e 20\n\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 20\nresult.value! #=\u003e 20 or raise\nresult.error #=\u003e nil or raises an exception\nresult.type #=\u003e :integer\n```\n\n### Step or Check\n\n```ruby\nresult = Zx.Success(5, type: :integer)\n  .step{ |number| number + 5 }\n  .on_success(:integer) {|number| puts number } #=\u003e 10\n  .on(:success, :integer) {|number| puts number } #=\u003e 10\n  .on_success {|number| puts number } #=\u003e 10\n\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 10\nresult.value! #=\u003e 10 or raise\nresult.error #=\u003e nil or raises a  n exception\nresult.type #=\u003e :integer\n```\n\n```ruby\nresult = Zx.Success(5, type: :integer)\n  .step{ |number| number + 5 }\n  .check { |number| number == 10 }\n  .on_success{|number| puts number } #=\u003e 10\n\nresult.success? #=\u003e true\nresult.failure? #=\u003e false\nresult.value #=\u003e 10\nresult.value! #=\u003e 10 or raise\nresult.error #=\u003e nil or raises an exception\nresult.type #=\u003e :integer\n```\n\n```ruby\nresult = Zx.Success(5, type: :integer)\n  .step{ |number| number + 5 }\n  .check(:number_valid) { |number| number == 15 }\n  .on_failure { |error| puts error } #=\u003e 10\n```\n\n### Try\n\n```ruby\nresult = Zx.Try { Zx.Success(5) }\n  .step { |number| number + 5 }\n  .check(:number_invalid) { |number| number == 15 }\n  .on_failure{ |error, (type)| puts [error, type] } # failure! because, number == 10, right?\n  .on_success{ |number| puts number }\n```\n\n```ruby\nresult = Zx.Try { Zx.Success(10) }\n  .step { |number| number + 1 }\n  .then { |number| number + 1 }\n  .and_then { |number| number + 1 }\n  .fmap { |number| number + 1 }\n  .check(:number_invalid) { |number| number == 15 }\n  .on_failure{ |error, (type)| puts [:failure, error, type] }\n  .on_success{ |number| puts [:success, number] }\n```\n\n```ruby\nresult = Zx.Try { Zx.Failure(10) }\n  .step { |number| number + 1 }\n  .then { |number| number + 1 }\n  .and_then { |number| number + 1 }\n  .fmap { |number| number + 1 }\n  .check(:number_invalid) { |number| number == 15 }\n  .on_failure{ |error, (type)| puts [:failure, error, type] }\n  .on_success{ |number| puts [:success, number] }\n```\n\nYou can use one or multiples listeners in your result. We see some use cases.\n\n**Simple composition**\n\n```ruby\nclass AsIncluded\n  include Zx\n\n  def pass(...)\n    Success(...)\n  end\n\n  def passthrough(value)\n    Success[value]\n  end\n\n  def failed(error)\n    Failure[error, type: :error]\n  end\nend\n\nresult = AsIncluded.new.pass('save record!')\n\nresult\n  .on(:success, :success) { expect(_1).to eq(a: 1) }\n  .on(:success, :mailer) { expect(_1).to eq(a: 1) }\n  .on(:success, :persisted) { expect(_1).to eq('save record!') }\n  .on(:success) { |value, (type)| expect([value, type]).to eq(['save record!', :persisted]) }\n  .on(:failure, :error) { expect(_1).to eq('on error') }\n  .on(:failure, :record_not_found) { expect(_1).to eq('not found user') }\n```\n\n**Match**\n\n```ruby\nresult = AsIncluded.new.pass('save record!')\n\nresult.match(\n  Ok: -\u003e(v) { [:ok, v] },\n  Err: -\u003e(err) { raise [:err, err] }\n)\n\nresult.match(\n  Ok: -\u003e { expect(_1).to eq(2) },\n  Err: -\u003e { raise [:err, _1] }\n)\n```\n\n**Otherwise**\n\n```ruby\nresult = Zx.Failure('invalid type tagged')\n\nresult\n  .on_success(:jump_this) { [:jump_this, _1] }\n  .on_failure(:jump_this1) { [:jump_this1, _1] }\n  .on_failure(:jump_this2) { [:jump_this2, _1] }\n  .otherwise { p [:otherwise, _1] }\n```\n\nYou can use directly methods, for example:\n\n```ruby\nZx.Success(relation)\n\n# or\n\nZx::Success[relation]\n```\n\n```ruby\nZx:.Failure('error', type: :invalid)\n\n# or\n\nZx::Failure['user was not found', { type: :invalid_user }]\n```\n\n\n[⬆️ \u0026nbsp;Back to Top](#table-of-contents-)\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` 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 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/thadeu/zx-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/thadeu/zx-result/blob/master/CODE_OF_CONDUCT.md).\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthadeu%2Fzx-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthadeu%2Fzx-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthadeu%2Fzx-result/lists"}