inspectable 0.6.0 → 1.0.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: d68dedc3064ef8392cffa6200fa2f02c4ac228eb0665d7599fdf1a889d2e23bc
4
- data.tar.gz: be155e34876e51bd71b6383300725d2e53b83b2c8d6e1abc5d034931e88c58c2
3
+ metadata.gz: f4a4c3c388c5294c596d5fa7a6a4bf4959ce29cbfae40563fcd4d3e3464d800e
4
+ data.tar.gz: 15475b71448155390f1ca1f374daea21d9efaed0ea8b27b763eea97fdcd35cf3
5
5
  SHA512:
6
- metadata.gz: f1db3e293fa9b157135c0b09fe7ca17bf140340a0c8cdb32b75dd63771ddce1bd82c6a6fed336a4f8d67d568fb9b9c4393b16bc6a74572b8d0309882dd070d71
7
- data.tar.gz: 31bf8b9a6030025c2140949b8a2aef9848bb5893abac4e98359723c97ea3d473894f3b3be11a954bbb299dafb78c3a1e10c095b6c2ba245fa6a7149f1feddefa
6
+ metadata.gz: 69c70eb5c696419513d64b18008a1880318e0b1fb31f42d27282a380d8ba6ae9119f5f3a8adc2ebcf4ee5c8256638d3bc1297bc313362054be7e2fc0bd9b0f6a
7
+ data.tar.gz: d6ebb06b52f577f2e2acc85c054d8b918aa5cd0dfdccf8f47ce051afcead6fbea2f7d0159462b1f439fad7d06628ffd3839d8ef0482843f462821bc454bdcd89
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  :dry_schema_link: link:https://dry-rb.org/gems/dry-schema[Dry Schema]
6
6
  :object_inspection_link: link:https://alchemists.io/articles/ruby_object_inspection[Ruby Object Inspection]
7
+ :rspec_link: link:https://rspec.info[RSpec]
7
8
 
8
9
  = Inspectable
9
10
 
@@ -13,9 +14,9 @@ toc::[]
13
14
 
14
15
  == Features
15
16
 
16
- - Allows you to customize and configure `Object#inspect` with minimal effort.
17
- - Allows you to exclude instance variables that are too verbose or undesired.
18
- - Allows you to register reusable transformers for more advanced behavior.
17
+ * Allows you to customize and configure `Object#inspect` with minimal effort.
18
+ * Allows you to exclude instance variables that are too verbose or undesired.
19
+ * Allows you to register reusable transformers for more advanced behavior.
19
20
 
20
21
  == Requirements
21
22
 
@@ -125,46 +126,47 @@ Demo.new.inspect
125
126
 
126
127
  === Transformers
127
128
 
128
- This gem includes a few basic transformers that can help you hide sensitive information or reduce verbosity. To view them, use:
129
+ This gem includes a few built-in transformers that can help you hide sensitive information or reduce verbosity. To view them, use:
129
130
 
130
131
  [source,ruby]
131
132
  ----
132
133
  Inspectable.transformers
134
+
133
135
  # {
134
- # class: #<Proc:0x000000010e09dc10 inspectable/transformers/classifier.rb:6 (lambda)>,
135
- # redact: #<Proc:0x000000010e09cf40 inspectable/transformers/redactor.rb:6 (lambda)>
136
+ # redact: Inspectable::Transformers::Redactor,
137
+ # type: Inspectable::Transformers::Typer
136
138
  # }
137
139
  ----
138
140
 
139
141
  Each is explained below.
140
142
 
141
- ==== Classifier
142
-
143
- This is a simple transformer that always asks for the class of the instance variable's value.
143
+ ==== Redactor
144
144
 
145
- This transformer is most helpful for objects, like {dry_schema_link}, 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.
145
+ 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.
146
146
 
147
147
  To use, supply the instance variable you want to transform as the key and the transformer's key (symbol) as the value. Example:
148
148
 
149
149
  [source,ruby]
150
150
  ----
151
- include Inspectable[demo: :class]
151
+ include Inspectable[demo: :redact]
152
152
  ----
153
153
 
154
- ==== Redactor
154
+ ==== Typer
155
155
 
156
- 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.
156
+ This transformer always computes the instance variable's type.
157
+
158
+ This transformer is most helpful for objects, like {dry_schema_link}, 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.
157
159
 
158
160
  To use, supply the instance variable you want to transform as the key and the transformer's key (symbol) as the value. Example:
159
161
 
160
162
  [source,ruby]
161
163
  ----
162
- include Inspectable[demo: :redact]
164
+ include Inspectable[demo: :type]
163
165
  ----
164
166
 
165
167
  ==== Overrides
166
168
 
167
- Should you not like default transformer behavior, you can override an existing transformer with your own. For example, maybe you'd like the `Redactor` transform to use `"[FILTERED]"` instead of `"[REDACTED]"`. Here's how you do that:
169
+ 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:
168
170
 
169
171
  [source,ruby]
170
172
  ----
@@ -175,7 +177,7 @@ The above will override default behavior with your own functionality.
175
177
 
176
178
  ==== Custom
177
179
 
178
- You can add as many transformers as you like by using the `.add_transformer` method. Several examples have been presented already but here are the guidelines for customization:
180
+ You can add as many transformers as you like by using the `.add_transformer` method. Here are the guidelines for customization:
179
181
 
180
182
  * 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.
181
183
  * 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 {object_inspection_link} behavior.
@@ -191,6 +193,96 @@ The above would strip hexes from the output. Notice the guard to check if the `v
191
193
 
192
194
  ℹ️ 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 {object_inspection_link} behavior.
193
195
 
196
+ === Native
197
+
198
+ 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:
199
+
200
+ [source,ruby]
201
+ ----
202
+ Class.new do
203
+ include Inspectable(:name)
204
+
205
+ def initialize name = "test"
206
+ @name = name
207
+ end
208
+
209
+ private
210
+
211
+ def instance_variables_to_inspect = [:@name]
212
+ end
213
+
214
+ # undefined method 'Inspectable' for class (NoMethodError)
215
+ ----
216
+
217
+ To resolve the above exception, remove `instance_variables_to_inspect` and let this gem handle everything for you.
218
+
219
+ === RSpec
220
+
221
+ To aid in your testing, you can use the `match_inspection` RSpec matcher by requiring it in your spec helper:
222
+
223
+ [source,ruby]
224
+ ----
225
+ # spec_helper.rb
226
+
227
+ require "inspectable/rspec/matchers/match_inspection"
228
+ ----
229
+
230
+ Once required, you can use the matcher by supplying the expected attributes as follows:
231
+
232
+ [source,ruby]
233
+ ----
234
+ RSpec.describe Demo do
235
+ subject(:demo) { described_class.new }
236
+
237
+ describe "#inspect" do
238
+ it "has inspected attributes" do
239
+ expect(demo.inspect).to match_inspection(label: "Demo", name: :demo)
240
+ end
241
+ end
242
+ end
243
+ ----
244
+
245
+ You can also customize the matcher by providing a `by` hash _before_ any expected attributes consisting of the following attributes:
246
+
247
+ * `prefix`: The prefix of your attribute names. Default: `"@"`.
248
+ * `delimiter`: The delimiter between each attribute name and value. Default: `"="`.
249
+ * `separator`: The separator between each attribute. Default: `", "`.
250
+
251
+ For example, the following is an explicit demonstration of what the matcher implicitly provides for you by default:
252
+
253
+ [source,ruby]
254
+ ----
255
+ RSpec.describe Demo do
256
+ subject(:demo) { described_class.new }
257
+
258
+ describe "#inspect" do
259
+ it "has inspected attributes" do
260
+ expect(demo.inspect).to match_inspection(
261
+ by: {prefix: "@", delimiter: "=", separator: ", "},
262
+ label: "Demo",
263
+ name: :demo
264
+ )
265
+ end
266
+ end
267
+ end
268
+ ----
269
+
270
+ In situations where the spec fails, you’ll get a formatted error to help you quickly resolve and fix the issue:
271
+
272
+ ----
273
+ expected (using prefix: "@", delimiter: "=", and separator: ", "):
274
+
275
+ "#<Demo:0x00000000000014d0
276
+ @label="Demo",
277
+ @name=:demo
278
+ @email="demo@demo.io"
279
+
280
+ to match:
281
+
282
+ @label="Demo",
283
+ @name=:demo
284
+ ----
285
+
194
286
  == Development
195
287
 
196
288
  To contribute, run:
data/inspectable.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "inspectable"
5
- spec.version = "0.6.0"
5
+ spec.version = "1.0.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/inspectable"
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.signing_key = Gem.default_key_path
23
23
  spec.cert_chain = [Gem.default_cert_path]
24
24
 
25
- spec.required_ruby_version = ">= 3.4"
25
+ spec.required_ruby_version = ">= 4.0"
26
26
 
27
27
  spec.extra_rdoc_files = Dir["README*", "LICENSE*"]
28
28
  spec.files = Dir["*.gemspec", "lib/**/*"]
@@ -15,6 +15,12 @@ module Inspectable
15
15
  end
16
16
 
17
17
  def included descendant
18
+ descendant.define_singleton_method :method_added do |name|
19
+ return super(name) unless name == :instance_variables_to_inspect
20
+
21
+ fail NoMethodError, "Defining method :instance_variables_to_inspect is disabled."
22
+ end
23
+
18
24
  case descendant
19
25
  when Class, Struct, Data then super
20
26
  else fail TypeError, "Use Class, Struct, or Data."
@@ -59,7 +65,7 @@ module Inspectable
59
65
 
60
66
  def render instance, type
61
67
  container.fetch(type) { fail TypeError, "Unknown type. Use Class, Struct, or Data." }
62
- .then { |serializer| serializer.call(instance, *excludes, **transformers) }
68
+ .then { |sanitizer| sanitizer.call(instance, *excludes, **transformers) }
63
69
  end
64
70
  end
65
71
  end
@@ -4,8 +4,8 @@ module Inspectable
4
4
  # Provides global regsitry for further customization.
5
5
  module Registry
6
6
  def self.extended descendant
7
- descendant.add_transformer(:class, Inspectable::Transformers::Classifier)
8
- .add_transformer :redact, Inspectable::Transformers::Redactor
7
+ descendant.add_transformer(:redact, Inspectable::Transformers::Redactor)
8
+ .add_transformer(:type, Inspectable::Transformers::Typer)
9
9
  end
10
10
 
11
11
  def add_transformer key, function
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :match_inspection do |by: {}, **|
4
+ match do |actual|
5
+ defaults = {prefix: "@", delimiter: "=", separator: ", "}
6
+ @prefix, @delimiter, @separator = defaults.merge!(by).values_at(*defaults.keys)
7
+ @content = expected.map { |key, value| "#{@prefix}#{key}#{@delimiter}#{value}" }
8
+ .join @separator
9
+
10
+ actual.match?(/#{@content}/)
11
+ end
12
+
13
+ failure_message do |actual|
14
+ using = "using prefix: #{@prefix.inspect}, " \
15
+ "delimiter: #{@delimiter.inspect}, and " \
16
+ "separator: #{@separator.inspect}"
17
+
18
+ <<~MESSAGE
19
+ expected (#{using}):
20
+
21
+ #{actual.inspect.gsub @separator, "#{@separator}\n"}
22
+
23
+ to match:
24
+
25
+ #{@content.gsub @separator, "#{@separator}\n"}
26
+ MESSAGE
27
+ end
28
+ end
@@ -3,7 +3,7 @@
3
3
  module Inspectable
4
4
  module Sanitizers
5
5
  # Excludes and transforms data members.
6
- class Dater
6
+ class Data
7
7
  def initialize pattern: "#<data%<class>s %<body>s>", inspector: INSPECTOR
8
8
  @pattern = pattern
9
9
  @inspector = inspector
@@ -23,7 +23,11 @@ module Inspectable
23
23
  def exclude_and_transform instance, excludes, transformers
24
24
  (instance.members - excludes).reduce(+"") do |body, member|
25
25
  value = instance.public_send member
26
- body << "#{member}=#{transformers.fetch(member, inspector).call value}, "
26
+ transformer = transformers.fetch member, inspector
27
+
28
+ fail ArgumentError, "Invalid transformer registered for: #{member}." unless transformer
29
+
30
+ body << "#{member}=#{transformer.call value}, "
27
31
  end
28
32
  end
29
33
  end
@@ -3,7 +3,7 @@
3
3
  module Inspectable
4
4
  module Sanitizers
5
5
  # Excludes and transforms struct members.
6
- class Structer
6
+ class Struct
7
7
  def initialize pattern: "#<struct%<class>s %<body>s>", inspector: Inspectable::INSPECTOR
8
8
  @pattern = pattern
9
9
  @inspector = inspector
@@ -22,7 +22,11 @@ module Inspectable
22
22
 
23
23
  def exclude_and_transform instance, excludes, transformers
24
24
  (instance.members - excludes).reduce(+"") do |body, member|
25
- body << "#{member}=#{transformers.fetch(member, inspector).call instance[member]}, "
25
+ transformer = transformers.fetch member, inspector
26
+
27
+ fail ArgumentError, "Invalid transformer registered for: #{member}." unless transformer
28
+
29
+ body << "#{member}=#{transformer.call instance[member]}, "
26
30
  end
27
31
  end
28
32
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Inspectable
4
4
  module Sanitizers
5
- # Excludes and transforms class instance variables.
6
- class Classer
5
+ # Excludes and transforms object types.
6
+ class Type
7
7
  def initialize pattern: "#<%<class>s:%<id>#018x %<body>s>", inspector: INSPECTOR
8
8
  @pattern = pattern
9
9
  @inspector = inspector
@@ -26,9 +26,11 @@ module Inspectable
26
26
  def exclude_and_transform instance, variables, transformers
27
27
  variables.reduce(+"") do |body, variable|
28
28
  key = variable.to_s.delete_prefix("@").to_sym
29
- value = instance.instance_variable_get variable
29
+ transformer = transformers.fetch key, inspector
30
30
 
31
- body << "#{variable}=#{transformers.fetch(key, inspector).call value}, "
31
+ fail ArgumentError, "Invalid transformer registered for: #{key}." unless transformer
32
+
33
+ body << "#{variable}=#{transformer.call instance.instance_variable_get(variable)}, "
32
34
  end
33
35
  end
34
36
  end
@@ -3,6 +3,10 @@
3
3
  module Inspectable
4
4
  module Transformers
5
5
  # Redacts sensitive information.
6
- Redactor = -> value { "[REDACTED]".inspect if value }
6
+ module Redactor
7
+ module_function
8
+
9
+ def call(value) = ("[REDACTED]".inspect if value)
10
+ end
7
11
  end
8
12
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Inspectable
4
+ module Transformers
5
+ # Answers object's type.
6
+ module Typer
7
+ module_function
8
+
9
+ def call(value) = value.class.name
10
+ end
11
+ end
12
+ end
data/lib/inspectable.rb CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  require "inspectable/builder"
4
4
  require "inspectable/registry"
5
- require "inspectable/sanitizers/classer"
6
- require "inspectable/sanitizers/dater"
7
- require "inspectable/sanitizers/structer"
8
- require "inspectable/transformers/classifier"
5
+ require "inspectable/sanitizers/data"
6
+ require "inspectable/sanitizers/struct"
7
+ require "inspectable/sanitizers/type"
9
8
  require "inspectable/transformers/redactor"
9
+ require "inspectable/transformers/typer"
10
10
 
11
11
  # Main namespace.
12
12
  module Inspectable
@@ -16,9 +16,9 @@ module Inspectable
16
16
 
17
17
  CONTAINER = {
18
18
  registry: self,
19
- class: Sanitizers::Classer.new,
20
- data: Sanitizers::Dater.new,
21
- struct: Sanitizers::Structer.new
19
+ class: Sanitizers::Type.new,
20
+ data: Sanitizers::Data.new,
21
+ struct: Sanitizers::Struct.new
22
22
  }.freeze
23
23
 
24
24
  def self.[](*, **) = Builder.new(*, **)
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspectable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
@@ -49,11 +49,12 @@ files:
49
49
  - lib/inspectable.rb
50
50
  - lib/inspectable/builder.rb
51
51
  - lib/inspectable/registry.rb
52
- - lib/inspectable/sanitizers/classer.rb
53
- - lib/inspectable/sanitizers/dater.rb
54
- - lib/inspectable/sanitizers/structer.rb
55
- - lib/inspectable/transformers/classifier.rb
52
+ - lib/inspectable/rspec/matchers/match_inspection.rb
53
+ - lib/inspectable/sanitizers/data.rb
54
+ - lib/inspectable/sanitizers/struct.rb
55
+ - lib/inspectable/sanitizers/type.rb
56
56
  - lib/inspectable/transformers/redactor.rb
57
+ - lib/inspectable/transformers/typer.rb
57
58
  homepage: https://alchemists.io/projects/inspectable
58
59
  licenses:
59
60
  - Hippocratic-2.1
@@ -72,14 +73,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
73
  requirements:
73
74
  - - ">="
74
75
  - !ruby/object:Gem::Version
75
- version: '3.4'
76
+ version: '4.0'
76
77
  required_rubygems_version: !ruby/object:Gem::Requirement
77
78
  requirements:
78
79
  - - ">="
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
82
  requirements: []
82
- rubygems_version: 3.7.2
83
+ rubygems_version: 4.0.3
83
84
  specification_version: 4
84
85
  summary: A customizable object inspector.
85
86
  test_files: []
metadata.gz.sig CHANGED
Binary file
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Inspectable
4
- module Transformers
5
- # Answers value's class.
6
- Classifier = -> value { value.class.name }
7
- end
8
- end