1require 'digest/sha1'
 
2require 'ostruct'
 
3
 
4module CacheHelper
 
5  class CacheKey
 
6    class Dependencies
 
7      attr_reader :fragments
 
8
 
 9      def initialize(fragments)
 
10        @fragments = fragments
 
11      end
 
 
13      def for(name)
 
14        dependencies_for(name).map{ |d| [d, dependencies_for(d)] }.flatten.uniq
 
15      end
 
 
17      private
 
 
19      def dependencies_for(name)
 
20        fragments.fetch(name).fetch(:dependencies, [])
 
21      end
 
22    end
 
 
24    class Keys
 
25      attr_reader :template
 
 
27      delegate :assigns, :params, to: :template
 
28      delegate :archived_petition_page?, to: :template
 
29      delegate :create_petition_page?, to: :template
 
30      delegate :open_petition_page?, to: :template
 
31      delegate :home_page?, to: :template
 
32      delegate :last_signature_at, to: :template
 
33      delegate :last_government_response_updated_at, to: :template
 
34      delegate :last_debate_outcome_updated_at, to: :template
 
35      delegate :petition_page?, to: :template
 
36      delegate :page_title, to: :template
 
37      delegate :request, to: :template
 
 
39      def initialize(template)
 
40        @template = template
 
41      end
 
 
43      def archived_petition_page
 
44        archived_petition_page?
 
45      end
 
 
47      def constituency
 
48        assigns['constituency']
 
49      end
 
 
51      def create_petition_page
 
52        create_petition_page?
 
53      end
 
 
55      def open_petition_page
 
56        open_petition_page?
 
57      end
 
 
59      def home_page
 
60        home_page?
 
61      end
 
 
63      def last_petition_created_at
 
64        Site.last_petition_created_at
 
65      end
 
 
67      def petition
 
68        assigns['petition'] if petition_page? || archived_petition_page?
 
69      end
 
 
71      def petition_page
 
72        petition_page?
 
73      end
 
 
75      def reveal_response
 
76        params[:reveal_response] == 'yes'
 
77      end
 
 
79      def site_updated_at
 
80        Site.updated_at
 
81      end
 
 
83      def url
 
84        request.original_url.force_encoding('utf-8')
 
85      end
 
 
87      def for(keys)
 
88        keys.map{ |key| [key, value_for(key)] }.uniq
 
89      end
 
 
91      def method_missing(name, *args, &block)
 
92        if assigns.key?(name.to_s)
 
93          assigns[name.to_s]
 
94        else
 
95          super
 
96        end
 
97      end
 
 
 99      private
 
 
101      def value_for(key)
 
102        cache_key_for(public_send(key))
 
103      end
 
 
105      def cache_key_for(value)
 
106        if value.respond_to?(:cache_key)
 
107          value.cache_key
 
108        elsif Array === value
 
109          value.map{ |v| cache_key_for(v) }.to_param
 
110        elsif Time === value
 
111          value.to_s(:nsec)
 
112        else
 
113          value.to_param
 
114        end
 
115      end
 
116    end
 
 
118    class Fragment
 
119      attr_reader :keys, :dependencies, :version, :options
 
 
121      def initialize(fragment)
 
122        @keys         = fragment.fetch(:keys, [])
 
123        @dependencies = fragment.fetch(:dependencies, [])
 
124        @version      = fragment.fetch(:version, 1)
 
125        @options      = fragment.fetch(:options, {})
 
126      end
 
127    end
 
 
129    attr_reader :template, :name, :fragment
 
130    delegate :options, to: :fragment
 
 
132    class << self
 
133      def build(template, args)
 
134        new(template, args).build
 
135      end
 
 
137      def fragments
 
138        @fragments ||= load_yaml.deep_symbolize_keys
 
139      end
 
 
141      def reset_fragments
 
142        @fragments = nil
 
143      end
 
 
145      private
 
 
147      def load_yaml
 
148        YAML.load_file(Rails.root.join('config', 'fragments.yml'))
 
149      end
 
150    end
 
 
152    def initialize(template, name)
 
153      @template, @name = template, name
 
154      @fragment = Fragment.new(fragments.fetch(name))
 
155    end
 
 
157    def build
 
158      [cache_key, options]
 
159    end
 
 
161    private
 
 
163    def cache_key
 
164      ["#{name}-#{version(name)}", digest].to_param
 
165    end
 
 
167    def digest
 
168      Digest::SHA1.hexdigest(Hash[digest_keys].to_param)
 
169    end
 
 
171    def digest_keys
 
172      keys.for(fragment.keys) + dependency_keys
 
173    end
 
 
175    def dependencies
 
176      @dependencies ||= Dependencies.new(fragments)
 
177    end
 
 
179    def dependency_keys
 
180      dependencies.for(name).map{ |d| [d, version(d) ] }
 
181    end
 
 
183    def keys
 
184      @keys ||= Keys.new(template)
 
185    end
 
 
187    def fragment_keys
 
188      fragment.fetch(:keys, [])
 
189    end
 
 
191    def fragments
 
192      self.class.fragments
 
193    end
 
 
195    def version(dependency)
 
196      fragments.fetch(dependency).fetch(:version, 1)
 
197    end
 
198  end
 
 
200  def cache_for(name, &block)
 
201    cache(*(CacheKey.build(self, name)), &block)
 
202  end
 
 
204  def last_signature_at
 
205    @_last_signature_at ||= Petition.maximum(:last_signed_at)
 
206  end
 
 
208  def last_government_response_updated_at
 
209    @_last_government_response_updated_at ||= GovernmentResponse.maximum(:updated_at)
 
210  end
 
 
212  def last_debate_outcome_updated_at
 
213    @_last_debate_outcome_updated_at ||= DebateOutcome.maximum(:updated_at)
 
214  end
 
  • ManualDispatch - manually dispatches method call » reek
216  def csv_cache(name, options = nil, &block)
 
217    if controller.respond_to?(:perform_caching) && controller.perform_caching
 
218      key = ActiveSupport::Cache.expand_cache_key(name, :csv)
 
219      Rails.cache.fetch(key, options, &block)
 
220    else
 
221      yield
 
222    end
 
223  end
 
224end