activesupport 3.0.0.beta3 → 3.0.0.beta4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- data/CHANGELOG +57 -0
- data/lib/active_support/builder.rb +6 -0
- data/lib/active_support/cache.rb +428 -70
- data/lib/active_support/cache/compressed_mem_cache_store.rb +6 -15
- data/lib/active_support/cache/file_store.rb +139 -41
- data/lib/active_support/cache/mem_cache_store.rb +115 -76
- data/lib/active_support/cache/memory_store.rb +127 -27
- data/lib/active_support/cache/strategy/local_cache.rb +109 -57
- data/lib/active_support/cache/synchronized_memory_store.rb +2 -38
- data/lib/active_support/callbacks.rb +27 -27
- data/lib/active_support/configurable.rb +19 -18
- data/lib/active_support/core_ext/array/conversions.rb +30 -26
- data/lib/active_support/core_ext/array/random_access.rb +19 -5
- data/lib/active_support/core_ext/benchmark.rb +0 -12
- data/lib/active_support/core_ext/class/attribute.rb +1 -4
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -0
- data/lib/active_support/core_ext/date/calculations.rb +27 -8
- data/lib/active_support/core_ext/date/conversions.rb +1 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +9 -3
- data/lib/active_support/core_ext/file.rb +1 -0
- data/lib/active_support/core_ext/hash/conversions.rb +14 -137
- data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
- data/lib/active_support/core_ext/kernel/reporting.rb +2 -1
- data/lib/active_support/core_ext/load_error.rb +1 -0
- data/lib/active_support/core_ext/logger.rb +1 -1
- data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
- data/lib/active_support/core_ext/object/to_param.rb +2 -2
- data/lib/active_support/core_ext/object/with_options.rb +2 -0
- data/lib/active_support/core_ext/string.rb +1 -0
- data/lib/active_support/core_ext/string/conversions.rb +35 -1
- data/lib/active_support/core_ext/string/encoding.rb +11 -0
- data/lib/active_support/core_ext/string/filters.rb +29 -0
- data/lib/active_support/core_ext/string/inflections.rb +0 -11
- data/lib/active_support/core_ext/string/interpolation.rb +1 -0
- data/lib/active_support/core_ext/string/multibyte.rb +16 -19
- data/lib/active_support/core_ext/time/calculations.rb +7 -6
- data/lib/active_support/core_ext/uri.rb +8 -3
- data/lib/active_support/dependencies.rb +33 -1
- data/lib/active_support/duration.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +5 -1
- data/lib/active_support/i18n.rb +7 -2
- data/lib/active_support/inflector/transliterate.rb +58 -38
- data/lib/active_support/json/encoding.rb +28 -5
- data/lib/active_support/lazy_load_hooks.rb +14 -4
- data/lib/active_support/locale/en.yml +4 -1
- data/lib/active_support/message_verifier.rb +4 -4
- data/lib/active_support/multibyte.rb +1 -19
- data/lib/active_support/multibyte/chars.rb +143 -427
- data/lib/active_support/multibyte/unicode.rb +393 -0
- data/lib/active_support/notifications/fanout.rb +15 -5
- data/lib/active_support/notifications/instrumenter.rb +10 -4
- data/lib/active_support/railtie.rb +36 -0
- data/lib/active_support/rescuable.rb +1 -0
- data/lib/active_support/ruby/shim.rb +1 -0
- data/lib/active_support/testing/declarative.rb +1 -1
- data/lib/active_support/testing/isolation.rb +2 -1
- data/lib/active_support/testing/setup_and_teardown.rb +3 -0
- data/lib/active_support/values/time_zone.rb +20 -30
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +1 -1
- data/lib/active_support/xml_mini.rb +126 -1
- metadata +8 -61
- data/lib/active_support/multibyte/unicode_database.rb +0 -71
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'monitor'
|
2
2
|
|
3
3
|
module ActiveSupport
|
4
4
|
module Cache
|
@@ -6,54 +6,154 @@ module ActiveSupport
|
|
6
6
|
# same process. If you're running multiple Ruby on Rails server processes
|
7
7
|
# (which is the case if you're using mongrel_cluster or Phusion Passenger),
|
8
8
|
# then this means that your Rails server process instances won't be able
|
9
|
-
# to share cache data with each other
|
10
|
-
#
|
11
|
-
# then using MemoryStore is ok. Otherwise, consider carefully whether you
|
12
|
-
# should be using this cache store.
|
9
|
+
# to share cache data with each other and this may not be the most
|
10
|
+
# appropriate cache for you.
|
13
11
|
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# This cache has a bounded size specified by the :size options to the
|
13
|
+
# initializer (default is 32Mb). When the cache exceeds the alotted size,
|
14
|
+
# a cleanup will occur which tries to prune the cache down to three quarters
|
15
|
+
# of the maximum size by removing the least recently used entries.
|
16
16
|
#
|
17
|
-
# MemoryStore is
|
18
|
-
# if you need thread-safety.
|
17
|
+
# MemoryStore is thread-safe.
|
19
18
|
class MemoryStore < Store
|
20
|
-
def initialize
|
19
|
+
def initialize(options = nil)
|
20
|
+
options ||= {}
|
21
|
+
super(options)
|
21
22
|
@data = {}
|
23
|
+
@key_access = {}
|
24
|
+
@max_size = options[:size] || 32.megabytes
|
25
|
+
@max_prune_time = options[:max_prune_time] || 2
|
26
|
+
@cache_size = 0
|
27
|
+
@monitor = Monitor.new
|
28
|
+
@pruning = false
|
22
29
|
end
|
23
30
|
|
24
|
-
def
|
25
|
-
|
26
|
-
@data
|
31
|
+
def clear(options = nil)
|
32
|
+
synchronize do
|
33
|
+
@data.clear
|
34
|
+
@key_access.clear
|
35
|
+
@cache_size = 0
|
27
36
|
end
|
28
37
|
end
|
29
38
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
39
|
+
def cleanup(options = nil)
|
40
|
+
options = merged_options(options)
|
41
|
+
instrument(:cleanup, :size => @data.size) do
|
42
|
+
keys = synchronize{ @data.keys }
|
43
|
+
keys.each do |key|
|
44
|
+
entry = @data[key]
|
45
|
+
delete_entry(key, options) if entry && entry.expired?
|
46
|
+
end
|
33
47
|
end
|
34
48
|
end
|
35
49
|
|
36
|
-
|
37
|
-
|
38
|
-
|
50
|
+
# Prune the cache down so the entries fit within the specified memory size by removing
|
51
|
+
# the least recently accessed entries.
|
52
|
+
def prune(target_size, max_time = nil)
|
53
|
+
return if pruning?
|
54
|
+
@pruning = true
|
55
|
+
begin
|
56
|
+
start_time = Time.now
|
57
|
+
cleanup
|
58
|
+
instrument(:prune, target_size, :from => @cache_size) do
|
59
|
+
keys = synchronize{ @key_access.keys.sort{|a,b| @key_access[a].to_f <=> @key_access[b].to_f} }
|
60
|
+
keys.each do |key|
|
61
|
+
delete_entry(key, options)
|
62
|
+
return if @cache_size <= target_size || (max_time && Time.now - start_time > max_time)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
ensure
|
66
|
+
@pruning = false
|
39
67
|
end
|
40
68
|
end
|
41
69
|
|
42
|
-
|
43
|
-
|
44
|
-
|
70
|
+
# Return true if the cache is currently be pruned to remove older entries.
|
71
|
+
def pruning?
|
72
|
+
@pruning
|
73
|
+
end
|
74
|
+
|
75
|
+
# Increment an integer value in the cache.
|
76
|
+
def increment(name, amount = 1, options = nil)
|
77
|
+
synchronize do
|
78
|
+
options = merged_options(options)
|
79
|
+
if num = read(name, options)
|
80
|
+
num = num.to_i + amount
|
81
|
+
write(name, num, options)
|
82
|
+
num
|
83
|
+
else
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Decrement an integer value in the cache.
|
90
|
+
def decrement(name, amount = 1, options = nil)
|
91
|
+
synchronize do
|
92
|
+
options = merged_options(options)
|
93
|
+
if num = read(name, options)
|
94
|
+
num = num.to_i - amount
|
95
|
+
write(name, num, options)
|
96
|
+
num
|
97
|
+
else
|
98
|
+
nil
|
99
|
+
end
|
45
100
|
end
|
46
101
|
end
|
47
102
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
103
|
+
def delete_matched(matcher, options = nil)
|
104
|
+
options = merged_options(options)
|
105
|
+
instrument(:delete_matched, matcher.inspect) do
|
106
|
+
matcher = key_matcher(matcher, options)
|
107
|
+
keys = synchronize { @data.keys }
|
108
|
+
keys.each do |key|
|
109
|
+
delete_entry(key, options) if key.match(matcher)
|
110
|
+
end
|
51
111
|
end
|
52
112
|
end
|
53
113
|
|
54
|
-
def
|
55
|
-
@data.
|
114
|
+
def inspect # :nodoc:
|
115
|
+
"<##{self.class.name} entries=#{@data.size}, size=#{@cache_size}, options=#{@options.inspect}>"
|
56
116
|
end
|
117
|
+
|
118
|
+
# Synchronize calls to the cache. This should be called wherever the underlying cache implementation
|
119
|
+
# is not thread safe.
|
120
|
+
def synchronize(&block) # :nodoc:
|
121
|
+
@monitor.synchronize(&block)
|
122
|
+
end
|
123
|
+
|
124
|
+
protected
|
125
|
+
def read_entry(key, options) # :nodoc:
|
126
|
+
entry = @data[key]
|
127
|
+
synchronize do
|
128
|
+
if entry
|
129
|
+
@key_access[key] = Time.now.to_f
|
130
|
+
else
|
131
|
+
@key_access.delete(key)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
entry
|
135
|
+
end
|
136
|
+
|
137
|
+
def write_entry(key, entry, options) # :nodoc:
|
138
|
+
synchronize do
|
139
|
+
old_entry = @data[key]
|
140
|
+
@cache_size -= old_entry.size if old_entry
|
141
|
+
@cache_size += entry.size
|
142
|
+
@key_access[key] = Time.now.to_f
|
143
|
+
@data[key] = entry
|
144
|
+
prune(@max_size * 0.75, @max_prune_time) if @cache_size > @max_size
|
145
|
+
true
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def delete_entry(key, options) # :nodoc:
|
150
|
+
synchronize do
|
151
|
+
@key_access.delete(key)
|
152
|
+
entry = @data.delete(key)
|
153
|
+
@cache_size -= entry.size if entry
|
154
|
+
!!entry
|
155
|
+
end
|
156
|
+
end
|
57
157
|
end
|
58
158
|
end
|
59
159
|
end
|
@@ -4,108 +4,160 @@ require 'active_support/core_ext/string/inflections'
|
|
4
4
|
module ActiveSupport
|
5
5
|
module Cache
|
6
6
|
module Strategy
|
7
|
+
# Caches that implement LocalCache will be backed by an in memory cache for the
|
8
|
+
# duration of a block. Repeated calls to the cache for the same key will hit the
|
9
|
+
# in memory cache for faster access.
|
7
10
|
module LocalCache
|
8
|
-
#
|
9
|
-
|
11
|
+
# Simple memory backed cache. This cache is not thread safe but is intended only
|
12
|
+
# for serving as a temporary memory cache for a single thread.
|
13
|
+
class LocalStore < Store
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@data = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
# Since it isn't thread safe, don't allow synchronizing.
|
20
|
+
def synchronize # :nodoc:
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
def clear(options = nil)
|
25
|
+
@data.clear
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_entry(key, options)
|
29
|
+
@data[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_entry(key, value, options)
|
33
|
+
@data[key] = value
|
34
|
+
true
|
35
|
+
end
|
10
36
|
|
37
|
+
def delete_entry(key, options)
|
38
|
+
!!@data.delete(key)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Use a local cache to front for the cache for the duration of a block.
|
11
43
|
def with_local_cache
|
12
|
-
Thread.current[thread_local_key]
|
13
|
-
|
14
|
-
|
15
|
-
|
44
|
+
save_val = Thread.current[thread_local_key]
|
45
|
+
begin
|
46
|
+
Thread.current[thread_local_key] = LocalStore.new
|
47
|
+
yield
|
48
|
+
ensure
|
49
|
+
Thread.current[thread_local_key] = save_val
|
50
|
+
end
|
16
51
|
end
|
17
52
|
|
53
|
+
# Middleware class can be inserted as a Rack handler to use a local cache for the
|
54
|
+
# duration of a request.
|
18
55
|
def middleware
|
19
56
|
@middleware ||= begin
|
20
57
|
klass = Class.new
|
21
58
|
klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
59
|
+
class << self
|
60
|
+
def name
|
61
|
+
"ActiveSupport::Cache::Strategy::LocalCache"
|
62
|
+
end
|
63
|
+
alias :to_s :name
|
64
|
+
end
|
65
|
+
|
22
66
|
def initialize(app)
|
23
67
|
@app = app
|
24
68
|
end
|
25
69
|
|
26
70
|
def call(env)
|
27
|
-
Thread.current[:#{thread_local_key}] =
|
71
|
+
Thread.current[:#{thread_local_key}] = LocalStore.new
|
28
72
|
@app.call(env)
|
29
73
|
ensure
|
30
74
|
Thread.current[:#{thread_local_key}] = nil
|
31
75
|
end
|
32
76
|
EOS
|
33
|
-
|
34
|
-
def klass.to_s
|
35
|
-
"ActiveSupport::Cache::Strategy::LocalCache"
|
36
|
-
end
|
37
|
-
|
38
77
|
klass
|
39
78
|
end
|
40
79
|
end
|
41
80
|
|
42
|
-
def
|
43
|
-
|
44
|
-
if value == NULL
|
45
|
-
nil
|
46
|
-
elsif value.nil?
|
47
|
-
value = super
|
48
|
-
local_cache.mute { local_cache.write(key, value || NULL) } if local_cache
|
49
|
-
value.duplicable? ? value.dup : value
|
50
|
-
else
|
51
|
-
# forcing the value to be immutable
|
52
|
-
value.duplicable? ? value.dup : value
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def write(key, value, options = nil)
|
57
|
-
value = value.to_s if respond_to?(:raw?) && raw?(options)
|
58
|
-
local_cache.mute { local_cache.write(key, value || NULL) } if local_cache
|
81
|
+
def clear(options = nil) # :nodoc:
|
82
|
+
local_cache.clear(options) if local_cache
|
59
83
|
super
|
60
84
|
end
|
61
85
|
|
62
|
-
def
|
63
|
-
local_cache.
|
86
|
+
def cleanup(options = nil) # :nodoc:
|
87
|
+
local_cache.clear(options) if local_cache
|
64
88
|
super
|
65
89
|
end
|
66
90
|
|
67
|
-
def
|
68
|
-
value =
|
69
|
-
if
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
91
|
+
def increment(name, amount = 1, options = nil) # :nodoc:
|
92
|
+
value = bypass_local_cache{super}
|
93
|
+
if local_cache
|
94
|
+
local_cache.mute do
|
95
|
+
if value
|
96
|
+
local_cache.write(name, value, options)
|
97
|
+
else
|
98
|
+
local_cache.delete(name, options)
|
99
|
+
end
|
100
|
+
end
|
75
101
|
end
|
102
|
+
value
|
76
103
|
end
|
77
104
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
105
|
+
def decrement(name, amount = 1, options = nil) # :nodoc:
|
106
|
+
value = bypass_local_cache{super}
|
107
|
+
if local_cache
|
108
|
+
local_cache.mute do
|
109
|
+
if value
|
110
|
+
local_cache.write(name, value, options)
|
111
|
+
else
|
112
|
+
local_cache.delete(name, options)
|
113
|
+
end
|
114
|
+
end
|
84
115
|
end
|
116
|
+
value
|
85
117
|
end
|
86
118
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
119
|
+
protected
|
120
|
+
def read_entry(key, options) # :nodoc:
|
121
|
+
if local_cache
|
122
|
+
entry = local_cache.read_entry(key, options)
|
123
|
+
unless entry
|
124
|
+
entry = super
|
125
|
+
local_cache.write_entry(key, entry, options)
|
126
|
+
end
|
127
|
+
entry
|
128
|
+
else
|
129
|
+
super
|
130
|
+
end
|
93
131
|
end
|
94
|
-
end
|
95
132
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
133
|
+
def write_entry(key, entry, options) # :nodoc:
|
134
|
+
local_cache.write_entry(key, entry, options) if local_cache
|
135
|
+
super
|
136
|
+
end
|
137
|
+
|
138
|
+
def delete_entry(key, options) # :nodoc:
|
139
|
+
local_cache.delete_entry(key, options) if local_cache
|
140
|
+
super
|
141
|
+
end
|
100
142
|
|
101
143
|
private
|
102
144
|
def thread_local_key
|
103
|
-
@thread_local_key ||= "#{self.class.name.underscore}
|
145
|
+
@thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
|
104
146
|
end
|
105
147
|
|
106
148
|
def local_cache
|
107
149
|
Thread.current[thread_local_key]
|
108
150
|
end
|
151
|
+
|
152
|
+
def bypass_local_cache
|
153
|
+
save_cache = Thread.current[thread_local_key]
|
154
|
+
begin
|
155
|
+
Thread.current[thread_local_key] = nil
|
156
|
+
yield
|
157
|
+
ensure
|
158
|
+
Thread.current[thread_local_key] = save_cache
|
159
|
+
end
|
160
|
+
end
|
109
161
|
end
|
110
162
|
end
|
111
163
|
end
|
@@ -2,45 +2,9 @@ module ActiveSupport
|
|
2
2
|
module Cache
|
3
3
|
# Like MemoryStore, but thread-safe.
|
4
4
|
class SynchronizedMemoryStore < MemoryStore
|
5
|
-
def initialize
|
5
|
+
def initialize(*args)
|
6
|
+
ActiveSupport::Deprecation.warn('ActiveSupport::Cache::SynchronizedMemoryStore has been deprecated in favor of ActiveSupport::Cache::MemoryStore.', caller)
|
6
7
|
super
|
7
|
-
@guard = Monitor.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def fetch(key, options = {})
|
11
|
-
@guard.synchronize { super }
|
12
|
-
end
|
13
|
-
|
14
|
-
def read(name, options = nil)
|
15
|
-
@guard.synchronize { super }
|
16
|
-
end
|
17
|
-
|
18
|
-
def write(name, value, options = nil)
|
19
|
-
@guard.synchronize { super }
|
20
|
-
end
|
21
|
-
|
22
|
-
def delete(name, options = nil)
|
23
|
-
@guard.synchronize { super }
|
24
|
-
end
|
25
|
-
|
26
|
-
def delete_matched(matcher, options = nil)
|
27
|
-
@guard.synchronize { super }
|
28
|
-
end
|
29
|
-
|
30
|
-
def exist?(name,options = nil)
|
31
|
-
@guard.synchronize { super }
|
32
|
-
end
|
33
|
-
|
34
|
-
def increment(key, amount = 1)
|
35
|
-
@guard.synchronize { super }
|
36
|
-
end
|
37
|
-
|
38
|
-
def decrement(key, amount = 1)
|
39
|
-
@guard.synchronize { super }
|
40
|
-
end
|
41
|
-
|
42
|
-
def clear
|
43
|
-
@guard.synchronize { super }
|
44
8
|
end
|
45
9
|
end
|
46
10
|
end
|