{"id":13879807,"url":"https://github.com/saturnflyer/direct","last_synced_at":"2025-03-17T06:31:34.254Z","repository":{"id":37445354,"uuid":"126365641","full_name":"saturnflyer/direct","owner":"saturnflyer","description":"direct your Ruby objects to perform a block. avoid using \"if\"","archived":false,"fork":false,"pushed_at":"2024-12-19T10:36:07.000Z","size":113,"stargazers_count":6,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-27T19:03:00.455Z","etag":null,"topics":["ruby"],"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/saturnflyer.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}},"created_at":"2018-03-22T16:39:57.000Z","updated_at":"2024-11-15T19:14:11.000Z","dependencies_parsed_at":"2024-10-27T12:11:47.055Z","dependency_job_id":"d3a594bb-846c-476b-8a4e-f5a5f24ef460","html_url":"https://github.com/saturnflyer/direct","commit_stats":{"total_commits":101,"total_committers":3,"mean_commits":"33.666666666666664","dds":0.09900990099009899,"last_synced_commit":"b03b01db6f25fcda291b03146ef91579af5c8d0b"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saturnflyer%2Fdirect","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saturnflyer%2Fdirect/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saturnflyer%2Fdirect/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/saturnflyer%2Fdirect/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/saturnflyer","download_url":"https://codeload.github.com/saturnflyer/direct/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243847062,"owners_count":20357317,"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":["ruby"],"created_at":"2024-08-06T08:02:34.104Z","updated_at":"2025-03-17T06:31:33.741Z","avatar_url":"https://github.com/saturnflyer.png","language":"Ruby","readme":"# Direct\n\nTell your objects what to do when things work properly or when they fail.\n\nThis allows you to encapsulate behavior inside the object. Avoid using `if`\noutside of your objects and just tell them what to do.\n\n## Usage\n\n```ruby\nrequire 'direct'\nclass SomeClass\n  def procedure\n    Direct.defer(object: self){\n      # return a truthy or falsey object\n      # to execute success or failure blocks\n    }\n  end\nend\n\nSomeClass.new.procedure.success{ |deferred_object, result, object|\n  puts \"it worked!\"\n}.failure { |deferred_object, result, object|\n  puts \"it failed :-(\"\n}.execute\n```\n\nIf the example `procedure` method above raises an exception instead of just returning a falsey object, the `failure` block will be run.\n\nBut you can specify what to do when an exception is raised instead:\n\n```ruby\nSomeClass.new.procedure.success{ |deferred_object, result, object|\n  puts \"it worked!\"\n}.failure { |deferred_object, result, object|\n  puts \"it failed :-(\"\n}.exception { |deferred_object, exception, object|\n  puts \"Oh no! An exception was raised!\"\n}.execute\n```\n\nBy default it will handle `StandardError` execeptions but you can be more specific if you like:\n\n```ruby\nSomeClass.new.procedure.success{ |deferred_object, result, object|\n  puts \"it worked!\"\n}.failure { |deferred_object, result, object|\n  puts \"it failed :-(\"\n}.exception(SomeLibrary::SomeSpecialError){ |deferred_object, exception, object|\n  puts \"Oh no! An exception was raised!\"\n}.execute\n```\n\nYou can also handle different exceptions with different blocks:\n\n```ruby\nSomeClass.new.procedure.exception(SomeLibrary::SomeSpecialError){ |deferred_object, exception, object|\n  puts \"Oh no! A Special Error!\"\n}.exception(ArgumentError){ |deferred_object, exception, object|\n  puts \"Oops! The arguments are wrong!\"\n}.execute\n```\n\nThe `defer` method uses built-in classes but you can build your own to manage executing named blocks\n\n```ruby\nclass DeferrableClass\n  include Direct\n\n  def save\n    # do stuff\n    as_directed(:success, 'some', 'success', 'values')\n  rescue =\u003e e\n    as_directed(:failure, 'some', 'failure', e.message)\n  end\nend\n\nDeferrableClass.new.direct(:success){|instance, *data|\n  STDOUT.puts data\n}.direct(:failure){|instance, *errors|\n  STDERR.puts errors\n}.save\n```\n\nYour blocks will always receive the object itself as the first argument.\n\nIf you want to have a better API, just make it your own:\n\n```ruby\nclass DeferrableClass\n  def when_it_works(\u0026)\n    direct(:success, \u0026)\n  end\n\n  def when_it_fails(\u0026)\n    direct(:oopsies, \u0026)\n  end\n\n  def do_it\n    if it_worked?\n      as_directed(:success)\n    else\n      as_directed(:oopsies)\n    end\n  end\nend\n\nDeferrableClass.new.when_it_works do |instance|\n  # successful path\nend.when_it_fails do |instance|\n  # failure path\nend\n```\n\n## Why?\n\nYou could easily write code that says `if` this `else` that.\n\nFor example:\n\n```ruby\nif Something.new.save!\n  puts \"yay!\"\nelse\n  puts \"boo!\"\nend\n```\n\nBut eventually you may want more information about your successes and failures\n\n```ruby\nsomething = Something.new\nif something.save!\n  puts \"yay! #{something}\"\nelse\n  puts \"boo! #{something}: #{something.errors}\"\nend\n```\n\nThat's intially not so bad that you need to initialize the object separately \nfrom the `if` expression. But when we discover a third or fourth scenario, then\nthe code can become complicated:\n\n```ruby\nsomething = Something.new\nif something.save!\n  puts \"yay! #{something}\"\nelsif something.valid? \u0026\u0026 !something.persisted?\n  puts \"it sort of worked\"\nelsif !something.valid? || something.need_some_other_thing_set?\n  puts \"an alternative to it not working\"\nelse\n  puts \"boo! #{something}: #{something.errors}\"\nend\n```\n\nIt's just too easy to expand logic *and* knowledge about the internal state of\nthe object with `if` and `else` and `elsif`.\n\nInstead, we can name these scenarios and allow the object to handle them; we\nmerely provide the block of code to execute:\n\n```ruby\nSomething.new.direct(:success){ |obj|\n    puts \"yay! #{obj}\"\n  }.direct(:failure){ |obj, errors|\n    puts \"boo! #{obj}: #{errors}\"\n  }.direct(:other_scenario){ |obj|\n    puts \"here's what happened and what to do...\"\n  }\n```\n\n_Inside_ of the object is where we can handle these named scenarios. If the\nmeaning of `:success` or `:failure` or any other name changes, the object\nitself can handle it with no changes implicitly required in the calling code.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'direct'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install direct\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` 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 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/saturnflyer/direct. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\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## Code of Conduct\n\nEveryone interacting in the Direct project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/saturnflyer/direct/blob/master/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaturnflyer%2Fdirect","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsaturnflyer%2Fdirect","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsaturnflyer%2Fdirect/lists"}