Inspectable
Inspectable is a customizable object inspector which enhances Ruby Object Inspection behavior beyond what is provided by default. You can always implement your own #inspect method but this becomes tedious when you need to exclude or transform the same instance variables across multiple objects.
- Features
- Requirements
- Setup
- Usage
- Registry
- Transformers
- Redactor
- Typer
- Overrides
- Custom
- Native
- RSpec
- Development
- Tests
- License
- Security
- Code of Conduct
- Contributions
- Developer Certificate of Origin
- Versions
- Community
- Credits
Features
-
Allows you to customize and configure
Object#inspectwith minimal effort. -
Allows you to exclude instance variables that are too verbose or undesired.
-
Allows you to register reusable transformers for more advanced behavior.
Requirements
-
Ruby.
-
A good understanding of default Ruby Object Inspection behavior.
Setup
To install with security, run:
# đź’ˇ Skip this line if you already have the public certificate installed.
gem cert --add <(curl --compressed --location https://alchemists.io/gems.pem)
gem install inspectable --trust-policy HighSecurityTo install without security, run:
gem install inspectableYou can also add the gem directly to your project:
bundle add inspectableOnce the gem is installed, you only need to require it:
require "inspectable"Usage
By default, Object#inspect includes all instance variables so this gem is designed to exclude what you don’t need or transform variables before being inspected. Otherwise, you don’t need this gem at all. Take the following, for example:
class Demo
include Inspectable[:token]
def initialize token: "secret", uri: "https://demo.io"
@token = token
@uri = uri
end
end
Demo.new.inspect
#<Demo:0x00000000000005d0 @uri="https://demo.io">Notice, when inspecting the instance of Demo, the token instance variable was excluded. This is important when you want to hide sensitive information, information that is too verbose, or you don’t need at all.
You can also use the built-in transformers to transform sensitive information as well. Example:
class Demo
include Inspectable[token: :redact]
def initialize token: "secret", uri: "https://demo.io"
@token = token
@uri = uri
end
end
Demo.new.inspect
"#<Demo:0x0000000000000ea0 @token=\"[REDACTED]\", @uri=\"https://demo.io\">"The above is nearly identical to the first example except we’ve used the built-in :redact transformer which replaced "secret" with "[REDACTED]". This is why this transformer is provided for you because leaking sensitive information is a fairly common occurrence that this gem has made this simple for you to manage. To learn more, see the Transformers section below.
Registry
At the moment, this gem’s registry only allows the registration of custom transformers. Example:
Inspectable.add_transformer(:upcase, -> value { value.upcase if value })
.add_transformer "cleaner", -> value { value.sub(/\h{10}/, "") if value }Notice you can register transformers using strings or symbols. The latter is preferred because all transformers are identified by symbol when referenced. The value must be a lambda where the value is the value of your instance variable which will be transformed at time of object inspection.
Here’s an example using the above registered transformers:
class Demo
include Inspectable[name: :suffix, label: :upcase]
def initialize name: "demo-ab12ef", label: "Demo"
@name = name
@label = label
end
end
Demo.new.inspect
"#<Demo:0x00000000000012f0 @name=\"demo\", @label=\"DEMO\">"Transformers
This gem includes a few built-in transformers that can help you hide sensitive information or reduce verbosity. To view them, use:
Inspectable.transformers
# {
# redact: Inspectable::Transformers::Redactor,
# type: Inspectable::Transformers::Typer
# }Each is explained below.
Redactor
This transformer’s sole purpose is to hide sensitive information and is most helpful for obscuring credentials, passwords, and secrets in general. When your instance variable’s value is not nil, you’ll see "[REDACTED]" as the value. Otherwise, if your instance variable’s value is nil, you’ll see nil instead.
To use, supply the instance variable you want to transform as the key and the transformer’s key (symbol) as the value. Example:
include Inspectable[demo: :redact]Typer
This transformer always computes the instance variable’s type.
This transformer is most helpful for objects, like Dry Schema, that are extremely verbose. With this transformer, you can see the type of schema without all of the additional details. This transformer is also handy when you only want type information in general.
To use, supply the instance variable you want to transform as the key and the transformer’s key (symbol) as the value. Example:
include Inspectable[demo: :type]Overrides
Should you not like default transformer behavior, you can override an existing transformer with your own. For example, maybe you’d like the Redactor transformer to use "[FILTERED]" instead of "[REDACTED]". Here’s how you do that:
Inspectable.add_transformer :redact, -> value { "[FILTERED]" if value }The above will override default behavior with your own functionality.
Custom
You can add as many transformers as you like by using the .add_transformer method. Here are the guidelines for customization:
-
Use only a string or symbol for the first argument (a symbol is preferred). This allows you to quickly identify and use your transformer when applying custom inspection behavior to your objects.
-
Use a lambda for the second argument. The lambda must accept a value as the first positional parameter. How you transform the value is up to you but you’ll want to adhere to default Ruby Object Inspection behavior.
Let’s say you need to remove hex values from showing up when inspecting a variable value, you could register a custom transformer for this:
Inspectable.add_transformer :dehexer, -> value { value.sub(/\h+/, "").inspect if value }The above would strip hexes from the output. Notice the guard to check if the value exists before performing the transformation. This is good to have when your value might be nil so you don’t have exceptions.
ℹ️ In most cases, you must send the #inspect message to the transformed value. In situations, where you are dealing with a constant, you’ll want to avoid sending the #inspect message because constants shouldn’t be quoted. All of this is important when adhering to default Ruby Object Inspection behavior.
Native
As of Ruby 4.0.0, limited native support is provided via the private instance_variables_to_inspect instance method. This method is not compatible with this gem because this gem makes instance_variables_to_inspect redundant and will fail with a NoMethodError exception. Example:
Class.new do
include Inspectable(:name)
def initialize name = "test"
@name = name
end
private
def instance_variables_to_inspect = [:@name]
end
# undefined method 'Inspectable' for class (NoMethodError)To resolve the above exception, remove instance_variables_to_inspect and let this gem handle everything for you.
RSpec
To aid in your testing, you can use the match_inspection RSpec matcher by requiring it in your spec helper:
# spec_helper.rb
require "inspectable/rspec/matchers/match_inspection"Once required, you can use the matcher by supplying the expected attributes as follows:
RSpec.describe Demo do
subject(:demo) { described_class.new }
describe "#inspect" do
it "has inspected attributes" do
expect(demo.inspect).to match_inspection(label: "Demo", name: :demo)
end
end
endYou can also customize the matcher by providing a by hash before any expected attributes consisting of the following attributes:
-
prefix: The prefix of your attribute names. Default:"@". -
delimiter: The delimiter between each attribute name and value. Default:"=". -
separator: The separator between each attribute. Default:", ".
For example, the following is an explicit demonstration of what the matcher implicitly provides for you by default:
RSpec.describe Demo do
subject(:demo) { described_class.new }
describe "#inspect" do
it "has inspected attributes" do
expect(demo.inspect).to match_inspection(
by: {prefix: "@", delimiter: "=", separator: ", "},
label: "Demo",
name: :demo
)
end
end
endIn situations where the spec fails, you’ll get a formatted error to help you quickly resolve and fix the issue:
expected (using prefix: "@", delimiter: "=", and separator: ", "): "#<Demo:0x00000000000014d0 @label="Demo", @name=:demo @email="demo@demo.io" to match: @label="Demo", @name=:demo
Development
To contribute, run:
git clone https://github.com/bkuhlmann/inspectable
cd inspectable
bin/setupYou can also use the IRB console for direct access to all objects:
bin/consoleTests
To test, run:
bin/rakeCredits
-
Built with Gemsmith.
-
Engineered by Brooke Kuhlmann.