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.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Alex Boussinet
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
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.
data/README ADDED
@@ -0,0 +1,160 @@
1
+ = merb-cache
2
+
3
+ A plugin for the Merb framework that provides caching
4
+
5
+ Currently supported methods:
6
+
7
+ - page caching:
8
+ - action caching
9
+ - fragment caching
10
+ - object caching
11
+
12
+ Implemented cache stores:
13
+
14
+ - memory
15
+ - memcache
16
+ - file
17
+ - database (sequel, datamapper, activerecord)
18
+
19
+ == Quick intro
20
+ With fragment caching, you can mix dynamic and static content.
21
+
22
+ With action caching, the whole template is cached
23
+ but the before filters are still processed.
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
128
+
129
+ def some_action_that_invalidates_cache
130
+ expire_page(:action_name)
131
+ expire_action(:another_action)
132
+ render
133
+ end
134
+
135
+ def delete
136
+ expire("active_users")
137
+ render
138
+ end
139
+
140
+ def archives
141
+ @archives = User.archives unless cached?("users_archives")
142
+ render
143
+ end
144
+
145
+ def index
146
+ render
147
+ end
148
+ end
149
+
150
+
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
+
157
+ ====views/users/archive.html.erb
158
+ <%- cache "users_archives" do %>
159
+ <div>some big template</div>
160
+ <% end -%>
data/Rakefile ADDED
@@ -0,0 +1,59 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require "extlib"
4
+ require 'merb-core/tasks/merb_rake_helper'
5
+
6
+ ##############################################################################
7
+ # Package && release
8
+ ##############################################################################
9
+ RUBY_FORGE_PROJECT = "thorero"
10
+ PROJECT_URL = "http://merbivore.com"
11
+ PROJECT_SUMMARY = "Merb plugin that provides caching (page, action, fragment, object)"
12
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY
13
+
14
+ GEM_AUTHOR = "Alex Boussinet"
15
+ GEM_EMAIL = "alex.boussinet@gmail.com"
16
+
17
+ GEM_NAME = "thorero-cache"
18
+ PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
19
+ GEM_VERSION = (Merb::MORE_VERSION rescue "0.9.4") + PKG_BUILD
20
+
21
+ RELEASE_NAME = "REL #{GEM_VERSION}"
22
+
23
+ require "extlib/tasks/release"
24
+
25
+ spec = Gem::Specification.new do |s|
26
+ s.rubyforge_project = RUBY_FORGE_PROJECT
27
+ s.name = GEM_NAME
28
+ s.version = GEM_VERSION
29
+ s.platform = Gem::Platform::RUBY
30
+ s.has_rdoc = true
31
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
32
+ s.summary = PROJECT_SUMMARY
33
+ s.description = PROJECT_DESCRIPTION
34
+ s.author = GEM_AUTHOR
35
+ s.email = GEM_EMAIL
36
+ s.homepage = PROJECT_URL
37
+ s.add_dependency('merb-core', '>= 0.9.4')
38
+ s.add_dependency('builder', '>= 2.0.0')
39
+ s.require_path = 'lib'
40
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
41
+ end
42
+
43
+ Rake::GemPackageTask.new(spec) do |pkg|
44
+ pkg.gem_spec = spec
45
+ end
46
+
47
+ desc "Install the gem"
48
+ task :install => [:package] do
49
+ sh %{#{sudo} gem install #{install_home} pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}
50
+ end
51
+
52
+ namespace :jruby do
53
+
54
+ desc "Run :package and install the resulting .gem with jruby"
55
+ task :install => :package do
56
+ sh %{#{sudo} jruby -S gem install #{install_home} pkg/#{GEM_NAME}-#{GEM_VERSION}.gem --no-rdoc --no-ri}
57
+ end
58
+
59
+ end
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ capture_#{engine} and concat_#{engine} are required for cache()
2
+ implement other cache store ?
data/lib/merb-cache.rb ADDED
@@ -0,0 +1,10 @@
1
+ if defined?(Merb::Plugins)
2
+ Merb::Plugins.add_rakefiles "merb-cache/merbtasks"
3
+ unless 1.respond_to? :minutes
4
+ class Numeric
5
+ def minutes; self * 60; end
6
+ def from_now(now = Time.now); now + self; end
7
+ end
8
+ end
9
+ require "merb-cache/merb-cache"
10
+ end
@@ -0,0 +1,135 @@
1
+ class Merb::Cache
2
+ cattr_accessor :cached_actions
3
+ self.cached_actions = {}
4
+ end
5
+
6
+ module Merb::Cache::ControllerClassMethods
7
+ # Mixed in Merb::Controller. Provides methods related to action caching
8
+
9
+ # Register the action for action caching
10
+ #
11
+ # ==== Parameters
12
+ # action<Symbol>:: The name of the action to register
13
+ # from_now<~minutes>::
14
+ # The number of minutes (from now) the cache should persist
15
+ #
16
+ # ==== Examples
17
+ # cache_action :mostly_static
18
+ # cache_action :barely_dynamic, 10
19
+ def cache_action(action, from_now = nil, opts = {})
20
+ cache_actions([action, from_now, opts])
21
+ end
22
+
23
+ # Register actions for action caching (before and after filters)
24
+ #
25
+ # ==== Parameter
26
+ # actions<Symbol,Array[Symbol,~minutes]>:: See #cache_action
27
+ #
28
+ # ==== Example
29
+ # cache_actions :mostly_static, [:barely_dynamic, 10]
30
+ def cache_actions(*actions)
31
+ actions.each do |action, from_now, opts|
32
+ from_now, opts = nil, from_now if Hash === from_now
33
+
34
+ before("cache_#{action}_before", opts.merge(:only => action))
35
+ after("cache_#{action}_after", opts.merge(:only => action))
36
+ alias_method "cache_#{action}_before", :cache_action_before
37
+ alias_method "cache_#{action}_after", :cache_action_after
38
+
39
+ _actions = Merb::Cache.cached_actions[controller_name] ||= {}
40
+ _actions[action] = from_now
41
+ end
42
+ true
43
+ end
44
+ end
45
+
46
+ module Merb::Cache::ControllerInstanceMethods
47
+ # Mixed in Merb::Controller. Provides methods related to action caching
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_action?(:action => 'show', :params => [params[:page]])
59
+ def cached_action?(options)
60
+ key = Merb::Controller._cache.key_for(options, controller_name, true)
61
+ Merb::Controller._cache.store.cached?(key)
62
+ end
63
+
64
+ # Expires the action identified by the key computed after the parameters
65
+ #
66
+ # ==== Parameter
67
+ # options<String,Hash>:: The options that will be passed to #expire_key_for
68
+ #
69
+ # ==== Examples
70
+ # expire_action(:action => 'show', :controller => 'news')
71
+ # expire_action(:action => 'show', :match => true)
72
+ def expire_action(options)
73
+ Merb::Controller._cache.expire_key_for(options, controller_name, true) do |key, match|
74
+ if match
75
+ Merb::Controller._cache.store.expire_match(key)
76
+ else
77
+ Merb::Controller._cache.store.expire(key)
78
+ end
79
+ end
80
+ true
81
+ end
82
+
83
+ # You can call this method if you need to prevent caching the action
84
+ # after it has been rendered.
85
+ def abort_cache_action
86
+ @capture_action = false
87
+ end
88
+
89
+ private
90
+
91
+ # Called by the before and after filters. Stores or recalls a cache entry.
92
+ # The key is based on the result of request.path
93
+ # If the key with "/" then it is removed
94
+ # If the key is "/" then it will be replaced by "index"
95
+ #
96
+ # ==== Parameters
97
+ # data<String>:: the data to put in cache using the cache store
98
+ #
99
+ # ==== Examples
100
+ # If request.path is "/", the key will be "index"
101
+ # If request.path is "/news/show/1", the key will be "/news/show/1"
102
+ # If request.path is "/news/show/", the key will be "/news/show"
103
+ def _cache_action(data = nil)
104
+ controller = controller_name
105
+ action = action_name.to_sym
106
+ actions = Merb::Controller._cache.cached_actions[controller]
107
+ return unless actions && actions.key?(action)
108
+ path = request.path.chomp("/")
109
+ path = "index" if path.empty?
110
+ if data
111
+ from_now = Merb::Controller._cache.cached_actions[controller][action]
112
+ Merb::Controller._cache.store.cache_set(path, data, from_now)
113
+ else
114
+ @capture_action = false
115
+ _data = Merb::Controller._cache.store.cache_get(path)
116
+ throw(:halt, _data) unless _data.nil?
117
+ @capture_action = true
118
+ end
119
+ true
120
+ end
121
+
122
+ # before filter
123
+ def cache_action_before
124
+ # recalls a cached entry or set @capture_action to true in order
125
+ # to grab the response in the after filter
126
+ _cache_action
127
+ end
128
+
129
+ # after filter
130
+ def cache_action_after
131
+ # takes the body of the response and put it in cache
132
+ # if the cache entry expired, if it doesn't exist or status is 200
133
+ _cache_action(body) if @capture_action && status == 200
134
+ end
135
+ end
@@ -0,0 +1,95 @@
1
+ module Merb::Cache::ControllerInstanceMethods
2
+ # Mixed in Merb::Controller. Provides methods related to fragment caching
3
+
4
+ # Checks whether a cache entry exists
5
+ #
6
+ # ==== Parameter
7
+ # options<String,Hash>:: The options that will be passed to #key_for
8
+ #
9
+ # ==== Returns
10
+ # true if the cache entry exists, false otherwise
11
+ #
12
+ # ==== Example
13
+ # cached_action?("my_key")
14
+ def cached?(options)
15
+ key = Merb::Controller._cache.key_for(options, controller_name)
16
+ Merb::Controller._cache.store.cached?(key)
17
+ end
18
+
19
+ # ==== Example
20
+ # In your view:
21
+ # <%- cache("my_key") do -%>
22
+ # <%= partial :test, :collection => @test %>
23
+ # <%- end -%>
24
+ def cache(options, from_now = nil, &block)
25
+ key = Merb::Controller._cache.key_for(options, controller_name)
26
+ Merb::Controller._cache.store.cache(self, key, from_now, &block)
27
+ end
28
+
29
+ # Fetch data from cache
30
+ #
31
+ # ==== Parameter
32
+ # options<String,Hash>:: The options that will be passed to #key_for
33
+ #
34
+ # ==== Returns
35
+ # data<Object,NilClass>::
36
+ # nil is returned if the cache entry is not found
37
+ #
38
+ # ==== Example
39
+ # if cache_data = cache_get("my_key")
40
+ # @var1, @var2 = *cache_data
41
+ # else
42
+ # @var1 = MyModel.big_query1
43
+ # @var2 = MyModel.big_query2
44
+ # cache_set("my_key", nil, [@var1, @var2])
45
+ # end
46
+ def cache_get(options)
47
+ key = Merb::Controller._cache.key_for(options, controller_name)
48
+ Merb::Controller._cache.store.cache_get(key)
49
+ end
50
+
51
+ # Store data to cache
52
+ #
53
+ # ==== Parameter
54
+ # options<String,Hash>:: The options that will be passed to #key_for
55
+ # object<Object>:: The object(s) to put in cache
56
+ # from_now<~minutes>::
57
+ # The number of minutes (from now) the cache should persist
58
+ #
59
+ # ==== Returns
60
+ # data<Object,NilClass>::
61
+ # nil is returned if the cache entry is not found
62
+ #
63
+ # ==== Example
64
+ # if cache_data = cache_get("my_key")
65
+ # @var1, @var2 = *cache_data
66
+ # else
67
+ # @var1 = MyModel.big_query1
68
+ # @var2 = MyModel.big_query2
69
+ # cache_set("my_key", nil, [@var1, @var2])
70
+ # end
71
+ def cache_set(options, object, from_now = nil)
72
+ key = Merb::Controller._cache.key_for(options, controller_name)
73
+ Merb::Controller._cache.store.cache_set(key, object, from_now)
74
+ end
75
+
76
+ # Expires the entry identified by the key computed after the parameters
77
+ #
78
+ # ==== Parameter
79
+ # options<String,Hash>:: The options that will be passed to #key_for
80
+ #
81
+ # ==== Examples
82
+ # expire("my_key")
83
+ # expire(:key => "my_", :match => true)
84
+ # expire(:key => "my_key", :params => [session[:me], params[:ref]])
85
+ def expire(options)
86
+ Merb::Controller._cache.expire_key_for(options, controller_name) do |key, match|
87
+ if match
88
+ Merb::Controller._cache.store.expire_match(key)
89
+ else
90
+ Merb::Controller._cache.store.expire(key)
91
+ end
92
+ end
93
+ true
94
+ end
95
+ end