Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 180 lines (159 sloc) 5.713 kb
9ee0ffb @urielka Patched Marshal#load to work with constant autoloading (active_suppor…
urielka authored
1 require 'active_support/core_ext/marshal'
36e934a @jeremy Fix require
jeremy authored
2 require 'active_support/core_ext/file/atomic'
f802eb2 @spastorino require conversions to use String#ord
spastorino authored
3 require 'active_support/core_ext/string/conversions'
3a59eab @spastorino Remove wrong rack/utils dependency from AS
spastorino authored
4 require 'uri/common'
db214a1 @jeremy Explicit File#atomic_write dependency
jeremy authored
5
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
6 module ActiveSupport
7 module Cache
b047929 @lifo Merge with docrails
lifo authored
8 # A cache store implementation which stores everything on the filesystem.
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
9 #
10 # FileStore implements the Strategy::LocalCache strategy which implements
a02b40a @chuyeow Fix "in memory" where it should be "in-memory".
chuyeow authored
11 # an in-memory cache inside of a block.
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
12 class FileStore < Store
13 attr_reader :cache_path
14
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
15 DIR_FORMATTER = "%03X"
c33334f @Floppy Change FILENAME_MAX_SIZE in FileStore to 228.
Floppy authored
16 FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
7670a51 @kennyj Refactor ActiveSupport::Cache::FileStore. used method and deleted du…
kennyj authored
17 EXCLUDED_DIRS = ['.', '..'].freeze
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
18
19 def initialize(cache_path, options = nil)
20 super(options)
49b0f9e @grzuy Fix ActiveSupport::Cache::FileStore#file_path_key does not work if in…
grzuy authored
21 @cache_path = cache_path.to_s
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
22 extend Strategy::LocalCache
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
23 end
24
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
25 def clear(options = nil)
447b6a4 @fxn removes usage of Object#in? from the code base (the method remains de…
fxn authored
26 root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
27 FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
28 end
29
30 def cleanup(options = nil)
31 options = merged_options(options)
32 each_key(options) do |key|
33 entry = read_entry(key, options)
34 delete_entry(key, options) if entry && entry.expired?
b5775c2 @lifo Add expiry support File cache store [#1693 state:resolved] [Roman Sht…
lifo authored
35 end
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
36 end
37
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
38 def increment(name, amount = 1, options = nil)
39 file_name = key_file_path(namespaced_key(name, options))
40 lock_file(file_name) do
41 options = merged_options(options)
42 if num = read(name, options)
43 num = num.to_i + amount
44 write(name, num, options)
45 num
46 else
47 nil
48 end
4215e9a @josevalim Instrumenting cache stores.
josevalim authored
49 end
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
50 end
51
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
52 def decrement(name, amount = 1, options = nil)
53 file_name = key_file_path(namespaced_key(name, options))
54 lock_file(file_name) do
55 options = merged_options(options)
56 if num = read(name, options)
57 num = num.to_i - amount
58 write(name, num, options)
59 num
60 else
61 nil
62 end
4215e9a @josevalim Instrumenting cache stores.
josevalim authored
63 end
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
64 end
65
f3fd44f @NZKoz Add default options to file_store to make it align with the other cac…
NZKoz authored
66 def delete_matched(matcher, options = nil)
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
67 options = merged_options(options)
68 instrument(:delete_matched, matcher.inspect) do
69 matcher = key_matcher(matcher, options)
70 search_dir(cache_path) do |path|
71 key = file_path_key(path)
72 delete_entry(key, options) if key.match(matcher)
73 end
74 end
75 end
76
77 protected
78
79 def read_entry(key, options)
80 file_name = key_file_path(key)
81 if File.exist?(file_name)
9cafc28 @joshk Removed deprecated methods and related tests from ActiveSupport
joshk authored
82 File.open(file_name) { |f| Marshal.load(f) }
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
83 end
b52c66f @winston #read_entry in ActiveSupport::Cache::FileStore should log details of …
winston authored
84 rescue => e
85 logger.error("FileStoreError (#{e}): #{e.message}") if logger
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
86 nil
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
87 end
88
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
89 def write_entry(key, entry, options)
90 file_name = key_file_path(key)
91 ensure_cache_path(File.dirname(file_name))
92 File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
93 true
94 end
95
96 def delete_entry(key, options)
97 file_name = key_file_path(key)
98 if File.exist?(file_name)
99 begin
100 File.delete(file_name)
101 delete_empty_directories(File.dirname(file_name))
102 true
103 rescue => e
104 # Just in case the error was caused by another process deleting the file first.
105 raise e if File.exist?(file_name)
106 false
107 end
108 end
4215e9a @josevalim Instrumenting cache stores.
josevalim authored
109 end
99860b7 @josevalim Add fragment_exist? and exist? methods to cache stores. [#203 state:r…
josevalim authored
110
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
111 private
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
112 # Lock a file for a block so only one process can modify it at a time.
113 def lock_file(file_name, &block) # :nodoc:
114 if File.exist?(file_name)
92537b8 @arunagw File should be open in read/write mode. When doing lock on a file.
arunagw authored
115 File.open(file_name, 'r+') do |f|
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
116 begin
117 f.flock File::LOCK_EX
118 yield
119 ensure
120 f.flock File::LOCK_UN
121 end
122 end
123 else
124 yield
125 end
126 end
127
128 # Translate a key into a file path.
129 def key_file_path(key)
3a59eab @spastorino Remove wrong rack/utils dependency from AS
spastorino authored
130 fname = URI.encode_www_form_component(key)
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
131 hash = Zlib.adler32(fname)
132 hash, dir_1 = hash.divmod(0x1000)
133 dir_2 = hash.modulo(0x1000)
134 fname_paths = []
667ef57 @spastorino No need to create a range object
spastorino authored
135
8d63678 @phuibonhoa Fixed issue in file store where it could create a filename that was t…
phuibonhoa authored
136 # Make sure file name doesn't exceed file system limits.
137 begin
667ef57 @spastorino No need to create a range object
spastorino authored
138 fname_paths << fname[0, FILENAME_MAX_SIZE]
8d63678 @phuibonhoa Fixed issue in file store where it could create a filename that was t…
phuibonhoa authored
139 fname = fname[FILENAME_MAX_SIZE..-1]
140 end until fname.blank?
667ef57 @spastorino No need to create a range object
spastorino authored
141
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
142 File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
143 end
144
145 # Translate a file path into a key.
146 def file_path_key(path)
0b1ce07 @mirakui Bug: cache_path.size doesn't return length of filename but size of fi…
mirakui authored
147 fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
3a59eab @spastorino Remove wrong rack/utils dependency from AS
spastorino authored
148 URI.decode_www_form_component(fname, Encoding::UTF_8)
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
149 end
150
151 # Delete empty directories in the cache.
152 def delete_empty_directories(dir)
b883706 @chuckbjones Fix deletion of empty directories:
chuckbjones authored
153 return if File.realpath(dir) == File.realpath(cache_path)
447b6a4 @fxn removes usage of Object#in? from the code base (the method remains de…
fxn authored
154 if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
b883706 @chuckbjones Fix deletion of empty directories:
chuckbjones authored
155 Dir.delete(dir) rescue nil
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
156 delete_empty_directories(File.dirname(dir))
157 end
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
158 end
159
ee51b51 ActiveSupport::Cache refactoring
Brian Durand authored
160 # Make sure a file path's directories exist.
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
161 def ensure_cache_path(path)
99860b7 @josevalim Add fragment_exist? and exist? methods to cache stores. [#203 state:r…
josevalim authored
162 FileUtils.makedirs(path) unless File.exist?(path)
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
163 end
164
165 def search_dir(dir, &callback)
0baa8f8 @phuibonhoa Added fix so that file store does not raise an exception when cache d…
phuibonhoa authored
166 return if !File.exist?(dir)
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
167 Dir.foreach(dir) do |d|
447b6a4 @fxn removes usage of Object#in? from the code base (the method remains de…
fxn authored
168 next if EXCLUDED_DIRS.include?(d)
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
169 name = File.join(dir, d)
170 if File.directory?(name)
171 search_dir(name, &callback)
172 else
173 callback.call name
174 end
175 end
176 end
1b94d5d @jeremy Fix indentation mismatch
jeremy authored
177 end
2a9ad9c @dhh Moved the caching stores from ActionController::Caching::Fragments::*…
dhh authored
178 end
c1a8690 @josh Consistently use the framework's configured logger and avoid revertin…
josh authored
179 end
Something went wrong with that request. Please try again.