{"id":29906656,"url":"https://github.com/erikcameron/decorum","last_synced_at":"2025-08-01T21:11:46.086Z","repository":{"id":12949177,"uuid":"15627277","full_name":"erikcameron/decorum","owner":"erikcameron","description":"Tasteful decorators for Ruby.","archived":false,"fork":false,"pushed_at":"2021-01-19T19:25:47.000Z","size":54,"stargazers_count":8,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-09T13:37:41.424Z","etag":null,"topics":["decorators","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/erikcameron.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2014-01-04T05:51:36.000Z","updated_at":"2022-05-31T21:12:18.000Z","dependencies_parsed_at":"2022-09-01T12:21:23.850Z","dependency_job_id":null,"html_url":"https://github.com/erikcameron/decorum","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/erikcameron/decorum","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikcameron%2Fdecorum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikcameron%2Fdecorum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikcameron%2Fdecorum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikcameron%2Fdecorum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/erikcameron","download_url":"https://codeload.github.com/erikcameron/decorum/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/erikcameron%2Fdecorum/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267368854,"owners_count":24076087,"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-07-27T02:00:11.917Z","response_time":82,"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":["decorators","ruby"],"created_at":"2025-08-01T21:11:40.627Z","updated_at":"2025-08-01T21:11:46.068Z","avatar_url":"https://github.com/erikcameron.png","language":"Ruby","readme":"# Decorum\n[![Gem Version](https://badge.fury.io/rb/decorum.png)](http://badge.fury.io/rb/decorum)\n[![Build Status](https://travis-ci.org/erikcameron/decorum.png?branch=main)](https://travis-ci.org/erikcameron/decorum)\n\nDecorum implements lightweight decorators for Ruby, which we're\ncalling \"tasteful decorators.\" (See below.)  It is very small, and\nhas no requirements outside of the standard library.  Use it wherever.\n\n## Quick Start\n\n```ruby\ngem install decorum\n\nclass BirthdayParty\n  include Decorum::Decorations\nend\n\nclass Confetti \u003c Decorum::Decorator\n  def shoot_confetti\n    \"boom, yay\"\n  end\nend\n\nbp = BirthdayParty.new\nbp.respond_to?(:shoot_confetti) # ==\u003e false\nbp.is_decorated? # ==\u003e false\n# but wait!\nbp.decorate(Confetti)\n# and now...\nbp.respond_to?(:shoot_confetti) # ==\u003e true\nbp.is_decorated? # ==\u003e true\nbp.shoot_confetti # ==\u003e \"boom, yay\"\n```\n\n## 0.5.1\n- Test suite maintenance, all is well.\n\n## New in 0.5.0\n- The (public) methods in Decorum::Decorations (decorate, undecorate, etc.) may now be aliased. If you were having weird bugs beacuse Decorum was clobbering some existing method named `#decorate`, this is the version for you. More below.\n\n## New in 0.4.0\n- Decorators and their attributes may now be specified in class definitions, and loaded with `#load_decorators_from_class`\n- `#is_decorated?`\n\n## New in 0.3.0\n- Methods may be called directly on decorators\n- Decorator namespaces\n- Set superhash and shared state classes via environment\n- Entire decorator interfaces may be declared `immediate` (see below)\n- #post_decorate callback\n\n## About\n\n[Skip to the action](#usage)\n\nDecorum expands on the traditional Decorator concept by satisfying [a few additional \ncontraints](#tasteful-decorators). The constraints are designed to make decorators' role in your \noverall object structure both clear and safe. More on these points below.\n\n- Object Identity: After you decorate an object, you're still dealing with that same\nobject. \n- Defers to the original interface: by default, Decorum decorators will _not_ override\nthe decorated object's public methods. (Though you can instruct it to.) This is intentional.\n- Respects existing overrides of `#method_missing`\n- Decorators are unloadable\n\nBy adhering to these constraints, decorators tend to do the Right Thing, i.e, integrate into existing\napplications easily, and stay out of the way when they aren't doing your bidding. Hence \"[tasteful\ndecorators](#tasteful-decorators).\" (Not meant to imply others are tacky. The name just stuck.)\n\nIn addition, Decorum provides a few helpful features:\n\n- Stackable decorators, with shared state \n- Recursion, via `#decorated_tail`\n- Intercept/change/reroute messages, a la Chain of Reponsibility\n- Build stuff entirely out of decorators\n\nAs an example of how this is in use right now, suppose you're interfacing a content\nmanagement system with an existing data application. You want to build a sidebar \nof image links. The images are in the CMS, but their metadata are stored in the application.\nYou want those systems to stay uncoupled. You can use a decorator to slap the metadata \non the image at runtime, e.g.,:\n\n```ruby\nimage_collection = Application::ImageData.sidebar_images\nimages = Cms::Images.where(identifier: image_collection.keys)\n# ==\u003e { blah: { url: 'http://blah.foo/', alt: 'The Blah Conglomerate' ... }}\n\nimages.each do |img|\n  img.decorate(ImageMetaDecorator, image_collection[img.identifier])\nend\n\n# defined in ImageMetaDecorator:\nimages[0].url # ==\u003e 'http://blah.foo'\nimages[0].alt # ==\u003e 'The Blah Conglomerate'\nimages[0].fetch_thumbnail_or_queue_to_create\n```\n\n### Isn't a Decorator like a Presenter which is like an HTML macro?\n\nIn Blogylvania there is some disagreement\nabout what these terms entail.  [For example, in\nRefineryCMS](http://refinerycms.com/guides/extending-controllers-and-models-with-decorators),\n\"decorating\" a class means opening it up with a `class_eval`. (In this\nconception, the decorator isn't even an object, which is astonishing\nin Ruby.) I use the terms as follows: a \"presenter\" is an object which\nmediates between a model, controller, etc. and a view. A \"decorator\" is\nan object which answers messages ostensibly bound for another object, and\neither responds on its behalf or lets it do whatever it was going to in\nthe first place. Presenters may or may not be implemented as Decorators;\nDecorators may or may not present.\n\nLike \"traditional\" (i.e., [Gang of Four](http://en.wikipedia.org/wiki/Design_Patterns)-style)\ndecorator patterns, Decorum is a general purpose, object-oriented tool. Use it wherever.\n\n### Tasteful Decorators\n\n#### Object Identity\n\nDecorators, as conceived of by GoF, Python, etc., masquerade as the\nobjects they decorate by responding to their interface. They are _not_ the\noriginal objects themselves. This may or may not be a problem, depending\non how your code is structured. In general though, (I doubt this is news)\nit risks breaking encapsulation: Any code which stores direct references\nto the original object will have to update them to get the decorated\nbehavior.  For example, in a common Rails idiom, in order to do this:\n\n```ruby\nrender @user\n```\n\n...having already `@user = User.find(params[:id])`, you have to do this:\n\n```ruby\nif latest_winners.include(@user.id)\n  @user = FreeVacationCruiseDecorator.new(@user)\nend\n```\n\n`@user` is an instance variable of the controller, but it has to be\nupdated in order for the model to be decorated.  In practical terms,\nif you store multiple references to the same object, (say the\noriginal object is in an array somewhere, in addition to `@user`) \nyou have to update both references to get consistent behavior. The model's\ndecoration status has essentially become part of the controller's state.\n\n```ruby\nusers.include?(@user) # ==\u003e true\nwinning_users = users.map { |u| FreeVacationCruiseDecorator.new(u) }\ndecorated = winning_users.detect { |u| u.id == @user.id } # the decorated object---should be the \"same\" thing\ndecorated.destination # ==\u003e \"tahiti\"\n@user.destination # ==\u003e NoMethodError\n```\nIn Decorum, objects use decorator classes (descendents of Decorum::Decorator) to decorate themselves:\n\n```ruby\nusers.each do |user|\n  user.decorate(FreeVacationCruiseDecorator, because: \"You are teh awesome!\")\nend\n\n@user.assault_with_flashing_gifs! # ==\u003e that method wasn't there before!\n```\n\nThe \"decorated object\" is the same old object, because it manages all of its\nstate, including its decorations. References don't need to change, \nand state stays where it should.\n\n#### Defers to the original interface\n\nUnless instructed otherwise, Decorum will not override existing methods\non the decorated object. (See below for how to instruct it otherwise.)\nThere are certainly cases where this is useful, and having come across\none myself since the 0.2.0 release, I've amended my position slightly. But\nnot much.\n\nI'm suspicious of this practice, as it blurs the line between\n\"decorating\" an object and straight-up monkey patching it.  The fact that the method\n_needs_ overriding implies the original object doesn't have the relevant\nstate to fulfill it. The method is now spread out over two classes,\nthat of the original object and that of the decorator. This may be\nunavoidable, or monkey patching may be your intention---in that case,\nby all means. But I don't think it's a pattern to be designed to.\n\nThe paradigm cases of decorators don't generally address overriding either. Consider three\ncommon examples:\n\n- Adding a scrollbar to a window\n- Adding milk to a cup of coffee\n- Providing `#full_name` to an object that supplies `#first_name` and `#last_name`\n\nIn all of these cases, the decorators provide some new functionality;\nthey don't change the object's original implementation. Obviously, you\nshouldn't rule it out just because the common examples don't have it,\nbut it's by no means an essential use of the pattern. And from a design\nperspective, it's a red flag that concerns are becoming... unseparated.\n(It also risks weird bugs by breaking transparency, i.e., you can have\ncases where `a` and `b` are literally identical, but have different \nattributes.) With Decorum, you can implement that entire domain of behavior,\npossibly using a [namespace](#namespaced-decorators).\n\nWhen an object is decorated, Decorum inserts its own `#method_missing`\nand `#respond_to_missing?` into the object's eigenclass. Decorum's `#method_missing`\nis only consulted after the original object has abandoned the message. (When\noverriding original methods with `immediate`, each method gets its own \nredirect in the eigenclass as well, intercepting the message before the\noriginal definition is found.)\n\n#### Respects existing overrides of `#method_missing`\n\nA sizeable amount of the world's total Ruby functionality is implemented\nby overriding `#method_missing`, so decorators shouldn't get in the way.\nBecause Decorum intercepts messages in the objects eigenclass, it also\nrespects existing class-level overrides. If the decorator chain doesn't claim the\nmessage, `super` is called and lookup proceeds normally.\n\n#### Unloadable decorators\n\nDecorators can be unloaded, if necessary:\n\n```ruby\n@bob.decorate(IsTheMole)\n\n# meanwhile:\nclass IsTheMole \u003c Decorum::Decorator\n  # hook method\n  def post_decorate\n    object.revoke_security_clearances\n  end\n\n  def revoke_security_clearances\n    clearances = object.decorators.select { |d| d.is_a?(SecurityClearance) }\n    clearances.each { |clearance| object.undecorate(clearance) }\n  end\nend\n```\nThis example also shows the use of the `#post_decorate` hook.\n\n\n### Implementation\n\nAs in other implementations, Decorum decorators wrap another object\nto which they forward unknown messages.  In both cases, all decorators\nother than the first wrap another decorator.  Instead of wrapping the\noriginal object however, the first decorator in Decorum wraps an instance\nof Decorum::ChainStop. If a method reaches the bottom of the chain, this\nobject throws a signal back up the stack to Decorum's `#method_missing`,\nwhich then calls `super.` (This is a throw/catch, not an exception,\nwhich would be significantly slower.)\n\nSee the source for more details.\n\n## Usage\n\nFirst, objects need to be decoratable. You can do this for a whole class,\nby including Decorum::Decorations, or for a single object, by extending it.\nNote: this alone doesn't change the object's method lookup. It just makes\n`#decorate` and friends available on the object. (Note: As of 0.5.0 this\nisn't strictly true. See [Aliasing Decorum Methods](#aliasing-decorum-methods) below.) The behavior is only changed when\n`#decorate` is called. The easiest way is probably including \nDecorum::Decorations at whatever point(s) of the class hierarchy you\nfeel appropriate.\n\n### Using Decorators\nThe decorated object is accessible as either `#root` or `#object`. A helper method:\n\n```ruby\nclass Royalty \u003c Human\n  # makes our model decoratable\n  include Decorum::Decorations\n  attr_accessor :fname, :lname, :array_of_middle_names, :array_of_styles\nend\n\nclass StyledNameDecorator \u003c Decorum::Decorator\n  def styled_name\n    ProperOrderOfStyles.sort_and_join_this_madness(object)\n  end\nend\n\nr = Royalty.find_by_palace_name(:bob)\nr.respond_to? :styled_name # ==\u003e false\nr.decorate StyledNameDecorator\nr.respond_to? :styled_name # ==\u003e true\nr.styled_name # ==\u003e \"His Grace Most Potent Baron Sir Percy Arnold Robert \\\"Bob\\\" Gorpthwaite, Esq.\"\n```\n\nA decorator that keeps state: (code for these is in Examples)\n\n```ruby\nc = Coffee.new\n# two milks\nc.decorate(MilkDecorator, animal: \"cow\")\nc.decorate(MilkDecorator, animal: \"soycow\")\n# one sugar\nc.decorate(SugarDecorator)\nc.add_milk\nc.add_sugar\nc.milk_level  # # ==\u003e 2\nc.sugar_level # # ==\u003e 1\n```\n\nDecorators are stackable, and can take an options hash. You\ncan declare decorator attributes in a few ways:\n\n```ruby\nclass MilkDecorator \u003c Decorum::Decorator\n  attr_accessor :milk_type    \n  share :milk_level\n  default_attributes animal: \"cow\", milk_type: \"two percent\"\n  ...\n```\n\n`attr_accessor` works like normal, in that it gets/sets state on the\ndecorator; when they are called on the decorated object, the most recent\ndecorator that implements the method will answer it. `share` declares an\nattribute that is shared across all decorators of the same class on that\nobject; this shared state can be used for a number of purposes. Finally,\n`default_attributes` lets you set class-level defaults; these will be\npreempted by options passed to the constructor.\n\n#### Specifying decorators in classes\n\nDecoration may be performed by the instance method `#decorate` above,\nor you can include default decorators and arguments in your class\ndefinition:\n\n```ruby\nclass Coffee\n  include Decorum::Decorations\n  # two bovine dairy no sugar by default, please:\n  decorum Milk, { animal: \"cow\" }, Milk, { animal: \"cow\" }\n  ...\nend\n\nc = Coffee.new\nc.load_decorators_from_class\nc.add_milk\nc.milk_level # ==\u003e 2\n```\n\n(This class method was called `decorators` in versions prior to 0.5.0,\nbut in the interest of avoiding naming conflicts, it's been changed to \n`decorum`. If however you're using the standard [method aliases](#aliasing-decorum-methods),\nit will install an alias for `decorators`, allowing your existing stuff to \nkeep working without changes.)\n\nNote that this usage does _not_ automatically include decorators on new\nobjects. That would require invasive procedures on your object initialization.\nInstead, Decorum provides `#load_decorators_from_class`, which you can call \nin your initializations, or later.\n\nAs a side note, you can disable another decorators methods thus:\n\n```ruby\nclass MethodDisabler \u003c Decorum::Decorator\n  def method_to_be_disabled(*args)\n    throw :chain_stop, Decorum::ChainStop.new\n  end\nend\n```\n\n### Shared State\n\nWhen attributes are declared with `share` (or `accumulator`), they\nare shared among all decorators of that class on a given object:\nif an object has three MilkDecorators, the `#milk_level`/`#milk_level=` methods\nliterally access the same state on all three.\nIn addition, you get `#milk_level?` and `#reset_milk_level` to\nperform self-evident functions.\n\nAccess to the shared state is proxied first through the root object,\nand then through an instance of Decorum::DecoratedState, before\nultimately pointing to an instance of Decorum::SharedState. This\nclass can be set via the environment (see lib/decorum.rb).\n\nIn the examples above and below, shared state is mainly used to\naccumulate results, like in `#milk_level`. It can also be used for \nother things:\n- Serialize it, stick it in an HTML `data` attribute, and use it \n  to initailize Javascript applications\n- Store a Rails view context for rendering\n- Provide context-specific response selections for decorators, e.g.,\n  `return current_shared_responder.message(my_condition)` \n\nAnd so on. \n\n### #decorated_tail\n\nHow exactly did the first MilkDecorator pass `#add_milk`\ndown the chain instead of returning? In general, the decision\nwhether to return directly or to pass the request down the chain for further\ninput rests with the decorator itself. Cumulative decorators, like the milk example,\ncan be implemented in Decorum with a form of tail recursion:\n\n```ruby\nclass MilkDecorator \u003c Decorum::Decorator\n  share :milk_level\n  ...\n  def add_milk\n    self.milk_level = milk_level.to_i + 1\n    decorated_tail(milk_level) { next_link.add_milk }\n  end\nend\n```\n\nBecause `milk_level` is shared across all of the instances of \nMilkDecorator attached to the current cup of coffee, each\ndecorator can update it individually. The \"tail call\" actually\ngoes down the decorator chain, and is picked up by the next \ndecorator that implements the method. The call is wrapped\nin `#decorated_tail`, which will catch the end of the decorator\nchain, and return its argument; in this case, `#milk_level` called \non the final instance, so, the total amount of milk in the coffee.\nThe state is saved, and because it's shared among all the MilkDecorators,\nthe most recent one on the chain can service the getter method\nlike a normal decorated attribute.\n\nFor a demonstration of tail recursion in Decorum, see\nDecorum::Examples::FibonacciDecorator:\n\n```ruby\nfibber = SomeDecoratableClass.new\n# generate the first 100 terms of the Fibonacci sequence\n100.times do\n  fibber.decorate(FibonacciDecorator)\nend\n# call it\nfibber.fib  # ==\u003e 927372692193078999176\n# it stores both the return and the sequence in shared state:\nfibber.sequence.length == 100  # ==\u003e true\nfibber.current  # ==\u003e 927372692193078999176\n```\n\n`#decorated_tail` can be used to produce other results.\nNormally, methods are handled by the most recent decorator in the chain\nto implement it. To give the method to the _oldest_ decorator to\nimplement it, call `#decorated_tail` with a non-shared attribute/method:\n\n```ruby\nclass MilkDecorator \n  attr_accessor :animal\n  ...\n  def first_animal\n    decorated_tail(animal) { next_link.first_animal }\n  end\nend\n```\n\nThis returns the animal responsible for the first MilkDecorator\nBob took. Or call it with some other object:\n\n```ruby\n  def all_animals(animals=[])\n    animals \u003c\u003c animal\n    decorated_tail(animals) { next_link.all_animals(animals) }\n  end\n```\n\nThis will return a list of all of the animals who have contributed milk\nto Bob's coffee.\n\nIf such a method returns normally, `#decorated_tail` will return that\nvalue instead, enabling Chain of Responsibility-looking things like this:\n(sorry, no code in the examples for this one)\n\n```ruby\n  handlers = condition ? [ErrorA, SuccessA] : [ErrorB, SuccessB]\n  handlers.each do |handler|\n    @agent.decorate(handler)\n  end\n  this_service = determine_service_decorator(params) # # ==\u003e SomeServiceHandler\n  @agent.decorate(this_service)\n  @agent.service_request(params)\n\n  # meanwhile:\n  class SomeServiceHandler \u003c Decorum::Decorators\n    def service_request\n      status = perform_request_on(object)\n      if status\n        decorated_tail(DefaultSuccess.new) { next_link.special_success_method(\"Outstanding!\") }\n      else\n        decorated_tail(DefaultFailure.new) { next_link.special_failure_method(\"uh-oh\") }\n      end\n    end\n  end  \n```\n\nYou can now parameterize your responses based on whatever conditions you like,\nby loading different decorators before the request is serviced. If nobody claims\nthe specialized method, your default will be returned instead.\n\n### Calling decorators directly\n\nAn object's decorators are available via `#decorators`, or by passing a block to `#decorate`:\n\n```ruby\n@object.decorate(SomeDecorator) { |dec| @this_decorator = dec }\n@object.decorators # \u003c== array of decorators\n\n# start a request in the middle of the decorator chain:\n@the_decorator_im_looking_for = @object.decorators.detect { |d| d.name == \"cool decorator, bro\" }\n@the_decorator_im_looking_for.some_method\n```\n\n(Note that these methods pass the decorator(s) back wrapped in Decorum::CallableDecorator, \nwhich is necessary to call methods on them directly.)\n\n### Namespaced Decorators\n\nAs noted above, overriding an objects public methods breaks behavior over two \ndifferent classes. To package a domain of behavior in a namespace using Decorum:\n\n```ruby\n@request.decorate(MyRouter, namespace: \"routes\")\n@request.routes.my_route(path: \"foo/bar\")\n# namespaces pass anything they can't do back to the root\n# object, so they effectively override it:\n@request.routes.defined_on_request_object? # \u003c== true\n```\n\nThis way, you don't have to define a `#my_route` stub on the original class for\nundecorated instances, and the public method of the original object is available\non (and in) the namespace.\n\n### Overriding existing methods\n\nTo give decorator methods preference over an objects existing methods (if you\nmust) declare the method `immediate`:\n\n```ruby\nclass StrongWilledDecorator \u003c Decorum::Decorator\n  immediate :method_in_question\n\n  def method_in_question\n    \"overridden\"\n  end\nend\n\nx = WeakWilledClass.new\nx.method_in_question # \u003c== \"original\"\nx.decorate(StrongWilledDecorator)\nx.method_in_question # \u003c== \"overridden\"\n```\n\nIf you declare `immediate` with no arguments, the decorators entire public interface\nis used. \n\n### Aliasing Decorum methods\n\nIncluding/extending Decorum::Decorations makes a number of public methods (e.g., `#decorate`)\navailable on an object, which can be a problem if methods under those names\nalready exist. As of 0.5.0, these methods are actually implemented with \"internalized\"\nnames, (e.g., `#_decorum_decorate`) and aliased after the fact. Decorum is loaded by requiring \ntwo files: \n\n```ruby\n# lib/decorum.rb\nrequire_relative 'decorum/noaliases'\nrequire_relative 'decorum/aliases'\n```\n\nThe `noaliases` file loads up everything except aliasing; after requiring this file,\npublic methods in Decorum::Decorations are available by their internal names. Aliases\nare then loaded for each of the public methods, either as specified in upcased environment variables\n(e.g., `_DECORUM_DECORATE=\"my_decorate_alias\"`) or failing that, the default values\nin `aliases`. Here are the public methods and their defaults:\n\n```ruby\n# from lib/decorum/aliases.rb\n    DEFAULT_ALIASES = { _decorum_decorate: \"decorate\",\n      _decorum_undecorate: \"undecorate\",\n      _decorum_is_decorated?: \"is_decorated?\",\n      _decorum_decorators: \"decorators\",\n      _decorum_decorated_state: \"decorated_state\",\n      _decorum_load_decorators_from_class: \"load_decorators_from_class\",\n      _decorum_raw_decorators: nil,\n      _decorum_raw_decorators!: nil,\n      _decorum_namespaces: nil }\n```\n\nThe `.decorum` method made available to modules when they include Decorum::Decorations is also\naliased here as either the value of `_DECORUM_CLASS_DECORATORS` or just `.decorators`.\n\nIf you need something else, (e.g., more finely grained aliases) you can just `require decorum/noaliases`,\nand all of Decorum's aliasing will be skipped. You can then implement your own scheme however you like. \n\n\n### Decorators All the Way Down\n\nDecorum includes a class called Decorum::BareParticular, which descends from\nSuperHash. You can initialize any values you like on it, call them as methods,\nand any method it doesn't understand will return nil. The only other distinguishing\nfeature of this class is that it can be decorated, so you can create objects\nwhose interfaces are defined entirely by their decorators, and which will\nreturn nil by default.\n\n## To-do\nA few things I can imagine showing up soon:\n- See open issues\n- Thread safety: probably not an issue if you're retooling your Rails helpers,\n  but consider a case like this:\n\n```ruby\n  10.times do |i|\n    port = port_base + i  # 3001, 3002, 3003...\n    @server.decorate(RequestHandler, port: port ) \n    Thread.new do\n      @server.listen_for_changes_to_shared_state(port: port)\n    end\n  end\n```\n\n\u0026c. I'm open to suggestion.\n \n## Contributing\nI wrote most of this super late at night, (don't worry, the tests pass!) so that would be awesome:\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%2Ferikcameron%2Fdecorum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ferikcameron%2Fdecorum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ferikcameron%2Fdecorum/lists"}