thorero-cache 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,144 @@
1
+ class Merb::Cache::DatabaseStore
2
+ # Provides the database cache store for merb-cache
3
+
4
+ def initialize
5
+ @config = Merb::Controller._cache.config
6
+ prepare
7
+ end
8
+
9
+ class OrmNotFound < Exception #
10
+ def initialize
11
+ super("No valid ORM found (did you specify use_orm in init.rb?)")
12
+ end
13
+ end
14
+
15
+ # Requires the ORM at startup, raising an OrmNotFound exception if
16
+ # the backend is not found
17
+ Merb::Controller._cache.config[:table_name] ||= "merb_cache"
18
+ if defined?(Merb::Orms::ActiveRecord)
19
+ require "merb-cache/cache-store/database-activerecord.rb"
20
+ include Merb::Cache::DatabaseStore::ActiveRecord
21
+ elsif defined?(Merb::Orms::DataMapper)
22
+ require "merb-cache/cache-store/database-datamapper.rb"
23
+ include Merb::Cache::DatabaseStore::DataMapper
24
+ elsif defined?(Merb::Orms::Sequel)
25
+ require "merb-cache/cache-store/database-sequel.rb"
26
+ include Merb::Cache::DatabaseStore::Sequel
27
+ else
28
+ raise OrmNotFound
29
+ end
30
+
31
+ # This method is there to ensure minimal requirements are met
32
+ # (file permissions, table exists, connected to server, ...)
33
+ def prepare
34
+ CacheModel.check_table
35
+ true
36
+ end
37
+
38
+ # Checks whether a cache entry exists
39
+ #
40
+ # ==== Parameter
41
+ # key<String>:: The key identifying the cache entry
42
+ #
43
+ # ==== Returns
44
+ # true if the cache entry exists, false otherwise
45
+ def cached?(key)
46
+ not CacheModel.cache_get(key).nil?
47
+ end
48
+
49
+ # Capture or restore the data in cache.
50
+ # If the cache entry expired or does not exist, the data are taken
51
+ # from the execution of the block, marshalled and stored in cache.
52
+ # Otherwise, the data are loaded from the cache and returned unmarshalled
53
+ #
54
+ # ==== Parameters
55
+ # _controller<Merb::Controller>:: The instance of the current controller
56
+ # key<String>:: The key identifying the cache entry
57
+ # from_now<~minutes>::
58
+ # The number of minutes (from now) the cache should persist
59
+ # &block:: The template to be used or not
60
+ #
61
+ # ==== Additional information
62
+ # When fetching data (the cache entry exists and has not expired)
63
+ # The data are loaded from the cache and returned unmarshalled.
64
+ # Otherwise:
65
+ # The controller is used to capture the rendered template (from the block).
66
+ # It uses the capture_#{engine} and concat_#{engine} methods to do so.
67
+ # The captured data are then marshalled and stored.
68
+ def cache(_controller, key, from_now = nil, &block)
69
+ _data = CacheModel.cache_get(key)
70
+ if _data.nil?
71
+ _expire = from_now ? from_now.minutes.from_now : nil
72
+ _data = _controller.send(:capture, &block)
73
+ CacheModel.cache_set(key, Marshal.dump(_data), _expire, false)
74
+ else
75
+ _data = Marshal.load(_data)
76
+ end
77
+ _controller.send(:concat, _data, block.binding)
78
+ true
79
+ end
80
+
81
+ # Store data to the database using the specified key
82
+ #
83
+ # ==== Parameters
84
+ # key<Sting>:: The key identifying the cache entry
85
+ # data<String>:: The data to be put in cache
86
+ # from_now<~minutes>::
87
+ # The number of minutes (from now) the cache should persist
88
+ def cache_set(key, data, from_now = nil)
89
+ _expire = from_now ? from_now.minutes.from_now : nil
90
+ CacheModel.cache_set(key, Marshal.dump(data), _expire)
91
+ Merb.logger.info("cache: set (#{key})")
92
+ true
93
+ end
94
+
95
+ # Fetch data from the database using the specified key
96
+ # The entry is deleted if it has expired
97
+ #
98
+ # ==== Parameter
99
+ # key<Sting>:: The key identifying the cache entry
100
+ #
101
+ # ==== Returns
102
+ # data<String, NilClass>::
103
+ # nil is returned whether the entry expired or was not found
104
+ def cache_get(key)
105
+ data = CacheModel.cache_get(key)
106
+ Merb.logger.info("cache: #{data.nil? ? "miss" : "hit"} (#{key})")
107
+ data.nil? ? nil : Marshal.load(data)
108
+ end
109
+
110
+ # Expire the cache entry identified by the given key
111
+ #
112
+ # ==== Parameter
113
+ # key<Sting>:: The key identifying the cache entry
114
+ def expire(key)
115
+ CacheModel.expire(key)
116
+ Merb.logger.info("cache: expired (#{key})")
117
+ true
118
+ end
119
+
120
+ # Expire the cache entries matching the given key
121
+ #
122
+ # ==== Parameter
123
+ # key<Sting>:: The key matching the cache entries
124
+ def expire_match(key)
125
+ CacheModel.expire_match(key)
126
+ Merb.logger.info("cache: expired matching (#{key})")
127
+ true
128
+ end
129
+
130
+ # Expire all the cache entries
131
+ def expire_all
132
+ CacheModel.expire_all
133
+ Merb.logger.info("cache: expired all")
134
+ true
135
+ end
136
+
137
+ # Gives info on the current cache store
138
+ #
139
+ # ==== Returns
140
+ # The type of the current cache store
141
+ def cache_store_type
142
+ "database"
143
+ end
144
+ end
@@ -0,0 +1,106 @@
1
+ class Merb::Cache::DummyStore
2
+ # Provides dummy cache store for merb-cache
3
+
4
+ def initialize
5
+ @config = Merb::Controller._cache.config
6
+ prepare
7
+ end
8
+
9
+ # This method is there to ensure minimal requirements are met
10
+ # (directories are accessible, table exists, connected to server, ...)
11
+ def prepare
12
+ true
13
+ end
14
+
15
+ # Checks whether a cache entry exists
16
+ #
17
+ # ==== Parameter
18
+ # key<String>:: The key identifying the cache entry
19
+ #
20
+ # ==== Returns
21
+ # true if the cache entry exists, false otherwise
22
+ def cached?(key)
23
+ false
24
+ end
25
+
26
+ # Capture or restore the data in cache.
27
+ # If the cache entry expired or does not exist, the data are taken
28
+ # from the execution of the block, marshalled and stored in cache.
29
+ # Otherwise, the data are loaded from the cache and returned unmarshalled
30
+ #
31
+ # ==== Parameters
32
+ # _controller<Merb::Controller>:: The instance of the current controller
33
+ # key<String>:: The key identifying the cache entry
34
+ # from_now<~minutes>::
35
+ # The number of minutes (from now) the cache should persist
36
+ # &block:: The template to be used or not
37
+ #
38
+ # ==== Additional information
39
+ # When fetching data (the cache entry exists and has not expired)
40
+ # The data are loaded from the cache and returned unmarshalled.
41
+ # Otherwise:
42
+ # The controller is used to capture the rendered template (from the block).
43
+ # It uses the capture_#{engine} and concat_#{engine} methods to do so.
44
+ # The captured data are then marshalled and stored.
45
+ def cache(_controller, key, from_now = nil, &block)
46
+ _data = _controller.send(:capture, &block)
47
+ _controller.send(:concat, _data, block.binding)
48
+ true
49
+ end
50
+
51
+ # Store data to memcache using the specified key
52
+ #
53
+ # ==== Parameters
54
+ # key<Sting>:: The key identifying the cache entry
55
+ # data<String>:: The data to be put in cache
56
+ # from_now<~minutes>::
57
+ # The number of minutes (from now) the cache should persist
58
+ def cache_set(key, data, from_now = nil)
59
+ true
60
+ end
61
+
62
+ # Fetch data from memcache using the specified key
63
+ # The entry is deleted if it has expired
64
+ #
65
+ # ==== Parameter
66
+ # key<Sting>:: The key identifying the cache entry
67
+ #
68
+ # ==== Returns
69
+ # data<String, NilClass>::
70
+ # nil is returned whether the entry expired or was not found
71
+ def cache_get(key)
72
+ nil
73
+ end
74
+
75
+ # Expire the cache entry identified by the given key
76
+ #
77
+ # ==== Parameter
78
+ # key<Sting>:: The key identifying the cache entry
79
+ def expire(key)
80
+ true
81
+ end
82
+
83
+ # Expire the cache entries matching the given key
84
+ #
85
+ # ==== Parameter
86
+ # key<Sting>:: The key matching the cache entries
87
+ #
88
+ # ==== Warning !
89
+ # This does not work in memcache.
90
+ def expire_match(key)
91
+ true
92
+ end
93
+
94
+ # Expire all the cache entries
95
+ def expire_all
96
+ true
97
+ end
98
+
99
+ # Gives info on the current cache store
100
+ #
101
+ # ==== Returns
102
+ # The type of the current cache store
103
+ def cache_store_type
104
+ "dummy"
105
+ end
106
+ end
@@ -0,0 +1,194 @@
1
+ require 'fileutils'
2
+ class Merb::Cache::FileStore
3
+ # Provides the file cache store for merb-cache
4
+
5
+ def initialize
6
+ @config = Merb::Controller._cache.config
7
+ @config[:cache_directory] ||= Merb.root_path("tmp/cache")
8
+ # @config[:cache_action_directory] ||= Merb.dir_for(:public) / "cache"
9
+ prepare
10
+ end
11
+
12
+ class NotAccessible < Exception #
13
+ def initialize(message)
14
+ super("Cache directories are not readable/writeable (#{message})")
15
+ end
16
+ end
17
+
18
+ # This method is there to ensure minimal requirements are met
19
+ # (directories are accessible, table exists, connected to server, ...)
20
+ def prepare
21
+ FileUtils.mkdir_p @config[:cache_directory]
22
+ unless File.readable?(@config[:cache_directory]) &&
23
+ File.writable?(@config[:cache_directory])
24
+ raise NotAccessible, @config[:cache_directory]
25
+ end
26
+ true
27
+ end
28
+
29
+ # Checks whether a cache entry exists
30
+ #
31
+ # ==== Parameter
32
+ # key<String>:: The key identifying the cache entry
33
+ #
34
+ # ==== Returns
35
+ # true if the cache entry exists, false otherwise
36
+ def cached?(key)
37
+ cache_file = @config[:cache_directory] / "#{key}.cache"
38
+ _data = _expire = nil
39
+ if File.file?(cache_file)
40
+ _data, _expire = Marshal.load(cache_read(cache_file))
41
+ return true if _expire.nil? || Time.now < _expire
42
+ FileUtils.rm_f(cache_file)
43
+ end
44
+ false
45
+ end
46
+
47
+ # Capture or restore the data in cache.
48
+ # If the cache entry expired or does not exist, the data are taken
49
+ # from the execution of the block, marshalled and stored in cache.
50
+ # Otherwise, the data are loaded from the cache and returned unmarshalled
51
+ #
52
+ # ==== Parameters
53
+ # _controller<Merb::Controller>:: The instance of the current controller
54
+ # key<String>:: The key identifying the cache entry
55
+ # from_now<~minutes>::
56
+ # The number of minutes (from now) the cache should persist
57
+ # &block:: The template to be used or not
58
+ #
59
+ # ==== Additional information
60
+ # When fetching data (the cache entry exists and has not expired)
61
+ # The data are loaded from the cache and returned unmarshalled.
62
+ # Otherwise:
63
+ # The controller is used to capture the rendered template (from the block).
64
+ # It uses the capture_#{engine} and concat_#{engine} methods to do so.
65
+ # The captured data are then marshalled and stored.
66
+ def cache(_controller, key, from_now = nil, &block)
67
+ cache_file = @config[:cache_directory] / "#{key}.cache"
68
+ _cache_hit = _data = _expire = nil
69
+
70
+ if File.file?(cache_file)
71
+ _data, _expire = Marshal.load(cache_read(cache_file))
72
+ _cache_hit = true if _expire.nil? || Time.now < _expire
73
+ end
74
+ unless _cache_hit
75
+ cache_directory = File.dirname(cache_file)
76
+ FileUtils.mkdir_p(cache_directory)
77
+ _expire = from_now ? from_now.minutes.from_now : nil
78
+ _data = _controller.send(:capture, &block)
79
+ cache_write(cache_file, Marshal.dump([_data, _expire]))
80
+ end
81
+ _controller.send(:concat, _data, block.binding)
82
+ true
83
+ end
84
+
85
+ # Store data to the file using the specified key
86
+ #
87
+ # ==== Parameters
88
+ # key<Sting>:: The key identifying the cache entry
89
+ # data<String>:: The data to be put in cache
90
+ # from_now<~minutes>::
91
+ # The number of minutes (from now) the cache should persist
92
+ def cache_set(key, data, from_now = nil)
93
+ cache_file = @config[:cache_directory] / "#{key}.cache"
94
+ cache_directory = File.dirname(cache_file)
95
+ FileUtils.mkdir_p(cache_directory)
96
+ _expire = from_now ? from_now.minutes.from_now : nil
97
+ cache_write(cache_file, Marshal.dump([data, _expire]))
98
+ Merb.logger.info("cache: set (#{key})")
99
+ true
100
+ end
101
+
102
+ # Fetch data from the file using the specified key
103
+ # The entry is deleted if it has expired
104
+ #
105
+ # ==== Parameter
106
+ # key<Sting>:: The key identifying the cache entry
107
+ #
108
+ # ==== Returns
109
+ # data<String, NilClass>::
110
+ # nil is returned whether the entry expired or was not found
111
+ def cache_get(key)
112
+ cache_file = @config[:cache_directory] / "#{key}.cache"
113
+ if File.file?(cache_file)
114
+ _data, _expire = Marshal.load(cache_read(cache_file))
115
+ if _expire.nil? || Time.now < _expire
116
+ Merb.logger.info("cache: hit (#{key})")
117
+ return _data
118
+ end
119
+ FileUtils.rm_f(cache_file)
120
+ end
121
+ Merb.logger.info("cache: miss (#{key})")
122
+ nil
123
+ end
124
+
125
+ # Expire the cache entry identified by the given key
126
+ #
127
+ # ==== Parameter
128
+ # key<Sting>:: The key identifying the cache entry
129
+ def expire(key)
130
+ FileUtils.rm_f(@config[:cache_directory] / "#{key}.cache")
131
+ Merb.logger.info("cache: expired (#{key})")
132
+ true
133
+ end
134
+
135
+ # Expire the cache entries matching the given key
136
+ #
137
+ # ==== Parameter
138
+ # key<Sting>:: The key matching the cache entries
139
+ def expire_match(key)
140
+ #files = Dir.glob(@config[:cache_directory] / "#{key}*.cache")
141
+ files = Dir.glob(@config[:cache_directory] / "#{key}*")
142
+ FileUtils.rm_rf(files)
143
+ Merb.logger.info("cache: expired matching (#{key})")
144
+ true
145
+ end
146
+
147
+ # Expire all the cache entries
148
+ def expire_all
149
+ FileUtils.rm_rf(Dir.glob("#{@config[:cache_directory]}/*"))
150
+ Merb.logger.info("cache: expired all")
151
+ true
152
+ end
153
+
154
+ # Gives info on the current cache store
155
+ #
156
+ # ==== Returns
157
+ # The type of the current cache store
158
+ def cache_store_type
159
+ "file"
160
+ end
161
+
162
+ private
163
+
164
+ # Read data from the file using exclusive lock
165
+ #
166
+ # ==== Parameters
167
+ # cache_file<String>:: The full path to the file
168
+ #
169
+ # ==== Returns
170
+ # _data<String>:: The data read from the file
171
+ def cache_read(cache_file)
172
+ _data = nil
173
+ File.open(cache_file, "r") do |cache_data|
174
+ cache_data.flock(File::LOCK_EX)
175
+ _data = cache_data.read
176
+ cache_data.flock(File::LOCK_UN)
177
+ end
178
+ _data
179
+ end
180
+
181
+ # Write data to the file using exclusive lock
182
+ #
183
+ # ==== Parameters
184
+ # cache_file<String>:: The full path to the file
185
+ # data<String>:: The data to be put in cache
186
+ def cache_write(cache_file, data)
187
+ File.open(cache_file, "w+") do |cache_data|
188
+ cache_data.flock(File::LOCK_EX)
189
+ cache_data.write(data)
190
+ cache_data.flock(File::LOCK_UN)
191
+ end
192
+ true
193
+ end
194
+ end
@@ -0,0 +1,199 @@
1
+ class Merb::Cache::MemcacheStore
2
+ # Provides the memcache cache store for merb-cache
3
+
4
+ def initialize
5
+ @config = Merb::Controller._cache.config
6
+ prepare
7
+ end
8
+
9
+ class NotReady < Exception
10
+ def initialize
11
+ super("Memcache server is not ready")
12
+ end
13
+ end
14
+
15
+ class NotDefined < Exception
16
+ def initialize
17
+ super("Memcache is not defined (require it in init.rb)")
18
+ end
19
+ end
20
+
21
+ # This method is there to ensure minimal requirements are met
22
+ # (directories are accessible, table exists, connected to server, ...)
23
+ def prepare
24
+ namespace = @config[:namespace] || 'merb-cache'
25
+ host = @config[:host] || '127.0.0.1:11211'
26
+ @memcache = MemCache.new(host, {:namespace => namespace})
27
+ @tracking_key = "_#{namespace}_keys" unless @config[:no_tracking] == true
28
+ raise NotReady unless @memcache.active?
29
+ true
30
+ rescue NameError
31
+ raise NotDefined
32
+ end
33
+
34
+ # Checks whether a cache entry exists
35
+ #
36
+ # ==== Parameter
37
+ # key<String>:: The key identifying the cache entry
38
+ #
39
+ # ==== Returns
40
+ # true if the cache entry exists, false otherwise
41
+ def cached?(key)
42
+ not cache_get(key).nil?
43
+ end
44
+
45
+ # Capture or restore the data in cache.
46
+ # If the cache entry expired or does not exist, the data are taken
47
+ # from the execution of the block, marshalled and stored in cache.
48
+ # Otherwise, the data are loaded from the cache and returned unmarshalled
49
+ #
50
+ # ==== Parameters
51
+ # _controller<Merb::Controller>:: The instance of the current controller
52
+ # key<String>:: The key identifying the cache entry
53
+ # from_now<~minutes>::
54
+ # The number of minutes (from now) the cache should persist
55
+ # &block:: The template to be used or not
56
+ #
57
+ # ==== Additional information
58
+ # When fetching data (the cache entry exists and has not expired)
59
+ # The data are loaded from the cache and returned unmarshalled.
60
+ # Otherwise:
61
+ # The controller is used to capture the rendered template (from the block).
62
+ # It uses the capture_#{engine} and concat_#{engine} methods to do so.
63
+ # The captured data are then marshalled and stored.
64
+ def cache(_controller, key, from_now = nil, &block)
65
+ _data = cache_get(key)
66
+ if _data.nil?
67
+ _data = _controller.send(:capture, &block)
68
+ cache_set(key, _data, from_now)
69
+ end
70
+ _controller.send(:concat, _data, block.binding)
71
+ true
72
+ end
73
+
74
+ # Store data to memcache using the specified key
75
+ #
76
+ # ==== Parameters
77
+ # key<Sting>:: The key identifying the cache entry
78
+ # data<String>:: The data to be put in cache
79
+ # from_now<~minutes>::
80
+ # The number of minutes (from now) the cache should persist
81
+ def cache_set(key, data, from_now = nil)
82
+ _expire = from_now ? from_now.minutes.from_now.to_i : 0
83
+ @memcache.set(key, data, _expire)
84
+ cache_start_tracking(key)
85
+ Merb.logger.info!("cache: set (#{key})")
86
+ true
87
+ end
88
+
89
+ # Fetch data from memcache using the specified key
90
+ # The entry is deleted if it has expired
91
+ #
92
+ # ==== Parameter
93
+ # key<Sting>:: The key identifying the cache entry
94
+ #
95
+ # ==== Returns
96
+ # data<String, NilClass>::
97
+ # nil is returned whether the entry expired or was not found
98
+ def cache_get(key)
99
+ data = @memcache.get(key)
100
+ if data.nil?
101
+ Merb.logger.info!("cache: miss (#{key})")
102
+ else
103
+ Merb.logger.debug!("cache: hit (#{key})")
104
+ end
105
+ data
106
+ end
107
+
108
+ # Expire the cache entry identified by the given key
109
+ #
110
+ # ==== Parameter
111
+ # key<Sting>:: The key identifying the cache entry
112
+ def expire(key)
113
+ @memcache.delete(key)
114
+ cache_stop_tracking(key)
115
+ Merb.logger.info!("cache: expired (#{key})")
116
+ true
117
+ end
118
+
119
+ # Expire the cache entries matching the given key
120
+ #
121
+ # ==== Parameter
122
+ # key<Sting>:: The key matching the cache entries
123
+ #
124
+ # ==== Additional info
125
+ # In memcache this requires to keep track of all keys (on by default).
126
+ # If you don't need this, set :no_tracking => true in the config.
127
+ def expire_match(key)
128
+ Merb.logger.debug!("cache: attempting to expire #{key}")
129
+ if @tracking_key
130
+ for _key in get_tracked_keys
131
+ expire(_key) if /#{key}/ =~ _key
132
+ end
133
+ else
134
+ Merb.logger.info("cache: expire_match is not supported with memcache (set :no_tracking => false in your config")
135
+ end
136
+ true
137
+ end
138
+
139
+ # Expire all the cache entries
140
+ def expire_all
141
+ @memcache.flush_all
142
+ stop_tracking_keys
143
+ Merb.logger.info("cache: expired all")
144
+ true
145
+ end
146
+
147
+ # Gives info on the current cache store
148
+ #
149
+ # ==== Returns
150
+ # The type of the current cache store
151
+ def cache_store_type
152
+ "memcache"
153
+ end
154
+
155
+ private
156
+
157
+ # Store the tracked keys in memcache (used by expire_match)
158
+ #
159
+ # ==== Parameter
160
+ # keys<Array[String]>:: The keys to keep track of
161
+ def set_tracked_keys(keys)
162
+ @memcache.set(@tracking_key, keys)
163
+ end
164
+
165
+ # Retrieve tracked keys from memcache
166
+ #
167
+ # ==== Returns
168
+ # keys<Array[String]>:: The tracked keys
169
+ def get_tracked_keys
170
+ @memcache.get(@tracking_key) || []
171
+ end
172
+
173
+ # Remove all tracked keys
174
+ def stop_tracking_keys
175
+ @memcache.delete(@tracking_key) if @tracking_key
176
+ end
177
+
178
+ # Add a key in the array of tracked keys (used by expire_match)
179
+ #
180
+ # ==== Parameter
181
+ # key<String>:: the key to add
182
+ def cache_start_tracking(key)
183
+ return unless @tracking_key
184
+ keys = get_tracked_keys
185
+ keys.push(key)
186
+ set_tracked_keys(keys)
187
+ end
188
+
189
+ # Remove a key from the array of tracked keys (used by expire_match)
190
+ #
191
+ # ==== Parameter
192
+ # key<String>:: the key to remove
193
+ def cache_stop_tracking(key)
194
+ return unless @tracking_key
195
+ keys = get_tracked_keys
196
+ keys.delete(key)
197
+ set_tracked_keys(keys)
198
+ end
199
+ end