Class: Farce::ClassMirror

Inherits:
Object
  • Object
show all
Includes:
Shareable::Tracked
Defined in:
lib/farce/class_mirror.rb

Overview

Map Ruby classes to a parallel inheritance hierarchy. Undefined classes use the nearest defined ancestor's mirror. An optional block turns each lookup into a cached value for that source class.

Examples:

Specialize behavior with ordinary method inheritance

mirror = Farce::ClassMirror.new
mirror.define(Object) { def label = "object" }
mirror.define(Array) { def label = "array of #{super}" }
mirror[Array].new.label # => "array of object"

Instance Method Summary collapse

Methods included from Shareable

#ractor_shareable?

Constructor Details

#initialize(base_class = Class.new) {|mirror, klass| ... } ⇒ ClassMirror

Note:

The block must be convertible to a shareable proc.

Returns a new instance of ClassMirror.

Parameters:

  • base_class (Class) (defaults to: Class.new) —

    the mirror for BasicObject

Yield Parameters:

  • mirror (Class) —

    the nearest defined mirror class

  • klass (Class) —

    the class passed to #[]

Yield Returns:

  • (Object) —

    a Ractor-shareable value to cache for klass

Raises:

  • (TypeError)


23
24
25
26
27
28
29
# File 'lib/farce/class_mirror.rb', line 23

def initialize(base_class = Class.new, &)
  raise TypeError, "Class expected, got #{base_class.class}" unless base_class.is_a?(Class)
  @mapping  = Strict::Map.new({ BasicObject => base_class })
  @cache    = Strict::WeakKeyMap.new
  @callback = block_given? ? Ractor.shareable_proc(&) : nil
  super()
end

Instance Method Details

#[](klass) ⇒ Object

Look up a mirror or the cached result of the constructor block.

Parameters:

  • klass (Class) —

    the source class

Returns:

  • (Object) —

    the mirror class or the block result

Raises:

  • (TypeError) —

    if klass is not a Class



52
53
54
55
56
57
58
# File 'lib/farce/class_mirror.rb', line 52

def [](klass)
  raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
  @cache.store_if_absent(klass) do
    result = lookup(klass)
    @callback ? @callback.call(result, klass) : result
  end
end

#define(klass) { ... } ⇒ Class

Define or extend a mirror class from the main Ractor. Missing ancestors are defined automatically. The block defines methods in an included module, so super can call earlier definitions. Existing lookup results are invalidated after each definition.

Parameters:

  • klass (Class) —

    the source class

Yields:

  • evaluated as a module body

Returns:

  • (Class) —

    the mirror class

Raises:



69
70
71
72
73
74
75
76
77
78
# File 'lib/farce/class_mirror.rb', line 69

def define(klass, &)
  check_frozen!
  raise Ractor::IsolationError, "must be called from main Ractor" unless Ractor.main?
  raise TypeError, "Class expected, got #{klass.class}" unless klass.is_a?(Class)
  mixin  = Module.new(&) if block_given?
  mirror = @mapping.store_if_absent(klass) { Class.new(define(klass.superclass)) }
  mirror.include(mixin) if mixin
  @cache.clear
  mirror
end