merb_piece_cache 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,20 @@
1
+ Copyright(c) 2009 kuwata-lab.com all rights reserverd.
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,142 @@
1
+ ======
2
+ README
3
+ ======
4
+
5
+ $Release: 0.1.0 $
6
+
7
+
8
+ About
9
+ -----
10
+
11
+ merb_piece_cache is a Merb plug-in to cache html fragments.
12
+ You can eliminate database access with this plugin.
13
+
14
+ Currently only file-base cache store is provided.
15
+ (Memory-base cache store may be provided in future.)
16
+
17
+
18
+ Install
19
+ -------
20
+
21
+ ### install gem
22
+ $ sudo gem install merb_helpers_monkey
23
+ ## add 'dependency "merb_piece_cache"'
24
+ $ vi config/dependencies.rb
25
+
26
+ This plugin requires 'called_from' library.
27
+ If you install this plugin manually, don't forget to install 'called_from'.
28
+
29
+
30
+ Example
31
+ -------
32
+
33
+ * [controller] delay database access by if_not_cached() helper.
34
+ * [template] specify cache area by cache_with() helper.
35
+
36
+
37
+ controller:
38
+
39
+ def index
40
+ ## @user is necessary every time
41
+ @user = User.current()
42
+ ## @members is necessary only when cache is refreshed
43
+ ## (block parameter is called only when cache is expired or not found)
44
+ if_not_cached {|cache_key|
45
+ @members = Members.all(:order=>[:created_at])
46
+ }
47
+ render
48
+ end
49
+
50
+
51
+ template:
52
+
53
+ <!-- rendered every time (because never cached) -->
54
+ <% if @user %>
55
+ Hello <%=h @user %>!
56
+ <% else %>
57
+ <a href="/login">Login</a>
58
+ <% end %>
59
+
60
+ <!-- rendered only when cache is expired or not found -->
61
+ <% lifetime = 5*60 # sec, default is nil(=infinity) %>
62
+ <% cache_with("members/index", lifetime) do %>
63
+ <ul>
64
+ <% for member in @members %>
65
+ <li><%=h @member.name %></li>
66
+ <% end %>
67
+ <ul>
68
+ <% end %>
69
+
70
+
71
+ Reference
72
+ ---------
73
+
74
+ Merb_piece_cache plugin provides the following helpers.
75
+
76
+ * if_not_cached() {|cache_key| ... }
77
+ Register block as callback object which is called when fragment
78
+ cache is re-generated (In other words, block parameter is called
79
+ only when cache is re-generated).
80
+ Block paramter can receive cache_key as 1st argument so that
81
+ you can switch data processing according to cache_key.
82
+
83
+ * piece_cache(cache_key, lifetime=nil) { ... }
84
+ When cache is found and not expired, then add cache data into view.
85
+ Otherwise, call block to re-generate cache data and cache it.
86
+ (Notice that block parameter is called only when cache is re-generated.)
87
+ It is able to specify cache lifetime. For example, if you specify
88
+ lifetime=5*60, cache data will be expired after 5 minutes.
89
+
90
+ * cache_with(cache_key, lifetime=nil) { ... }
91
+ Alias of piece_cache().
92
+ You can re-define this helper to extend cache behaviour.
93
+
94
+
95
+ Configuration
96
+ -------------
97
+
98
+ You can configure merb_piece_cache plugin in your environment file.
99
+ (config/environments/{production,development,test}.rb).
100
+ For example, add the followings into your 'development.rb':
101
+
102
+ Merb::Plugins.config[:merb_piece_cache] = {
103
+ ## show cached area in template (convenient for debug!)
104
+ :show_area => true,
105
+ ## directory to place cache files (default: "MERB_ROOT/tmp/cache")
106
+ :root_dir => "/var/tmp",
107
+ }
108
+
109
+
110
+ Tips
111
+ ----
112
+
113
+ * You can prepare different data in if_not_exist() according to cache_key.
114
+
115
+ def index
116
+ if_not_cached {|cache_key|
117
+ case cache_key
118
+ when /^post\/(\d+)$/
119
+ @post = BlogPost.get($1)
120
+ when /^post\/(\d+)/comments$/
121
+ @comments = BlogComment.all(:post_id=>$1)
122
+ end
123
+ }
124
+ render
125
+ end
126
+
127
+ * piece_cache() (and also cache_with()) checks template timestamp.
128
+ If cache data is older than template timestamp, then cache data will be
129
+ re-generated.
130
+
131
+
132
+ Author
133
+ ------
134
+
135
+ makoto kuwata <kwa(at)kuwata-lab.com>
136
+ copyright(c) 2009 kuwata-lab.com all rights reserved.
137
+
138
+
139
+ License
140
+ -------
141
+
142
+ MIT License
@@ -0,0 +1,87 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ require 'merb-core'
5
+ require 'merb-core/tasks/merb'
6
+
7
+ GEM_NAME = "merb_piece_cache"
8
+ GEM_VERSION = "0.1.0"
9
+ AUTHOR = "makoto kuwata"
10
+ EMAIL = "kwa.at.kuwata-lab.com"
11
+ HOMEPAGE = "http://github.com/kwatch/merb_piece_cache/"
12
+ SUMMARY = "Merb plugin to cache html fragment"
13
+
14
+ @copyright = 'copyright(c) 2009 kuwata-lab.com all rights reserved.'
15
+ @license = 'MIT License'
16
+
17
+ spec = Gem::Specification.new do |s|
18
+ s.rubyforge_project = 'merb_piece_cache'
19
+ s.name = GEM_NAME
20
+ s.version = GEM_VERSION
21
+ s.platform = Gem::Platform::RUBY
22
+ s.has_rdoc = false
23
+ s.extra_rdoc_files = ["README", "MIT-LICENSE", 'TODO']
24
+ s.summary = SUMMARY
25
+ s.description = s.summary
26
+ s.author = AUTHOR
27
+ s.email = EMAIL
28
+ s.homepage = HOMEPAGE
29
+ #s.add_dependency('merb', '>= 1.0.12')
30
+ s.add_dependency('called_from', ['>= 0.1.0'])
31
+ s.require_path = 'lib'
32
+ #s.files = %w(MIT-LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
33
+ s.files = %w(MIT-LICENSE README Rakefile TODO) + Dir.glob("{lib}/**/*")
34
+ end
35
+
36
+ #Rake::GemPackageTask.new(spec) do |pkg|
37
+ # pkg.gem_spec = spec
38
+ #end
39
+
40
+ desc "install the plugin as a gem"
41
+ task :install do
42
+ Merb::RakeHelper.install(GEM_NAME, :version => GEM_VERSION)
43
+ end
44
+
45
+ desc "Uninstall the gem"
46
+ task :uninstall do
47
+ Merb::RakeHelper.uninstall(GEM_NAME, :version => GEM_VERSION)
48
+ end
49
+
50
+ desc "Create a gemspec file"
51
+ task :gemspec do
52
+ File.open("#{GEM_NAME}.gemspec", "w") do |file|
53
+ file.puts spec.to_ruby
54
+ end
55
+ end
56
+
57
+ desc "create gem package"
58
+ task :package => [:gemspec] do
59
+ dir = "build"
60
+ rm_rf dir
61
+ mkdir dir
62
+ #cp_r spec.files, dir
63
+ sh "tar cf - #{spec.files.join(' ')} | (cd #{dir}; tar xf -)"
64
+ Dir.glob("#{dir}/**/*").each do |fpath|
65
+ next unless File.file?(fpath)
66
+ next if File.basename(fpath) == 'Rakefile'
67
+ s = File.open(fpath, 'rb') {|f| f.read }
68
+ s.gsub! /\$Release\$/, GEM_VERSION
69
+ s.gsub! /\$Release:.*?\$/, "$Release: #{GEM_VERSION} $"
70
+ s.gsub! /\$Copyright\$/, @copyright
71
+ s.gsub! /\$License\$/, @license
72
+ File.open(fpath, 'wb') {|f| f.write s }
73
+ end
74
+ cp "#{GEM_NAME}.gemspec", dir
75
+ chdir dir do
76
+ sh "gem build #{GEM_NAME}.gemspec"
77
+ mv "#{GEM_NAME}-#{GEM_VERSION}.gem", ".."
78
+ end
79
+ end
80
+
81
+ desc "install gem package"
82
+ task :inst => [:package] do
83
+ sh "gem install #{GEM_NAME}-#{GEM_VERSION}.gem"
84
+ end
85
+
86
+
87
+ spec
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ TODO:
2
+ * implement MemcachedCacheStore
@@ -0,0 +1,47 @@
1
+ ###
2
+ ### $Release: 0.1.0 $
3
+ ### copyright(c) 2009 kuwata-lab.com all rights reserved.
4
+ ### MIT License
5
+ ###
6
+
7
+
8
+ # make sure we're running inside Merb
9
+ if defined?(Merb::Plugins)
10
+
11
+ # Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
12
+ default_conf = {
13
+ :show_area => false,
14
+ :root_dir => nil,
15
+ :cache_store => nil,
16
+ }
17
+ curr_conf = Merb::Plugins.config[:merb_piece_cache]
18
+ Merb::Plugins.config[:merb_piece_cache] = default_conf.merge(curr_conf || {})
19
+
20
+ Merb::BootLoader.before_app_loads do
21
+ # require code that must be loaded before the application
22
+ require 'merb-cache/cache'
23
+ require 'merb_piece_cache/file_cache_store'
24
+ require 'merb_piece_cache/helpers'
25
+ begin
26
+ require 'called_from.so'
27
+ rescue LoadError
28
+ require 'called_from.rb'
29
+ Merb.logger.info "[piece_cache] it is strongly recommended to install 'called_from.so' instead of 'called_from.rb' for good performance"
30
+ end
31
+ end
32
+
33
+ Merb::BootLoader.after_app_loads do
34
+ # code that can be required after the application loads
35
+ cache_store = Merb::Plugins.config[:merb_piece_cache][:cache_store]
36
+ if cache_store
37
+ Merb.logger.debug "[piece_cache] using #{cache_store.class}"
38
+ else
39
+ root_dir = Merb::Plugins.config[:merb_piece_cache][:root_dir]
40
+ cache_store = PieceCache::FileCacheStore.new(:dir=>root_dir)
41
+ Merb::Plugins.config[:merb_piece_cache][:cache_store] = cache_store
42
+ Merb.logger.debug "[piece_cache] using #{cache_store.class} (root_dir=#{cache_store.dir})"
43
+ end
44
+ end
45
+
46
+ #Merb::Plugins.add_rakefiles "merb_piece_cache/merbtasks"
47
+ end
@@ -0,0 +1,77 @@
1
+ ###
2
+ ### $Release: 0.1.0 $
3
+ ### copyright(c) 2009 kuwata-lab.com all rights reserved.
4
+ ### MIT License
5
+ ###
6
+
7
+
8
+ module PieceCache
9
+
10
+
11
+ class FileCacheStore < Merb::Cache::FileStore
12
+
13
+ def pathify(key, parameters = {})
14
+ filename = super
15
+ ctype = parameters[:content_type]
16
+ ctype ? "#{filename}.#{ctype}" : filename
17
+ end
18
+
19
+ def read(key, parameters = {})
20
+ ## if cache file is not found, return nil
21
+ fpath = pathify(key) # or pathify(key, parameters)
22
+ return nil unless File.exist?(fpath)
23
+ ## if cache file is created before timestamp, return nil
24
+ timestamp = parameters[:timestamp]
25
+ return nil if timestamp && File.ctime(fpath) < timestamp
26
+ ## if cache file is expired, return nil
27
+ return nil if File.mtime(fpath) <= Time.now
28
+ ## return cache file content
29
+ return read_file(fpath)
30
+ end
31
+
32
+ MAX_TIMESTAMP = Time.at(0x7fffffff) # Tue Jan 19 03:14:07 UTC 2038
33
+
34
+ def write(key, data = nil, parameters = {}, conditions = {})
35
+ ## create directory for cache
36
+ fpath = pathify(key) # or pathify(key, parameters)
37
+ dir = File.dirname(fpath)
38
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
39
+ ## write data into file
40
+ File.unlink(fpath) if File.exist?(fpath)
41
+ write_file(fpath, data)
42
+ ## set mtime (which is regarded as cache expired timestamp)
43
+ lifetime = parameters[:lifetime]
44
+ timestamp = lifetime && lifetime > 0 ? Time.now + lifetime : MAX_TIMESTAMP
45
+ File.utime(timestamp, timestamp, fpath)
46
+ ## return data
47
+ return data
48
+ end
49
+
50
+ if RUBY_PLATFORM =~ /mswin(?!ce)|mingw|cygwin|bccwin/i
51
+ def read_file(path)
52
+ File.open(path, 'rb') {|f| f.read() }
53
+ end
54
+ else
55
+ def read_file(path)
56
+ File.read(path)
57
+ end
58
+ end
59
+
60
+ def write_file(path, data)
61
+ ## write into tempoary file and rename it to path (in order not to call flock)
62
+ tmppath = "#{path}#{rand().to_s[1,6]}"
63
+ begin
64
+ File.open(tmppath, 'wb') {|f| f.write(data) }
65
+ File.rename(tmppath, path)
66
+ rescue => ex
67
+ File.unlink tmppath if File.exist?(tmppath)
68
+ File.unlink path if File.exist?(path)
69
+ raise ex
70
+ end
71
+ true
72
+ end
73
+
74
+ end
75
+
76
+
77
+ end
@@ -0,0 +1,45 @@
1
+ ###
2
+ ### $Release: 0.1.0 $
3
+ ### copyright(c) 2009 kuwata-lab.com all rights reserved.
4
+ ### MIT License
5
+ ###
6
+
7
+
8
+ module Merb
9
+ module GlobalHelpers
10
+
11
+ def if_not_cached(&block)
12
+ @_callback = block # or @_deffered?
13
+ end
14
+
15
+ def piece_cache(cache_key, lifetime=nil)
16
+ #caller_str = caller(1)[0] # caller() is too high cost to use in production mode
17
+ #template_path = caller_str =~ /:\d+:in `/ && $` #`
18
+ template_path, linenum, funcname = called_from()
19
+ ts = File.exist?(template_path) && File.mtime(template_path)
20
+ cache_store = Merb::Plugins.config[:merb_piece_cache][:cache_store]
21
+ piece = cache_store.read(cache_key, :timestamp=>ts, :content_type=>self.content_type)
22
+ show_area = Merb::Plugins.config[:merb_piece_cache][:show_area]
23
+ if piece
24
+ Merb.logger.debug "[piece_cache] #{cache_key.inspect}: found."
25
+ @_erb_buf << "<div style=\"font-weight:bold\">----- cached key=#{cache_key.inspect} -----</div>\n" if show_area
26
+ @_erb_buf << piece
27
+ @_erb_buf << "<div style=\"font-weight:bold\">----- /cached -----</div>\n" if show_area
28
+ else
29
+ Merb.logger.debug "[piece_cache] #{cache_key.inspect}: expired or not found."
30
+ values = @_callback && @_callback.call(cache_key) || {}
31
+ #values.each_pair {|k, v| self.instance_variable_set("@#{k}", v) } if values.is_a?(Hash)
32
+ @_erb_buf << "<div style=\"font-weight:bold;color:red\">----- rendered key=#{cache_key.inspect} -----</div>\n" if show_area
33
+ pos = @_erb_buf.length
34
+ yield(values)
35
+ piece = @_erb_buf[pos..-1]
36
+ @_erb_buf << "<div style=\"font-weight:bold;color:red\">----- /rendered -----</div>\n" if show_area
37
+ cache_store.write(cache_key, piece, :lifetime=>lifetime, :content_type=>self.content_type)
38
+ end
39
+ nil
40
+ end
41
+
42
+ alias cache_with piece_cache
43
+
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: merb_piece_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - makoto kuwata
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-30 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: called_from
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.0
24
+ version:
25
+ description: Merb plugin to cache html fragment
26
+ email: kwa.at.kuwata-lab.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - MIT-LICENSE
34
+ - TODO
35
+ files:
36
+ - MIT-LICENSE
37
+ - README
38
+ - Rakefile
39
+ - TODO
40
+ - lib/merb_piece_cache/file_cache_store.rb
41
+ - lib/merb_piece_cache/helpers.rb
42
+ - lib/merb_piece_cache.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/kwatch/merb_piece_cache/
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project: merb_piece_cache
67
+ rubygems_version: 1.3.5
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: Merb plugin to cache html fragment
71
+ test_files: []
72
+