{"id":13880286,"url":"https://github.com/stevenharman/dumb_delegator","last_synced_at":"2025-05-15T19:06:33.757Z","repository":{"id":2772236,"uuid":"3771091","full_name":"stevenharman/dumb_delegator","owner":"stevenharman","description":"Delegator and SimpleDelegator in Ruby's stdlib are useful, but they pull in most of Kernel. This is not appropriate for many uses; for instance, delegation to Rails models.","archived":false,"fork":false,"pushed_at":"2024-12-17T16:11:48.000Z","size":79,"stargazers_count":65,"open_issues_count":0,"forks_count":6,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-04-08T00:37:21.727Z","etag":null,"topics":["design-patterns","proxy-object","ruby","simpledelegator"],"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/stevenharman.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-03-20T02:35:24.000Z","updated_at":"2024-12-17T16:09:58.000Z","dependencies_parsed_at":"2025-01-12T10:01:14.306Z","dependency_job_id":"f5c64ca8-8812-49cc-ad1f-1409aad46858","html_url":"https://github.com/stevenharman/dumb_delegator","commit_stats":{"total_commits":94,"total_committers":9,"mean_commits":"10.444444444444445","dds":"0.37234042553191493","last_synced_commit":"d4348748dec3633f6e904676a86aa7f78f1397de"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenharman%2Fdumb_delegator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenharman%2Fdumb_delegator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenharman%2Fdumb_delegator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenharman%2Fdumb_delegator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevenharman","download_url":"https://codeload.github.com/stevenharman/dumb_delegator/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254404357,"owners_count":22065641,"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":["design-patterns","proxy-object","ruby","simpledelegator"],"created_at":"2024-08-06T08:02:55.000Z","updated_at":"2025-05-15T19:06:33.736Z","avatar_url":"https://github.com/stevenharman.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# DumbDelegator\n\n[![Gem Version](https://badge.fury.io/rb/dumb_delegator.svg?icon=si%3Arubygems\u0026icon_color=%23ff2600)](https://badge.fury.io/rb/dumb_delegator)\n[![CI](https://github.com/stevenharman/dumb_delegator/actions/workflows/ci.yml/badge.svg)](https://github.com/stevenharman/dumb_delegator/actions/workflows/ci.yml)\n[![Maintainability](https://api.codeclimate.com/v1/badges/b684cbe08af745cbe957/maintainability)](https://codeclimate.com/github/stevenharman/dumb_delegator/maintainability)\n[![Test Coverage](https://api.codeclimate.com/v1/badges/b684cbe08af745cbe957/test_coverage)](https://codeclimate.com/github/stevenharman/dumb_delegator/test_coverage)\n\nRuby provides the `delegate` standard library.\nHowever, we found that it is not appropriate for cases that require nearly every call to be proxied.\n\nFor instance, Rails uses `#class` and `#instance_of?` to introspect on Model classes when generating forms and URL helpers.\nThese methods are not forwarded when using `Delegator` or `SimpleDelegator`.\n\n```ruby\nrequire \"delegate\"\n\nclass MyAwesomeClass\n  # ...\nend\n\no = MyAwesomeClass.new\nd = SimpleDelegator.new(o)\n\nd.class                #=\u003e SimpleDelegator\nd.is_a? MyAwesomeClass #=\u003e false\n```\n\n`DumbDelegator`, on the other hand, forwards almost ALL THE THINGS:\n\n```ruby\nrequire \"dumb_delegator\"\n\nclass MyAwesomeClass\n  # ...\nend\n\no = MyAwesomeClass.new\nd = DumbDelegator.new(o)\n\nd.class                #=\u003e MyAwesomeClass\nd.is_a? MyAwesomeClass #=\u003e true\n```\n\n## Installation\n\nAdd this line to your Gemfile:\n\n```ruby\ngem \"dumb_delegator\"\n```\n\nAnd then install:\n\n```bash\n$ bundle\n```\n\nOr install it yourself:\n\n```bash\n$ gem install dumb_delegator\n```\n\n### Versioning\n\nThis project adheres to [Semantic Versioning][semver].\n\n#### Version `0.8.x`\n\nThe `0.8.0` release was downloaded 1.2MM times before the `1.0.0` work began.\nWhich is great! 🎉\nBut, we wanted to clean up some cruft, fix a few small things, and improve ergonomics.\nAnd we wanted to do all of that while, hopefully, not breaking existing usage.\n\nTo that end, `1.0.0` dropped support for all [EoL'd Rubies][ruby-releases] and only officially supported Ruby `2.4` - `2.7` when it was released.\nHowever, most older Rubies, _should_ still work.\nMaybe… Shmaybe?\nExcept for Ruby 1.9, which probably _does not work_ with `DumbDelegator` `\u003e 1.0.0`.\nIf you're on an EoL'd Ruby, please try the `0.8.x` versions of this gem.\n\n## Usage\n\n`DumbDelegator`'s API and usage patters were inspired by Ruby stdlib's `SimpleDelegator`.\nSo the usage and ergonomics are quite similar.\n\n```ruby\nrequire \"dumb_delegator\"\n\nclass Coffee\n  def cost\n    2\n  end\n\n  def origin\n    \"Colombia\"\n  end\nend\n\nclass Milk \u003c DumbDelegator\n  def cost\n    super + 0.4\n  end\nend\n\nclass Sugar \u003c DumbDelegator\n  def cost\n    super + 0.2\n  end\nend\n\ncoffee = Coffee.new\n\ncup_o_coffee = Sugar.new(Milk.new(coffee))\ncup_o_coffee.origin        #=\u003e Colombia\ncup_o_coffee.cost          #=\u003e 2.6\n\n# Introspection\ncup_o_coffee.class         #=\u003e Coffee\ncup_o_coffee.__getobj__    #=\u003e #\u003cCoffee:0x00007fabed1d6910\u003e\ncup_o_coffee.inspect       #=\u003e \"#\u003cSugar:70188197507600 obj: #\u003cMilk:70188197507620 obj: #\u003cCoffee:0x00007fabed1d6910\u003e\u003e\u003e\"\ncup_o_coffee.is_a?(Coffee) #=\u003e true\ncup_o_coffee.is_a?(Milk)   #=\u003e true\ncup_o_coffee.is_a?(Sugar)  #=\u003e true\n```\n\n### Rails Model Decorator\n\nThere are [many decorator implementations](http://robots.thoughtbot.com/post/14825364877/evaluating-alternative-decorator-implementations-in) in Ruby.\nOne of the simplest is \"`SimpleDelegator` + `super` + `__getobj__`,\" but it has the drawback of confusing Rails.\nIt is necessary to redefine `#class`, at a minimum.\nIf you're relying on Rails' URL Helpers with a delegated object, you also need to redefine `#instance_of?`.\nWe've also observed the need to redefine other Rails-y methods to get various bits of 🧙 Rails Magic 🧙 to work as expected.\n\nWith `DumbDelegator`, there's not a need for redefining these things because nearly every possible method is delegated.\n\n### Optional `case` statement support\n\nInstances of `DumbDelegator` will delegate `#===` out of the box.\nMeaning an instance can be used in a `case` statement so long as the `when` clauses rely on instance comparison.\nFor example, when using a `case` with a regular expression, range, etc...\n\nIt's also common to use Class/Module in the `where` clauses.\nIn such usage, it's the Class/Module's `::===` method that gets called, rather than the `#===` method on the `DumbDelegator` instance.\nThat means we need to override each Class/Module's `::===` method, or even monkey-patch `::Module::===`.\n\n`DumbDelegator` ships with an optional extension to override a Class/Module's `::===` method.\nBut you need to extend each Class/Module you use in a `where` clause.\n\n```ruby\ndef try_a_case(thing)\n  case thing\n  when MyAwesomeClass\n    \"thing is a MyAwesomeClass.\"\n  when DumbDelegator\n    \"thing is a DumbDelegator.\"\n  else\n    \"Bad. This is bad.\"\n  end\nend\n\ntarget = MyAwesomeClass.new\ndummy = DumbDelegator.new(target)\n\ntry_a_case(dummy) #=\u003e thing is a DumbDelegator.\n\nMyAwesomeClass.extend(DumbDelegator::TripleEqualExt)\n\ntry_a_case(dummy) #=\u003e thing is a MyAwesomeClass.\n```\n\n#### Overriding `Module::===`\nIf necessary, you could also override the base `Module::===`, though that's pretty invasive.\n\n🐲 _There be dragons!_ 🐉\n\n```ruby\n::Module.extend(DumbDelegator::TripleEqualExt)\n```\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n\n## Contribution Ideas/Needs\n\n1. Ruby 1.8 support (use the `blankslate` gem?)\n\n\n[ruby-releases]: https://www.ruby-lang.org/en/downloads/branches/ \"The current maintenance status of the various Ruby branches\"\n[semver]: https://semver.org/spec/v2.0.0.html \"Semantic Versioning 2.0.0\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenharman%2Fdumb_delegator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevenharman%2Fdumb_delegator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenharman%2Fdumb_delegator/lists"}