{"id":28799251,"url":"https://github.com/janlelis/object_shadow","last_synced_at":"2025-09-04T05:48:38.581Z","repository":{"id":56886093,"uuid":"180160789","full_name":"janlelis/object_shadow","owner":"janlelis","description":"The Shadow of a Ruby Object lets you See and Manipulate its Instance Variables and Methods","archived":false,"fork":false,"pushed_at":"2021-12-28T17:12:35.000Z","size":124,"stargazers_count":28,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-11T06:24:41.955Z","etag":null,"topics":["chain","hacktoberfest","introspection","lookup","metaprogramming","method","reflection","ruby"],"latest_commit_sha":null,"homepage":"https://idiosyncratic-ruby.com/25-meta-methodology.html","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/janlelis.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"MIT-LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-08T13:58:01.000Z","updated_at":"2024-11-26T14:26:52.000Z","dependencies_parsed_at":"2022-08-20T13:00:06.531Z","dependency_job_id":null,"html_url":"https://github.com/janlelis/object_shadow","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/janlelis/object_shadow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janlelis%2Fobject_shadow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janlelis%2Fobject_shadow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janlelis%2Fobject_shadow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janlelis%2Fobject_shadow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/janlelis","download_url":"https://codeload.github.com/janlelis/object_shadow/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/janlelis%2Fobject_shadow/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260499343,"owners_count":23018289,"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":["chain","hacktoberfest","introspection","lookup","metaprogramming","method","reflection","ruby"],"created_at":"2025-06-18T06:04:51.347Z","updated_at":"2025-08-21T15:18:12.767Z","avatar_url":"https://github.com/janlelis.png","language":"Ruby","readme":"# Object#shadow [![[version]](https://badge.fury.io/rb/object_shadow.svg)](https://badge.fury.io/rb/object_shadow)  [![[ci]](https://github.com/janlelis/object_shadow/workflows/Test/badge.svg)](https://github.com/janlelis/object_shadow/actions?query=workflow%3ATest)\n\nHave you ever been [confused by some of Ruby's meta-programming methods?](https://idiosyncratic-ruby.com/25-meta-methodology.html)\n\nIf your answer is *Yes*, you have come to the right place:\n\n![Object and Shadow](/object_shadow.png)\n\nWith **shadow**, every Ruby object has a shadow which provides a clean API to access the object's variables and methods.\n\nNever again you will have to do the `x.methods - Object.methods` trick to get a meaningful method list.\n\n## Setup\n\nAdd to your `Gemfile`:\n\n```ruby\ngem \"object_shadow\"\n```\n\n## Usage Example\n\n```ruby\nclass P\n  def a_public_parent_method\n  end\nend\n\nclass C \u003c P\n  def initialize\n    @ivar = 42\n    @another_variable = 43\n  end\n\n  attr_reader :another_variable\n\n  def a_public_method\n  end\n\n  protected\n\n  def a_protected_method\n  end\n\n  private\n\n  def a_private_method\n  end\nend\n\nobject = C.new\n\ndef object.a_public_singleton_method\nend\n```\n\n### # Get an Overview\n\n```ruby\nrequire \"object_shadow\"\nobject.shadow # ObjectShadow of Object #47023274596520\n\n## Lookup Chain\n\n    #\u003cClass:#\u003cC:0x00005588eb283150\u003e\u003e → C → P → Object → …\n\n## 2 Instance Variables\n\n    [:ivar, :another_variable]\n\n## 4 Public Methods (Non-Class/Object)\n\n    [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]\n\n## 1 Protected Method (Non-Class/Object)\n\n    [:a_protected_method]\n\n## 2 Private Methods (Non-Class/Object)\n\n    [:a_private_method, :initialize]\n\n```\n\n### # Read \u0026 Manipulate Instance Variables\n\n```ruby\nobject.shadow[:ivar] # =\u003e 42\nobject.shadow[:another_variable] = 23; object.another_variable # =\u003e 23\nobject.shadow.variables # =\u003e [:ivar, :another_variable]\nobject.shadow.to_h # =\u003e {:ivar=\u003e42, :another_variable=\u003e23}\nobject.shadow.remove(:ivar) # =\u003e 42 (and removed)\n```\n\n### # List Available Methods\n\n```ruby\n# shadow features a single method called `methods` which takes some keyword arguments for further listing options\nobject.shadow.methods # =\u003e [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]\n\n# Use scope: option to toggle visibility (default is public)\nobject.shadow.methods(scope: :public) # =\u003e [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]\nobject.shadow.methods(scope: :protected) # =\u003e [:a_protected_method]\nobject.shadow.methods(scope: :private) # =\u003e [:a_private_method, :initialize]\nobject.shadow.methods(scope: :all) # =\u003e [:a_private_method, :a_protected_method, :a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable, :initialize]\n\n# Use inherit: option to allow or prevent traversal of the inheritance chain\nobject.shadow.methods(scope: :public, inherit: :singleton) # =\u003e [:a_public_singleton_method]\nobject.shadow.methods(scope: :public, inherit: :self) # =\u003e [:a_public_method, :a_public_singleton_method, :another_variable]\nobject.shadow.methods(scope: :public, inherit: :exclude_object) # =\u003e [:a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable]\nobject.shadow.methods(scope: :public, inherit: :all) # =\u003e [:!, :!=, :!~, :\u003c=\u003e, :==, :===, :=~, :__id__, :__send__, :a_public_method, :a_public_parent_method, :a_public_singleton_method, :another_variable, :class, :clone, :define_singleton_method, :display, :dup, :enum_for, :eql?, :equal?, :extend, :freeze, :frozen?, :hash, :inspect, :instance_eval, :instance_exec, :instance_of?, :instance_variable_defined?, :instance_variable_get, :instance_variable_set, :instance_variables, :is_a?, :itself, :kind_of?, :method, :methods, :nil?, :object_id, :private_methods, :protected_methods, :public_method, :public_methods, :public_send, :remove_instance_variable, :respond_to?, :send, :shadow, :singleton_class, :singleton_method, :singleton_methods, :taint, :tainted?, :tap, :then, :to_enum, :to_s, :trust, :untaint, :untrust, :untrusted?, :yield_self]\n\n# Use target: :instances or :class to jump between child and class method listings\nC.shadow.methods == C.new.shadow.methods(target: :class) #=\u003e true\nC.shadow.methods(target: :instances) == C.new.shadow.methods #=\u003e true\nEnumerable.shadow.methods(target: :instances) # (lists Enumerables' methods)\n```\n\n## Documentation\n\n### Instance Variables\n\nShadow exposes instance variables in a Hash-like manner:\n\nMethod     | Description\n-----------|------------\n`[]`       | Retrieve instance variables. Takes a symbol without `@` to identify variable.\n`[]=`      | Sets instance variables. Takes a symbol without `@` to identify variable.\n`remove`   | Removes an instance variables. Takes a symbol without `@` to identify variable.\n`variable?`| Checks if a variable with that name exists. Takes a symbol without `@` to identify variable.\n`variables`| Returns the list of instance variables as symbols without `@`.\n`to_h`     | Returns a hash of instance variable names with `@`-less variables names as the keys.\n`to_a`     | Returns an array of all instance variable values.\n\n### Method Introspection\n\nAll method introspection methods get called on the shadow and take a `target:` keyword argument, which defaults to `:self`. It can take one of the following values:\n\nValue of `target:` | Meaning\n-------------------|--------\n`:self`            | Operate on the current object\n`:class`           | Operate on the current object's class (the class for instances, the singleton class for classes)\n`:instances`       | Operate on potential instances created by the object, which is a class (or module)\n\n#### `methods(target: :self, scope: :public, inherit: :exclude_class)`\n\nReturns a list of methods available to the object.\n\nOnly shows methods matching the given `scope:`, i.e. when you request all **public** methods, **protected** and **private** methods will not be included. You can also pass in `:all` to get methods of *all* scopes.\n\nThe `inherit:` option lets you choose how deep you want to dive into the inheritance chain:\n\nValue of `inherit:` | Meaning\n--------------------|--------\n`:singleton`        | Show only methods directly defined in the object's singleton class\n`:self`             | Show singleton methods and methods directly defined in the object's class, but do not traverse the inheritance chain\n`:exclude_class`    | Stop inheritance chain just before Class or Module. For non-modules it fallbacks to `:exclude_object`\n`:exclude_object`   | Stop inheritance chain just before Object\n`:all`              | Show methods from the whole inheritance chain\n\n#### `method?(method_name, target: :self)`\n\nReturns `true` if such a method can be found, `false` otherwise\n\n#### `method_scope(method_name, target: :self)`\n\nReturns the visibility scope of the method in question, one of `:public`, `:protected`, `:private`. If the method cannot be located, returns `nil`.\n\n#### `method(method_name, target: :self, unbind: false, all: false)`\n\nReturns the `Method` or `UnboundMethod` object of the method requested. Use `unbind: true` to force the return value to be an `UnboundMethod` object. Will always return `UnboundMethod`s if used in conjunction with `target: :instances`.\n\nIf you pass in `all: true`, it will return an array of all (unbound) method objects found in the inheritance chain for the given method name.\n\n#### `method_lookup_chain(target: :self, inherit: :exclude_class)`\n\nShows the lookup chain for the target. See `methods()` for description of the `inherit:` option.\n\n## Q \u0026 A\n\n### Can I Access Hidden Instance Variables?\n\nSome of Ruby's core classes use `@`-less instance variables, such as [Structs](https://ruby-doc.org/core/Struct.html). They cannot be accessed using shadow.\n\n### Does It Support Refinements?\n\n[Currently not.](https://ruby-doc.org/core/doc/syntax/refinements_rdoc.html#label-Methods+Introspection)\n\n### Other Meta Programming?\n\nOnly some aspects of Ruby meta-programming are covered. However, **shadow** aims to cover all kinds of meta-programming. Maybe you have an idea about how to integrate `eval`, `method_missing`, and friends?\n\n### Does this Gem Include a Secret Mode which Activates an Improved Shadow Inspect?\n\nYes, run the following command.\n\n```ruby\nObjectShadow.include(ObjectShadow::DeepInspect)\n42.shadow\n```\n\nRequires the following gems: **paint**, **wirb**, **io-console**\n\n\n## J-_-L\n\nCopyright (C) 2019-2021 Jan Lelis \u003chttps://janlelis.com\u003e. Released under the MIT license.\n\nPS: This gem would not exist if the [instance gem](https://rubyworks.github.io/instance/) did not come up with the idea.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanlelis%2Fobject_shadow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjanlelis%2Fobject_shadow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjanlelis%2Fobject_shadow/lists"}