Module: ExpiringMemoize

Defined in:
lib/expiring_memoize.rb,
lib/expiring_memoize/version.rb

Constant Summary collapse

VERSION =
"0.1.1"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.gettimeObject



40
41
42
# File 'lib/expiring_memoize.rb', line 40

def self.gettime
  Process.clock_gettime Process::CLOCK_MONOTONIC_COARSE
end

Instance Method Details

#memoize(name, ttl: Float::INFINITY) ⇒ Object

Memoize a nullary method.

Parameters:

  • name (Symbol)

    method to memoize

  • ttl (Hash) (defaults to: Float::INFINITY)

    a customizable set of options

Options Hash (ttl:):

  • time (Number)

    to live of the value, in seconds

Raises:

  • (ArgumentError)


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
# File 'lib/expiring_memoize.rb', line 7

def memoize name, ttl: Float::INFINITY
  original = instance_method name
  raise ArgumentError, 'only nullary methods supported' unless original.arity.zero?

  define_method name do
    data = ((@_expiring_memoize_data ||= {})[name] ||= {})
    loop do
      # no need to synchronize here -- worst case,
      # we return a value fresher than expected.
      # (plus, global interpreter lock in MRI makes it safe regardless)
      unless (ts = data[:timestamp]) && (ExpiringMemoize.gettime - ts) < ttl
        # value is stale, race to fetch
        mutex ||= (data[:mutex] ||= Mutex.new)
        if mutex.try_lock
          # our thread won the race, let's get the value
          begin
            data[:value] = original.bind(self).call
            data[:timestamp] = ExpiringMemoize.gettime
          ensure
            mutex.unlock
          end
        else
          # our thread lost, block on the mutex and try again
          mutex.synchronize {}
          next
        end
      end
      return data[:value]
    end
  end
end