object_inspector 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd42799a69895e8a918436eaa6688090722f4dde5d406f34b36eae201459f82a
4
- data.tar.gz: 1ea233bec14fddd0b57ffb797da6009471f8f9f989fee45f204af8813b70f59e
3
+ metadata.gz: b814c9a90935be0339c6bf2ce1e2e0547b5ccb4b59a26feb40d91082cd9c7b2a
4
+ data.tar.gz: 6f024e9206a497fa173a3e7aff23ae9334b1eacd8cacb4559675ae5db5c3aa1f
5
5
  SHA512:
6
- metadata.gz: f30121bcdddfd33c7990197e4513eb44a84a487afc4f346a5823067760e2754aaf7ae0871ae58157ef45686de4a885e2db798e97e7a35671c5b0143fedafb11c
7
- data.tar.gz: 4ed8d2ac33b52b4af28bc621a2e92fd429039c6951a2d8d10a38e866a9d101e2f18d24a60125fb189b367232293068c63a3ca0ec0bdaa1bb6df37119f26db5d0
6
+ metadata.gz: 41a2fc9fa580c3ce169bfb5dd8b110d5c9d66635d85cde16c689e42ff91354dbbdd042a7bd45f57381c46e04de6938d91310b79f79a64a56dbd53f6d7f81e37b
7
+ data.tar.gz: 9ab9384c13706f95f8a1ced4fdf2d0e2162dbad989a709f226026ff6d0be89844392eaf182044fb62d314eac9a2f645a3bacf1ba351a95821b555960fb8d38fa
data/README.md CHANGED
@@ -66,108 +66,33 @@ ObjectInspector.configure do |config|
66
66
  config.default_scope = ObjectInspector::Scope.new(:self)
67
67
  config.wild_card_scope = "all"
68
68
  config.out_of_scope_placeholder = "*"
69
- config.presenter_inspect_flags = " ⇨ "
69
+ config.presented_object_separator = " ⇨ "
70
70
  config.name_separator = " - "
71
71
  config.flags_separator = " / "
72
72
  config.issues_separator = " | "
73
73
  config.info_separator = " | "
74
+ config.error_handler = ->(exception, object:) {}
74
75
  end
75
76
  ```
76
77
 
77
78
  ## Usage
78
79
 
79
- Pass an object of any type into `ObjectInspector::Inspector.inspect`.
80
-
81
- ```ruby
82
- class MyObject
83
- def inspect
84
- ObjectInspector::Inspector.inspect(self)
85
- end
86
- end
87
-
88
- MyObject.new.inspect # => "<MyObject>"
89
-
90
- MyObject.new # =>
91
- <MyObject>
92
- ```
93
-
94
- See: [Helper Usage](#helper-usage) for simpler usage.
95
-
96
- ### Output Customization
97
-
98
- Use the `identification`, `flags`, `issues`, `info`, and/or `name` options to customize inspect output.
99
-
100
- ```ruby
101
- class MyObject
102
- def inspect
103
- ObjectInspector::Inspector.inspect(
104
- self,
105
- identification: "My Object",
106
- flags: "FLAG1 / FLAG2",
107
- issues: "ISSUE1",
108
- info: "INFO",
109
- name: "NAME")
110
- end
111
- end
112
-
113
- MyObject.new # =>
114
- <My Object(FLAG1 / FLAG2) !!ISSUE1!! INFO :: NAME>
115
- ```
116
-
117
- Or, define `inspect_identification`, `inspect_flags`, `inspect_issues`, `inspect_info`, and/or `inspect_name` (or `display_name`) as either public or private methods on Object.
118
-
119
- ```ruby
120
- class MyObject
121
- def inspect
122
- ObjectInspector::Inspector.inspect(self)
123
- end
124
-
125
- private
126
-
127
- def inspect_identification = "My Object"
128
- def inspect_flags = "FLAG1 / FLAG2"
129
- def inspect_issues = "ISSUE1 | ISSUE2"
130
- def inspect_info = "INFO"
131
- def inspect_name = "NAME" # Or: def display_name = "NAME"
132
- end
133
-
134
- MyObject.new # =>
135
- <My Object(FLAG1 / FLAG2) !!ISSUE1 | ISSUE2!! INFO :: NAME>
136
- ```
137
-
138
- ## Helper Usage
139
-
140
- To save some typing, include ObjectInspector::InspectBehaviors into an object and `ObjectInspector::Inspector.inspect` will be called on `self` automatically.
80
+ Including `ObjectInspector::InspectBehaviors` into an object will cause `ObjectInspector::Inspector.inspect` to be called on `self` automatically.
141
81
 
142
82
  ```ruby
143
83
  class MyObject
144
84
  include ObjectInspector::InspectBehaviors
145
85
  end
146
86
 
147
- MyObject.new # =>
148
- <MyObject>
149
- ```
150
-
151
- To access the ObjectInspector::Inspector's options via the helper, call into `super`.
152
-
153
- ```ruby
154
- class MyObject
155
- include ObjectInspector::InspectBehaviors
156
-
157
- def inspect
158
- super(identification: "My Object",
159
- flags: "FLAG1",
160
- issues: "ISSUE1 | ISSUE2",
161
- info: "INFO",
162
- name: "NAME")
163
- end
164
- end
87
+ MyObject.new.inspect # =>
88
+ "<MyObject>"
165
89
 
90
+ # NOTE: IRB's Pretty Print processor calls `inspect` and unwraps the quotes:
166
91
  MyObject.new # =>
167
- <My Object(FLAG1) !!ISSUE1 | ISSUE2!! INFO :: NAME>
92
+ <MyObject>
168
93
  ```
169
94
 
170
- Or, define `inspect_identification`, `inspect_flags`, `inspect_info`, and/or `inspect_name` (or `display_name`) in Object.
95
+ Build out the inspect String by defining any of: `inspect_identification`, `inspect_flags`, `inspect_issues`, `inspect_info`, and `inspect_name` (or `display_name`).
171
96
 
172
97
  ```ruby
173
98
  class MyObject
@@ -182,40 +107,11 @@ class MyObject
182
107
  def inspect_name = "NAME" # Or: def display_name = "NAME"
183
108
  end
184
109
 
185
- MyObject.new # =>
186
- <My Object(FLAG1) !!ISSUE1 | ISSUE2!! INFO :: NAME>
187
- ```
188
-
189
- ### Disabling ObjectInspector
190
-
191
- You may disable / re-enable Object Inspector output (via the included helper method) for the current session:
192
-
193
- ```ruby
194
- MyObject.new # =>
195
- <My Object(FLAG1 / FLAG2) !!ISSUE1 | ISSUE2!! INFO :: NAME>
196
-
197
- ObjectInspector.configuration.disable # =>
198
- -> ObjectInspector disabled
199
- MyObject.new # =>
200
- #<MyObject:0x000000012332c458>
201
-
202
- ObjectInspector.configuration.enable # =>
203
- -> ObjectInspector enabled
204
110
  MyObject.new # =>
205
111
  <My Object(FLAG1 / FLAG2) !!ISSUE1 | ISSUE2!! INFO :: NAME>
206
112
  ```
207
113
 
208
- Or, toggle current state:
209
-
210
- ```ruby
211
- ObjectInspector.configuration.toggle; # =>
212
- -> ObjectInspector disabled
213
-
214
- ObjectInspector.configuration.toggle; # =>
215
- -> ObjectInspector enabled
216
- ```
217
-
218
- ### Helper Inclusion
114
+ ### Customizing `ObjectInspector::InspectBehaviors`
219
115
 
220
116
  Instead of including `ObjectInspector::InspectBehaviors` directly, it may be useful to define your own mix-in.
221
117
 
@@ -231,52 +127,41 @@ module ObjectInspectionBehaviors
231
127
  end
232
128
  ```
233
129
 
234
- #### Usage:
235
-
236
- ```ruby
237
- class MyObject
238
- include ObjectInspectionBehaviors # 👀 Defined above.
239
-
240
- private
241
-
242
- def inspect_identification = "My Object"
243
- def inspect_flags = "FLAG1 / FLAG2"
244
- def inspect_issues = "ISSUE1 | ISSUE2"
245
- def inspect_info = "INFO"
246
- def inspect_name = "NAME" # Or: def display_name = "NAME"
247
- end
248
-
249
- MyObject.new # =>
250
- <My Object(FLAG1) !!ISSUE1 | ISSUE2!! INFO :: NAME>
251
- ```
252
-
253
130
  ## Scopes
254
131
 
255
- Use the `scope` option to define the scope of the `inspect_*` methods. The supplied value will be wrapped by the ObjectInspector::Scope helper object.
256
- The default value is `ObjectInspector::Scope.new(:self)`.
132
+ Use the `scope` option to define when each of the `inspect_*` methods should be included. The default scope is `:self`, or `ObjectInspector::Scope.new(:self)`.
257
133
 
258
134
  ### Scope Names
259
135
 
260
- ObjectInspector::Scope acts like an [ActiveSupport::StringInquirer](http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html). This is a prettier way to test for a given type of "scope" within objects.
136
+ ObjectInspector::Scope acts like an [ActiveSupport::StringInquirer](http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html).
261
137
 
262
- The ObjectInspector::Scope objects in these examples are the same as specifying `<scope_name>` like this:
138
+ Call `inspect` with a scope name like:
263
139
 
264
140
  ```ruby
265
141
  my_object.inspect(scope: <scope_name>)
266
142
  ```
267
143
 
268
- Options:
144
+ #### Default Scope Names:
145
+
146
+ The default scope is: `:self`.
269
147
 
270
- - `:self` (Default)--Is meant to confine object interrogation to self (don't interrogate neighboring objects).
271
- - `:all`--Is meant to match on all scopes, regardless of their name.
272
- - `<custom>`--Anything else that makes sense for the object to key on.
148
+ - `:self` (Default): Is meant to restrict object interrogation to self.
149
+
150
+ #### Custom Scope Names:
151
+
152
+ Beyond just `:self`, any name can be used to define any scope that makes sense for your project. No need to provision them up front, just start using them! Suggested additional scope names include:
153
+
154
+ - `:verbose`: For extra detail that may not normally be needed.
155
+ - `:complex`: For revealing collaborating objects (used to prevent n+1 queries in the normal case)
273
156
 
274
157
  ```ruby
275
- scope = ObjectInspector::Scope.new
276
- scope.self? # => true
277
- scope.verbose? # => false
278
- scope.complex? # => false
279
- scope.<anything>? # => false
158
+ def inspect(scope:)
159
+ scope.inspect # => <ObjectInspector::Scope :: ["self"]>
160
+ scope.self? # => true
161
+ scope.verbose? # => false
162
+ scope.complex? # => false
163
+ scope.<anything>? # => false
164
+ end
280
165
  ```
281
166
 
282
167
  #### Multiple Scope Names
@@ -284,10 +169,12 @@ scope.<anything>? # => false
284
169
  It is also possible to pass in multiple scope names to match on.
285
170
 
286
171
  ```ruby
287
- scope = ObjectInspector::Scope.new(%i[verbose complex])
288
- scope.self? # => false
289
- scope.verbose? # => true
290
- scope.complex? # => true
172
+ def inspect(scope: %i[verbose complex])
173
+ scope.inspect # => <ObjectInspector::Scope :: ["complex", "verbose"]>
174
+ scope.self? # => false
175
+ scope.verbose? # => true
176
+ scope.complex? # => true
177
+ end
291
178
  ```
292
179
 
293
180
  #### The "Wild Card" Scope
@@ -295,11 +182,13 @@ scope.complex? # => true
295
182
  Finally, `:all` is a "wild card" scope name, and will match on all scope names.
296
183
 
297
184
  ```ruby
298
- scope = ObjectInspector::Scope.new(:all)
299
- scope.self? # => true
300
- scope.verbose? # => true
301
- scope.complex? # => true
302
- scope.all? # => true
185
+ def inspect(scope: :all)
186
+ scope.inspect # => <ObjectInspector::Scope :: ["all"]>
187
+ scope.self? # => true
188
+ scope.verbose? # => true
189
+ scope.complex? # => true
190
+ scope.all? # => true
191
+ end
303
192
  ```
304
193
 
305
194
  _**NOTE**_: Calling `#inspect!` on an object that mixes in `ObjectInspector::InspectBehaviors` is equivalent to passing in the "wild card" scope.
@@ -365,7 +254,8 @@ class MyObject
365
254
  private
366
255
 
367
256
  def inspect_identification
368
- identify(:a2)
257
+ # Or use `identify(:a2)` from the Object Identifier gem (see Supporting Gems).
258
+ "#{self.class.name}[#{a2}]"
369
259
  end
370
260
 
371
261
  def inspect_flags(scope:)
@@ -463,10 +353,10 @@ class MyWrappedObject
463
353
  end
464
354
 
465
355
  MyWrapperObject.new # =>
466
- <MyWrapperObject(WRAPPER_FLAG1) !!*!!> <MyWrappedObject(FLAG1 / FLAG2) !!*!! INFO>
356
+ <MyWrapperObject(WRAPPER_FLAG1) !!*!!> <MyWrappedObject(FLAG1 / FLAG2) !!*!! INFO>
467
357
 
468
- MyWrapperObject.new! # =>
469
- <MyWrapperObject(WRAPPER_FLAG1) !!CI1!!> <MyWrappedObject(FLAG1 / FLAG2) !!CI1!! INFO>
358
+ MyWrapperObject.new.inspect! # =>
359
+ <MyWrapperObject(WRAPPER_FLAG1) !!CI1!!> <MyWrappedObject(FLAG1 / FLAG2) !!CI1!! INFO>
470
360
  ```
471
361
 
472
362
  This feature is recursive.
@@ -523,7 +413,7 @@ class MyWrappedObject
523
413
  end
524
414
 
525
415
  MyDelegatingWrapperObject.new(MyWrappedObject.new) # =>
526
- <MyDelegatingWrapperObject> <MyWrappedObject(FLAG1) !!ISSUE1!! INFO :: NAME>
416
+ <MyDelegatingWrapperObject> <MyWrappedObject(FLAG1) !!ISSUE1!! INFO :: NAME>
527
417
  ```
528
418
 
529
419
  ## On-the-fly Inspect Methods
@@ -568,6 +458,38 @@ MyObject.new.inspect(identification: nil, info: nil, flags: nil, issues: nil, na
568
458
  # => "<MyObject>"
569
459
  ```
570
460
 
461
+ ## Temporarily Disabling ObjectInspector
462
+
463
+ To disable / re-enable Object Inspector output for the current session:
464
+
465
+ ```ruby
466
+ MyObject.new # =>
467
+ <My Object(FLAG1 / FLAG2) !!ISSUE1 | ISSUE2!! INFO :: NAME>
468
+
469
+ ObjectInspector.configuration.disable # =>
470
+ -> ObjectInspector disabled
471
+ MyObject.new # =>
472
+ #<MyObject:0x000000012332c458>
473
+
474
+ ObjectInspector.configuration.enable # =>
475
+ -> ObjectInspector enabled
476
+ MyObject.new # =>
477
+ <My Object(FLAG1 / FLAG2) !!ISSUE1 | ISSUE2!! INFO :: NAME>
478
+ ```
479
+
480
+ Or, simply toggle the current state:
481
+
482
+ ```ruby
483
+ ObjectInspector.configuration.toggle; # =>
484
+ -> ObjectInspector disabled
485
+
486
+ ObjectInspector.configuration.toggle; # =>
487
+ -> ObjectInspector enabled
488
+ ```
489
+
490
+ _**NOTE**_: `#inspect!` ignores the enabled/disabled setting and still invokes Object
491
+ Inspector -- using the wild-card (`:all`) scope.
492
+
571
493
  ## Custom Formatters
572
494
 
573
495
  A custom inspect formatter can be defined by implementing the interface defined by [ObjectInspector::BaseFormatter](https://github.com/pdobb/object_inspector/blob/master/lib/object_inspector/formatters/base_formatter.rb). Then, either override the ObjectInspector::Configuration#formatter_class value (see [Configuration](#configuration)) or just pass your custom class name into ObjectInspector::Inspector.new.
@@ -601,11 +523,29 @@ See examples:
601
523
  - [ObjectInspector::TemplatingFormatter]
602
524
  - [ObjectInspector::CombiningFormatter]
603
525
 
526
+ ## Handling Errors
527
+
528
+ When `#inspect` raises any `StandardError`, Object Inspector falls back to the original `#inspect` method and records the error in `ObjectInspector.last_error`:
529
+
530
+ ```ruby
531
+ user = User.select(:id).first
532
+ user.inspect
533
+ # => #<User id: 1>
534
+
535
+ ObjectInspector.last_error
536
+ # => #<ActiveModel::MissingAttributeError: missing attribute: ...>
537
+ puts ObjectInspector.last_error.full_message
538
+ # ...: undefined local variable or method 'asdf' for an instance of Game (NameError)
539
+ # ...
540
+
541
+ ObjectInspector.clear_error
542
+ ```
543
+
604
544
  ## Help
605
545
 
606
546
  ### How can I see the original inspect output on ActiveRecord objects?
607
547
 
608
- Simply [disable Object Inspector](#disabling-object-inspector) and you'll see ActiveRecord's Pretty Print formatting shine through again. For example:
548
+ Simply [disable Object Inspector](#temporarily-disabling-objectinspector) and you'll see ActiveRecord's Pretty Print formatting shine through again. For example:
609
549
 
610
550
  ```ruby
611
551
  class User < ApplicationRecord
@@ -677,9 +617,9 @@ def get_object_inspector_current_scope
677
617
  end
678
618
  alias oi get_object_inspector_current_scope
679
619
 
680
- # :simple is the default inspection scope.
681
- def set_object_inspector_scope_simple = set_object_inspector_scope(:simple)
682
- alias ois set_object_inspector_scope_simple
620
+ # :self is the gem's default inspection scope.
621
+ def set_object_inspector_scope_self = set_object_inspector_scope(:self)
622
+ alias ois set_object_inspector_scope_self
683
623
 
684
624
  def set_object_inspector_scope_complex = set_object_inspector_scope(:complex)
685
625
  alias oic set_object_inspector_scope_complex
@@ -704,11 +644,28 @@ end
704
644
  alias oiset set_object_inspector_scope
705
645
  ```
706
646
 
647
+ ## Default State
648
+
649
+ > [!IMPORTANT]
650
+ > ObjectInspector defaults to `enabled`. However, this implies an increased chance of causing additional queries/processing based on your usage.
651
+
652
+ If your inspect methods reference other ActiveRecord models, you will be introducing Database queries into basic object inspection. While you can control this through careful use of `complex` scopes, you may wish to default ObjectInspector to be `disabled` by default e.g. in production:
653
+
654
+ ```ruby
655
+ # config/initializers/object_inspector.rb
656
+ ObjectInspector.configure do |config|
657
+ config.enabled = !Rails.env.production?
658
+
659
+ # OR:
660
+ config.enabled = Rails.configuration.x.object_inspector.enabled
661
+ end
662
+ ```
663
+
707
664
  ## Performance
708
665
 
709
666
  ### Benchmarking Object Inspector
710
667
 
711
- ObjectInspetor is ~2.75x slower than Ruby's default inspect, in Ruby v3.4.
668
+ ObjectInspector is ~2.75x slower than Ruby's default inspect, in Ruby v3.4.
712
669
 
713
670
  Performance of Object Inspector can be tested by playing the [Object Inspector Benchmarking Script](https://github.com/pdobb/object_inspector/blob/master/script/benchmarking/object_inspector.rb) in the IRB console for this gem.
714
671
 
@@ -795,7 +752,7 @@ To release a new version of this gem to RubyGems:
795
752
 
796
753
  1. Update the version number in `version.rb`
797
754
  2. Update `CHANGELOG.md`
798
- 3. Run `bundle` to update Gemfile.lock with the latest version info
755
+ 3. Run `bundle install` to update Gemfile.lock with the latest version info
799
756
  4. Commit the changes. e.g. `Bump to vX.Y.Z`
800
757
  5. Run `rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
801
758
 
@@ -817,6 +774,7 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/pdobb/
817
774
 
818
775
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
819
776
 
777
+ [ObjectInspector::Configuration]: https://github.com/pdobb/object_inspector/blob/master/lib/object_inspector.rb
820
778
  [ObjectInspector::TemplatingFormatter]: https://github.com/pdobb/object_inspector/blob/master/lib/object_inspector/formatters/templating_formatter.rb
821
779
  [ObjectInspector::CombiningFormatter]: https://github.com/pdobb/object_inspector/blob/master/lib/object_inspector/formatters/combining_formatter.rb
822
780
  [Object Inspector Benchmarking Scripts]: https://github.com/pdobb/object_inspector/blob/master/script/benchmarking/object_inspector.rb
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # An abstract base class that interfaces with {ObjectInspector::Inspector}
4
- # objects to combine the supplied {#identification}, {#flags}, {#info}, and
5
- # {#name} strings into a friendly "inspect" String.
4
+ # objects to combine the supplied {#identification}, {#flags}, {#issues},
5
+ # {#info}, and {#name} Strings into a friendly "inspect" String.
6
6
  class ObjectInspector::BaseFormatter
7
7
  attr_reader :inspector
8
8
 
@@ -21,7 +21,7 @@ class ObjectInspector::BaseFormatter
21
21
  # Delegates to {Inspector#wrapped_object_inspection_result}.
22
22
  #
23
23
  # @return [String] If given.
24
- # @return [NilClass] If not given.
24
+ # @return [nil] If not given.
25
25
  def wrapped_object_inspection_result
26
26
  @wrapped_object_inspection_result ||=
27
27
  inspector.wrapped_object_inspection_result
@@ -37,7 +37,7 @@ class ObjectInspector::BaseFormatter
37
37
  # Delegates to {Inspector#flags}.
38
38
  #
39
39
  # @return [String] If given.
40
- # @return [NilClass] If not given.
40
+ # @return [nil] If not given.
41
41
  def flags
42
42
  @flags ||= inspector.flags
43
43
  end
@@ -45,7 +45,7 @@ class ObjectInspector::BaseFormatter
45
45
  # Delegates to {Inspector#issues}.
46
46
  #
47
47
  # @return [String] If given.
48
- # @return [NilClass] If not given.
48
+ # @return [nil] If not given.
49
49
  def issues
50
50
  @issues ||= inspector.issues
51
51
  end
@@ -53,7 +53,7 @@ class ObjectInspector::BaseFormatter
53
53
  # Delegates to {Inspector#info}.
54
54
  #
55
55
  # @return [String] If given.
56
- # @return [NilClass] If not given.
56
+ # @return [nil] If not given.
57
57
  def info
58
58
  @info ||= inspector.info
59
59
  end
@@ -61,7 +61,7 @@ class ObjectInspector::BaseFormatter
61
61
  # Delegates to {Inspector#name}.
62
62
  #
63
63
  # @return [String] If given.
64
- # @return [NilClass] If not given.
64
+ # @return [nil] If not given.
65
65
  def name
66
66
  @name ||= inspector.name
67
67
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Specializes on {ObjectInspector::BaseFormatter} to return the standard/default
4
- # inspect output format by combining Strings.
3
+ # Specializes on {ObjectInspector::BaseFormatter} to build inspect output by
4
+ # combining Strings (as opposed to {ObjectInspector::TemplatingFormatter}).
5
5
  #
6
6
  # @attr (see BaseFormatter)
7
7
  class ObjectInspector::CombiningFormatter < ObjectInspector::BaseFormatter
@@ -19,8 +19,8 @@ class ObjectInspector::CombiningFormatter < ObjectInspector::BaseFormatter
19
19
  private
20
20
 
21
21
  def build_wrapped_object_string
22
- "#{build_string} "\
23
- "#{ObjectInspector.configuration.presented_object_separator} "\
22
+ "#{build_string}"\
23
+ "#{ObjectInspector.configuration.presented_object_separator}"\
24
24
  "#{wrapped_object_inspection_result}"
25
25
  end
26
26
 
@@ -4,8 +4,8 @@
4
4
  # :reek:TooManyMethods
5
5
  # rubocop:disable Metrics/ClassLength
6
6
 
7
- # Specializes on {ObjectInspector::BaseFormatter} to return the standard/default
8
- # inspect output format using String templates.
7
+ # Specializes on {ObjectInspector::BaseFormatter} to build the default inspect
8
+ # output format using String templates.
9
9
  #
10
10
  # @attr (see BaseFormatter)
11
11
  class ObjectInspector::TemplatingFormatter < ObjectInspector::BaseFormatter
@@ -46,8 +46,8 @@ class ObjectInspector::TemplatingFormatter < ObjectInspector::BaseFormatter
46
46
  private
47
47
 
48
48
  def build_wrapped_object_string
49
- "#{build_string} "\
50
- "#{ObjectInspector.configuration.presented_object_separator} "\
49
+ "#{build_string}"\
50
+ "#{ObjectInspector.configuration.presented_object_separator}"\
51
51
  "#{wrapped_object_inspection_result}"
52
52
  end
53
53
 
@@ -4,24 +4,47 @@
4
4
  # the default `#inspect` method for that object to instead call
5
5
  # {ObjectInspector::Inspector.inspect}.
6
6
  module ObjectInspector::InspectBehaviors
7
- # Calls {ObjectInspector::Inspector.inspect} on the passed in `object`,
8
- # passing through any keyword arguments.
7
+ # :reek:TooManyStatements
8
+
9
+ # Calls {ObjectInspector::Inspector.inspect} on `self`, passing through any
10
+ # keyword arguments.
11
+ #
12
+ # If building the gem inspect String raises a {StandardError}, records the
13
+ # error via {ObjectInspector.record_error} and falls back to the ancestor
14
+ # `#inspect` via `super` (Object / ActiveRecord / etc.).
9
15
  #
10
16
  # @return [String]
11
17
  def inspect(**)
12
18
  return super() if ObjectInspector.configuration.disabled?
13
19
 
14
- ObjectInspector::Inspector.inspect(self, **)
20
+ ObjectInspector::Inspector.inspect(self, **).tap {
21
+ ObjectInspector.clear_error
22
+ }
23
+ rescue => ex
24
+ ObjectInspector.record_error(ex, object: self)
25
+ super()
15
26
  end
16
27
 
17
- # Like {#inspect} but forces scope to `:all`. This (the bang (!) version) is
18
- # considered the "more dangerous" version of {#inspect} in the sense that the
19
- # `:all` scope may result in additional queries or extra processing--depending
20
- # on how the inspect hooks are setup.
28
+ # Like {#inspect} but:
29
+ # - Ignores the enabled/disabled state of ObjectInspector
30
+ # - Forces scope to `:all`
31
+ #
32
+ # This (the bang (!) version) is considered the "more dangerous" version of
33
+ # {#inspect} since the `:all` scope may result in additional queries or extra
34
+ # processing--depending on how the inspect hooks are setup.
35
+ #
36
+ # On {StandardError}, records the error and falls back to {#inspect} (which
37
+ # then falls back to the original ancestor `#inspect` if needed).
21
38
  #
22
39
  # @return [String]
23
40
  def inspect!(**)
24
- ObjectInspector::Inspector.inspect(self, **, scope: :all)
41
+ ObjectInspector::Inspector.inspect(self, **, scope: :all).tap {
42
+ ObjectInspector.clear_error
43
+ }
44
+ rescue => ex
45
+ ObjectInspector.record_error(ex, object: self)
46
+
47
+ inspect
25
48
  end
26
49
 
27
50
  private
@@ -24,7 +24,7 @@ class ObjectInspector::Inspector
24
24
  # @return [String]
25
25
  def self.inspect(...) = new(...).to_s
26
26
 
27
- # :reek:DuplicateMethodCall (ObjectInspecto.configuration)
27
+ # :reek:DuplicateMethodCall
28
28
 
29
29
  # @param object [Object] the object being inspected
30
30
  # @param scope [Symbol] Object inspection type. For example:
@@ -32,7 +32,7 @@ class ObjectInspector::Inspector
32
32
  # <custom> -- Anything else that makes sense for {#object} to key
33
33
  # on
34
34
  # @param formatter [ObjectInspector::BaseFormatter]
35
- # (ObjectInspector.configuration.formatter) The formatter object type
35
+ # (ObjectInspector.configuration.formatter_class) The formatter object type
36
36
  # to use for formatting the inspect String.
37
37
  # @param kwargs [Hash] Options to be sent to {#object} via
38
38
  # {ObjectInspector::InterrogateObject} when calling the `inspect_*`
@@ -60,7 +60,7 @@ class ObjectInspector::Inspector
60
60
  # object.
61
61
  #
62
62
  # @return [String] If {#object_is_a_wrapper?}.
63
- # @return [NilClass] If not {#object_is_a_wrapper?}.
63
+ # @return [nil] If not {#object_is_a_wrapper?}.
64
64
  def wrapped_object_inspection_result
65
65
  return unless object_is_a_wrapper?
66
66
 
@@ -83,7 +83,7 @@ class ObjectInspector::Inspector
83
83
  # Boolean flags/states applicable to {#object}.
84
84
  #
85
85
  # @return [String] If given.
86
- # @return [NilClass] If not given.
86
+ # @return [nil] If not given.
87
87
  def flags
88
88
  value(key: :flags)
89
89
  end
@@ -91,7 +91,7 @@ class ObjectInspector::Inspector
91
91
  # Issues/Warnings applicable to {#object}.
92
92
  #
93
93
  # @return [String] If given.
94
- # @return [NilClass] If not given.
94
+ # @return [nil] If not given.
95
95
  def issues
96
96
  value(key: :issues)
97
97
  end
@@ -99,7 +99,7 @@ class ObjectInspector::Inspector
99
99
  # Informational details applicable to {#object}.
100
100
  #
101
101
  # @return [String] If given.
102
- # @return [NilClass] If not given.
102
+ # @return [nil] If not given.
103
103
  def info
104
104
  value(key: :info)
105
105
  end
@@ -107,7 +107,7 @@ class ObjectInspector::Inspector
107
107
  # A human-friendly identifier for {#object}.
108
108
  #
109
109
  # @return [String] If given.
110
- # @return [NilClass] If not given.
110
+ # @return [nil] If not given.
111
111
  def name
112
112
  key = :name
113
113
 
@@ -134,7 +134,7 @@ class ObjectInspector::Inspector
134
134
 
135
135
  # @return [String] If `key` is found in {#kwargs} or if {#object} responds to
136
136
  # `#{object_inspection_method_name}` (e.g. `inspect_flags`).
137
- # @return [NilClass] If not found in {#kwargs} or {#object}.
137
+ # @return [nil] If not found in {#kwargs} or {#object}.
138
138
  def value(key:)
139
139
  return_value =
140
140
  if kwargs.key?(key)
@@ -151,8 +151,8 @@ class ObjectInspector::Inspector
151
151
  #
152
152
  # @return [#to_s] If {#object} responds to `value` and if the call result
153
153
  # isn't nil.
154
- # @return [#nil] If {#object} doesn't respond to `value` or if the call
155
- # result is nil.
154
+ # @return [nil] If {#object} doesn't respond to `value` or if the call result
155
+ # is nil.
156
156
  def evaluate_passed_in_value(value)
157
157
  if value.is_a?(Symbol)
158
158
  interrogate_object(method_name: value) || value
@@ -165,7 +165,7 @@ class ObjectInspector::Inspector
165
165
  #
166
166
  # @return [String] If {#object} responds to
167
167
  # `#{object_inspection_method_name}` (e.g. `inspect_flags`).
168
- # @return [NilClass] If not found on {#object}.
168
+ # @return [nil] If not found on {#object}.
169
169
  def interrogate_object_inspect_method(
170
170
  name,
171
171
  prefix: ObjectInspector.configuration.inspect_method_prefix
@@ -6,6 +6,7 @@
6
6
  # If {#object}#{#method_name} accepts the supplied `kwargs` then they are passed
7
7
  # in as well. If not, then any supplied `kwargs` will be ignored.
8
8
  class ObjectInspector::InterrogateObject
9
+ # @return (see #call)
9
10
  def self.call(...) = new(...).call
10
11
 
11
12
  attr_reader :object,
@@ -18,9 +19,10 @@ class ObjectInspector::InterrogateObject
18
19
  @kwargs = kwargs
19
20
  end
20
21
 
21
- # @return [String, ...] Whatever type Object#{#method_name} returns.
22
+ # @return [Object, nil] The result of calling {#method_name} on {#object},
23
+ # or `nil` if {#object} does not respond to {#method_name}.
22
24
  #
23
- # @raise [ArgumentError] If Object#{#method_name} has an unexpected method
25
+ # @raise [ArgumentError] If {#object}#{#method_name} has an unexpected method
24
26
  # signature.
25
27
  def call
26
28
  return unless object_responds_to_method_name?
@@ -22,6 +22,9 @@
22
22
  # ObjectInspector::Scope.new(%w[verbose complex])
23
23
  # # => <ObjectInspector::Scope :: ["complex", "verbose"]>
24
24
  #
25
+ # ObjectInspector::Scope.new(:verbose, :self)
26
+ # # => <ObjectInspector::Scope :: ["self", "verbose"]>
27
+ #
25
28
  # @see ActiveSupport::StringInquirer
26
29
  # http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html
27
30
  #
@@ -39,7 +42,7 @@ class ObjectInspector::Scope
39
42
  # Join the passed in name parts with the passed in separator.
40
43
  #
41
44
  # @param parts [Array<#to_s>]
42
- # @param separator [#to_s] (ObjectInspector.configuration.flags_separator)
45
+ # @param separator [#to_s] (ObjectInspector.configuration.name_separator)
43
46
  def join_name(
44
47
  parts,
45
48
  separator: ObjectInspector.configuration.name_separator
@@ -100,6 +103,7 @@ class ObjectInspector::Scope
100
103
  names
101
104
  end
102
105
 
106
+ # @return [String] A pretty representation of the scope and its {#names}.
103
107
  def inspect
104
108
  "<#{self.class.name} :: #{names.inspect}>"
105
109
  end
@@ -4,6 +4,6 @@
4
4
 
5
5
  module ObjectInspector
6
6
  # The current ObjectInspector gem version.
7
- VERSION = "1.0.0"
7
+ VERSION = "1.2.0"
8
8
  public_constant :VERSION
9
9
  end
@@ -3,6 +3,9 @@
3
3
  # Defines the base namespace for all modules/classes used by the
4
4
  # object_inspector gem.
5
5
  module ObjectInspector
6
+ LAST_ERROR_THREAD_KEY = :object_inspector_last_error
7
+ private_constant :LAST_ERROR_THREAD_KEY
8
+
6
9
  # Accessor for the {ObjectInspector::Configuration} object.
7
10
  def self.configuration
8
11
  @configuration ||= Configuration.new
@@ -11,6 +14,8 @@ module ObjectInspector
11
14
  # @yieldparam configuration [ObjectInspector::Configuration]
12
15
  def self.configure
13
16
  yield(configuration)
17
+
18
+ configuration
14
19
  end
15
20
 
16
21
  # Reset the current configuration settings memoized by
@@ -19,7 +24,41 @@ module ObjectInspector
19
24
  @configuration = Configuration.new
20
25
  end
21
26
 
22
- # :reek:TooManyInstanceVariables
27
+ # Remember the given exception--which will have caused inspect to fall back to
28
+ # the original `#inspect`. Stored per-thread/fiber so concurrent use does not
29
+ # clobber it.
30
+ #
31
+ # Calls {Configuration#error_handler} with `exception, object:` after
32
+ # recording.
33
+ #
34
+ # @param exception [Exception]
35
+ # @param object [Object, nil] The object being inspected, when known.
36
+ #
37
+ # @return [Exception]
38
+ def self.record_error(exception, object: nil)
39
+ Thread.current[LAST_ERROR_THREAD_KEY] = exception
40
+
41
+ configuration.error_handler.(exception, object:)
42
+
43
+ exception
44
+ end
45
+
46
+ # The most recent exception passed to {record_error} on this thread/fiber.
47
+ # Cleared by {clear_error} (including after a successful gem inspect).
48
+ #
49
+ # @return [Exception, nil]
50
+ def self.last_error
51
+ Thread.current[LAST_ERROR_THREAD_KEY]
52
+ end
53
+
54
+ # Clear {#last_error} for the current thread/fiber.
55
+ #
56
+ # @return [nil]
57
+ def self.clear_error
58
+ Thread.current[LAST_ERROR_THREAD_KEY] = nil
59
+ end
60
+
61
+ # :reek:TooManyInstanceVariables, :reek:TooManyMethods
23
62
 
24
63
  # ObjectInspector::Configuration stores the default configuration options for
25
64
  # the ObjectInspector gem. Modification of attributes is possible at any time,
@@ -34,34 +73,63 @@ module ObjectInspector
34
73
  :name_separator,
35
74
  :flags_separator,
36
75
  :issues_separator,
37
- :info_separator
38
-
39
- def initialize # rubocop:disable Metrics/MethodLength
40
- @enabled = true
41
- @formatter_class = TemplatingFormatter
42
- @inspect_method_prefix = "inspect"
43
- @default_scope = Scope.new(:self)
44
- @wild_card_scope = "all"
45
- @out_of_scope_placeholder = "*"
46
- @presented_object_separator = " #{[0x21E8].pack("U")} "
47
- @name_separator = " - "
48
- @flags_separator = " / "
49
- @issues_separator = " | "
50
- @info_separator = " | "
76
+ :info_separator,
77
+ :error_handler
78
+
79
+ # :reek:LongParameterList, :reek:BooleanParameter
80
+ def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
81
+ enabled: true,
82
+ formatter_class: TemplatingFormatter,
83
+ inspect_method_prefix: "inspect",
84
+ default_scope: Scope.new(:self),
85
+ wild_card_scope: "all",
86
+ out_of_scope_placeholder: "*",
87
+ presented_object_separator: " #{[0x21E8].pack("U")} ", # This is: " ⇨ "
88
+ name_separator: " - ",
89
+ flags_separator: " / ",
90
+ issues_separator: " | ",
91
+ info_separator: " | ",
92
+ error_handler: ->(exception, object:) {}
93
+ )
94
+ @enabled = enabled
95
+ @formatter_class = formatter_class
96
+ @inspect_method_prefix = inspect_method_prefix
97
+ @default_scope = default_scope
98
+ @wild_card_scope = wild_card_scope
99
+ @out_of_scope_placeholder = out_of_scope_placeholder
100
+ @presented_object_separator = presented_object_separator
101
+ @name_separator = name_separator
102
+ @flags_separator = flags_separator
103
+ @issues_separator = issues_separator
104
+ @info_separator = info_separator
105
+ self.error_handler = error_handler
51
106
  end
52
107
 
108
+ # @param value [Object] Coerced to a Boolean with `!!value`.
109
+ def enabled=(value)
110
+ @enabled = !!value
111
+ end
112
+
113
+ # Toggles between {#enable} and {#disable} based on {#enabled?}.
53
114
  def toggle = enabled? ? disable : enable
115
+
116
+ # @return [Boolean]
54
117
  def enabled? = @enabled
55
118
 
119
+ # Enable Object Inspector for the current process and print a status
120
+ # message to `$stdout`.
56
121
  def enable
57
- @enabled = true
122
+ self.enabled = true
58
123
  puts(" -> ObjectInspector enabled")
59
124
  end
60
125
 
126
+ # @return [Boolean]
61
127
  def disabled? = !enabled?
62
128
 
129
+ # Disable Object Inspector for the current process and print a status
130
+ # message to `$stdout`.
63
131
  def disable
64
- @enabled = false
132
+ self.enabled = false
65
133
  puts(" -> ObjectInspector disabled")
66
134
  end
67
135
 
@@ -108,6 +176,21 @@ module ObjectInspector
108
176
  def info_separator=(value)
109
177
  @info_separator = value.to_s.freeze
110
178
  end
179
+
180
+ # :reek:ManualDispatch
181
+
182
+ # A callable, invoked by {ObjectInspector.record_error}.
183
+ #
184
+ # Expected signature: `->(exception, object:) { ... }`.
185
+ #
186
+ # @param value [#call]
187
+ def error_handler=(value)
188
+ unless value.respond_to?(:call)
189
+ raise(TypeError, "error_handler must be callable")
190
+ end
191
+
192
+ @error_handler = value
193
+ end
111
194
  end
112
195
  end
113
196
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: object_inspector
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul DobbinSchmaltz
@@ -67,7 +67,7 @@ dependencies:
67
67
  version: '0'
68
68
  description: Object Inspector takes Object#inspect to the next level. Specify any
69
69
  combination of identification attributes, flags, issues, info, and/or a name along
70
- with an optional, self-definable scope option to represents objects. Great for the
70
+ with an optional, self-definable scope option to represent objects. Great for the
71
71
  console, logging, etc.
72
72
  email:
73
73
  - p.dobbinschmaltz@icloud.com