From 049b6e670f0a9d7dc03ee4ddf9334ddbeb6c7900 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 23 Dec 2015 16:01:32 +0800 Subject: Porting ActionController::Caching to ActionMailer::Caching --- actionmailer/lib/action_mailer.rb | 1 + actionmailer/lib/action_mailer/base.rb | 1 + actionmailer/lib/action_mailer/caching.rb | 73 +++++++ .../lib/action_mailer/caching/fragments.rb | 148 +++++++++++++ actionmailer/lib/action_mailer/railtie.rb | 1 + actionmailer/test/caching_test.rb | 232 +++++++++++++++++++++ .../test/fixtures/caching_mailer/_partial.html.erb | 3 + .../caching_mailer/fragment_cache.html.erb | 3 + .../fragment_cache_in_partials.html.erb | 1 + .../skip_fragment_cache_digesting.html.erb | 3 + actionmailer/test/mailers/caching_mailer.rb | 15 ++ 11 files changed, 481 insertions(+) create mode 100644 actionmailer/lib/action_mailer/caching.rb create mode 100644 actionmailer/lib/action_mailer/caching/fragments.rb create mode 100644 actionmailer/test/caching_test.rb create mode 100644 actionmailer/test/fixtures/caching_mailer/_partial.html.erb create mode 100644 actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb create mode 100644 actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb create mode 100644 actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb create mode 100644 actionmailer/test/mailers/caching_mailer.rb 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 views/, + # 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 + # pages/45/notes. + # * Hash - Treated as an implicit call to +url_for+, like + # { controller: 'pages', action: 'notes', id: 45} + # * Regexp - Will remove any fragment that matches, so + # %r{pages/\d*/notes} might remove all notes. Make sure you + # don't use anchors in the regex (^ or $) because + # the actual filename matched looks like + # ./cache/filename/path.cache. 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 delete_matched, 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 diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb new file mode 100644 index 0000000000..a92f43a6c5 --- /dev/null +++ b/actionmailer/test/caching_test.rb @@ -0,0 +1,232 @@ +require 'fileutils' +require 'abstract_unit' +require 'mailers/base_mailer' +require 'mailers/caching_mailer' +require 'byebug' + +CACHE_DIR = 'test_cache' +# Don't change '/../temp/' cavalierly or you might hose something you don't want hosed +FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR) + +class FragmentCachingMailer < ActionMailer::Base + abstract! + + include ActionMailer::Caching + + def some_action; end +end + +class BaseCachingTest < ActiveSupport::TestCase + def setup + super + @store = ActiveSupport::Cache::MemoryStore.new + @mailer = FragmentCachingMailer.new + @mailer.perform_caching = true + @mailer.cache_store = @store + end + + def test_fragment_cache_key + assert_equal 'views/what a key', @mailer.fragment_cache_key('what a key') + end +end + +class FragmentCachingTest < BaseCachingTest + def test_read_fragment_with_caching_enabled + @store.write('views/name', 'value') + assert_equal 'value', @mailer.read_fragment('name') + end + + def test_read_fragment_with_caching_disabled + @mailer.perform_caching = false + @store.write('views/name', 'value') + assert_nil @mailer.read_fragment('name') + end + + def test_fragment_exist_with_caching_enabled + @store.write('views/name', 'value') + assert @mailer.fragment_exist?('name') + assert !@mailer.fragment_exist?('other_name') + end + + def test_fragment_exist_with_caching_disabled + @mailer.perform_caching = false + @store.write('views/name', 'value') + assert !@mailer.fragment_exist?('name') + assert !@mailer.fragment_exist?('other_name') + end + + def test_write_fragment_with_caching_enabled + assert_nil @store.read('views/name') + assert_equal 'value', @mailer.write_fragment('name', 'value') + assert_equal 'value', @store.read('views/name') + end + + def test_write_fragment_with_caching_disabled + assert_nil @store.read('views/name') + @mailer.perform_caching = false + assert_equal 'value', @mailer.write_fragment('name', 'value') + assert_nil @store.read('views/name') + end + + def test_expire_fragment_with_simple_key + @store.write('views/name', 'value') + @mailer.expire_fragment 'name' + assert_nil @store.read('views/name') + end + + def test_expire_fragment_with_regexp + @store.write('views/name', 'value') + @store.write('views/another_name', 'another_value') + @store.write('views/primalgrasp', 'will not expire ;-)') + + @mailer.expire_fragment(/name/) + + assert_nil @store.read('views/name') + assert_nil @store.read('views/another_name') + assert_equal 'will not expire ;-)', @store.read('views/primalgrasp') + end + + def test_fragment_for + @store.write('views/expensive', 'fragment content') + fragment_computed = false + + view_context = @mailer.view_context + + buffer = 'generated till now -> '.html_safe + buffer << view_context.send(:fragment_for, 'expensive') { fragment_computed = true } + + assert !fragment_computed + assert_equal 'generated till now -> fragment content', buffer + end + + def test_html_safety + assert_nil @store.read('views/name') + content = 'value'.html_safe + assert_equal content, @mailer.write_fragment('name', content) + + cached = @store.read('views/name') + assert_equal content, cached + assert_equal String, cached.class + + html_safe = @mailer.read_fragment('name') + assert_equal content, html_safe + assert html_safe.html_safe? + end +end + +class FunctionalFragmentCachingTest < BaseCachingTest + def setup + super + @store = ActiveSupport::Cache::MemoryStore.new + @mailer = CachingMailer.new + @mailer.perform_caching = true + @mailer.cache_store = @store + end + + def test_fragment_caching + email = @mailer.fragment_cache + expected_body = "\"Welcome\"" + + assert_match expected_body, email.body.encoded + assert_match "\"Welcome\"", + @store.read("views/caching/#{template_digest("caching_mailer/fragment_cache")}") + end + + def test_fragment_caching_in_partials + email = @mailer.fragment_cache_in_partials + assert_match(/Old fragment caching in a partial/, email.body.encoded) + + assert_match("Old fragment caching in a partial", + @store.read("views/caching/#{template_digest("caching_mailer/_partial")}")) + end + + def test_skip_fragment_cache_digesting + email = @mailer.skip_fragment_cache_digesting + expected_body = "No Digest" + + assert_match expected_body, email.body.encoded + assert_match expected_body, @store.read("views/no_digest") + end + + private + + def template_digest(name) + ActionView::Digestor.digest(name: name, finder: @mailer.lookup_context) + end +end + +class CacheHelperOutputBufferTest < BaseCachingTest + + class MockController + def read_fragment(name, options) + return false + end + + def write_fragment(name, fragment, options) + fragment + end + end + + def setup + super + end + + def test_output_buffer + output_buffer = ActionView::OutputBuffer.new + controller = MockController.new + cache_helper = Class.new do + def self.controller; end; + def self.output_buffer; end; + def self.output_buffer=; end; + end + cache_helper.extend(ActionView::Helpers::CacheHelper) + + cache_helper.stub :controller, controller do + cache_helper.stub :output_buffer, output_buffer do + assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do + assert_nothing_raised do + cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + end + end + end + end + end + + def test_safe_buffer + output_buffer = ActiveSupport::SafeBuffer.new + controller = MockController.new + cache_helper = Class.new do + def self.controller; end; + def self.output_buffer; end; + def self.output_buffer=; end; + end + cache_helper.extend(ActionView::Helpers::CacheHelper) + + cache_helper.stub :controller, controller do + cache_helper.stub :output_buffer, output_buffer do + assert_called_with cache_helper, :output_buffer=, [output_buffer.class.new(output_buffer)] do + assert_nothing_raised do + cache_helper.send :fragment_for, 'Test fragment name', 'Test fragment', &Proc.new{ nil } + end + end + end + end + end +end + +class ViewCacheDependencyTest < BaseCachingTest + class NoDependenciesMailer < ActionMailer::Base + end + class HasDependenciesMailer < ActionMailer::Base + view_cache_dependency { "trombone" } + view_cache_dependency { "flute" } + end + + def test_view_cache_dependencies_are_empty_by_default + assert NoDependenciesMailer.new.view_cache_dependencies.empty? + end + + def test_view_cache_dependencies_are_listed_in_declaration_order + assert_equal %w(trombone flute), HasDependenciesMailer.new.view_cache_dependencies + end +end diff --git a/actionmailer/test/fixtures/caching_mailer/_partial.html.erb b/actionmailer/test/fixtures/caching_mailer/_partial.html.erb new file mode 100644 index 0000000000..8e965f52b4 --- /dev/null +++ b/actionmailer/test/fixtures/caching_mailer/_partial.html.erb @@ -0,0 +1,3 @@ +<% cache :caching do %> + Old fragment caching in a partial +<% end %> diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb new file mode 100644 index 0000000000..90189627da --- /dev/null +++ b/actionmailer/test/fixtures/caching_mailer/fragment_cache.html.erb @@ -0,0 +1,3 @@ +<% cache :caching do %> +"Welcome" +<% end %> diff --git a/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb b/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb new file mode 100644 index 0000000000..2957d083e8 --- /dev/null +++ b/actionmailer/test/fixtures/caching_mailer/fragment_cache_in_partials.html.erb @@ -0,0 +1 @@ +<%= render "partial" %> diff --git a/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb b/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb new file mode 100644 index 0000000000..0d52429a81 --- /dev/null +++ b/actionmailer/test/fixtures/caching_mailer/skip_fragment_cache_digesting.html.erb @@ -0,0 +1,3 @@ +<%= cache :no_digest, skip_digest: true do %> + No Digest +<% end %> diff --git a/actionmailer/test/mailers/caching_mailer.rb b/actionmailer/test/mailers/caching_mailer.rb new file mode 100644 index 0000000000..345d267a36 --- /dev/null +++ b/actionmailer/test/mailers/caching_mailer.rb @@ -0,0 +1,15 @@ +class CachingMailer < ActionMailer::Base + self.mailer_name = "caching_mailer" + + def fragment_cache + mail(subject: "welcome", template_name: "fragment_cache") + end + + def fragment_cache_in_partials + mail(subject: "welcome", template_name: "fragment_cache_in_partials") + end + + def skip_fragment_cache_digesting + mail(subject: "welcome", template_name: "skip_fragment_cache_digesting") + end +end -- cgit v1.2.3 From a24d067343e7b14f066490c011828d57599fa94f Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 30 Dec 2015 20:48:27 +0800 Subject: Move caching/fragments in ActionMailer and ActionController to action_dispatch/caching/fragments --- actionmailer/lib/action_mailer/caching.rb | 7 +- .../lib/action_mailer/caching/fragments.rb | 148 --------------------- actionpack/lib/action_controller/caching.rb | 7 +- .../lib/action_controller/caching/fragments.rb | 148 --------------------- .../lib/action_dispatch/caching/fragments.rb | 148 +++++++++++++++++++++ 5 files changed, 152 insertions(+), 306 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/caching/fragments.rb delete mode 100644 actionpack/lib/action_controller/caching/fragments.rb create mode 100644 actionpack/lib/action_dispatch/caching/fragments.rb diff --git a/actionmailer/lib/action_mailer/caching.rb b/actionmailer/lib/action_mailer/caching.rb index 27da8148d1..c5b4890178 100644 --- a/actionmailer/lib/action_mailer/caching.rb +++ b/actionmailer/lib/action_mailer/caching.rb @@ -1,14 +1,11 @@ require 'active_support/descendants_tracker' +require 'action_dispatch/caching/fragments' module ActionMailer module Caching extend ActiveSupport::Concern extend ActiveSupport::Autoload - eager_autoload do - autoload :Fragments - end - module ConfigMethods def cache_store config.cache_store @@ -26,7 +23,7 @@ module ActionMailer include AbstractController::Helpers include ConfigMethods - include Fragments + include ActionDispatch::Caching::Fragments included do extend ConfigMethods diff --git a/actionmailer/lib/action_mailer/caching/fragments.rb b/actionmailer/lib/action_mailer/caching/fragments.rb deleted file mode 100644 index 6c70137626..0000000000 --- a/actionmailer/lib/action_mailer/caching/fragments.rb +++ /dev/null @@ -1,148 +0,0 @@ -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 views/, - # 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 - # pages/45/notes. - # * Hash - Treated as an implicit call to +url_for+, like - # { controller: 'pages', action: 'notes', id: 45} - # * Regexp - Will remove any fragment that matches, so - # %r{pages/\d*/notes} might remove all notes. Make sure you - # don't use anchors in the regex (^ or $) because - # the actual filename matched looks like - # ./cache/filename/path.cache. 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 delete_matched, 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/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 0b8fa2ea09..ae868e23ed 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -1,3 +1,4 @@ +require 'action_dispatch/caching/fragments' require 'fileutils' require 'uri' @@ -26,10 +27,6 @@ module ActionController extend ActiveSupport::Concern extend ActiveSupport::Autoload - eager_autoload do - autoload :Fragments - end - module ConfigMethods def cache_store config.cache_store @@ -48,7 +45,7 @@ module ActionController include AbstractController::Callbacks include ConfigMethods - include Fragments + include ActionDispatch::Caching::Fragments included do extend ConfigMethods diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb deleted file mode 100644 index b9ad51a9cf..0000000000 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ /dev/null @@ -1,148 +0,0 @@ -module ActionController - 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 views/, - # 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 - # pages/45/notes. - # * Hash - Treated as an implicit call to +url_for+, like - # { controller: 'pages', action: 'notes', id: 45} - # * Regexp - Will remove any fragment that matches, so - # %r{pages/\d*/notes} might remove all notes. Make sure you - # don't use anchors in the regex (^ or $) because - # the actual filename matched looks like - # ./cache/filename/path.cache. 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 delete_matched, 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/actionpack/lib/action_dispatch/caching/fragments.rb b/actionpack/lib/action_dispatch/caching/fragments.rb new file mode 100644 index 0000000000..c410aaf03a --- /dev/null +++ b/actionpack/lib/action_dispatch/caching/fragments.rb @@ -0,0 +1,148 @@ +module ActionDispatch + 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 views/, + # 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 + # pages/45/notes. + # * Hash - Treated as an implicit call to +url_for+, like + # { controller: 'pages', action: 'notes', id: 45} + # * Regexp - Will remove any fragment that matches, so + # %r{pages/\d*/notes} might remove all notes. Make sure you + # don't use anchors in the regex (^ or $) because + # the actual filename matched looks like + # ./cache/filename/path.cache. 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 delete_matched, 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 -- cgit v1.2.3 From 3e824d3f5b32bae70832fe0c1c329ce062f79ff9 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 30 Dec 2015 21:07:40 +0800 Subject: Move most caching methods to ActionDispatch::Caching, and let ActionMailer and ActionController to include it --- actionmailer/lib/action_mailer/caching.rb | 57 ++------------------------- actionmailer/test/caching_test.rb | 1 - actionpack/lib/action_controller/caching.rb | 57 ++------------------------- actionpack/lib/action_dispatch/caching.rb | 61 +++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 109 deletions(-) create mode 100644 actionpack/lib/action_dispatch/caching.rb diff --git a/actionmailer/lib/action_mailer/caching.rb b/actionmailer/lib/action_mailer/caching.rb index c5b4890178..319909d1a7 100644 --- a/actionmailer/lib/action_mailer/caching.rb +++ b/actionmailer/lib/action_mailer/caching.rb @@ -1,62 +1,11 @@ -require 'active_support/descendants_tracker' -require 'action_dispatch/caching/fragments' +require 'action_dispatch/caching' module ActionMailer module Caching - extend ActiveSupport::Concern extend ActiveSupport::Autoload - - 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 ActionDispatch::Caching::Fragments - + extend ActiveSupport::Concern 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 + include ActionDispatch::Caching end def perform_caching diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb index a92f43a6c5..e48d2042ac 100644 --- a/actionmailer/test/caching_test.rb +++ b/actionmailer/test/caching_test.rb @@ -2,7 +2,6 @@ require 'fileutils' require 'abstract_unit' require 'mailers/base_mailer' require 'mailers/caching_mailer' -require 'byebug' CACHE_DIR = 'test_cache' # Don't change '/../temp/' cavalierly or you might hose something you don't want hosed diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ae868e23ed..e832f4adf4 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -1,6 +1,4 @@ -require 'action_dispatch/caching/fragments' -require 'fileutils' -require 'uri' +require 'action_dispatch/caching' module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of @@ -24,61 +22,12 @@ module ActionController # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211') # config.action_controller.cache_store = MyOwnStore.new('parameter') module Caching - extend ActiveSupport::Concern extend ActiveSupport::Autoload - - 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 - + extend ActiveSupport::Concern include AbstractController::Callbacks - include ConfigMethods - include ActionDispatch::Caching::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 + include ActionDispatch::Caching 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 end end diff --git a/actionpack/lib/action_dispatch/caching.rb b/actionpack/lib/action_dispatch/caching.rb new file mode 100644 index 0000000000..ac4a35d1db --- /dev/null +++ b/actionpack/lib/action_dispatch/caching.rb @@ -0,0 +1,61 @@ +require 'action_dispatch/caching/fragments' + +module ActionDispatch + module Caching + extend ActiveSupport::Concern + extend ActiveSupport::Autoload + + 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 ActionDispatch::Caching::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 + end +end -- cgit v1.2.3 From 1de6f7938dbc58a618a89397aad5f217a6221d17 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 31 Dec 2015 09:41:48 +0800 Subject: Make caching configuration more flexible --- actionmailer/lib/action_mailer/caching.rb | 11 +++++++---- actionpack/lib/action_controller/caching.rb | 12 ++++++++++++ actionpack/lib/action_dispatch/caching/fragments.rb | 9 ++------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/actionmailer/lib/action_mailer/caching.rb b/actionmailer/lib/action_mailer/caching.rb index 319909d1a7..8e659981a1 100644 --- a/actionmailer/lib/action_mailer/caching.rb +++ b/actionmailer/lib/action_mailer/caching.rb @@ -8,12 +8,15 @@ module ActionMailer include ActionDispatch::Caching end - def perform_caching - Base.perform_caching + def instrument_payload(key) + { + mailer: mailer_name, + key: key + } end - def controller_name - "ActionMailer" + def instrument_name + "action_mailer" end end end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index e832f4adf4..999c2636da 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -29,5 +29,17 @@ module ActionController included do include ActionDispatch::Caching end + + def instrument_payload(key) + { + controller: controller_name, + action: action_name, + key: key + } + end + + def instrument_name + "action_controller" + end end end diff --git a/actionpack/lib/action_dispatch/caching/fragments.rb b/actionpack/lib/action_dispatch/caching/fragments.rb index c410aaf03a..dcc490776e 100644 --- a/actionpack/lib/action_dispatch/caching/fragments.rb +++ b/actionpack/lib/action_dispatch/caching/fragments.rb @@ -135,13 +135,8 @@ module ActionDispatch 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 } + payload = instrument_payload(key) + ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield } end end end -- cgit v1.2.3 From 98fe07a946543a6f4d2d7dc9758f0145e1ab932e Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Wed, 6 Jan 2016 15:29:22 +0800 Subject: Move ActionMailer::Caching's content into ActionMailer::Base instead of including it Remove useless helper in ActionDispatch::Caching and fix indentation --- actionmailer/lib/action_mailer/base.rb | 16 +++++++++++++++- actionmailer/lib/action_mailer/caching.rb | 22 ---------------------- actionmailer/test/caching_test.rb | 2 -- actionpack/lib/action_dispatch/caching.rb | 13 ++++++------- 4 files changed, 21 insertions(+), 32 deletions(-) delete mode 100644 actionmailer/lib/action_mailer/caching.rb diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 9b14a96d48..25be5c3a7f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -6,6 +6,8 @@ require 'active_support/core_ext/module/anonymous' require 'action_mailer/log_subscriber' +require 'action_dispatch/caching' + module ActionMailer # Action Mailer allows you to send email from your application using a mailer model and views. # @@ -420,7 +422,6 @@ module ActionMailer class Base < AbstractController::Base include DeliveryMethods include Previews - include Caching abstract! @@ -431,6 +432,7 @@ module ActionMailer include AbstractController::Translation include AbstractController::AssetPaths include AbstractController::Callbacks + include ActionDispatch::Caching include ActionView::Layouts @@ -828,6 +830,18 @@ module ActionMailer message end + # This and #instrument_name is for caching instrument + def instrument_payload(key) + { + mailer: mailer_name, + key: key + } + end + + def instrument_name + "action_mailer" + end + protected # Used by #mail to set the content type of the message. diff --git a/actionmailer/lib/action_mailer/caching.rb b/actionmailer/lib/action_mailer/caching.rb deleted file mode 100644 index 8e659981a1..0000000000 --- a/actionmailer/lib/action_mailer/caching.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'action_dispatch/caching' - -module ActionMailer - module Caching - extend ActiveSupport::Autoload - extend ActiveSupport::Concern - included do - include ActionDispatch::Caching - end - - def instrument_payload(key) - { - mailer: mailer_name, - key: key - } - end - - def instrument_name - "action_mailer" - end - end -end diff --git a/actionmailer/test/caching_test.rb b/actionmailer/test/caching_test.rb index e48d2042ac..b4344eb167 100644 --- a/actionmailer/test/caching_test.rb +++ b/actionmailer/test/caching_test.rb @@ -10,8 +10,6 @@ FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR) class FragmentCachingMailer < ActionMailer::Base abstract! - include ActionMailer::Caching - def some_action; end end diff --git a/actionpack/lib/action_dispatch/caching.rb b/actionpack/lib/action_dispatch/caching.rb index ac4a35d1db..4e38098541 100644 --- a/actionpack/lib/action_dispatch/caching.rb +++ b/actionpack/lib/action_dispatch/caching.rb @@ -20,7 +20,6 @@ module ActionDispatch end end - include AbstractController::Helpers include ConfigMethods include ActionDispatch::Caching::Fragments @@ -50,12 +49,12 @@ module ActionDispatch 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 + def cache(key, options = {}, &block) + if cache_configured? + cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block) + else + yield + end end - end end end -- cgit v1.2.3 From f25cc147e90bf9bbfea44e3803bab8ca33ff35bd Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 7 Jan 2016 13:10:44 +0800 Subject: Remove unnecessarily included modules in ActionController::Caching --- actionpack/lib/action_controller/caching.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 999c2636da..ff3678d952 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -24,7 +24,6 @@ module ActionController module Caching extend ActiveSupport::Autoload extend ActiveSupport::Concern - include AbstractController::Callbacks included do include ActionDispatch::Caching -- cgit v1.2.3 From 3d7b0d480467b17715941503b1fe598e57c13ac1 Mon Sep 17 00:00:00 2001 From: Stan Lo Date: Thu, 7 Jan 2016 13:27:07 +0800 Subject: Change ActionMailer's default caching configuration and update generator's environment templates --- actionmailer/lib/action_mailer/railtie.rb | 2 +- .../rails/app/templates/config/environments/development.rb.tt | 10 ++++++++++ .../rails/app/templates/config/environments/production.rb.tt | 1 + .../rails/app/templates/config/environments/test.rb.tt | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb index fce0d0f9f1..215d0199af 100644 --- a/actionmailer/lib/action_mailer/railtie.rb +++ b/actionmailer/lib/action_mailer/railtie.rb @@ -25,7 +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 + options.cache_store ||= Rails.cache if options.show_previews options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt index d4e2b1c756..8e9890378a 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt @@ -15,12 +15,22 @@ Rails.application.configure do # Enable/disable caching. By default caching is disabled. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true + + <%- unless options.skip_action_mailer? -%> + config.action_mailer.perform_caching = false + <%- end -%> + config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=172800' } else config.action_controller.perform_caching = false + + <%- unless options.skip_action_mailer? -%> + config.action_mailer.perform_caching = false + <%- end -%> + config.cache_store = :null_store end <%- unless options.skip_action_mailer? -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index e14e2c7286..236e42fcd7 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -71,6 +71,7 @@ Rails.application.configure do # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "<%= app_name %>_#{Rails.env}" <%- unless options.skip_action_mailer? -%> + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index e8c8b00669..42fee3b036 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -28,6 +28,7 @@ Rails.application.configure do # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false <%- unless options.skip_action_mailer? -%> + config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the -- cgit v1.2.3 From 40fa818580a9277d8d1a02241f1422dbf83a8aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 23 Feb 2016 19:55:49 -0300 Subject: Move Caching module to Abstract Controller Abstract Controller is the common component between Action Mailer and Action Controller so if we need to share the caching component it need to be there. --- actionmailer/lib/action_mailer.rb | 1 - actionmailer/lib/action_mailer/base.rb | 4 +- actionpack/lib/abstract_controller.rb | 6 + actionpack/lib/abstract_controller/caching.rb | 62 +++++++++ .../lib/abstract_controller/caching/fragments.rb | 143 +++++++++++++++++++++ actionpack/lib/action_controller.rb | 10 +- actionpack/lib/action_controller/caching.rb | 4 +- actionpack/lib/action_dispatch/caching.rb | 60 --------- .../lib/action_dispatch/caching/fragments.rb | 143 --------------------- 9 files changed, 217 insertions(+), 216 deletions(-) create mode 100644 actionpack/lib/abstract_controller/caching.rb create mode 100644 actionpack/lib/abstract_controller/caching/fragments.rb delete mode 100644 actionpack/lib/action_dispatch/caching.rb delete mode 100644 actionpack/lib/action_dispatch/caching/fragments.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 26fbb48600..55c017e338 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -44,7 +44,6 @@ 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 25be5c3a7f..129aa1edfe 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -6,8 +6,6 @@ require 'active_support/core_ext/module/anonymous' require 'action_mailer/log_subscriber' -require 'action_dispatch/caching' - module ActionMailer # Action Mailer allows you to send email from your application using a mailer model and views. # @@ -432,7 +430,7 @@ module ActionMailer include AbstractController::Translation include AbstractController::AssetPaths include AbstractController::Callbacks - include ActionDispatch::Caching + include AbstractController::Caching include ActionView::Layouts diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 56c4033387..1e57cbaac4 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -6,6 +6,7 @@ module AbstractController extend ActiveSupport::Autoload autoload :Base + autoload :Caching autoload :Callbacks autoload :Collector autoload :DoubleRenderError, "abstract_controller/rendering" @@ -15,4 +16,9 @@ module AbstractController autoload :Translation autoload :AssetPaths autoload :UrlFor + + def self.eager_load! + super + AbstractController::Caching.eager_load! + end end diff --git a/actionpack/lib/abstract_controller/caching.rb b/actionpack/lib/abstract_controller/caching.rb new file mode 100644 index 0000000000..0dea50889a --- /dev/null +++ b/actionpack/lib/abstract_controller/caching.rb @@ -0,0 +1,62 @@ +module AbstractController + 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 ConfigMethods + include AbstractController::Caching::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 + end +end diff --git a/actionpack/lib/abstract_controller/caching/fragments.rb b/actionpack/lib/abstract_controller/caching/fragments.rb new file mode 100644 index 0000000000..3257a731ed --- /dev/null +++ b/actionpack/lib/abstract_controller/caching/fragments.rb @@ -0,0 +1,143 @@ +module AbstractController + 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 views/, + # 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 + # pages/45/notes. + # * Hash - Treated as an implicit call to +url_for+, like + # { controller: 'pages', action: 'notes', id: 45} + # * Regexp - Will remove any fragment that matches, so + # %r{pages/\d*/notes} might remove all notes. Make sure you + # don't use anchors in the regex (^ or $) because + # the actual filename matched looks like + # ./cache/filename/path.cache. 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 delete_matched, 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 = instrument_payload(key) + ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield } + end + end + end +end diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 40f33a9de0..62f5905205 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -9,12 +9,15 @@ module ActionController autoload :API autoload :Base - autoload :Caching autoload :Metal autoload :Middleware autoload :Renderer autoload :FormBuilder + eager_autoload do + autoload :Caching + end + autoload_under "metal" do autoload :ConditionalGet autoload :Cookies @@ -47,11 +50,6 @@ module ActionController autoload :TestCase, 'action_controller/test_case' autoload :TemplateAssertions, 'action_controller/test_case' - - def self.eager_load! - super - ActionController::Caching.eager_load! - end end # Common Active Support usage in Action Controller diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index ff3678d952..9df4ab6c7a 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -1,5 +1,3 @@ -require 'action_dispatch/caching' - module ActionController # \Caching is a cheap way of speeding up slow applications by keeping the result of # calculations, renderings, and database calls around for subsequent requests. @@ -26,7 +24,7 @@ module ActionController extend ActiveSupport::Concern included do - include ActionDispatch::Caching + include AbstractController::Caching end def instrument_payload(key) diff --git a/actionpack/lib/action_dispatch/caching.rb b/actionpack/lib/action_dispatch/caching.rb deleted file mode 100644 index 4e38098541..0000000000 --- a/actionpack/lib/action_dispatch/caching.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'action_dispatch/caching/fragments' - -module ActionDispatch - module Caching - extend ActiveSupport::Concern - extend ActiveSupport::Autoload - - 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 ConfigMethods - include ActionDispatch::Caching::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 - end -end diff --git a/actionpack/lib/action_dispatch/caching/fragments.rb b/actionpack/lib/action_dispatch/caching/fragments.rb deleted file mode 100644 index dcc490776e..0000000000 --- a/actionpack/lib/action_dispatch/caching/fragments.rb +++ /dev/null @@ -1,143 +0,0 @@ -module ActionDispatch - 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 views/, - # 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 - # pages/45/notes. - # * Hash - Treated as an implicit call to +url_for+, like - # { controller: 'pages', action: 'notes', id: 45} - # * Regexp - Will remove any fragment that matches, so - # %r{pages/\d*/notes} might remove all notes. Make sure you - # don't use anchors in the regex (^ or $) because - # the actual filename matched looks like - # ./cache/filename/path.cache. 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 delete_matched, 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 = instrument_payload(key) - ActiveSupport::Notifications.instrument("#{name}.#{instrument_name}", payload) { yield } - end - end - end -end -- cgit v1.2.3 From 338750393d0f19b193f0ead26adff2b9f586ec18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 24 Feb 2016 00:08:01 -0300 Subject: Move private methods to the private visibility --- actionmailer/lib/action_mailer/base.rb | 24 ++++++++++++------------ actionpack/lib/action_controller/caching.rb | 22 ++++++++++++---------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 129aa1edfe..559cd06d91 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -828,18 +828,6 @@ module ActionMailer message end - # This and #instrument_name is for caching instrument - def instrument_payload(key) - { - mailer: mailer_name, - key: key - } - end - - def instrument_name - "action_mailer" - end - protected # Used by #mail to set the content type of the message. @@ -960,6 +948,18 @@ module ActionMailer container.add_part(part) end + # This and #instrument_name is for caching instrument + def instrument_payload(key) + { + mailer: mailer_name, + key: key + } + end + + def instrument_name + "action_mailer" + end + ActiveSupport.run_load_hooks(:action_mailer, self) end end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 9df4ab6c7a..a9a8508abc 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -27,16 +27,18 @@ module ActionController include AbstractController::Caching end - def instrument_payload(key) - { - controller: controller_name, - action: action_name, - key: key - } - end + private - def instrument_name - "action_controller" - end + def instrument_payload(key) + { + controller: controller_name, + action: action_name, + key: key + } + end + + def instrument_name + "action_controller" + end end end -- cgit v1.2.3