merb-cache 0.9.7 → 0.9.8
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.
- data/LICENSE +2 -2
- data/README +207 -143
- data/Rakefile +55 -10
- data/TODO +0 -2
- data/lib/merb-cache/cache.rb +84 -0
- data/lib/merb-cache/core_ext/enumerable.rb +9 -0
- data/lib/merb-cache/core_ext/hash.rb +20 -0
- data/lib/merb-cache/merb_ext/controller.rb +167 -0
- data/lib/merb-cache/stores/fundamental/abstract_store.rb +101 -0
- data/lib/merb-cache/stores/fundamental/file_store.rb +112 -0
- data/lib/merb-cache/stores/fundamental/memcached_store.rb +112 -0
- data/lib/merb-cache/stores/strategy/abstract_strategy_store.rb +123 -0
- data/lib/merb-cache/stores/strategy/action_store.rb +56 -0
- data/lib/merb-cache/stores/strategy/adhoc_store.rb +69 -0
- data/lib/merb-cache/stores/strategy/gzip_store.rb +63 -0
- data/lib/merb-cache/stores/strategy/page_store.rb +64 -0
- data/lib/merb-cache/stores/strategy/sha1_store.rb +62 -0
- data/lib/merb-cache.rb +8 -7
- data/spec/merb-cache/cache_spec.rb +88 -0
- data/spec/merb-cache/core_ext/enumerable_spec.rb +22 -0
- data/spec/merb-cache/core_ext/hash_spec.rb +20 -0
- data/spec/merb-cache/merb_ext/controller_spec.rb +284 -0
- data/spec/merb-cache/stores/fundamental/abstract_store_spec.rb +166 -0
- data/spec/merb-cache/stores/fundamental/file_store_spec.rb +186 -0
- data/spec/merb-cache/stores/fundamental/memcached_store_spec.rb +243 -0
- data/spec/merb-cache/stores/strategy/abstract_strategy_store_spec.rb +78 -0
- data/spec/merb-cache/stores/strategy/action_store_spec.rb +189 -0
- data/spec/merb-cache/stores/strategy/adhoc_store_spec.rb +225 -0
- data/spec/merb-cache/stores/strategy/gzip_store_spec.rb +51 -0
- data/spec/merb-cache/stores/strategy/page_store_spec.rb +111 -0
- data/spec/merb-cache/stores/strategy/sha1_store_spec.rb +75 -0
- data/spec/spec_helper.rb +69 -72
- metadata +42 -31
- data/lib/merb-cache/cache-action.rb +0 -144
- data/lib/merb-cache/cache-fragment.rb +0 -95
- data/lib/merb-cache/cache-page.rb +0 -203
- data/lib/merb-cache/cache-store/database-activerecord.rb +0 -88
- data/lib/merb-cache/cache-store/database-datamapper.rb +0 -79
- data/lib/merb-cache/cache-store/database-sequel.rb +0 -78
- data/lib/merb-cache/cache-store/database.rb +0 -144
- data/lib/merb-cache/cache-store/dummy.rb +0 -106
- data/lib/merb-cache/cache-store/file.rb +0 -194
- data/lib/merb-cache/cache-store/memcache.rb +0 -199
- data/lib/merb-cache/cache-store/memory.rb +0 -168
- data/lib/merb-cache/merb-cache.rb +0 -165
- data/lib/merb-cache/merbtasks.rb +0 -6
- data/spec/config/database.yml +0 -14
- data/spec/controller.rb +0 -101
- data/spec/log/merb_test.log +0 -433
- data/spec/merb-cache-action_spec.rb +0 -162
- data/spec/merb-cache-fragment_spec.rb +0 -100
- data/spec/merb-cache-page_spec.rb +0 -150
- data/spec/merb-cache_spec.rb +0 -15
- data/spec/views/cache_controller/action1.html.erb +0 -4
- data/spec/views/cache_controller/action2.html.haml +0 -4
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2008
|
1
|
+
Copyright (c) 2008 Ben Burkert
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
CHANGED
@@ -1,160 +1,224 @@
|
|
1
|
-
|
1
|
+
merb-cache
|
2
|
+
==========
|
2
3
|
|
3
|
-
A plugin for the Merb framework that provides caching
|
4
|
+
A plugin for the Merb framework that provides caching stores,
|
5
|
+
strategies and helpers.
|
4
6
|
|
5
|
-
Currently supported methods:
|
6
7
|
|
7
|
-
- page caching:
|
8
|
-
- action caching
|
9
|
-
- fragment caching
|
10
|
-
- object caching
|
11
8
|
|
12
|
-
|
9
|
+
Tutorial
|
10
|
+
==========
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
Stores usually set up in application init file
|
13
|
+
(init.rb) or environment specific init file (so you can
|
14
|
+
use different stores for production, staging and development
|
15
|
+
environment if you need to).
|
18
16
|
|
19
|
-
|
20
|
-
With fragment caching, you can mix dynamic and static content.
|
17
|
+
# create a fundamental memcache store named :memcached for localhost
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
With page caching, the whole template is put in html files in a special
|
26
|
-
directory in order to be handled directly without triggering Merb.
|
27
|
-
|
28
|
-
== Quick API
|
29
|
-
|
30
|
-
=== Merb::Controller class methods
|
31
|
-
cache_action(action, expiration)
|
32
|
-
cache_actions(action, [action, expiration], ...)
|
33
|
-
cache_page(action, expiration)
|
34
|
-
cache_pages(action, [action, expiration], ...)
|
35
|
-
|
36
|
-
=== Merb::Controller instance methods
|
37
|
-
expire_page(key)
|
38
|
-
cached_page?(key)
|
39
|
-
expire_all_pages()
|
40
|
-
|
41
|
-
expire_action(key)
|
42
|
-
cached_action?(key)
|
43
|
-
|
44
|
-
cached?(key)
|
45
|
-
cache_get(key)
|
46
|
-
cache_set(key, data, expiration)
|
47
|
-
expire(key)
|
48
|
-
expire_all()
|
49
|
-
|
50
|
-
=== Inside your template
|
51
|
-
cache(key, expiration) do ... end
|
52
|
-
|
53
|
-
# expiration is given in minutes
|
54
|
-
|
55
|
-
# key can be a string or a hash
|
56
|
-
# possible keys when it's a hash:
|
57
|
-
# :key (full key)
|
58
|
-
# :params (array of params to be added to the key)
|
59
|
-
# :action, :controller
|
60
|
-
# :match (true or partial key)
|
61
|
-
|
62
|
-
# Don't forget to look at the specs !!
|
63
|
-
|
64
|
-
== Specs
|
65
|
-
$ rake specs:<cache_store>
|
66
|
-
example:
|
67
|
-
$ rake specs:memory
|
68
|
-
$ rake specs:file
|
69
|
-
or just:
|
70
|
-
$ cd spec
|
71
|
-
$ STORE=<cache_store> spec merb-cache_spec.rb
|
72
|
-
# cache_store can be:
|
73
|
-
# memory, memcache, file, sequel, datamapper, activerecord
|
74
|
-
|
75
|
-
== Sample configuration
|
76
|
-
|
77
|
-
Merb::Plugins.config[:merb_cache] = {
|
78
|
-
:cache_html_directory => Merb.dir_for(:public) / "cache",
|
79
|
-
|
80
|
-
#:store => "database",
|
81
|
-
#:table_name => "merb_cache",
|
82
|
-
|
83
|
-
#:disable => "development", # disable merb-cache in development
|
84
|
-
#:disable => true, # disable merb-cache in all environments
|
85
|
-
|
86
|
-
:store => "file",
|
87
|
-
:cache_directory => Merb.root_path("tmp/cache"),
|
88
|
-
|
89
|
-
#:store => "memcache",
|
90
|
-
#:host => "127.0.0.1:11211",
|
91
|
-
#:namespace => "merb_cache",
|
92
|
-
#:no_tracking => false,
|
93
|
-
|
94
|
-
#:store => "memory",
|
95
|
-
# store could be: file, memcache, memory, database, dummy, ...
|
96
|
-
}
|
97
|
-
|
98
|
-
|
99
|
-
== Quick Example
|
100
|
-
|
101
|
-
==== controller part
|
102
|
-
class Users < Merb::Controller
|
103
|
-
cache_page :action_name
|
104
|
-
# this will cache the action in public/cache/something.html
|
105
|
-
# this cache entry will never expire (no expiration provided)
|
106
|
-
# for permanent caching you could set your lighty/nginx so as to handle
|
107
|
-
# the .html file directly
|
108
|
-
# for multiple page caching:
|
109
|
-
# cache_pages :action_name, [:another_action, 5], :some_action
|
110
|
-
|
111
|
-
cache_action :another_action, 10
|
112
|
-
# this will cache the action using the cache store
|
113
|
-
# this cache entry will expire in 10 minutes
|
114
|
-
# for multiple action caching:
|
115
|
-
# cache_actions :action_name, [:another_action, 5], :some_action
|
116
|
-
|
117
|
-
def list
|
118
|
-
unless @users = cache_get("active_users")
|
119
|
-
@users = User.all(:active => true)
|
120
|
-
cache_set("active_users", @users)
|
121
|
-
# object caching can be used to avoid pulling huge amounts of data
|
122
|
-
# from the database.
|
123
|
-
# you could have calle cache_set with an expiration time as well:
|
124
|
-
# cache_set("active_users", @users, 10)
|
125
|
-
end
|
126
|
-
render
|
127
|
-
end
|
19
|
+
Merb::Cache.setup do
|
20
|
+
register(:memcached, MemcachedStore, :namespace => "my_app", :servers => ["127.0.0.1:11211"])
|
21
|
+
end
|
128
22
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
end
|
23
|
+
# a default FileStore
|
24
|
+
Merb::Cache.setup do
|
25
|
+
register(FileStore)
|
26
|
+
end
|
134
27
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
28
|
+
# another FileStore
|
29
|
+
Merb::Cache.setup do
|
30
|
+
register(:tmp_cache, FileStore, :dir => "/tmp")
|
31
|
+
end
|
32
|
+
|
33
|
+
Now lets see how we can use stores in the application:
|
139
34
|
|
140
|
-
|
141
|
-
|
142
|
-
|
35
|
+
class Tag
|
36
|
+
def find(parameters = {})
|
37
|
+
# poor man's identity map
|
38
|
+
|
39
|
+
if Merb::Cache[:memcached].exists?("tags", parameters)
|
40
|
+
Merb::Cache[:memcached].read("tags", parameters)
|
41
|
+
else
|
42
|
+
results = super(parameters)
|
43
|
+
Merb::Cache[:memcached].write("tags", results, parameters)
|
44
|
+
|
45
|
+
results
|
143
46
|
end
|
47
|
+
end
|
144
48
|
|
145
|
-
|
146
|
-
|
49
|
+
def popularity_rating
|
50
|
+
# lets keep the popularity rating cached for 30 seconds
|
51
|
+
# merb-cache will create a key from the model's id & the interval parameter
|
52
|
+
|
53
|
+
Merb::Cache[:memcached].fetch(self.id, :interval => Time.now.to_i / 30) do
|
54
|
+
self.run_long_popularity_rating_query
|
147
55
|
end
|
148
56
|
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
Or, if you want to use memcache’s built in expire option:
|
61
|
+
|
62
|
+
# expire a cache entry for "bar" (identified by the key "foo" and
|
63
|
+
# parameters {:baz => :bay}) in two hours
|
64
|
+
Merb::Cache[:memcached].write("foo", "bar", {:baz => :bay}, :expire_in => 2.hours)
|
65
|
+
|
66
|
+
# this will fail, because FileStore cannot expire cache entries
|
67
|
+
Merb::Cache[:default].write("foo", "bar", {:baz => :bay}, :expire_in => 2.hours)
|
68
|
+
|
69
|
+
# writing to the FileStore will fail, but the MemcachedStore will succeed
|
70
|
+
Merb::Cache[:default, :memcached].write("foo", "bar", {:baz => :bay}, :expire_in => 2.hours)
|
149
71
|
|
72
|
+
# this will fail
|
73
|
+
Merb::Cache[:default, :memcached].write_all("foo", "bar", {:baz => :bay}, :expire_in => 2.hours)
|
150
74
|
|
151
|
-
====views/users/index.html.erb
|
152
|
-
# this entry will expire in 10 minutes
|
153
|
-
<%- cache "users_index", 10 do %>
|
154
|
-
<div>some big template</div>
|
155
|
-
<% end -%>
|
156
75
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
76
|
+
Setting up strategy stores is very similar to fundamental stores:
|
77
|
+
|
78
|
+
Merb::Cache.setup do
|
79
|
+
|
80
|
+
# wraps the :memcached store we setup earlier
|
81
|
+
register(:zipped, GzipStore[:memcached])
|
82
|
+
|
83
|
+
# wrap a strategy store
|
84
|
+
register(:sha_and_zip, SHA1Store[:zipped])
|
85
|
+
|
86
|
+
# you can even use unnamed fundamental stores
|
87
|
+
register(:zipped_images, GzipStore[FileStore],
|
88
|
+
:dir => Merb.root / "public" / "images")
|
89
|
+
|
90
|
+
# or a combination or strategy & fundamental stores
|
91
|
+
register(:secured, SHA1Store[GzipStore[FileStore], FileStore],
|
92
|
+
:dir => Merb.root / "private")
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
You can use these strategy stores exactly like fundamental stores in your app code.
|
97
|
+
|
98
|
+
Action & Page Caching
|
99
|
+
Action & page caching have been implemented in strategy stores. So instead of manually specifying which type of caching you want for each action, you simply ask merb-cache to cache your action, and it will use the fastest cache available.
|
100
|
+
|
101
|
+
First, let’s setup our page & action stores:
|
102
|
+
|
103
|
+
config/environments/development.rb
|
104
|
+
|
105
|
+
Merb::Cache.setup do
|
106
|
+
|
107
|
+
# the order that stores are setup is important
|
108
|
+
# faster stores should be setup first
|
109
|
+
|
110
|
+
# page cache to the public dir
|
111
|
+
register(:page_store, PageStore[FileStore],
|
112
|
+
:dir => Merb.root / "public")
|
113
|
+
|
114
|
+
# action cache to memcache
|
115
|
+
register(:action_store, ActionStore[:sha_and_zip])
|
116
|
+
|
117
|
+
# sets up the ordering of stores when attempting to read/write cache entries
|
118
|
+
register(:default, AdhocStore[:page_store, :action_store])
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
And now in our controller:
|
123
|
+
class Tags < Merb::Controller
|
124
|
+
|
125
|
+
# index & show will be page cached to the public dir. The index
|
126
|
+
# action has no parameters, and the show parameter's are part of
|
127
|
+
# the URL, making them both page-cache'able
|
128
|
+
cache :index, :show
|
129
|
+
|
130
|
+
def index
|
131
|
+
render
|
132
|
+
end
|
133
|
+
|
134
|
+
def show(:slug)
|
135
|
+
display Tag.first(:slug => slug)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
Our controller now page caches but the index & show action. Furthermore,
|
140
|
+
the show action is cached separately for each slug parameter automatically.
|
141
|
+
|
142
|
+
|
143
|
+
class Tags < Merb::Controller
|
144
|
+
|
145
|
+
# the term is a route param, while the page & per_page params are part of the query string.
|
146
|
+
# If only the term param is supplied, the request can be page cached, but if the page and/or
|
147
|
+
# per_page param is part of the query string, the request will action cache.
|
148
|
+
cache :catalog
|
149
|
+
|
150
|
+
def catalog(term = 'a', page = 1, per_page = 20)
|
151
|
+
@tags = Tag.for_term(term).paginate(page, per_page)
|
152
|
+
|
153
|
+
display @tags
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
Because the specific type of caching is not specified, the same action can either
|
158
|
+
be page cached or action cached depending on the context of the request.
|
159
|
+
|
160
|
+
|
161
|
+
Keeping a “Hot” Cache
|
162
|
+
=====================
|
163
|
+
|
164
|
+
Cache expiration is a constant problem for developers. When should content
|
165
|
+
be expired? Should we “sweep” stale content? How do we balance serving fresh
|
166
|
+
content and maintaining fast response times? These are difficult questions
|
167
|
+
for developers, and are usually answered with ugly code added across our
|
168
|
+
models, views, and controllers. Instead of designing an elaborate
|
169
|
+
caching and expiring system, an alternate approach is to keep a “hot” cache.
|
170
|
+
|
171
|
+
So what is a “hot” cache? A hot cache is what you get when you ignore
|
172
|
+
trying to manually expire content, and instead focus on replacing old
|
173
|
+
content with fresh data as soon as it becomes stale. Keeping a hot
|
174
|
+
cache means no difficult expiration logic spread out across your
|
175
|
+
app, and will all but eliminate cache misses.
|
176
|
+
|
177
|
+
The problem until now with this approach has been the impact on
|
178
|
+
response times. If the request has to wait on any pages that
|
179
|
+
it has made stale to render the fresh version, it can slow down
|
180
|
+
the response time dramatically. Thankfully, Merb has the run_later
|
181
|
+
method which allows the fresh content to render after the
|
182
|
+
response has been sent to the browser.
|
183
|
+
It’s the best of both worlds. Here’s an example.
|
184
|
+
|
185
|
+
|
186
|
+
class Tags < Merb::Controller
|
187
|
+
|
188
|
+
cache :index
|
189
|
+
eager_cache :create, :index
|
190
|
+
|
191
|
+
def index
|
192
|
+
display Tag.all
|
193
|
+
end
|
194
|
+
|
195
|
+
def create(slug)
|
196
|
+
@tag = Tag.new(slug)
|
197
|
+
|
198
|
+
# redirect them back to the index action
|
199
|
+
redirect url(:tags)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
The controller will eager_cache the index action whenever the create action
|
204
|
+
is successfully called. If the client were to post a new tag to the
|
205
|
+
create action, they would be redirect back to the index action.
|
206
|
+
Right after the response had been sent to the client, the index action
|
207
|
+
would be rendered with the newly created tag included and replaced
|
208
|
+
in the cache. So when the user requests for the index action gets
|
209
|
+
to the server, the freshest version is already in the cache, and
|
210
|
+
the cache miss is avoided. This works regardless of the way
|
211
|
+
the index action is cached.
|
212
|
+
|
213
|
+
Hot cache helps fight dog pile effect
|
214
|
+
(http://highscalability.com/strategy-break-memcache-dog-pile) but
|
215
|
+
should be used with caution. It's great when you want to eagerly cache
|
216
|
+
some page that user is not going to see immediately after
|
217
|
+
creating/updating something because hot cache in current implementation
|
218
|
+
uses worker queue (knows as run_later) and it does not guarantee that
|
219
|
+
before redirect hits the action data is gonna be already cached.
|
220
|
+
|
221
|
+
A good use case of eager caching is front end page of
|
222
|
+
some newspaper site when staff updates site content, and
|
223
|
+
is not redirected to page that uses new cache values immediately,
|
224
|
+
but other users access it frequently.
|
data/Rakefile
CHANGED
@@ -2,6 +2,7 @@ require 'rubygems'
|
|
2
2
|
require 'rake/gempackagetask'
|
3
3
|
require "extlib"
|
4
4
|
require 'merb-core/tasks/merb_rake_helper'
|
5
|
+
require "spec/rake/spectask"
|
5
6
|
|
6
7
|
##############################################################################
|
7
8
|
# Package && release
|
@@ -11,12 +12,12 @@ PROJECT_URL = "http://merbivore.com"
|
|
11
12
|
PROJECT_SUMMARY = "Merb plugin that provides caching (page, action, fragment, object)"
|
12
13
|
PROJECT_DESCRIPTION = PROJECT_SUMMARY
|
13
14
|
|
14
|
-
GEM_AUTHOR = "
|
15
|
-
GEM_EMAIL = "
|
15
|
+
GEM_AUTHOR = "Ben Burkert"
|
16
|
+
GEM_EMAIL = "ben@benburkert.com"
|
16
17
|
|
17
18
|
GEM_NAME = "merb-cache"
|
18
19
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
19
|
-
GEM_VERSION = (Merb::MORE_VERSION rescue "0.9.
|
20
|
+
GEM_VERSION = (Merb::MORE_VERSION rescue "0.9.8") + PKG_BUILD
|
20
21
|
|
21
22
|
RELEASE_NAME = "REL #{GEM_VERSION}"
|
22
23
|
|
@@ -34,7 +35,7 @@ spec = Gem::Specification.new do |s|
|
|
34
35
|
s.author = GEM_AUTHOR
|
35
36
|
s.email = GEM_EMAIL
|
36
37
|
s.homepage = PROJECT_URL
|
37
|
-
s.add_dependency('merb-core', '>= 0.9.
|
38
|
+
s.add_dependency('merb-core', '>= 0.9.8')
|
38
39
|
s.require_path = 'lib'
|
39
40
|
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
|
40
41
|
end
|
@@ -44,15 +45,59 @@ Rake::GemPackageTask.new(spec) do |pkg|
|
|
44
45
|
end
|
45
46
|
|
46
47
|
desc "Install the gem"
|
47
|
-
task :install
|
48
|
-
|
48
|
+
task :install do
|
49
|
+
Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
|
49
50
|
end
|
50
51
|
|
51
|
-
|
52
|
+
desc "Uninstall the gem"
|
53
|
+
task :uninstall do
|
54
|
+
Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
|
55
|
+
end
|
52
56
|
|
53
|
-
|
54
|
-
|
55
|
-
|
57
|
+
desc "Create a gemspec file"
|
58
|
+
task :gemspec do
|
59
|
+
File.open("#{GEM_NAME}.gemspec", "w") do |file|
|
60
|
+
file.puts spec.to_ruby
|
56
61
|
end
|
62
|
+
end
|
57
63
|
|
64
|
+
desc "Run all examples (or a specific spec with TASK=xxxx)"
|
65
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
66
|
+
t.spec_opts = ["-cfs"]
|
67
|
+
t.spec_files = begin
|
68
|
+
if ENV["TASK"]
|
69
|
+
ENV["TASK"].split(',').map { |task| "spec/**/#{task}_spec.rb" }
|
70
|
+
else
|
71
|
+
FileList['spec/**/*_spec.rb']
|
72
|
+
end
|
73
|
+
end
|
58
74
|
end
|
75
|
+
|
76
|
+
desc 'Default: run spec examples'
|
77
|
+
task :default => 'spec'
|
78
|
+
|
79
|
+
##############################################################################
|
80
|
+
# memcached
|
81
|
+
##############################################################################
|
82
|
+
MEMCACHED_PORTS = 43042..43043
|
83
|
+
|
84
|
+
namespace :memcached do
|
85
|
+
desc "Start the memcached instances for specs"
|
86
|
+
task :start do
|
87
|
+
log = "/tmp/memcached.log"
|
88
|
+
system ">#{log}"
|
89
|
+
|
90
|
+
verbosity = (ENV['DEBUG'] ? "-vv" : "")
|
91
|
+
|
92
|
+
(MEMCACHED_PORTS).each do |port|
|
93
|
+
system "memcached #{verbosity} -p #{port} >> #{log} 2>&1 &"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
desc "Kill the memcached instances"
|
98
|
+
task :kill do
|
99
|
+
`ps awx`.split("\n").grep(/#{MEMCACHED_PORTS.to_a.join('|')}/).map do |process|
|
100
|
+
system("kill -9 #{process.to_i}") rescue nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/TODO
CHANGED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Merb
|
2
|
+
# A convinient way to get at Merb::Cache
|
3
|
+
def self.cache
|
4
|
+
Merb::Cache
|
5
|
+
end
|
6
|
+
|
7
|
+
module Cache
|
8
|
+
|
9
|
+
def self.setup(&blk)
|
10
|
+
instance_eval(&blk) unless blk.nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
# autoload is used so that gem dependencies can be required only when needed by
|
14
|
+
# adding the require statement in the store file.
|
15
|
+
autoload :AbstractStore, "merb-cache" / "stores" / "fundamental" / "abstract_store"
|
16
|
+
autoload :FileStore, "merb-cache" / "stores" / "fundamental" / "file_store"
|
17
|
+
autoload :MemcachedStore, "merb-cache" / "stores" / "fundamental" / "memcached_store"
|
18
|
+
|
19
|
+
autoload :AbstractStrategyStore, "merb-cache" / "stores" / "strategy" / "abstract_strategy_store"
|
20
|
+
autoload :ActionStore, "merb-cache" / "stores" / "strategy" / "action_store"
|
21
|
+
autoload :AdhocStore, "merb-cache" / "stores" / "strategy" / "adhoc_store"
|
22
|
+
autoload :GzipStore, "merb-cache" / "stores" / "strategy" / "gzip_store"
|
23
|
+
autoload :PageStore, "merb-cache" / "stores" / "strategy" / "page_store"
|
24
|
+
autoload :SHA1Store, "merb-cache" / "stores" / "strategy" / "sha1_store"
|
25
|
+
|
26
|
+
|
27
|
+
class << self
|
28
|
+
attr_accessor :stores
|
29
|
+
end
|
30
|
+
|
31
|
+
self.stores = {}
|
32
|
+
|
33
|
+
# Cache store lookup
|
34
|
+
# name<Symbol> : The name of a registered store
|
35
|
+
# Returns<Nil AbstractStore> : A thread-safe copy of the store
|
36
|
+
def self.[](*names)
|
37
|
+
if names.size == 1
|
38
|
+
Thread.current[:'merb-cache'] ||= {}
|
39
|
+
(Thread.current[:'merb-cache'][names.first] ||= stores[names.first].clone)
|
40
|
+
else
|
41
|
+
AdhocStore[*names]
|
42
|
+
end
|
43
|
+
rescue TypeError
|
44
|
+
raise(StoreNotFound, "Could not find the :#{names.first} store")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Clones the cache stores for the current thread
|
48
|
+
def self.clone_stores
|
49
|
+
@stores.inject({}) {|h, (k, s)| h[k] = s.clone; h}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Registers the cache store name with a type & options
|
53
|
+
# name<Symbol> : An optional symbol to give the cache. :default is used if no name is given.
|
54
|
+
# klass<Class> : A store type.
|
55
|
+
# opts<Hash> : A hash to pass through to the store for configuration.
|
56
|
+
def self.register(name, klass = nil, opts = {})
|
57
|
+
klass, opts = nil, klass if klass.is_a? Hash
|
58
|
+
name, klass = default_store_name, name if klass.nil?
|
59
|
+
|
60
|
+
raise StoreExists, "#{name} store already setup" if @stores.has_key?(name)
|
61
|
+
|
62
|
+
@stores[name] = (AdhocStore === klass) ? klass : klass.new(opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Checks to see if a given store exists already.
|
66
|
+
def self.exists?(name)
|
67
|
+
return true if self[name]
|
68
|
+
rescue StoreNotFound
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
# Default store name is :default.
|
73
|
+
def self.default_store_name
|
74
|
+
:default
|
75
|
+
end
|
76
|
+
|
77
|
+
class NotSupportedError < Exception; end
|
78
|
+
|
79
|
+
class StoreExists < Exception; end
|
80
|
+
|
81
|
+
# Raised when requested store cannot be found on the list of registered.
|
82
|
+
class StoreNotFound < Exception; end
|
83
|
+
end #Cache
|
84
|
+
end #Merb
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
class Hash
|
4
|
+
|
5
|
+
def to_sha2
|
6
|
+
string = ""
|
7
|
+
keys.sort_by{|k| k.to_s}.each do |k|
|
8
|
+
case self[k]
|
9
|
+
when Array
|
10
|
+
string << self[k].join
|
11
|
+
when Hash
|
12
|
+
string << self[k].to_sha2
|
13
|
+
else
|
14
|
+
string << self[k].to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
Digest::SHA2.hexdigest(string)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|