forked from headius/thread_safe
/
cache.rb
129 lines (110 loc) · 3.32 KB
/
cache.rb
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
require 'thread'
module ThreadSafe
autoload :JRubyCacheBackend, 'thread_safe/jruby_cache_backend'
autoload :MriCacheBackend, 'thread_safe/mri_cache_backend'
autoload :NonConcurrentCacheBackend, 'thread_safe/non_concurrent_cache_backend'
autoload :AtomicReferenceCacheBackend, 'thread_safe/atomic_reference_cache_backend'
autoload :SynchronizedCacheBackend, 'thread_safe/synchronized_cache_backend'
ConcurrentCacheBackend =
case defined?(RUBY_ENGINE) && RUBY_ENGINE
when 'jruby'; JRubyCacheBackend
when 'ruby'; MriCacheBackend
when 'rbx'; AtomicReferenceCacheBackend
else
warn 'ThreadSafe: unsupported Ruby engine, using a fully synchronized ThreadSafe::Cache implementation' if $VERBOSE
SynchronizedCacheBackend
end
class Cache < ConcurrentCacheBackend
def initialize(options = nil, &block)
if options.kind_of?(::Hash)
validate_options_hash!(options)
else
options = nil
end
super(options)
@default_proc = block
end
def [](key)
if value = super
value
elsif @default_proc && !key?(key)
@default_proc.call(self, key)
else
value
end
end
def fetch(key)
if value = self[key]
value
elsif !key?(key) && block_given?
self[key] = yield(key)
else
value
end
end
def put_if_absent(key, value)
computed = false
result = compute_if_absent(key) do
computed = true
value
end
computed ? nil : result
end unless method_defined?(:put_if_absent)
def keys
arr = []
each_pair {|k, v| arr << k}
arr
end unless method_defined?(:keys)
def values
arr = []
each_pair {|k, v| arr << v}
arr
end unless method_defined?(:values)
def each_key
each_pair {|k, v| yield k}
end unless method_defined?(:each_key)
def each_value
each_pair {|k, v| yield v}
end unless method_defined?(:each_value)
def empty?
each_pair {|k, v| return false}
true
end unless method_defined?(:empty?)
def size
count = 0
each_pair {|k, v| count += 1}
count
end unless method_defined?(:size)
def marshal_dump
raise TypeError, "can't dump hash with default proc" if @default_proc
h = {}
each_pair {|k, v| h[k] = v}
h
end
def marshal_load(hash)
initialize
populate_from(hash)
end
undef :freeze
private
def initialize_copy(other)
super
populate_from(other)
end
def populate_from(hash)
hash.each_pair {|k, v| self[k] = v}
self
end
def validate_options_hash!(options)
if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Fixnum) || initial_capacity < 0)
raise ArgumentError, ":initial_capacity must be a positive Fixnum"
end
if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
raise ArgumentError, ":load_factor must be a number between 0 and 1"
end
if (concurrency_level = options[:concurrency_level]) && (!concurrency_level.kind_of?(Fixnum) || concurrency_level < 1)
raise ArgumentError, ":concurrency_level must be a Fixnum greater or equal than 1"
end
end
end
end