aboutsummaryrefslogtreecommitdiffstats
path: root/actionmailer/lib
diff options
context:
space:
mode:
authorStan Lo <a22301613@yahoo.com.tw>2015-12-23 16:01:32 +0800
committerRafael Mendonça França <rafaelmfranca@gmail.com>2016-02-23 21:04:16 -0300
commit049b6e670f0a9d7dc03ee4ddf9334ddbeb6c7900 (patch)
treeae64580a2ead0ab9d076b400d2c382969844a321 /actionmailer/lib
parent2c02bc0a47777ad8cf98e1465c08b1a68151803e (diff)
downloadrails-049b6e670f0a9d7dc03ee4ddf9334ddbeb6c7900.tar.gz
rails-049b6e670f0a9d7dc03ee4ddf9334ddbeb6c7900.tar.bz2
rails-049b6e670f0a9d7dc03ee4ddf9334ddbeb6c7900.zip
Porting ActionController::Caching to ActionMailer::Caching
Diffstat (limited to 'actionmailer/lib')
-rw-r--r--actionmailer/lib/action_mailer.rb1
-rw-r--r--actionmailer/lib/action_mailer/base.rb1
-rw-r--r--actionmailer/lib/action_mailer/caching.rb73
-rw-r--r--actionmailer/lib/action_mailer/caching/fragments.rb148
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb1
5 files changed, 224 insertions, 0 deletions
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 55c017e338..26fbb48600 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -44,6 +44,7 @@ module ActionMailer
autoload :MailHelper
autoload :Preview
autoload :Previews, 'action_mailer/preview'
+ autoload :Caching
autoload :TestCase
autoload :TestHelper
autoload :MessageDelivery
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 4259eb0bee..9b14a96d48 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -420,6 +420,7 @@ module ActionMailer
class Base < AbstractController::Base
include DeliveryMethods
include Previews
+ include Caching
abstract!
diff --git a/actionmailer/lib/action_mailer/caching.rb b/actionmailer/lib/action_mailer/caching.rb
new file mode 100644
index 0000000000..27da8148d1
--- /dev/null
+++ b/actionmailer/lib/action_mailer/caching.rb
@@ -0,0 +1,73 @@
+require 'active_support/descendants_tracker'
+
+module ActionMailer
+ module Caching
+ extend ActiveSupport::Concern
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Fragments
+ end
+
+ module ConfigMethods
+ def cache_store
+ config.cache_store
+ end
+
+ def cache_store=(store)
+ config.cache_store = ActiveSupport::Cache.lookup_store(store)
+ end
+
+ private
+ def cache_configured?
+ perform_caching && cache_store
+ end
+ end
+
+ include AbstractController::Helpers
+ include ConfigMethods
+ include Fragments
+
+ included do
+ extend ConfigMethods
+
+ config_accessor :default_static_extension
+ self.default_static_extension ||= '.html'
+
+ config_accessor :perform_caching
+ self.perform_caching = true if perform_caching.nil?
+
+ class_attribute :_view_cache_dependencies
+ self._view_cache_dependencies = []
+ helper_method :view_cache_dependencies if respond_to?(:helper_method)
+ end
+
+ module ClassMethods
+ def view_cache_dependency(&dependency)
+ self._view_cache_dependencies += [dependency]
+ end
+ end
+
+ def view_cache_dependencies
+ self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
+ end
+
+ protected
+ # Convenience accessor.
+ def cache(key, options = {}, &block)
+ if cache_configured?
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
+ else
+ yield
+ end
+ end
+
+ def perform_caching
+ Base.perform_caching
+ end
+
+ def controller_name
+ "ActionMailer"
+ end
+ end
+end
diff --git a/actionmailer/lib/action_mailer/caching/fragments.rb b/actionmailer/lib/action_mailer/caching/fragments.rb
new file mode 100644
index 0000000000..6c70137626
--- /dev/null
+++ b/actionmailer/lib/action_mailer/caching/fragments.rb
@@ -0,0 +1,148 @@
+module ActionMailer
+ module Caching
+ # Fragment caching is used for caching various blocks within
+ # views without caching the entire action as a whole. This is
+ # useful when certain elements of an action change frequently or
+ # depend on complicated state while other parts rarely change or
+ # can be shared amongst multiple parties. The caching is done using
+ # the +cache+ helper available in the Action View. See
+ # ActionView::Helpers::CacheHelper for more information.
+ #
+ # While it's strongly recommended that you use key-based cache
+ # expiration (see links in CacheHelper for more information),
+ # it is also possible to manually expire caches. For example:
+ #
+ # expire_fragment('name_of_cache')
+ module Fragments
+ extend ActiveSupport::Concern
+
+ included do
+ if respond_to?(:class_attribute)
+ class_attribute :fragment_cache_keys
+ else
+ mattr_writer :fragment_cache_keys
+ end
+
+ self.fragment_cache_keys = []
+
+ helper_method :fragment_cache_key if respond_to?(:helper_method)
+ end
+
+ module ClassMethods
+ # Allows you to specify controller-wide key prefixes for
+ # cache fragments. Pass either a constant +value+, or a block
+ # which computes a value each time a cache key is generated.
+ #
+ # For example, you may want to prefix all fragment cache keys
+ # with a global version identifier, so you can easily
+ # invalidate all caches.
+ #
+ # class ApplicationController
+ # fragment_cache_key "v1"
+ # end
+ #
+ # When it's time to invalidate all fragments, simply change
+ # the string constant. Or, progressively roll out the cache
+ # invalidation using a computed value:
+ #
+ # class ApplicationController
+ # fragment_cache_key do
+ # @account.id.odd? ? "v1" : "v2"
+ # end
+ # end
+ def fragment_cache_key(value = nil, &key)
+ self.fragment_cache_keys += [key || ->{ value }]
+ end
+ end
+
+ # Given a key (as described in +expire_fragment+), returns
+ # a key suitable for use in reading, writing, or expiring a
+ # cached fragment. All keys begin with <tt>views/</tt>,
+ # followed by any controller-wide key prefix values, ending
+ # with the specified +key+ value. The key is expanded using
+ # ActiveSupport::Cache.expand_cache_key.
+ def fragment_cache_key(key)
+ head = self.class.fragment_cache_keys.map { |k| instance_exec(&k) }
+ tail = key.is_a?(Hash) ? url_for(key).split("://").last : key
+ ActiveSupport::Cache.expand_cache_key([*head, *tail], :views)
+ end
+
+ # Writes +content+ to the location signified by
+ # +key+ (see +expire_fragment+ for acceptable formats).
+ def write_fragment(key, content, options = nil)
+ return content unless cache_configured?
+
+ key = fragment_cache_key(key)
+ instrument_fragment_cache :write_fragment, key do
+ content = content.to_str
+ cache_store.write(key, content, options)
+ end
+ content
+ end
+
+ # Reads a cached fragment from the location signified by +key+
+ # (see +expire_fragment+ for acceptable formats).
+ def read_fragment(key, options = nil)
+ return unless cache_configured?
+
+ key = fragment_cache_key(key)
+ instrument_fragment_cache :read_fragment, key do
+ result = cache_store.read(key, options)
+ result.respond_to?(:html_safe) ? result.html_safe : result
+ end
+ end
+
+ # Check if a cached fragment from the location signified by
+ # +key+ exists (see +expire_fragment+ for acceptable formats).
+ def fragment_exist?(key, options = nil)
+ return unless cache_configured?
+ key = fragment_cache_key(key)
+
+ instrument_fragment_cache :exist_fragment?, key do
+ cache_store.exist?(key, options)
+ end
+ end
+
+ # Removes fragments from the cache.
+ #
+ # +key+ can take one of three forms:
+ #
+ # * String - This would normally take the form of a path, like
+ # <tt>pages/45/notes</tt>.
+ # * Hash - Treated as an implicit call to +url_for+, like
+ # <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
+ # * Regexp - Will remove any fragment that matches, so
+ # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
+ # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
+ # the actual filename matched looks like
+ # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
+ # only supported on caches that can iterate over all keys (unlike
+ # memcached).
+ #
+ # +options+ is passed through to the cache store's +delete+
+ # method (or <tt>delete_matched</tt>, for Regexp keys).
+ def expire_fragment(key, options = nil)
+ return unless cache_configured?
+ key = fragment_cache_key(key) unless key.is_a?(Regexp)
+
+ instrument_fragment_cache :expire_fragment, key do
+ if key.is_a?(Regexp)
+ cache_store.delete_matched(key, options)
+ else
+ cache_store.delete(key, options)
+ end
+ end
+ end
+
+ def instrument_fragment_cache(name, key) # :nodoc:
+ payload = {
+ controller: controller_name,
+ action: action_name,
+ key: key
+ }
+
+ ActiveSupport::Notifications.instrument("#{name}.action_controller", payload) { yield }
+ end
+ end
+ end
+end
diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index ae89492b0f..fce0d0f9f1 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -25,6 +25,7 @@ module ActionMailer
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
options.show_previews = Rails.env.development? if options.show_previews.nil?
+ options.perform_caching ||= true
if options.show_previews
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil