{"id":17682188,"url":"https://github.com/emancu/double_dispatch","last_synced_at":"2025-10-30T11:39:02.569Z","repository":{"id":62557443,"uuid":"132200239","full_name":"emancu/double_dispatch","owner":"emancu","description":"Method overloading with runtime types made easy, using double dispatch in Ruby","archived":false,"fork":false,"pushed_at":"2018-05-05T00:20:51.000Z","size":3,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-16T01:56:46.314Z","etag":null,"topics":["double-dispatch","object-oriented-programming","ruby-gem"],"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/emancu.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2018-05-05T00:09:11.000Z","updated_at":"2018-05-05T01:38:02.000Z","dependencies_parsed_at":"2022-11-03T06:15:45.264Z","dependency_job_id":null,"html_url":"https://github.com/emancu/double_dispatch","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/emancu/double_dispatch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emancu%2Fdouble_dispatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emancu%2Fdouble_dispatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emancu%2Fdouble_dispatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emancu%2Fdouble_dispatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/emancu","download_url":"https://codeload.github.com/emancu/double_dispatch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/emancu%2Fdouble_dispatch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271902602,"owners_count":24841246,"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","status":"online","status_checked_at":"2025-08-24T02:00:11.135Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["double-dispatch","object-oriented-programming","ruby-gem"],"created_at":"2024-10-24T09:13:08.973Z","updated_at":"2025-10-30T11:38:57.546Z","avatar_url":"https://github.com/emancu.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"double_dispatch\n========\n\n[![Maintainability](https://api.codeclimate.com/v1/badges/23f111d2fc921ff531f6/maintainability)](https://codeclimate.com/github/emancu/double_dispatch/maintainability)\n\nCall different functions depending on the runtime types of two objects.\nExtremely simple to use and extend.\n\nI personally use it to compensate the lack of _method overloading_ in Ruby and\nseparate concerns into smaller `modules`.\n\n## Usage\n\n1. Define a unique `dispatch_id` for each class using the `dispatch_as` method.\n\n```ruby\nclass Dog\n  include DoubleDispatch\n\n  dispatch_as :dog\n\n  def pet\n    #...\n  end\nend\n\nclass Human\n  include DoubleDispatch\n\n  dispatch_as :human\n\n  attr_accessor :name\n\n  def initialize(name)\n    @name = name\n  end\nend\n```\n\n2. Write concrete functions for each class you want handle\n\n```ruby\nmodule Salutations\n  def salute_to_human(human)\n    \"Hi #{human.name}!\"\n  end\n\n  def salute_to_dog(dog)\n    dog.pet\n\n    \"Woof woof!\"\n  end\nend\n```\n\n3. Call `double_dispatch` to handle different non-necessary-polymorphic objects.\n\n```ruby\nDog.new.double_dispatch(:salute_to, Salutations)\n# =\u003e \"Woof woof!\"\n\nHuman.new(\"Emiliano\").double_dispatch(:salute_to, Salutations)\n# =\u003e \"Hi Emiliano!\"\n```\n\n## Common patterns\n\n### Create modules to encapsulate the logic\n\nThis is my favourite pattern.\nUsing the same example described above, we can create a better internal API if we\nencapsulate all the salutation logic into a single `module`\n\n```ruby\nmodule Salutations\n  def self.salute(somebody)\n    somebody.double_dispatch(:salute_to, self)\n  end\n\n  def salute_to_human(human)\n    \"Hi #{human.name}!\"\n  end\n\n  def salute_to_dog(dog)\n    dog.pet\n\n    \"Woof woof!\"\n  end\nend\n```\n\nAnd then, we use the module in a cleaner way:\n\n```\nSalutations.salute Dog.new\n# =\u003e \"Woof woof!\"\n\nSalutations.salute Human.new(\"Emiliano\")\n# =\u003e \"Hi Emiliano!\"\n```\n\n### Use `class.name` as `dispatch_id`\n\nI frequently find myself using the same `dispatch_id` as the _class name_, so\nI used to extend `DoubleDispatch` with the following snippet\n\n```ruby\nmodule DoubleDispatch\n  module ByClassName\n    module ClassMethods\n      def dispatch_id\n        @dispatch_id ||= self.name.split('::').last.downcase\n      end\n    end\n\n    def self.included(base)\n      base.include(::DoubleDispatch)\n      base.extend(ClassMethods)\n    end\n  end\nend\n```\n\n\n### Use _table's name_ as `dispatch_id`\n\nMost of the time, we will use Active Record objects (or Sequel models, etc) in\nour system and we want to identify these models by the table name.\n\nSince this gem is flexible and easy to extend, I suggest to extend `DoubleDispatch`\nwith a specific module using the ORM-specific methods.\n\nFor example, an extension for `Sequel` models would be:\n\n```ruby\nmodule DoubleDispatch\n  module ByTableName::Sequel\n    module ClassMethods\n      def dispatch_id\n        @dispatch_id ||= self.table_name\n      end\n    end\n\n    def self.included(base)\n      base.include(::DoubleDispatch)\n      base.extend(ClassMethods)\n    end\n  end\nend\n```\n\nAnd use this logic in a single line:\n\n```ruby\nclass User \u003c Sequel::Model\n  include DoubleDispatch::ByTableName::Sequel\n\n  ...\nend\n```\n\nAs you can see, it won't **need** to call `dispatch_as` method, but you can always\ncall it and overwrite the `dispatch_id`. This is extremely useful when you define\nmore than a model over the same _table name_.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femancu%2Fdouble_dispatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Femancu%2Fdouble_dispatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Femancu%2Fdouble_dispatch/lists"}