{"id":18002884,"url":"https://github.com/am-kantox/dry-behaviour","last_synced_at":"2025-03-26T08:31:20.214Z","repository":{"id":93852616,"uuid":"71275280","full_name":"am-kantox/dry-behaviour","owner":"am-kantox","description":"Tiny library inspired by Elixir protocol pattern.","archived":false,"fork":false,"pushed_at":"2023-02-01T09:20:01.000Z","size":97,"stargazers_count":13,"open_issues_count":2,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-21T12:22:04.560Z","etag":null,"topics":["behaviour","delegate","kantox","protocol","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/am-kantox.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2016-10-18T17:48:30.000Z","updated_at":"2023-05-25T07:21:00.000Z","dependencies_parsed_at":"2023-03-04T00:01:01.514Z","dependency_job_id":null,"html_url":"https://github.com/am-kantox/dry-behaviour","commit_stats":null,"previous_names":[],"tags_count":42,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fdry-behaviour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fdry-behaviour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fdry-behaviour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/am-kantox%2Fdry-behaviour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/am-kantox","download_url":"https://codeload.github.com/am-kantox/dry-behaviour/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245618760,"owners_count":20645064,"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":["behaviour","delegate","kantox","protocol","ruby"],"created_at":"2024-10-29T23:24:22.225Z","updated_at":"2025-03-26T08:31:20.206Z","avatar_url":"https://github.com/am-kantox.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Dry::Behaviour\n\n[![Build Status](https://travis-ci.org/am-kantox/dry-behaviour.svg?branch=master)](https://travis-ci.org/am-kantox/dry-behaviour)\n\n**Tiny library inspired by Elixir [`protocol`](http://elixir-lang.org/getting-started/protocols.html) pattern.**\n\n## Protocols\n\n### Declaration\n\n```ruby\nrequire 'dry/behaviour'\n\nmodule Protocols\n  module Adder\n    include Dry::Protocol\n\n    defprotocol do\n      defmethod :add, :this, :other\n      defmethod :subtract, :this, :other\n\n      def add_default(value)\n        add(3, 2) + value\n      end\n    end\n\n    defimpl Protocols::Adder, target: String do\n      def add(this, other)\n        this * other\n      end\n      def subtract(this, other)\n        this\n      end\n    end\n    defimpl Protocols::Adder, target: NilClass do\n      def add(this, other)\n        other\n      end\n      def subtract(this, other)\n        this\n      end\n    end\n\n    # delegate `to_s` as is, map `add` and `subtract` to `:+` and `:-` respectively\n    defimpl target: Integer, delegate: :to_s, map: { add: :+, subtract: :- }\n  end\nend\n```\n\n### Usage\n\n```ruby\nexpect(Protocols::Adder.add(5, 3)).to eq(8)\nexpect(Protocols::Adder.add(5, 10)).to eq(15)\nexpect(Protocols::Adder.subtract(5, 10)).to eq(-5)\nexpect(Protocols::Adder.add(15, 10)).to eq(25)\nexpect(Protocols::Adder.add(\"!\", 10)).to eq(\"!!!!!!!!!!\")\nexpect(Protocols::Adder.add(nil, 10)).to eq(10)\n\nexpect(Protocols::Adder.add_default(1)).to eq(6)\n```\n\n### Arguments types\n\nNormally, one would use a simple notation to declare a method. It includes `:this` receiver\nin te very first position and some optional required arguments afterward.\n\n```ruby\n  defmethod :add, :this, :addend\n  ...\n  defimpl ... do\n    def add(this, addend); this + addend; end\n  end\n```\n\nIf the argument is not generic (optional, or splatted, or keyword,) its type must be explicitly\nspecified in the protocol definition as shown below.\n\n```ruby\n  defprotocol implicit_inheritance: true do\n    defmethod :with_def_argument, :this, [:foo_opt, :opt]\n    defmethod :with_def_keyword_argument, :this, [:foo_key, :key]\n    defmethod :with_req_keyword_argument, :this, [:foo_key, :keyreq]\n\n    def with_def_argument(this, foo_opt = :super); foo_opt; end\n    def with_def_keyword_argument(this, foo_key: :super); foo_key; end\n    def with_req_keyword_argument(this, foo_key:); foo_key; end\n  ...\n```\n\nThat said, `:addend` argument declaration is a syntactic sugar for `[:addend, :req]`.\nPossible values for the type are:\n\n```ruby\nPARAM_TYPES = %i[req opt rest key keyrest keyreq block]\n```\n\nPlease note, that the signature of the method and its implementation must exactly match.\nOne cannot declare a method to have a `keyreq` argument and then make it defaulted in the\nimplementation. That is done by design.\n\n## Guards\n\nStarting with `v0.5.0` we support multiple function clauses and guards.\n\n```ruby\nclass GuardTest\n  include Dry::Guards\n\n  def a(p, p2 = nil, *_a, when: { p: Integer, p2: String }, **_b, \u0026cb); 1; end\n  def a(p, _p2 = nil, *_a, when: { p: Integer }, **_b, \u0026cb); 2; end\n  def a(p, _p2 = nil, *_a, when: { p: Float }, **_b, \u0026cb); 3; end\n  def a(p, _p2 = nil, *_a, when: { p: -\u003e(v) { v \u003c 42 } }, **_b, \u0026cb); 4; end\n  def a(_p, _p2 = nil, *_a, when: { cb: -\u003e(v) { !v.nil? } }, **_b, \u0026cb); 5; end\n  def a(p1, p2, p3); 6; end\n  def a(p, _p2 = nil, *_a, **_b, \u0026cb); 'ALL'; end\n\n  def b(p, \u0026cb)\n    'NOT GUARDED'\n  end\nend\n\ngt = GuardTest.new\n\nit 'performs routing to function clauses as by guards' do\n  expect(gt.a(42, 'Hello')).to eq(1)\n  expect(gt.a(42)).to eq(2)\n  expect(gt.a(3.14)).to eq(3)\n  expect(gt.a(3)).to eq(4)\n  expect(gt.a('Hello', \u0026-\u003e { puts 0 })).to eq(5)\n  expect(gt.a(*%w|1 2 3|)).to eq(6)\n  expect(gt.a('Hello')).to eq('ALL')\nend\n```\n\n## Authors\n\n@am-kantox, @saverio-kantox \u0026 @kantox\n\n## Changelog\n\n### `0.9.0` :: Warning On Wrong Arity\n\n- many error reporting improvements,\n- warning on wrong arity (declaration, arity 0 / implementation, wrong arity)\n\n### `0.8.0` :: Implicit Inheritance\n\n- deprecate implicit delegation to the target instance; error message saying “it’ll be removed in 1.0”\n- `implicit_inheritance: true` flag in call to `defprotocol` makes the implementation implicitly inherit the behaviour declared in the core protocol module itself, without the necessity to explicitly call `super`:\n\n```diff\n module ParentOKImplicit\n   include Dry::Protocol\n\n-  defprotocol do\n+  defprotocol implicit_inheritance: true do\n     defmethod :foo\n\n     def foo(this)\n       :ok\n     end\n\n     defimpl target: String do\n-      def foo(this)\n-        super(this)\n-      end\n     end\n   end\n end\n```\n\n### `0.7.0` :: Handling Errors\n\n- better error messages (very descriptive, with whys and howtos)\n- the whole stacktrace is carefully saved with `cause`\n- internal exceptions related to wrong implementation do now point to the proper lines in the client code (internal trace lines are removed)\n\n### `0.6.0` :: Bugfix\n\n- implementation for classes responding to **`to_a`** is handled properly\n\n### `0.5.0` :: Guards\n\n### `0.4.2` :: Removed the forgotten debug output :(\n\n### `0.4.1` :: Protocol-wide methods are allowed to call from implementation\n\n**NB** Works for all `defimpl`s.\n\n### `0.4.0` :: Protocol-wide methods are allowed to call from inside implementation\n\n```ruby\nmodule Protocols::Adder\n  include Dry::Protocol\n\n  defprotocol do\n    defmethod :add, :this, :other\n    def default\n      42\n    end\n  end\nend\n\nDry::Protocol.defimpl target: Integer do\n  def add(this)\n    this + default #⇒ 47 when called as Protocols::Adder.add(5)\n  end\nend\n```\n\n**NB** At the moment works only for external `defimpl`.\n\n### `0.3.1` :: `implemented_for?` and `implementation_for`\n\n### `0.3.0` :: version bump\n\n### `0.2.2` :: meaningful errors\n\n#### Throws an exception on wrong usage:\n\n```ruby\nProtocols::Adder.add({}, 42)\n#⇒ Protocols::NotImplemented: Protocol “Protocols::Adder” is not implemented for “Hash”\nProtocols::Adder.hello({}, 42)\n#⇒ Protocols::NotImplemented: Protocol “Protocols::Adder” does not declare method “hello”\n```\n\n### `0.2.1` :: multiple targets\n\n#### Multiple targets:\n\n```ruby\ndefimpl MyProto, target: [MyClass1, MyClass2], delegate: [:add, :subtract]\n```\n\n### `0.2.0` :: implicit delegate on incomplete implementation\n\nwhen `defimpl` does not fully cover the protocol declaration,\nmissing methods are implicitly delegated to the target,\nthe warning is being issued:\n\n```ruby\ndefimpl MyProto, target: MyClass, map: { add: :+, subtract: :- }\n#⇒ W, [2016-10-24T14:52:49.230808 #26382]  WARN -- : Implicit delegate MyProto#to_s to MyClass\n```\n\n### `0.1.1` :: delegate and map methods to receiver\n\n`defimpl` now accepts `delegate` and `map`:\n\n```ruby\ndefimpl MyProto, target: MyClass, delegate: :to_s, map: { add: :+, subtract: :- }\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'dry-behaviour'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install dry-behaviour\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` 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/[USERNAME]/dry-behaviour. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.\n\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Fdry-behaviour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fam-kantox%2Fdry-behaviour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fam-kantox%2Fdry-behaviour/lists"}