object_inspector 1.1.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: 1a5678d0a60ea94fd380f64bac99e4c0f8e5f1a6752597ce934b2687a87d5f57
4
- data.tar.gz: ca234feb9aa4dadd78fe1672f1bcbd897f6717597ab246d4b275140fd31de760
3
+ metadata.gz: b814c9a90935be0339c6bf2ce1e2e0547b5ccb4b59a26feb40d91082cd9c7b2a
4
+ data.tar.gz: 6f024e9206a497fa173a3e7aff23ae9334b1eacd8cacb4559675ae5db5c3aa1f
5
5
  SHA512:
6
- metadata.gz: 3585d51c146f4013022e2c2cad959580643f92efd843461f06432f42f406f0a71d6f45e47671c288036d46a94ce4b9f754b37ef446d5fa1590922afbcab23ad4
7
- data.tar.gz: f1d439874733a0596d63efe69da6eb95df1b8562b3321f14c468448d26a00fd22ee8e90febaf234cd11f9ebd1a9181bc68ba3c29156264ecdd770f4ebd663c08
6
+ metadata.gz: 41a2fc9fa580c3ce169bfb5dd8b110d5c9d66635d85cde16c689e42ff91354dbbdd042a7bd45f57381c46e04de6938d91310b79f79a64a56dbd53f6d7f81e37b
7
+ data.tar.gz: 9ab9384c13706f95f8a1ced4fdf2d0e2162dbad989a709f226026ff6d0be89844392eaf182044fb62d314eac9a2f645a3bacf1ba351a95821b555960fb8d38fa
data/README.md CHANGED
@@ -71,6 +71,7 @@ ObjectInspector.configure do |config|
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
 
@@ -522,6 +523,24 @@ See examples:
522
523
  - [ObjectInspector::TemplatingFormatter]
523
524
  - [ObjectInspector::CombiningFormatter]
524
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
+
525
544
  ## Help
526
545
 
527
546
  ### How can I see the original inspect output on ActiveRecord objects?
@@ -630,33 +649,15 @@ alias oiset set_object_inspector_scope
630
649
  > [!IMPORTANT]
631
650
  > ObjectInspector defaults to `enabled`. However, this implies an increased chance of causing additional queries/processing based on your usage.
632
651
 
633
- 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 (see below), you may wish to default ObjectInspector to be `disabled` across the board for active web / background processes. Then, since ObjectInspector's utility really shines on the the Rails console, you can default it to `enabled` there.
634
-
635
- To set Object inspector to be `disabled` by default, but `enabled` on the Rails console:
636
-
637
- 1. In an initializer, set [ObjectInspector::Configuration] to default to `disabled`:
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:
638
653
 
639
654
  ```ruby
640
655
  # config/initializers/object_inspector.rb
641
656
  ObjectInspector.configure do |config|
642
- config.enabled = false
643
- end
644
- ```
645
-
646
- 2. Enable ObjectInspector via `.irbrc` (or `.pryrc`, or the like):
647
-
648
- ```ruby
649
- # .irbrc:
650
-
651
- # ...
652
-
653
- ObjectInspector.configuration.enable
654
- # Note: ^ produces output: ` -> ObjectInspector enabled`
655
-
656
- # OR (silent version):
657
+ config.enabled = !Rails.env.production?
657
658
 
658
- ObjectInspector.configure do |config|
659
- config.enabled = true
659
+ # OR:
660
+ config.enabled = Rails.configuration.x.object_inspector.enabled
660
661
  end
661
662
  ```
662
663
 
@@ -4,14 +4,25 @@
4
4
  # the default `#inspect` method for that object to instead call
5
5
  # {ObjectInspector::Inspector.inspect}.
6
6
  module ObjectInspector::InspectBehaviors
7
+ # :reek:TooManyStatements
8
+
7
9
  # Calls {ObjectInspector::Inspector.inspect} on `self`, passing through any
8
10
  # keyword arguments.
9
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.).
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
28
  # Like {#inspect} but:
@@ -22,9 +33,18 @@ module ObjectInspector::InspectBehaviors
22
33
  # {#inspect} since the `:all` scope may result in additional queries or extra
23
34
  # processing--depending on how the inspect hooks are setup.
24
35
  #
36
+ # On {StandardError}, records the error and falls back to {#inspect} (which
37
+ # then falls back to the original ancestor `#inspect` if needed).
38
+ #
25
39
  # @return [String]
26
40
  def inspect!(**)
27
- 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
28
48
  end
29
49
 
30
50
  private
@@ -4,6 +4,6 @@
4
4
 
5
5
  module ObjectInspector
6
6
  # The current ObjectInspector gem version.
7
- VERSION = "1.1.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
@@ -21,6 +24,40 @@ module ObjectInspector
21
24
  @configuration = Configuration.new
22
25
  end
23
26
 
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
+
24
61
  # :reek:TooManyInstanceVariables, :reek:TooManyMethods
25
62
 
26
63
  # ObjectInspector::Configuration stores the default configuration options for
@@ -36,7 +73,8 @@ module ObjectInspector
36
73
  :name_separator,
37
74
  :flags_separator,
38
75
  :issues_separator,
39
- :info_separator
76
+ :info_separator,
77
+ :error_handler
40
78
 
41
79
  # :reek:LongParameterList, :reek:BooleanParameter
42
80
  def initialize( # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
@@ -50,7 +88,8 @@ module ObjectInspector
50
88
  name_separator: " - ",
51
89
  flags_separator: " / ",
52
90
  issues_separator: " | ",
53
- info_separator: " | "
91
+ info_separator: " | ",
92
+ error_handler: ->(exception, object:) {}
54
93
  )
55
94
  @enabled = enabled
56
95
  @formatter_class = formatter_class
@@ -63,6 +102,7 @@ module ObjectInspector
63
102
  @flags_separator = flags_separator
64
103
  @issues_separator = issues_separator
65
104
  @info_separator = info_separator
105
+ self.error_handler = error_handler
66
106
  end
67
107
 
68
108
  # @param value [Object] Coerced to a Boolean with `!!value`.
@@ -136,6 +176,21 @@ module ObjectInspector
136
176
  def info_separator=(value)
137
177
  @info_separator = value.to_s.freeze
138
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
139
194
  end
140
195
  end
141
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.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul DobbinSchmaltz