bchiu-merb_cache_more 0.9.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ module Merb::Cache::DatabaseStore::Sequel
2
+ # Module that provides Sequel support for the database backend
3
+
4
+ # The cache model
5
+ class CacheModel < Sequel::Model(Merb::Controller._cache.config[:table_name].to_sym)
6
+ set_schema do
7
+ primary_key :id
8
+ varchar :ckey, :index => true
9
+ varchar :data
10
+ timestamp :expire, :default => nil
11
+ end
12
+
13
+ # Fetch data from the database using the specified key
14
+ # The entry is deleted if it has expired
15
+ #
16
+ # ==== Parameter
17
+ # key<Sting>:: The key identifying the cache entry
18
+ #
19
+ # ==== Returns
20
+ # data<String, NilClass>::
21
+ # nil is returned whether the entry expired or was not found
22
+ def self.cache_get(key)
23
+ if entry = self.filter(:ckey => key).single_record(:limit => 1)
24
+ return entry.data if entry.expire.nil? || Time.now < entry.expire
25
+ self.expire(key)
26
+ end
27
+ nil
28
+ end
29
+
30
+ # Store data to the database using the specified key
31
+ #
32
+ # ==== Parameters
33
+ # key<Sting>:: The key identifying the cache entry
34
+ # data<String>:: The data to be put in cache
35
+ # expire<~minutes>::
36
+ # The number of minutes (from now) the cache should persist
37
+ # get<Boolean>::
38
+ # used internally to behave like this
39
+ # - when set to true, perform update_or_create on the cache entry
40
+ # - when set to false, force creation of the cache entry
41
+ def self.cache_set(key, data, expire = nil, get = true)
42
+ attributes = {:ckey => key, :data => data, :expire => expire }
43
+ if get
44
+ entry = self.filter(:ckey => key).single_record(:limit => 1)
45
+ entry.nil? ? self.create(attributes) : entry.set(attributes)
46
+ else
47
+ self.create(attributes)
48
+ end
49
+ true
50
+ end
51
+
52
+ # Expire the cache entry identified by the given key
53
+ #
54
+ # ==== Parameter
55
+ # key<Sting>:: The key identifying the cache entry
56
+ def self.expire(key)
57
+ self.filter(:ckey => key).delete
58
+ end
59
+
60
+ # Expire the cache entries matching the given key
61
+ #
62
+ # ==== Parameter
63
+ # key<Sting>:: The key matching the cache entries
64
+ def self.expire_match(key)
65
+ self.filter{:ckey.like key + "%"}.delete
66
+ end
67
+
68
+ # Expire all the cache entries
69
+ def self.expire_all
70
+ self.delete_all
71
+ end
72
+
73
+ # Perform auto-migration in case the table is unknown in the database
74
+ def self.check_table
75
+ self.create_table unless self.table_exists?
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,144 @@
1
+ class Merb::Cache::DatabaseStore
2
+ # Provides the database cache store for merb_cache_more
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_more/cache-store/database-activerecord.rb"
20
+ include Merb::Cache::DatabaseStore::ActiveRecord
21
+ elsif defined?(Merb::Orms::DataMapper)
22
+ require "merb_cache_more/cache-store/database-datamapper.rb"
23
+ include Merb::Cache::DatabaseStore::DataMapper
24
+ elsif defined?(Merb::Orms::Sequel)
25
+ require "merb_cache_more/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_more
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_more
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