{"id":26641231,"url":"https://github.com/a-chris/to-result","last_synced_at":"2025-04-10T20:54:24.162Z","repository":{"id":59624734,"uuid":"538198115","full_name":"a-chris/to-result","owner":"a-chris","description":"A wrapper over `dry-monads` to solve its flaws and offer a safe way to implement the Railway pattern.","archived":false,"fork":false,"pushed_at":"2023-08-16T17:13:12.000Z","size":48,"stargazers_count":28,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T18:35:53.611Z","etag":null,"topics":["dry-monads","monads","railway","railway-oriented-programming","ruby"],"latest_commit_sha":null,"homepage":"","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/a-chris.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-18T18:10:17.000Z","updated_at":"2024-11-26T09:31:41.000Z","dependencies_parsed_at":"2022-09-19T18:11:39.522Z","dependency_job_id":null,"html_url":"https://github.com/a-chris/to-result","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-chris%2Fto-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-chris%2Fto-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-chris%2Fto-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/a-chris%2Fto-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/a-chris","download_url":"https://codeload.github.com/a-chris/to-result/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248297787,"owners_count":21080312,"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":["dry-monads","monads","railway","railway-oriented-programming","ruby"],"created_at":"2025-03-24T18:27:35.568Z","updated_at":"2025-04-10T20:54:24.140Z","avatar_url":"https://github.com/a-chris.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# ToResult\n\nToResult is a wrapper built over `dry-monads` to make the `Do Notation`, `Result` and `Try` concepts more handy and consistent to use, especially to implement the **Railway Pattern**.\n\n## Why I created ToResult\n\n`dry-monads` requires to write boilerplate code everytime we want a method to return a `Success` or `Failure`, for example:\n\n```ruby\ndef my_method\n  Success(another_method.call)\nrescue StandardError =\u003e e\n  Failure(e)\nend\n```\n\nyes, we can use `Try` which makes the code easier to read and faster to write:\n```ruby\ndef my_method\n  Try do\n    another_method.call\n  end.to_result\nend\n```\n\nbut I feel like `to_result` is not really visible at the end of the block and if you forget to write it (as always happens to me) your application blows up.\n\nBut this is not the biggest problem, bear with me.\n\nOne of the biggest problem is that we cannot use the **Do Notation** inside a `Try` block:\n```ruby\n# this will return a Failure(Dry::Monads::Do::Halt)\ndef my_method\n  Try do\n    yield Failure('error code')\n  end.to_result\nend\n```\n\nand you cannot even use `yield` and `rescue` in the same method:\n\n```ruby\n# this will return a Failure(Dry::Monads::Do::Halt)\ndef my_method\n  yield Failure('error code')\nrescue StandardError =\u003e e\n  # e is an instance of Dry::Monads::Do::Halt\n  Failure(e)\nend\n```\n\nbecause they will raise a `Dry::Monads::Do::Halt` exception and the original error will be forever lost if we do not \"unbox\" the exception with `e.result` like this:\n\n```ruby\ndef my_method\n  yield Failure('error code')\nrescue Dry::Monads::Do::Halt =\u003e e\n  return e.result\nrescue StandardError =\u003e e\n  Failure(e)\nend\n```\n\nto be honest this is an implementation detail I don't want to care about while I'm writing my business logic and as far as I've seen this is really hard for junior developers to figure out what is happening with `Do::Halt`.\n\nWith this gem, `to-result`, that piece of code can be written as:\n```ruby\ndef my_method\n  ToResult do\n    yield Failure('error code')\n  end\nend\n```\n\nand it will return `Failure('error code')` without all the effort to think about `Do::Halt`. Moreover, you can keep using `ToResult` everytime you could have used `Try` or monads in general, so you have just **one way** to write monads in your code.\n\n## Installation\n\nTo install with bundler:\n```bash\nbundle add to-result\n```\nor with `gem`:\n```bash\ngem install to-result\n```\n\n## Usage\n\nTo use it with instances of a class, just include it\n```ruby\nrequire 'to_result'\n\nclass MyClass\n  include ToResultMixin\n\n  def my_method\n    ToResult do\n      whatever_method.call\n    end\n  end\nend\n```\n\nor if you want to use it with Singleton Classes:\n```ruby\nrequire 'to_result'\n\nclass MyClass\n  extend ToResultMixin\n\n  class \u003c\u003c self\n    def my_method\n      ToResult do\n        whatever_method.call\n      end\n    end\n  end\nend\n```\n\nnow you can always use `ToResult` all the time you wanted to use `Try` or return `Success/Failure` but with a more convenient interface and consistent behaviour, my goal is to have a solution that can be used for every use-case.\n\nLook at this:\n\n```ruby\nToResult { raise StandardError.new('error code') }\n# returns Failure(StandardError('error code'))\n\nToResult { yield Success('hello!') }\n# returns Success('hello!')\n\nToResult { yield Failure('error code') }\n# returns Failure('error code')\n\nToResult { yield Failure(StandardError.new('error code')) }\n# returns Failure(StandardError('error code'))\n\nToResult(only: [YourCustomError]) { yield Failure(YourCustomError.new('error code')) }\n# returns Failure(YourCustomError('error code'))\n\nToResult(only: [ArgumentError]) { yield Failure(YourCustomError.new('error code')) }\n# raises YourCustomError('error code')\n```\n\n## Local and global callback on errors\nto-result gives you the possibility to define a callback to be called when an error is raised inside the `ToResult` block, this is a handy place to log errors.\n\nYou can define a global callback, usually defined into an initializer:\n\n```ruby\n# initializers/to_result.rb\n\nToResultMixin.configure do |c|\n  c.on_error = Proc.new { |e| Logger.log_error(e) }\nend\n```\n\nor a local callback:\n\n```ruby\nToResult(on_error: proc { |e| Logger.log_error(e) }) do\n  yield Failure(StandardError.new('error code'))\nend\n```\n\nyou can even use both at the same time but keep in mind that **local callback overrides the global one**.\n\n\n## Changelog\n\n[Changelog](CHANGELOG.md)\n\n## Roadmap\nI'm already planning to implement some useful features:\n- [x] write more examples/documentation/tests\n- [x] configurable error logging when an exception is catched inside `DoResult`\ne.g. sending the log to Airbrake or whathever service you are using\n- [x] transform/process the catched error =\u003e this can be handled with `alt_map` or other methods already available in `dry-monads`\n- [ ] any type of suggestion is appreciated 😁\n\n## Relasing a new version\n\n```\n  gem build to-result\n  gem push to-result-\u003cversion\u003e.gem\n```\n\n## Authors\n\n- [@a-chris](https://www.github.com/a-chris)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-chris%2Fto-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fa-chris%2Fto-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fa-chris%2Fto-result/lists"}