{"id":13747303,"url":"https://github.com/michaelherold/interactor-contracts","last_synced_at":"2025-04-09T12:06:42.059Z","repository":{"id":56877795,"uuid":"51126092","full_name":"michaelherold/interactor-contracts","owner":"michaelherold","description":"Add contracts to your interactors","archived":false,"fork":false,"pushed_at":"2020-03-10T16:05:53.000Z","size":94,"stargazers_count":80,"open_issues_count":6,"forks_count":10,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-02T10:13:16.128Z","etag":null,"topics":["hacktoberfest","interactor"],"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/michaelherold.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-05T04:33:58.000Z","updated_at":"2025-01-01T20:50:36.000Z","dependencies_parsed_at":"2022-08-20T11:30:35.890Z","dependency_job_id":null,"html_url":"https://github.com/michaelherold/interactor-contracts","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelherold%2Finteractor-contracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelherold%2Finteractor-contracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelherold%2Finteractor-contracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/michaelherold%2Finteractor-contracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/michaelherold","download_url":"https://codeload.github.com/michaelherold/interactor-contracts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248036063,"owners_count":21037092,"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":["hacktoberfest","interactor"],"created_at":"2024-08-03T06:01:24.473Z","updated_at":"2025-04-09T12:06:42.037Z","avatar_url":"https://github.com/michaelherold.png","language":"Ruby","readme":"# Interactor::Contracts\n\n[![Build Status](https://travis-ci.org/michaelherold/interactor-contracts.svg)][travis]\n[![Code Climate](https://codeclimate.com/github/michaelherold/interactor-contracts/badges/gpa.svg)][codeclimate]\n[![Inline docs](http://inch-ci.org/github/michaelherold/interactor-contracts.svg?branch=master)][inch]\n\n[codeclimate]: https://codeclimate.com/github/michaelherold/interactor-contracts\n[inch]: http://inch-ci.org/github/michaelherold/interactor-contracts\n[travis]: https://travis-ci.org/michaelherold/interactor-contracts\n\nInteractor::Contracts is an extension to the [interactor] gem that gives you\nthe ability to specify the expectations (expected inputs) and promises\n(expected outputs) of your interactors.\n\n[interactor]: https://github.com/collectiveidea/interactor\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'interactor-contracts'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install interactor-contracts\n\n## Usage\n\nLet's extend the sample `AuthenticateUser` from the Interactor examples with a\ncontract that specifies its expectations and promises.\n\n```ruby\nclass AuthenticateUser\n  include Interactor\n  include Interactor::Contracts\n\n  expects do\n    required(:email).filled\n    required(:password).filled\n  end\n\n  promises do\n    required(:user).filled\n    required(:token).filled\n  end\n\n  on_breach do |breaches|\n    context.fail!(breaches)\n  end\n\n  def call\n    if user = User.authenticate(context.email, context.password)\n      context.user = user\n      context.token = user.secret_token\n    else\n      context.fail!(:message =\u003e \"authenticate_user.failure\")\n    end\n  end\nend\n```\n\n### Inputs: expectations\n\nThe `expects` block defines the expectations: the expected attributes of the\ncontext prior to the interactor running, along with any predicates that further\nconstrain the input.\n\n### Outputs: promises\n\nThe `promises` block defines the promises: the expected attributes of the\ncontext after the interactor runs and successfully completes, along with any\npredicates the further constrain the output. Note, for backward-compatibility,\nthis is also available via the `assures` method.\n\nBecause interactors can have transitive dependencies through the use of\norganizers, any other inputs or outputs are ignored from the perspective of\nthe contract and are passed along to the outgoing (successful) context.\n\nBoth `expects` and `promises` wrap [dry-validation], so you can use any\npredicates defined in it to describe the expected inputs and outputs of your\ninteractor.\n\n### Contract failures: breaches\n\nBy default, contract failures, or breaches, behave like the example above:\nbreached keys are set on the failed context with their contract failures as\nvalues. Note that you do not have to define an `on_breach` for the behavior, but\nif you do this behavior will not occur.\n\nIf there are more complicated things you wish to do with contract failures, you\ncan define one or more breach handlers.\n\nTo hook into a failed expectation or promise, you can use the `on_breach`\nmethod to defined a breach handler. It should take a 1-arity block that expects\nan array of `Breach` objects. These objects have a `property` attribute that\nwill give you the key that's in breach of its contract. Breaches also have a\n`messages` attribute that gives the reasons that property is in breach.\n\nBy default, when an `on_breach` consequence is not specified, the contract will\n`#fail!` the `Interactor::Context` with the keys that are in breach and arrays\nof messages about what the breaches are.\n\nFor example, the above interactor acts as follows:\n\n```ruby\nresult = AuthenticateUser.call({})\n#=\u003e #\u003cInteractor::Context email=[\"email is missing\"], password=[\"password is missing\"]\u003e\n\nresult.failure?  #=\u003e true\n```\n\nIf you would rather have your contract breaches be aggregated into, for example,\nan `errors` property, you could use a breach handler like the following:\n\n```ruby\non_breach do |breaches|\n  context.fail!(errors: breaches)\nend\n```\n\n[dry-validation]: https://github.com/dryrb/dry-validation\n\n### I18n support\n\nYou can [configure the underlying `dry-validation` contract][config] by passing\na block to the `config` method in your contract. This block will be evaluated on\nthe underlying configuration for the contract. For example, if you want to set\nup the contract to use I18n in your Rails app, you might do something like this:\n\n```ruby\nclass MyInteractor\n  include Interactor\n  include Interactor::Contracts\n\n  config do\n    messages.backend = :i18n\n    messages.load_paths \u003c\u003c Rails.root / 'config' / 'locales' / 'errors.yml'\n    messages.top_namespace = :interactor_contracts\n  end\nend\n```\n\nThis sets up the I18n system (assuming the delicate load-order has been done in\nthe right way - you have to require `i18n` prior to requiring\n`interactor-contracts` since we load `dry-validation` immediately) to use your\ncustom file. All lookups for error messages happen starting at the\n`interactor_contracts` key in this example.\n\nSee [the documentation for `dry-validation`][config] for more information.\n\n[config]: https://dry-rb.org/gems/dry-validation/1.0/configuration/\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run\n`rake spec` to run the tests. You can also run `bin/console` for an interactive\nprompt that will allow you to experiment.\n\nWhen writing code, you can use the helper application [Guard][guard] to\nautomatically run tests and coverage tools whenever you modify and save a file.\nThis helps to eliminate the tedium of running tests manually and reduces the\nchange that you will accidentally forget to run the tests. To use Guard, run\n`bundle exec guard`.\n\nBefore committing code, run `rake` to check that the code conforms to the style\nguidelines of the project, that all of the tests are green (if you're writing a\nfeature; if you're only submitting a failing test, then it does not have to\npass!), and that the changes are sufficiently documented.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To\nrelease a new version, update the version number in `version.rb`, and then run\n`bundle exec rake release`, which will create a git tag for the version, push\ngit commits and tags, and push the `.gem` file to [rubygems.org][rubygems].\n\n[guard]: http://guardgem.org\n[rubygems]: https://rubygems.org\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at\nhttps://github.com/michaelherold/interactor-contracts.\n\n## Supported Ruby Versions\n\nThis library aims to support and is [tested against][travis] the following Ruby\nversions:\n\n* Ruby 2.4\n* Ruby 2.5\n* Ruby 2.6\n* JRuby 9.2\n\nIf something doesn't work on one of these versions, it's a bug.\n\nThis library may inadvertently work (or seem to work) on other Ruby versions,\nhowever support will only be provided for the versions listed above.\n\nIf you would like this library to support another Ruby version or\nimplementation, you may volunteer to be a maintainer. Being a maintainer\nentails making sure all tests run and pass on that implementation. When\nsomething breaks on your implementation, you will be responsible for providing\npatches in a timely fashion. If critical issues for a particular implementation\nexist at the time of a major release, support for that Ruby version may be\ndropped.\n\n## Versioning\n\nThis library aims to adhere to [Semantic Versioning 2.0.0][semver]. Violations\nof this scheme should be reported as bugs. Specifically, if a minor or patch\nversion is released that breaks backward compatibility, that version should be\nimmediately yanked and/or a new version should be immediately released that\nrestores compatibility. Breaking changes to the public API will only be\nintroduced with new major versions. As a result of this policy, you can (and\nshould) specify a dependency on this gem using the [Pessimistic Version\nConstraint][pessimistic] with two digits of precision. For example:\n\n    spec.add_dependency \"interactor-contracts\", \"~\u003e 0.1\"\n\n[pessimistic]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint\n[semver]: http://semver.org/spec/v2.0.0.html\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License][license].\n\n[license]: http://opensource.org/licenses/MIT.\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelherold%2Finteractor-contracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmichaelherold%2Finteractor-contracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmichaelherold%2Finteractor-contracts/lists"}