merb_piece_cache 0.1.0

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,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
+