rails icon indicating copy to clipboard operation
rails copied to clipboard

ActiveSupport::Inspect: easily set up constrained inspect output

Open matthewd opened this issue 1 year ago • 2 comments

As noted in https://github.com/rails/rails/pull/44495, since Ruby 3.0, common exceptions like NoMethodError now include the full inspect value in their message, regardless of size.

That helps give control over the object's representation, but can be unpleasant when combined with the default all-ivar-values inspect behaviour: it makes for unworkably large exception messages, and can be particularly confusing when objects contain a reference to their "parent", ultimately spewing an enormous object graph, only a tiny fraction of which actually identifies the object originally intended.

We've already added several custom inspect methods (#44495 and #44456 among them), and had other reports that partly or completely trace back to this issue, including #44951 and #45121.


While some of our custom inspect methods have corresponding pretty print support, others do not -- which is fine for a trivial fixed string, but less fine for anything that includes further inspection. Meanwhile newer work on irb makes good pretty-printing more important for standard use cases.

My goal is to make it particularly easy to define a custom inspect behaviour, in a way that supports both inspect and pp, maintains some basic formatting consistency (e.g. use of #<..>), allows compact presentation of primary identifying info, and still has some escape hatches for a class author with a particular presentation in mind.

By making it trivial to define a nice inspect presentation, we can make it common to do so, and thereby curtail runaway object graphs. To that end, we can consciously trade inspection-performance-per-object for utility: the more useful this module (and its output) is, the more often it will be used, and the fewer total objects will be inspected at any one time.


As well as defining the base module, this PR switches a number of existing def inspect to use it, demonstrating that it involves similar-or-less code for similar-or-better results; it also employs the feature to set up new inspection for several notable runaway-inspections I've observed, such as AR adapters and pools.

matthewd avatar May 18 '22 03:05 matthewd

Not that it change the usefulness of this PR, but quick ref: https://bugs.ruby-lang.org/issues/18285

casperisfine avatar May 18 '22 15:05 casperisfine

What is missing here?

API. I have some ideas, but haven't had a chance to revisit this yet.

I really like the compactness for simple definitions I achieved here, but as I worked through applying it to some of the trickier edge cases in our existing needs (e.g. most of https://github.com/rails/rails/commit/044b959d3e6585c14f934021779db5f33bb0099b), I found them much less pleasant to define. My loose plan is to switch to a two-layer API, with a possibly-cut-down version of the current one for easy mode, and some sort of block/builder API for complex ones.

(I definitely do intend to return to this: it was floating in my head for a while before I even opened this, so it's not a quick throw-away thought... I've just been distracted with other things lately.)

matthewd avatar Jul 26 '22 12:07 matthewd