|
| 1 | +# frozen_string_literal: true |
| 2 | + |
1 | 3 | module OAuth2 |
2 | | - # Mixin that redacts sensitive instance variables in #inspect output. |
| 4 | + # Mixin that redacts sensitive instance variables in `#inspect` output. |
| 5 | + # |
| 6 | + # Classes include this module and declare which attribute names should be |
| 7 | + # filtered via {.filtered_attributes}. Matching and replacement behavior is |
| 8 | + # delegated to {ThingFilter}, which is initialized once per object. |
3 | 9 | # |
4 | | - # Classes include this module and declare which attributes should be filtered |
5 | | - # using {.filtered_attributes}. Any instance variable name that includes one of |
6 | | - # those attribute names will be shown as [FILTERED] in the object's inspect. |
| 10 | + # This means existing objects keep the filter configuration that was present |
| 11 | + # when they were initialized, even if global config or class-level filter |
| 12 | + # declarations change later. |
7 | 13 | module FilteredAttributes |
8 | 14 | # Hook invoked when the module is included. Extends the including class with |
9 | | - # class-level helpers. |
| 15 | + # class-level helpers and prepends the initializer hook. |
10 | 16 | # |
11 | 17 | # @param [Class] base The including class |
12 | 18 | # @return [void] |
13 | 19 | def self.included(base) |
14 | 20 | base.extend(ClassMethods) |
| 21 | + base.prepend(InitializerMethods) |
| 22 | + end |
| 23 | + |
| 24 | + # Initializer hook that snapshots the thing filter for this object. |
| 25 | + # |
| 26 | + # The snapshot captures both the class-level filtered attribute names and |
| 27 | + # the current `OAuth2.config[:filtered_label]` value. |
| 28 | + module InitializerMethods |
| 29 | + def initialize(*args, &block) |
| 30 | + super(*args, &block) |
| 31 | + @thing_filter = ThingFilter.new( |
| 32 | + self.class.filtered_attribute_names, |
| 33 | + label: OAuth2.config[:filtered_label], |
| 34 | + ) |
| 35 | + end |
15 | 36 | end |
16 | 37 |
|
17 | 38 | # Class-level helpers for configuring filtered attributes. |
@@ -50,16 +71,24 @@ def filtered_attribute_names |
50 | 71 | end |
51 | 72 | end |
52 | 73 |
|
| 74 | + # The initialized thing filter used by this object. |
| 75 | + # |
| 76 | + # This is a per-instance snapshot created during initialization. |
| 77 | + # |
| 78 | + # @return [ThingFilter] |
| 79 | + def thing_filter |
| 80 | + @thing_filter |
| 81 | + end |
| 82 | + |
53 | 83 | # Custom inspect that redacts configured attributes. |
54 | 84 | # |
55 | 85 | # @return [String] |
56 | 86 | def inspect |
57 | | - filtered_attribute_names = ClassMethods.filtered_attribute_names(self.class) |
58 | | - return super if filtered_attribute_names.empty? |
| 87 | + return super if thing_filter.things.empty? |
59 | 88 |
|
60 | 89 | inspected_vars = instance_variables.map do |var| |
61 | | - if filtered_attribute_names.any? { |filtered_var| var.to_s.include?(filtered_var.to_s) } |
62 | | - "#{var}=[FILTERED]" |
| 90 | + if thing_filter.filtered?(var) |
| 91 | + "#{var}=#{thing_filter.label}" |
63 | 92 | else |
64 | 93 | "#{var}=#{instance_variable_get(var).inspect}" |
65 | 94 | end |
|
0 commit comments