public
Description: Mirror for rails plugin : Timed Fragment Cache
Homepage: http://code.livsey.org/index.fcgi/wiki/TimedFragmentCache
Clone URL: git://github.com/veilleperso/timed_fragment_cache.git
timed_fragment_cache / lib / timed_fragment_cache.rb
100644 132 lines (111 sloc) 4.507 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Copyright (c) 2006 Richard Livsey
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
module ActionController
  module Caching
  
    # Timed Fragment Caching
    # Adds optional expiry to fragment caching which will cache that fragment for the alloted time
    # Works by adding a 'meta' fragment for each timed fragment (inspired by metafragment code in typo
    # http://www.typosphere.org/trac/browser/trunk/vendor/plugins/expiring_action_cache/lib/metafragment.rb)
    #
    # Works something like this:
    #
    # <% cache 'fragment_name', 10.minutes.from_now do %>
    # the cached fragment which does something intensive
    # <% end %>
    #
    # This will be cached for 10 minutes before it expires
    #
    # Also adds when_fragment_expired which you can use to only execute code if the fragment is not cached, or has
    # expired:
    #
    # when_fragment_expired 'fragment_name', 10.minutes_from_now do
    # # some intensive code
    # end
    #
    # Note that if using the 'when_fragment_expired' in the controller, you don't need the expiry in the call to cache
    # in the template, as 'when_fragment_expired' will expire the fragment for you.
    module TimedFragment
    
      def cache_erb_fragment(block, name = {}, options = nil, expiry = nil)
        unless perform_caching then block.call; return end
 
        fragment = get_fragment(name)
        if expiry && !fragment
          expire_and_write_meta(name, expiry)
        end
 
        buffer = eval("_erbout", block.binding)
 
        if fragment
          buffer.concat(fragment)
        else
          pos = buffer.length
          block.call
          write_fragment(name, buffer[pos..-1], options)
        end
  
      end
    
      def expiry_time(name)
        read_meta_fragment(name)
      end
    
      def get_fragment(name)
       return fragments[name] if fragments[name]
       fragment = read_fragment(name)
        return nil unless fragment
        fragments[name] = fragment
        expires = expiry_time(name)
        return expires && expires > Time.now ? fragment : nil
      end
 
      def read_meta_fragment(name)
        YAML.load(read_fragment(meta_fragment_key(name))) rescue nil
      end
    
      def write_meta_fragment(name, meta)
        write_fragment(meta_fragment_key(name), YAML.dump(meta))
      end
    
      def meta_fragment_key(name)
        fragment_cache_key(name) + '_meta'
      end
    
      def when_fragment_expired(name, expiry=nil)
       return if get_fragment( name )
        yield
        expire_and_write_meta(name, expiry)
      end
    
      def expire_and_write_meta(name, expiry)
        expire_fragment(name)
        write_meta_fragment(name, expiry) if expiry
      end
    
      def fragments
       @fragments ||= {}
      end
    end
  end
end
 
module ActionView
  module Helpers
    module TimedFragmentCacheHelper
    
      def self.included(base) # :nodoc:
        base.class_eval do
          alias_method :cache, :cache_with_expiry
        end
      end
    
      def cache_with_expiry(name = {}, expires = nil, &block)
        @controller.cache_erb_fragment(block, name, nil, expires)
      end
    
    end
  end
end
 
ActionController::Base.send :include, ActionController::Caching::TimedFragment
ActionView::Base.send(:include, ActionView::Helpers::TimedFragmentCacheHelper)