{"id":22350930,"url":"https://github.com/johnae/role_playing","last_synced_at":"2025-07-09T12:48:25.699Z","repository":{"id":6825906,"uuid":"8074099","full_name":"johnae/role_playing","owner":"johnae","description":"A Ruby implementation of DCI (data, context, interaction) using SimpleDelegator","archived":false,"fork":false,"pushed_at":"2014-11-05T11:23:27.000Z","size":316,"stargazers_count":41,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-16T23:06:33.034Z","etag":null,"topics":["dci","rails","ruby"],"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/johnae.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-02-07T14:21:06.000Z","updated_at":"2023-02-23T10:19:54.000Z","dependencies_parsed_at":"2022-08-27T20:32:55.219Z","dependency_job_id":null,"html_url":"https://github.com/johnae/role_playing","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/johnae/role_playing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnae%2Frole_playing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnae%2Frole_playing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnae%2Frole_playing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnae%2Frole_playing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/johnae","download_url":"https://codeload.github.com/johnae/role_playing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/johnae%2Frole_playing/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264460878,"owners_count":23612066,"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":["dci","rails","ruby"],"created_at":"2024-12-04T12:11:43.835Z","updated_at":"2025-07-09T12:48:25.682Z","avatar_url":"https://github.com/johnae.png","language":"Ruby","readme":"[![Build Status](https://travis-ci.org/johnae/role_playing.png)](https://travis-ci.org/johnae/role_playing)\n\n# RolePlaying\n\nA ruby DCI implementation using SimpleDelegator. This was extracted from a Rails app I'm working on. It's a very simple and straightforward implementation.\n\nI'm well aware that this is not \"true\" DCI but I believe it to be in the spirit of DCI while avoiding the awfulness that is object.extend.\n\nUsing object.extend in Ruby has two severe problems, one that makes it not true DCI and another that makes it really really slow:\n\n1. There is no unextend\n2. It blows rubys method cache when used\n\nA further comment on 2 is that it means EVERY time you call extend it blows Rubys ENTIRE method cache - it doesn't mean just the object you're extending, it means everything.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'role_playing'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install role_playing\n\n## Usage\n\nUsing it is as simple as defining (usually) a context like so:\n\n```ruby\nclass MoneyTransferring\n  include RolePlaying::Context\n\n  def initialize(from_account, to_account)\n    @from_account = from_account\n    @to_account = to_account\n  end\n  def call(amount)\n    ## this is a little contrived I know\n    ## it could be easily implemented using\n    ## increment/decrement methods - just\n    ## showing the block syntax here\n    SourceAccount(@from_account) do |source_account|\n      DestinationAccount(@to_account).deposit(source_account.withdraw(amount))\n    end\n  end\n\n  role :SourceAccount do\n    def withdraw(amount)\n      self.amount=self.amount-amount\n      amount\n    end\n  end\n\n  role :DestinationAccount do\n    def deposit(amount)\n      self.amount=self.amount+amount\n    end\n  end\n\nend\n```\n\nOr roles by themselves like so:\n\n```ruby\nclass MyRole \u003c RolePlaying::Role\n  def my_additional_method\n  end\nend\n\nclass MyOtherRole \u003c RolePlaying::Role\n  def my_other_method\n  end\nend\n```\n\nAnd, if defined by themselves, they can be applied in a few ways:\n\n```ruby\n## our data object which will play different roles (eg. get new/different behavior within a context)\nclass MyDataObject\nend\n\nMyRole.played_by(MyDataObject) do |role|\n  role.my_additional_method\nend\n```\n\nor\n\n```ruby\nrole = MyRole.played_by(MyDataObject)\nrole.my_additional_method\n```\n\nseveral roles can be applied too like so:\n\n```ruby\n[MyRole, MyOtherRole].played_by(MyDataObject) do |role|\n  role.my_additional_method\n  role.my_other_method\nend\n```\n\nor\n\n```ruby\nrole = [MyRole, MyOtherRole].played_by(MyDataObject)\nrole.my_additional_method\nrole.my_other_method\n```\n\nWithin a context a role is defined by the role class method. The syntax sugar of applying a role - eg. MyRole(MyDataObject) do |role| - is only available within classes including the RolePlaying::Context module. This was the way I envisioned it - to basically keep all code concerning a context within the same file (and inside the context class).\n\nPlease read the specs for a better understanding. Also please look up DCI (data, context, interaction) for a better understanding of what this is trying to accomplish.\n\n## RSpec\n\nThere's an RSpec extension included which basically aliases RSpecs context to role so the language used in RSpec can be closer to DCI when testing these things.\nTo use that extension just do require 'role_playing/rspec_role' in your spec_helper. Look at the specs in this gem to see what I mean.\n\n## Rails\n\nTheres a Railtie that adds autoloading of directory \"contexts\", the idea is to put all contexts and roles in there (roles are defined within the surrounding\ncontext in the same file).\n\nThe structure would look like:\n\n    app/\n        assets/\n    -\u003e  contexts/\n        controllers/\n        helpers/\n        models/\n        ...\n\n\n## Links\n\nhttp://dci-in-ruby.info\n\nhttp://www.clean-ruby.com\n\nhttp://tonyarcieri.com/dci-in-ruby-is-completely-broken - on why extend is bad\n\nhttp://rubysource.com/dci-the-evolution-of-the-object-oriented-paradigm/\n\nhttp://vimeo.com/8235394 - the inventor himself talks about DCI\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 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnae%2Frole_playing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjohnae%2Frole_playing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjohnae%2Frole_playing/lists"}