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.

Files changed (63) hide show
  1. data/CHANGELOG +57 -0
  2. data/lib/active_support/builder.rb +6 -0
  3. data/lib/active_support/cache.rb +428 -70
  4. data/lib/active_support/cache/compressed_mem_cache_store.rb +6 -15
  5. data/lib/active_support/cache/file_store.rb +139 -41
  6. data/lib/active_support/cache/mem_cache_store.rb +115 -76
  7. data/lib/active_support/cache/memory_store.rb +127 -27
  8. data/lib/active_support/cache/strategy/local_cache.rb +109 -57
  9. data/lib/active_support/cache/synchronized_memory_store.rb +2 -38
  10. data/lib/active_support/callbacks.rb +27 -27
  11. data/lib/active_support/configurable.rb +19 -18
  12. data/lib/active_support/core_ext/array/conversions.rb +30 -26
  13. data/lib/active_support/core_ext/array/random_access.rb +19 -5
  14. data/lib/active_support/core_ext/benchmark.rb +0 -12
  15. data/lib/active_support/core_ext/class/attribute.rb +1 -4
  16. data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -0
  17. data/lib/active_support/core_ext/date/calculations.rb +27 -8
  18. data/lib/active_support/core_ext/date/conversions.rb +1 -0
  19. data/lib/active_support/core_ext/date_time/conversions.rb +9 -3
  20. data/lib/active_support/core_ext/file.rb +1 -0
  21. data/lib/active_support/core_ext/hash/conversions.rb +14 -137
  22. data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
  23. data/lib/active_support/core_ext/kernel/reporting.rb +2 -1
  24. data/lib/active_support/core_ext/load_error.rb +1 -0
  25. data/lib/active_support/core_ext/logger.rb +1 -1
  26. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  27. data/lib/active_support/core_ext/object/to_param.rb +2 -2
  28. data/lib/active_support/core_ext/object/with_options.rb +2 -0
  29. data/lib/active_support/core_ext/string.rb +1 -0
  30. data/lib/active_support/core_ext/string/conversions.rb +35 -1
  31. data/lib/active_support/core_ext/string/encoding.rb +11 -0
  32. data/lib/active_support/core_ext/string/filters.rb +29 -0
  33. data/lib/active_support/core_ext/string/inflections.rb +0 -11
  34. data/lib/active_support/core_ext/string/interpolation.rb +1 -0
  35. data/lib/active_support/core_ext/string/multibyte.rb +16 -19
  36. data/lib/active_support/core_ext/time/calculations.rb +7 -6
  37. data/lib/active_support/core_ext/uri.rb +8 -3
  38. data/lib/active_support/dependencies.rb +33 -1
  39. data/lib/active_support/duration.rb +1 -0
  40. data/lib/active_support/hash_with_indifferent_access.rb +5 -1
  41. data/lib/active_support/i18n.rb +7 -2
  42. data/lib/active_support/inflector/transliterate.rb +58 -38
  43. data/lib/active_support/json/encoding.rb +28 -5
  44. data/lib/active_support/lazy_load_hooks.rb +14 -4
  45. data/lib/active_support/locale/en.yml +4 -1
  46. data/lib/active_support/message_verifier.rb +4 -4
  47. data/lib/active_support/multibyte.rb +1 -19
  48. data/lib/active_support/multibyte/chars.rb +143 -427
  49. data/lib/active_support/multibyte/unicode.rb +393 -0
  50. data/lib/active_support/notifications/fanout.rb +15 -5
  51. data/lib/active_support/notifications/instrumenter.rb +10 -4
  52. data/lib/active_support/railtie.rb +36 -0
  53. data/lib/active_support/rescuable.rb +1 -0
  54. data/lib/active_support/ruby/shim.rb +1 -0
  55. data/lib/active_support/testing/declarative.rb +1 -1
  56. data/lib/active_support/testing/isolation.rb +2 -1
  57. data/lib/active_support/testing/setup_and_teardown.rb +3 -0
  58. data/lib/active_support/values/time_zone.rb +20 -30
  59. data/lib/active_support/values/unicode_tables.dat +0 -0
  60. data/lib/active_support/version.rb +1 -1
  61. data/lib/active_support/xml_mini.rb +126 -1
  62. metadata +8 -61
  63. data/lib/active_support/multibyte/unicode_database.rb +0 -71
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/object/duplicable'
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. If your application never performs
10
- # manual cache item expiry (e.g. when you're using generational cache keys),
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
- # MemoryStore is not only able to store strings, but also arbitrary Ruby
15
- # objects.
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 not thread-safe. Use SynchronizedMemoryStore instead
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 read(name, options = nil)
25
- super do
26
- @data[name]
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 write(name, value, options = nil)
31
- super do
32
- @data[name] = (value.duplicable? ? value.dup : value).freeze
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
- def delete(name, options = nil)
37
- super do
38
- @data.delete(name)
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
- def delete_matched(matcher, options = nil)
43
- super do
44
- @data.delete_if { |k,v| k =~ matcher }
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 exist?(name,options = nil)
49
- super do
50
- @data.has_key?(name)
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 clear
55
- @data.clear
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
- # this allows caching of the fact that there is nothing in the remote cache
9
- NULL = 'remote_cache_store:null'
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] = MemoryStore.new
13
- yield
14
- ensure
15
- Thread.current[thread_local_key] = nil
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}] = MemoryStore.new
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 read(key, options = nil)
43
- value = local_cache && local_cache.read(key)
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 delete(key, options = nil)
63
- local_cache.mute { local_cache.write(key, NULL) } if 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 exist(key, options = nil)
68
- value = local_cache.read(key) if local_cache
69
- if value == NULL
70
- false
71
- elsif value
72
- true
73
- else
74
- super
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 increment(key, amount = 1)
79
- if value = super
80
- local_cache.mute { local_cache.write(key, value.to_s) } if local_cache
81
- value
82
- else
83
- nil
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
- def decrement(key, amount = 1)
88
- if value = super
89
- local_cache.mute { local_cache.write(key, value.to_s) } if local_cache
90
- value
91
- else
92
- nil
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
- def clear
97
- local_cache.clear if local_cache
98
- super
99
- end
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}_local_cache".gsub("/", "_").to_sym
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