sleeping_king_studios-tools 1.3.0.rc.1 → 1.3.0.rc.2

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: 4939601ed132d3b370114ce53c547e220277c715eb1d442dc20566049f041bea
4
- data.tar.gz: f5cf620154ef2245749693dc5da447adb6fc0e2644bf3aa1094d289881a32338
3
+ metadata.gz: 6b3b28222878c3e3bf364e7467ca85e76b4ba8c40e86b1062169d2943ab9a181
4
+ data.tar.gz: 31cbc80f6c696aebbb9f5ed955852424bda57b03ec65a4f37bdcd969fa6825d5
5
5
  SHA512:
6
- metadata.gz: 0da898a1ae8bd177700cbd6d3736fe171a534ad2f9b7e3c934f9e033c4b61136e4996dc8d375c98706a83b4ef573531901a3fa4de830a29fc3bff6c39f251307
7
- data.tar.gz: 050d5c3e8ae855129dc5aa0d426038bda2bd43706282c3b89e418e56d69973781d9996002168e15bcdc29ae0acd387494dc260402351058d76400c0ee4ace564
6
+ metadata.gz: e14d9a823ebbd1a3eb8cf4e00ae298bfbedfa1fd797b19b493b0187a6b5f128c6f2d08e05101a39619ea51755fb321798b27af6d8741b1cdea2695a2a8d4f204
7
+ data.tar.gz: b26b0693f575e15c0c1a91b97eae350d677f73fa6c7426b4b77b92537a6e988fa52c52f68e0a88a210fad78d617cdb298b663c0cae3fd20992aed7017e3c1145
data/CHANGELOG.md CHANGED
@@ -62,6 +62,8 @@ Added `#[]` support to `ObjectTools#dig`, allowing access through indexed data s
62
62
 
63
63
  Added `ObjectTools#fetch`, which tries to find the requested value via the named method call or by calling `#[]`.
64
64
 
65
+ Added `ObjectTools#format_inspect`, which generates a `String` representation of the object (similar to the built-in `Object#inspect`) with support for `BasicObject`s and for customizing the displayed properties.
66
+
65
67
  Deprecated methods with native equivalents:
66
68
 
67
69
  - Deprecated `#eigenclass` - use `Object#singleton_class` instead.
@@ -5,6 +5,9 @@ require 'sleeping_king_studios/tools'
5
5
  module SleepingKingStudios::Tools
6
6
  # Low-level tools for working with objects.
7
7
  class ObjectTools < SleepingKingStudios::Tools::Base # rubocop:disable Metrics/ClassLength
8
+ MEMORY_ADDRESS_PATTERN = /#<[:\w]+:(?'address'0x\h+)/
9
+ private_constant :MEMORY_ADDRESS_PATTERN
10
+
8
11
  TEMPORARY_METHOD_NAME =
9
12
  '__sleeping_king_studios_tools_apply_%i__'
10
13
  private_constant :TEMPORARY_METHOD_NAME
@@ -283,6 +286,39 @@ module SleepingKingStudios::Tools
283
286
  end
284
287
  end
285
288
 
289
+ # Creates a string representation of the object and specified properties.
290
+ #
291
+ # The string representation resembles the output of Object#inspect, but with
292
+ # support for BasicObjects and greater customization of the displayed
293
+ # properties.
294
+ #
295
+ # @param obj [BasicObject] the object to inspect.
296
+ # @param address [true, false] if true, includes the formatted memory
297
+ # address from Object#inspect. Defaults to true.
298
+ # @param properties [Array, Hash] the properties to display. If the given
299
+ # properties are an Array, maps the values to instance variables or public
300
+ # methods based on the property name.
301
+ #
302
+ # @return [String] the string representation of the object.
303
+ def format_inspect(obj, address: true, properties: {}) # rubocop:disable Metrics/MethodLength
304
+ str = "#<#{class_name(obj)}"
305
+
306
+ if address
307
+ native = Object.instance_method(:inspect).bind(obj).call
308
+ address = MEMORY_ADDRESS_PATTERN.match(native)&.[](:address)
309
+
310
+ str << ':' << address
311
+ end
312
+
313
+ if properties.is_a?(Hash)
314
+ format_properties_hash(str, **properties)
315
+ elsif properties.is_a?(Enumerable)
316
+ format_properties_array(str, obj, *properties)
317
+ end
318
+
319
+ str << '>'
320
+ end
321
+
286
322
  # Checks if the object is immutable.
287
323
  #
288
324
  # - nil, false, and true are always immutable, as are instances of Numeric
@@ -417,6 +453,17 @@ module SleepingKingStudios::Tools
417
453
 
418
454
  private
419
455
 
456
+ def class_name(obj)
457
+ klass =
458
+ if object?(obj)
459
+ obj.class
460
+ else
461
+ Object.instance_method(:class).bind(obj).call
462
+ end
463
+
464
+ klass.name || klass.inspect
465
+ end
466
+
420
467
  def fetch_array(ary, index, default = UNDEFINED, &)
421
468
  return toolbelt.array_tools.fetch(ary, index, &) if default == UNDEFINED
422
469
 
@@ -431,6 +478,45 @@ module SleepingKingStudios::Tools
431
478
  toolbelt.hash_tools.fetch(hsh, key, default, indifferent_key:, &)
432
479
  end
433
480
 
481
+ def format_properties_array(buf, obj, *properties)
482
+ properties.each do |property|
483
+ property_name = property.to_s
484
+
485
+ value = get_object_property(obj, property_name)
486
+
487
+ buf << ' ' << property_name << '=' << value
488
+ end
489
+ end
490
+
491
+ def format_properties_hash(buf, **properties)
492
+ properties.each do |property, value|
493
+ property_name = property.to_s
494
+
495
+ buf << ' ' << property_name << '=' << value.inspect
496
+ end
497
+ end
498
+
499
+ def get_instance_variable(obj, property_name)
500
+ return obj.instance_variable_get(property_name) if object?(obj)
501
+
502
+ Object
503
+ .instance_method(:instance_variable_get)
504
+ .bind(obj)
505
+ .call(property_name)
506
+ end
507
+
508
+ def get_object_property(obj, property_name)
509
+ if property_name.start_with?('@')
510
+ return 'undefined' unless has_instance_variable?(obj, property_name)
511
+
512
+ get_instance_variable(obj, property_name).inspect
513
+ else
514
+ return 'undefined' unless respond_to_method?(obj, property_name)
515
+
516
+ obj.__send__(property_name).inspect
517
+ end
518
+ end
519
+
434
520
  def handle_invalid_fetch(obj, key, default, &block)
435
521
  return block.call(key) if block_given?
436
522
 
@@ -443,6 +529,15 @@ module SleepingKingStudios::Tools
443
529
  raise NoMethodError, message
444
530
  end
445
531
 
532
+ def has_instance_variable?(obj, property_name) # rubocop:disable Naming/PredicatePrefix
533
+ return obj.instance_variable_defined?(property_name) if object?(obj)
534
+
535
+ Object
536
+ .instance_method(:instance_variable_defined?)
537
+ .bind(obj)
538
+ .call(property_name)
539
+ end
540
+
446
541
  def indifferent_get(object, key)
447
542
  case key
448
543
  when String
@@ -462,6 +557,15 @@ module SleepingKingStudios::Tools
462
557
  object.respond_to?(maybe_method_name)
463
558
  end
464
559
 
560
+ def respond_to_method?(obj, method_name)
561
+ return obj.respond_to?(method_name) if object?(obj)
562
+
563
+ Object
564
+ .instance_method(:respond_to?)
565
+ .bind(obj)
566
+ .call(method_name)
567
+ end
568
+
465
569
  def with_temporary_method(receiver, method_name, proc)
466
570
  metaclass = class << receiver; self; end
467
571
  metaclass.send :define_method, method_name, &proc
@@ -129,25 +129,18 @@ module SleepingKingStudios::Tools::Toolbox
129
129
  # @return [Class, nil] the parent data class.
130
130
  attr_reader :parent_class
131
131
 
132
- private
133
-
134
- def each_ancestor
135
- return enum_for(:each_ancestor) unless block_given?
132
+ # Iterates over the ::HeritableMethods modules for parent classes.
133
+ def each_heritable_module
134
+ return enum_for(:each_heritable_module) unless block_given?
136
135
 
137
- ancestor = parent_class
136
+ ancestor = self
138
137
 
139
138
  while ancestor
140
- yield ancestor::HeritableMethods
139
+ yield ancestor
141
140
 
142
- ancestor = ancestor::HeritableMethods.parent_class
141
+ ancestor = ancestor.parent_class&.then { |mod| mod::HeritableMethods }
143
142
  end
144
143
  end
145
-
146
- def included(other)
147
- super
148
-
149
- each_ancestor { |ancestor| other.include(ancestor) }
150
- end
151
144
  end
152
145
 
153
146
  class << self
@@ -174,7 +167,7 @@ module SleepingKingStudios::Tools::Toolbox
174
167
  HeritableData::HeritableMethods.new(parent_class:, &)
175
168
  )
176
169
 
177
- data_class.include(data_class::HeritableMethods)
170
+ data_class.include(*data_class::HeritableMethods.each_heritable_module)
178
171
  end
179
172
 
180
173
  def included(other)
@@ -15,7 +15,7 @@ module SleepingKingStudios
15
15
  # Prerelease version.
16
16
  PRERELEASE = :rc
17
17
  # Build metadata.
18
- BUILD = 1
18
+ BUILD = 2
19
19
 
20
20
  class << self
21
21
  # Generates the gem version string from the Version constants.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sleeping_king_studios-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0.rc.1
4
+ version: 1.3.0.rc.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob "Merlin" Smith