thorero-cache 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,203 @@
1
+ class Merb::Cache
2
+ cattr_accessor :cached_pages
3
+ self.cached_pages = {}
4
+ end
5
+
6
+ module Merb::Cache::ControllerClassMethods
7
+ # Mixed in Merb::Controller. Provides class methods related to page caching
8
+ # Page caching is mostly action caching with file backend using its own output directory of .html files
9
+
10
+ # Register the action for page caching
11
+ #
12
+ # ==== Parameters
13
+ # action<Symbol>:: The name of the action to register
14
+ # from_now<~minutes>::
15
+ # The number of minutes (from now) the cache should persist
16
+ #
17
+ # ==== Examples
18
+ # cache_page :mostly_static
19
+ # cache_page :barely_dynamic, 10
20
+ def cache_page(action, from_now = nil)
21
+ cache_pages([action, from_now])
22
+ end
23
+
24
+ # Register actions for page caching (before and after filters)
25
+ #
26
+ # ==== Parameter
27
+ # pages<Symbol,Array[Symbol,~minutes]>:: See #cache_page
28
+ #
29
+ # ==== Example
30
+ # cache_pages :mostly_static, [:barely_dynamic, 10]
31
+ def cache_pages(*pages)
32
+ if pages.any? && !Merb::Cache.cached_pages.key?(controller_name)
33
+ before(:cache_page_before)
34
+ after(:cache_page_after)
35
+ end
36
+ pages.each do |action, from_now|
37
+ _pages = Merb::Cache.cached_pages[controller_name] ||= {}
38
+ _pages[action] = [from_now, 0]
39
+ end
40
+ true
41
+ end
42
+ end
43
+
44
+ module Merb::Cache::ControllerInstanceMethods
45
+ # Mixed in Merb::Controller. Provides methods related to page caching
46
+
47
+ DEFAULT_PAGE_EXTENSION = 'html'
48
+
49
+ # Checks whether a cache entry exists
50
+ #
51
+ # ==== Parameter
52
+ # options<String,Hash>:: The options that will be passed to #key_for
53
+ #
54
+ # ==== Returns
55
+ # true if the cache entry exists, false otherwise
56
+ #
57
+ # ==== Example
58
+ # cached_page?(:action => 'show', :params => [params[:page]])
59
+ # cached_page?(:action => 'show', :extension => 'js')
60
+ def cached_page?(options)
61
+ key = Merb::Controller._cache.key_for(options, controller_name, true)
62
+ extension = options[:extension] || DEFAULT_PAGE_EXTENSION
63
+ File.file?(Merb::Controller._cache.config[:cache_html_directory] / "#{key}.#{extension}")
64
+ end
65
+
66
+ # Expires the page identified by the key computed after the parameters
67
+ #
68
+ # ==== Parameter
69
+ # options<String,Hash>:: The options that will be passed to #expire_key_for
70
+ #
71
+ # ==== Examples (See Merb::Cache#expire_key_for for more options)
72
+ # # will expire path/to/page/cache/news/show/1.html
73
+ # expire_page(:key => url(:news,News.find(1)))
74
+ #
75
+ # # will expire path/to/page/cache/news/show.html
76
+ # expire_page(:action => 'show', :controller => 'news')
77
+ #
78
+ # # will expire path/to/page/cache/news/show*
79
+ # expire_page(:action => 'show', :match => true)
80
+ #
81
+ # # will expire path/to/page/cache/news/show.js
82
+ # expire_page(:action => 'show', :extension => 'js')
83
+ def expire_page(options)
84
+ config_dir = Merb::Controller._cache.config[:cache_html_directory]
85
+ Merb::Controller._cache.expire_key_for(options, controller_name, true) do |key, match|
86
+ if match
87
+ files = Dir.glob(config_dir / "#{key}*")
88
+ else
89
+ extension = options[:extension] || DEFAULT_PAGE_EXTENSION
90
+ files = config_dir / "#{key}.#{extension}"
91
+ end
92
+ FileUtils.rm_rf(files)
93
+ end
94
+ true
95
+ end
96
+
97
+ # Expires all the pages stored in config[:cache_html_directory]
98
+ def expire_all_pages
99
+ FileUtils.rm_rf(Dir.glob(Merb::Controller._cache.config[:cache_html_directory] / "*"))
100
+ end
101
+
102
+ # You can call this method if you need to prevent caching the page
103
+ # after it has been rendered.
104
+ def abort_cache_page
105
+ @capture_page = false
106
+ end
107
+
108
+ private
109
+
110
+ # Called by the before and after filters. Stores or recalls a cache entry.
111
+ # The name used for the cache file is based on request.path
112
+ # If the name ends with "/" then it is removed
113
+ # If the name is "/" then it will be replaced by "index"
114
+ #
115
+ # ==== Parameters
116
+ # data<String>:: the data to put in cache
117
+ #
118
+ # ==== Examples
119
+ # All the file are written to config[:cache_html_directory]
120
+ # If request.path is "/", the name will be "/index.html"
121
+ # If request.path is "/news/show/1", the name will be "/news/show/1.html"
122
+ # If request.path is "/news/show/", the name will be "/news/show.html"
123
+ # If request.path is "/news/styles.css", the name will be "/news/styles.css"
124
+ def _cache_page(data = nil)
125
+ return if Merb::Controller._cache.config[:disable_page_caching]
126
+ controller = controller_name
127
+ action = action_name.to_sym
128
+ pages = Merb::Controller._cache.cached_pages[controller]
129
+ return unless pages && pages.key?(action)
130
+ path = request.path.chomp("/")
131
+ path = "index" if path.empty?
132
+ no_format = params[:format].nil? || params[:format].empty?
133
+ ext = "." + (no_format ? DEFAULT_PAGE_EXTENSION : params[:format])
134
+ ext = nil if File.extname(path) == ext
135
+ cache_file = Merb::Controller._cache.config[:cache_html_directory] / "#{path}#{ext}"
136
+ if data
137
+ cache_directory = File.dirname(cache_file)
138
+ FileUtils.mkdir_p(cache_directory)
139
+ _expire_in = pages[action][0]
140
+ pages[action][1] = _expire_in.minutes.from_now unless _expire_in.nil?
141
+ cache_write_page(cache_file, data)
142
+ Merb.logger.info("cache: set (#{path})")
143
+ else
144
+ @capture_page = false
145
+ if File.file?(cache_file)
146
+ _data = cache_read_page(cache_file)
147
+ _expire_in, _expire_at = pages[action]
148
+ if _expire_in.nil? || Time.now.to_i < _expire_at.to_i
149
+ Merb.logger.info("cache: hit (#{path})")
150
+ throw(:halt, _data)
151
+ end
152
+ FileUtils.rm_f(cache_file)
153
+ end
154
+ @capture_page = true
155
+ end
156
+ true
157
+ end
158
+
159
+ # Read data from a file using exclusive lock
160
+ #
161
+ # ==== Parameters
162
+ # cache_file<String>:: the full path to the file
163
+ #
164
+ # ==== Returns
165
+ # data<String>:: the data that has been read from the file
166
+ def cache_read_page(cache_file)
167
+ _data = nil
168
+ File.open(cache_file, "r") do |cache_data|
169
+ cache_data.flock(File::LOCK_EX)
170
+ _data = cache_data.read
171
+ cache_data.flock(File::LOCK_UN)
172
+ end
173
+ _data
174
+ end
175
+
176
+ # Write data to a file using exclusive lock
177
+ #
178
+ # ==== Parameters
179
+ # cache_file<String>:: the full path to the file
180
+ # data<String>:: the data that will be written to the file
181
+ def cache_write_page(cache_file, data)
182
+ File.open(cache_file, "w+") do |cache_data|
183
+ cache_data.flock(File::LOCK_EX)
184
+ cache_data.write(data)
185
+ cache_data.flock(File::LOCK_UN)
186
+ end
187
+ true
188
+ end
189
+
190
+ # before filter
191
+ def cache_page_before
192
+ # recalls a cached entry or set @capture_page to true in order
193
+ # to grab the response in the after filter
194
+ _cache_page
195
+ end
196
+
197
+ # after filter
198
+ def cache_page_after
199
+ # takes the body of the response
200
+ # if the cache entry expired, if it doesn't exist or status is 200
201
+ _cache_page(body) if @capture_page && status == 200
202
+ end
203
+ end
@@ -0,0 +1,88 @@
1
+ module Merb::Cache::DatabaseStore::ActiveRecord
2
+ # Module that provides ActiveRecord support for the database backend
3
+
4
+ # The cache model migration
5
+ class CacheMigration < ActiveRecord::Migration
6
+ def self.up
7
+ create_table (Merb::Controller._cache.config[:table_name]), :primary_key => :ckey do |t|
8
+ t.column :ckey, :string
9
+ t.column :data, :text
10
+ t.datetime :expire, :default => nil
11
+ end
12
+ end
13
+
14
+ def self.down
15
+ drop_table Merb::Controller._cache.config[:table_name]
16
+ end
17
+ end
18
+
19
+ # The cache model
20
+ class CacheModel < ActiveRecord::Base
21
+ set_table_name Merb::Controller._cache.config[:table_name]
22
+
23
+ # Fetch data from the database using the specified key
24
+ # The entry is deleted if it has expired
25
+ #
26
+ # ==== Parameter
27
+ # key<Sting>:: The key identifying the cache entry
28
+ #
29
+ # ==== Returns
30
+ # data<String, NilClass>::
31
+ # nil is returned whether the entry expired or was not found
32
+ def self.cache_get(key)
33
+ if entry = self.find(:first, :conditions => ["ckey=?", key])
34
+ return entry.data if entry.expire.nil? || Time.now < entry.expire
35
+ self.expire(key)
36
+ end
37
+ nil
38
+ end
39
+
40
+ # Store data to the database using the specified key
41
+ #
42
+ # ==== Parameters
43
+ # key<Sting>:: The key identifying the cache entry
44
+ # data<String>:: The data to be put in cache
45
+ # expire<~minutes>::
46
+ # The number of minutes (from now) the cache should persist
47
+ # get<Boolean>::
48
+ # used internally to behave like this
49
+ # - when set to true, perform update_or_create on the cache entry
50
+ # - when set to false, force creation of the cache entry
51
+ def self.cache_set(key, data, expire = nil, get = true)
52
+ attributes = {:ckey => key, :data => data, :expire => expire }
53
+ if get
54
+ entry = self.find(:first, :conditions => ["ckey=?",key])
55
+ entry.nil? ? self.create(attributes) : entry.update_attributes(attributes)
56
+ else
57
+ self.create(attributes)
58
+ end
59
+ true
60
+ end
61
+
62
+ # Expire the cache entry identified by the given key
63
+ #
64
+ # ==== Parameter
65
+ # key<Sting>:: The key identifying the cache entry
66
+ def self.expire(key)
67
+ self.delete_all(["ckey=?", key])
68
+ end
69
+
70
+ # Expire the cache entries matching the given key
71
+ #
72
+ # ==== Parameter
73
+ # key<Sting>:: The key matching the cache entries
74
+ def self.expire_match(key)
75
+ self.delete_all(["ckey like ?", key + "%"])
76
+ end
77
+
78
+ # Expire all the cache entries
79
+ def self.expire_all
80
+ self.delete_all
81
+ end
82
+
83
+ # Perform auto-migration in case the table is unknown in the database
84
+ def self.check_table
85
+ CacheMigration.up unless self.table_exists?
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,79 @@
1
+ module Merb::Cache::DatabaseStore::DataMapper
2
+ # Module that provides DataMapper support for the database backend
3
+
4
+ # The cache model
5
+ class CacheModel < DataMapper::Base
6
+ set_table_name Merb::Controller._cache.config[:table_name]
7
+ property :ckey, :string, :length => 255, :lazy => false, :key => true
8
+ property :data, :text, :lazy => false
9
+ property :expire, :datetime, :default => nil
10
+
11
+ # Fetch data from the database using the specified key
12
+ # The entry is deleted if it has expired
13
+ #
14
+ # ==== Parameter
15
+ # key<Sting>:: The key identifying the cache entry
16
+ #
17
+ # ==== Returns
18
+ # data<String, NilClass>::
19
+ # nil is returned whether the entry expired or was not found
20
+ def self.cache_get(key)
21
+ if entry = self.first(key)
22
+ return entry.data if entry.expire.nil? || DateTime.now < entry.expire
23
+ entry.destroy!
24
+ end
25
+ nil
26
+ end
27
+
28
+ # Store data to the database using the specified key
29
+ #
30
+ # ==== Parameters
31
+ # key<Sting>:: The key identifying the cache entry
32
+ # data<String>:: The data to be put in cache
33
+ # expire<~minutes>::
34
+ # The number of minutes (from now) the cache should persist
35
+ # get<Boolean>::
36
+ # used internally to behave like this:
37
+ # - when set to true, perform update_or_create on the cache entry
38
+ # - when set to false, force creation of the cache entry
39
+ def self.cache_set(key, data, expire = nil, get = true)
40
+ attributes = {:ckey => key, :data => data,
41
+ :expire => expire.nil? ? nil : expire.to_s_db }
42
+ if get
43
+ entry = self.first(key)
44
+ entry.nil? ? self.create(attributes) : entry.update_attributes(attributes)
45
+ else
46
+ self.create(attributes)
47
+ end
48
+ true
49
+ end
50
+
51
+ # Expire the cache entry identified by the given key
52
+ #
53
+ # ==== Parameter
54
+ # key<Sting>:: The key identifying the cache entry
55
+ def self.expire(key)
56
+ entry = self.first(key)
57
+ entry.destroy! unless entry.nil?
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
+ #FIXME
66
+ database.execute("DELETE FROM #{self.table.name} WHERE ckey LIKE '#{key}%'")
67
+ end
68
+
69
+ # Expire all the cache entries
70
+ def self.expire_all
71
+ self.truncate!
72
+ end
73
+
74
+ # Perform auto-migration in case the table is unknown in the database
75
+ def self.check_table
76
+ self.auto_migrate! unless self.table.exists?
77
+ end
78
+ end
79
+ end
@@ -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