1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
# frozen_string_literal: true
module Unparser
# Allows objects to be made immutable
#
# Original code before vendoring and reduction from: https://github.com/dkubb/adamantium.
module Adamantium
module InstanceMethods
# A noop #dup for immutable objects
#
# @return [self]
#
# @api public
def dup
self
end
# Freeze the object
#
# @return [Object]
#
# @api public
def freeze
memoized_method_cache
super()
end
private
def memoized_method_cache
@memoized_method_cache ||= Memory.new({})
end
end # InstanceMethods
# Storage for memoized methods
class Memory
# Initialize the memory storage for memoized methods
#
# @return [undefined]
#
# @api private
def initialize(values)
@values = values
@monitor = Monitor.new
freeze
end
# Fetch the value from memory, or evaluate if it does not exist
#
# @param [Symbol] name
#
# @yieldreturn [Object]
# the value to memoize
#
# @api public
def fetch(name)
@values.fetch(name) do # check for the key
@monitor.synchronize do # acquire a lock if the key is not found
@values.fetch(name) do # recheck under lock
@values[name] = yield # set the value
end
end
end
end
end # Memory
# Methods mixed in to adamantium classes
module ClassMethods
# Instantiate a new frozen object
#
# @return [Object]
#
# @api public
def new(*)
super.freeze
end
end # ClassMethods
# Methods mixed in to adamantium modules
module ModuleMethods
# Memoize a list of methods
#
# @param [Array<#to_s>] methods
# a list of methods to memoize
#
# @return [self]
#
# @api public
def memoize(*methods)
methods.each(&method(:memoize_method))
self
end
# Test if method is memoized
#
# @param [Symbol] name
#
# @return [Bool]
def memoized?(method_name)
memoized_methods.key?(method_name)
end
# Return unmemoized instance method
#
# @param [Symbol] name
#
# @return [UnboundMethod]
# the memoized method
#
# @raise [NameError]
# raised if the method is unknown
#
# @api public
def unmemoized_instance_method(method_name)
memoized_methods.fetch(method_name) do
fail ArgumentError, "##{method_name} is not memoized"
end
end
private
def memoize_method(method_name)
if memoized_methods.key?(method_name)
fail ArgumentError, "##{method_name} is already memoized"
end
memoized_methods[method_name] = MethodBuilder.new(self, method_name).call
end
def memoized_methods
@memoized_methods ||= {}
end
end # ModuleMethods
def self.included(descendant)
descendant.class_eval do
include InstanceMethods
extend ModuleMethods
extend ClassMethods if instance_of?(Class)
end
end
private_class_method :included
end # Adamantium
end # Unparser
|