Class: Farce::Reference

Inherits:
BasicObject
Includes:
Internal::Delegation
Defined in:
lib/farce/reference.rb,
lib/farce/integrations/active_support/duplicable.rb

Overview

A delegating reference to a Farce::Abstract::Value object. Marshal preserves the holder and wrapper without resolving the referenced value.

Examples:

atom = Farce::Atom.new(:example)
ref  = Farce::Reference.new(atom)
ref == :example # => true

atom.value = 42

ref == :example # => false
ref == 42       # => true

ActiveSupport Integration collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, deep: false) ⇒ Reference

Returns a new instance of Reference.

Parameters:

  • value (Farce::Abstract::Value) —

    the value to be referenced

  • deep (Boolean) (defaults to: false) —

    whether to resolve nested values (like an Envelope inside of an Atom)



114
# File 'lib/farce/reference.rb', line 114

def initialize(value) = @value = value

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing ⇒ BasicObject (private)

Delegates all methods to the referenced value



156
# File 'lib/farce/reference.rb', line 156

def method_missing(...) = @value.value.__send__(...) # rubocop:disable Style/MissingRespondToMissing

Class Method Details

.[](factory, deep: false) ⇒ BasicObject

Creates a subclass of Farce::Reference that uses the given factory to create new values to reference automatically.

Examples:

Creating a class for a specific value type

# Example class that turns its value into an uppercase string
class UpcaseValue
  include Farce::Abstract::Value
  attr_reader :value
  def initialize(value) = @value = -(value.to_s.upcase)
end

# Create a reference class for UpcaseValue
UpcaseRef = Farce::Reference[UpcaseValue]

UpcaseRef.new("hello") == "HELLO" # => true

Subclassing a generated reference class

# This is useful if you want to add custom methods
class UpcaseRef < Farce::Reference[UpcaseValue]
  def wordle_compatible? = size == 5
end

Raises:

  • (::ArgumentError)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/farce/reference.rb', line 60

def self.[](factory, deep: false)
  raise ::ArgumentError, "factory must be a class" unless factory.is_a?(::Class)
  raise ::ArgumentError, "factory must include Farce::Abstract::Value" unless factory < ::Farce::Abstract::Value

  klass = ::Class.new(deep ? Deep : Reference)
  klass.set_temporary_name("#{superclass.name}[#{factory.name}, deep: #{deep.inspect}]")
  klass.instance_variable_set(:@value_factory, factory)

  klass.class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def self.new(...) = super(value_factory.new(...))
    def self.value_factory
      return @value_factory if defined?(@value_factory) && @value_factory
      superclass.value_factory
    end
  RUBY

  klass.singleton_class.class_eval "undef []", __FILE__, __LINE__
  klass
end

.deref(reference) ⇒ Farce::Abstract::Value, BasicObject

If reference is a Farce::Reference, dereference it to get the underlying value object.

Examples:

ref = Farce::Reference.new(Farce::Atom.new(:example))
ref == :example # => true

atom = Farce::Reference.deref(ref)
atom.value = 42

ref == :example # => false
ref == 42       # => true

Returns:



106
107
108
109
# File 'lib/farce/reference.rb', line 106

def self.deref(reference)
  return reference unless Reference === reference
  ::Kernel.instance_method(:instance_variable_get).bind_call(reference, :@value)
end

.new(value, deep: false) ⇒ Farce::Reference

Creates a new reference to the given value.

Parameters:

  • value (Farce::Abstract::Value) —

    the value to be referenced

  • deep (Boolean) (defaults to: false) —

    whether to resolve nested values (like an Envelope inside of an Atom)

Returns:

Raises:

  • (::ArgumentError)

See Also:



85
86
87
88
89
90
# File 'lib/farce/reference.rb', line 85

def self.new(value, deep: false)
  value = deref(value)
  raise ::ArgumentError, "value must be a Farce::Abstract::Value" unless ::Farce::Abstract::Value === value
  return super(value) unless self == Reference && deep
  Deep.new(value)
end

Instance Method Details

#duplicable? ⇒ Boolean

Note:

This methods is only available if ActiveSupport has been loaded.

Returns true.

Returns:

  • (Boolean) —

    true



30
# File 'lib/farce/integrations/active_support/duplicable.rb', line 30

def duplicable? = true

#freeze ⇒ self

Freeze the current target, then this proxy. The value holder may remain mutable.

Returns:

  • (self) —

    this object



138
139
140
141
# File 'lib/farce/reference.rb', line 138

def freeze
  method_missing(:freeze)
  super
end

#frozen? ⇒ Boolean

Replacing the holder's value can make this false again.

Returns:

  • (Boolean) —

    whether this proxy and its current target are frozen



134
# File 'lib/farce/reference.rb', line 134

def frozen? = super && method_missing(:frozen?)

#respond_to?(name, include_private = false) ⇒ Boolean

Marshal hook discovery must inspect this wrapper without resolving its target.

Returns:

  • (Boolean)


126
127
128
129
130
# File 'lib/farce/reference.rb', line 126

def respond_to?(name, include_private = false) # rubocop:disable Style/OptionalBooleanParameter
  return true if name == :marshal_dump || name == :marshal_load
  return false if Internal.marshal_protocol_method?(name)
  method_missing(:respond_to?, name, include_private)
end