actionpack-action_caching-1.2.0/0000755000004100000410000000000013055312543016606 5ustar www-datawww-dataactionpack-action_caching-1.2.0/Rakefile0000644000004100000410000000031113055312543020246 0ustar www-datawww-data#!/usr/bin/env rake require "bundler/gem_tasks" require "rake/testtask" Rake::TestTask.new do |t| t.libs = ["test"] t.pattern = "test/**/*_test.rb" t.ruby_opts = ["-w"] end task default: :test actionpack-action_caching-1.2.0/Gemfile0000644000004100000410000000006413055312543020101 0ustar www-datawww-datasource "https://rubygems.org" gemspec gem "rails" actionpack-action_caching-1.2.0/LICENSE.txt0000644000004100000410000000207113055312543020431 0ustar www-datawww-dataCopyright (c) 2012 David Heinemeier Hansson MIT License 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. actionpack-action_caching-1.2.0/.travis.yml0000644000004100000410000000256213055312543020724 0ustar www-datawww-datalanguage: ruby sudo: false cache: bundler: true before_install: - gem install bundler rvm: - 1.9.3 - 2.0.0 - 2.1.9 - 2.2.6 - 2.3.3 - 2.4.0 gemfile: - Gemfile - gemfiles/Gemfile-4-0-stable - gemfiles/Gemfile-4-1-stable - gemfiles/Gemfile-4-2-stable - gemfiles/Gemfile-5-0-stable - gemfiles/Gemfile-edge matrix: allow_failures: - gemfile: gemfiles/Gemfile-edge exclude: - rvm: 1.9.3 gemfile: Gemfile - rvm: 2.0.0 gemfile: Gemfile - rvm: 2.1.9 gemfile: Gemfile - rvm: 1.9.3 gemfile: gemfiles/Gemfile-5-0-stable - rvm: 2.0.0 gemfile: gemfiles/Gemfile-5-0-stable - rvm: 2.1.9 gemfile: gemfiles/Gemfile-5-0-stable - rvm: 1.9.3 gemfile: gemfiles/Gemfile-edge - rvm: 2.0.0 gemfile: gemfiles/Gemfile-edge - rvm: 2.1.9 gemfile: gemfiles/Gemfile-edge - rvm: 2.4.0 gemfile: gemfiles/Gemfile-4-0-stable - rvm: 2.4.0 gemfile: gemfiles/Gemfile-4-1-stable notifications: email: false irc: on_success: change on_failure: always channels: - "irc.freenode.org#rails-contrib" campfire: on_success: change on_failure: always rooms: - secure: "WikBuknvGGTx/fNGc4qE+8WK+Glt+H+yZKhHXmavRV2zrN3hC0pTPwuGZhNs\nvkc6N9WKud7un2DtWu1v77BgFhIYjfJTRkmoZ8hoNsoHpe93W/a3s8LU30/l\nzDCKoTrqlHT5hJTmEKpNVqkhfFBPiXRFMgFWALUHiA8Q4Z9BUIc=" actionpack-action_caching-1.2.0/lib/0000755000004100000410000000000013055312543017354 5ustar www-datawww-dataactionpack-action_caching-1.2.0/lib/actionpack/0000755000004100000410000000000013055312543021470 5ustar www-datawww-dataactionpack-action_caching-1.2.0/lib/actionpack/action_caching.rb0000644000004100000410000000005413055312543024745 0ustar www-datawww-datarequire "actionpack/action_caching/railtie" actionpack-action_caching-1.2.0/lib/actionpack/action_caching/0000755000004100000410000000000013055312543024421 5ustar www-datawww-dataactionpack-action_caching-1.2.0/lib/actionpack/action_caching/railtie.rb0000644000004100000410000000045113055312543026377 0ustar www-datawww-datarequire "rails/railtie" module ActionPack module ActionCaching class Railtie < Rails::Railtie initializer "action_pack.action_caching" do ActiveSupport.on_load(:action_controller) do require "action_controller/action_caching" end end end end end actionpack-action_caching-1.2.0/lib/action_controller/0000755000004100000410000000000013055312543023074 5ustar www-datawww-dataactionpack-action_caching-1.2.0/lib/action_controller/action_caching.rb0000644000004100000410000000036613055312543026357 0ustar www-datawww-datarequire "action_controller/caching/actions" module ActionController module Caching eager_autoload do autoload :Actions end include Actions end end ActionController::Base.send(:include, ActionController::Caching::Actions) actionpack-action_caching-1.2.0/lib/action_controller/caching/0000755000004100000410000000000013055312543024470 5ustar www-datawww-dataactionpack-action_caching-1.2.0/lib/action_controller/caching/actions.rb0000644000004100000410000002151513055312543026461 0ustar www-datawww-datarequire "set" module ActionController module Caching # Action caching is similar to page caching by the fact that the entire # output of the response is cached, but unlike page caching, every # request still goes through Action Pack. The key benefit of this is # that filters run before the cache is served, which allows for # authentication and other restrictions on whether someone is allowed # to execute such action. # # class ListsController < ApplicationController # before_action :authenticate, except: :public # # caches_page :public # caches_action :index, :show # end # # In this example, the +public+ action doesn't require authentication # so it's possible to use the faster page caching. On the other hand # +index+ and +show+ require authentication. They can still be cached, # but we need action caching for them. # # Action caching uses fragment caching internally and an around # filter to do the job. The fragment cache is named according to # the host and path of the request. A page that is accessed at # http://david.example.com/lists/show/1 will result in a fragment named # david.example.com/lists/show/1. This allows the cacher to # differentiate between david.example.com/lists/ and # jamis.example.com/lists/ -- which is a helpful way of assisting # the subdomain-as-account-key pattern. # # Different representations of the same resource, e.g. # http://david.example.com/lists and # http://david.example.com/lists.xml # are treated like separate requests and so are cached separately. # Keep in mind when expiring an action cache that # action: "lists" is not the same as # action: "lists", format: :xml. # # You can modify the default action cache path by passing a # :cache_path option. This will be passed directly to # ActionCachePath.new. This is handy for actions with # multiple possible routes that should be cached differently. If a # block is given, it is called with the current controller instance. # If an object that responds to call is given, it'll be called # with the current controller instance. # # And you can also use :if (or :unless) to pass a # proc that specifies when the action should be cached. # # As of Rails 3.0, you can also pass :expires_in with a time # interval (in seconds) to schedule expiration of the cached item. # # The following example depicts some of the points made above: # # class CachePathCreator # def initialize(name) # @name = name # end # # def call(controller) # "cache-path-#{@name}" # end # end # # # class ListsController < ApplicationController # before_action :authenticate, except: :public # # caches_page :public # # caches_action :index, if: Proc.new do # !request.format.json? # cache if is not a JSON request # end # # caches_action :show, cache_path: { project: 1 }, # expires_in: 1.hour # # caches_action :feed, cache_path: Proc.new do # if params[:user_id] # user_list_url(params[:user_id, params[:id]) # else # list_url(params[:id]) # end # end # # caches_action :posts, cache_path: CachePathCreator.new("posts") # end # # If you pass layout: false, it will only cache your action # content. That's useful when your layout has dynamic information. # # Warning: If the format of the request is determined by the Accept HTTP # header the Content-Type of the cached response could be wrong because # no information about the MIME type is stored in the cache key. So, if # you first ask for MIME type M in the Accept header, a cache entry is # created, and then perform a second request to the same resource asking # for a different MIME type, you'd get the content cached for M. # # The :format parameter is taken into account though. The safest # way to cache by MIME type is to pass the format in the route. module Actions extend ActiveSupport::Concern module ClassMethods # Declares that +actions+ should be cached. # See ActionController::Caching::Actions for details. def caches_action(*actions) return unless cache_configured? options = actions.extract_options! options[:layout] = true unless options.key?(:layout) filter_options = options.extract!(:if, :unless).merge(only: actions) cache_options = options.extract!(:layout, :cache_path).merge(store_options: options) around_action ActionCacheFilter.new(cache_options), filter_options end end def _save_fragment(name, options) content = "" response_body.each do |parts| content << parts end if caching_allowed? write_fragment(name, content, options) else content end end def caching_allowed? (request.get? || request.head?) && response.status == 200 end protected def expire_action(options = {}) return unless cache_configured? if options.is_a?(Hash) && options[:action].is_a?(Array) options[:action].each { |action| expire_action(options.merge(action: action)) } else expire_fragment(ActionCachePath.new(self, options, false).path) end end class ActionCacheFilter # :nodoc: def initialize(options, &block) @cache_path, @store_options, @cache_layout = options.values_at(:cache_path, :store_options, :layout) end def around(controller) cache_layout = expand_option(controller, @cache_layout) path_options = expand_option(controller, @cache_path) cache_path = ActionCachePath.new(controller, path_options || {}) body = controller.read_fragment(cache_path.path, @store_options) unless body controller.action_has_layout = false unless cache_layout yield controller.action_has_layout = true body = controller._save_fragment(cache_path.path, @store_options) end body = render_to_string(controller, body) unless cache_layout controller.response_body = body controller.content_type = Mime[cache_path.extension || :html] end if ActionPack::VERSION::STRING < "4.1" def render_to_string(controller, body) controller.render_to_string(text: body, layout: true) end else def render_to_string(controller, body) controller.render_to_string(html: body, layout: true) end end private def expand_option(controller, option) option = option.to_proc if option.respond_to?(:to_proc) if option.is_a?(Proc) case option.arity when -2, -1, 1 controller.instance_exec(controller, &option) when 0 controller.instance_exec(&option) else raise ArgumentError, "Invalid proc arity of #{option.arity} - proc options should have an arity of 0 or 1" end elsif option.respond_to?(:call) option.call(controller) else option end end end class ActionCachePath attr_reader :path, :extension # If +infer_extension+ is +true+, the cache path extension is looked up from the request's # path and format. This is desirable when reading and writing the cache, but not when # expiring the cache - +expire_action+ should expire the same files regardless of the # request format. def initialize(controller, options = {}, infer_extension = true) if infer_extension if controller.params.key?(:format) @extension = controller.params[:format] elsif !controller.request.format.html? @extension = controller.request.format.to_sym else @extension = nil end options.reverse_merge!(format: @extension) if options.is_a?(Hash) end path = controller.url_for(options).split("://", 2).last @path = normalize!(path) end private def normalize!(path) ext = URI.parser.escape(extension.to_s) if extension path << "index" if path[-1] == ?/ path << ".#{ext}" if extension && !path.split("?", 2).first.ends_with?(".#{ext}") URI.parser.unescape(path) end end end end end actionpack-action_caching-1.2.0/gemfiles/0000755000004100000410000000000013055312543020401 5ustar www-datawww-dataactionpack-action_caching-1.2.0/gemfiles/Gemfile-5-0-stable0000644000004100000410000000015413055312543023443 0ustar www-datawww-datasource "https://rubygems.org" gemspec path: ".." gem "rails", github: "rails/rails", branch: "5-0-stable" actionpack-action_caching-1.2.0/gemfiles/Gemfile-4-2-stable0000644000004100000410000000027313055312543023446 0ustar www-datawww-datasource "https://rubygems.org" gemspec path: ".." gem "rails", github: "rails/rails", branch: "4-2-stable" gem "mime-types", "< 3" if RUBY_VERSION < "2.1" gem "nokogiri", "< 1.7" end actionpack-action_caching-1.2.0/gemfiles/Gemfile-4-1-stable0000644000004100000410000000020413055312543023437 0ustar www-datawww-datasource "https://rubygems.org" gemspec path: ".." gem "rails", github: "rails/rails", branch: "4-1-stable" gem "mime-types", "< 3" actionpack-action_caching-1.2.0/gemfiles/Gemfile-edge0000644000004100000410000000023313055312543022574 0ustar www-datawww-datasource "https://rubygems.org" gemspec path: ".." gem "rails", github: "rails/rails", branch: "master" gem "arel", github: "rails/arel", branch: "master" actionpack-action_caching-1.2.0/gemfiles/Gemfile-4-0-stable0000644000004100000410000000020413055312543023436 0ustar www-datawww-datasource "https://rubygems.org" gemspec path: ".." gem "rails", github: "rails/rails", branch: "4-0-stable" gem "mime-types", "< 3" actionpack-action_caching-1.2.0/.rubocop.yml0000644000004100000410000000507013055312543021062 0ustar www-datawww-dataAllCops: TargetRubyVersion: 2.2 # RuboCop has a bunch of cops enabled by default. This setting tells RuboCop # to ignore them, so only the ones explicitly set in this file are enabled. DisabledByDefault: true # Prefer &&/|| over and/or. Style/AndOr: Enabled: true # Do not use braces for hash literals when they are the last argument of a # method call. Style/BracesAroundHashParameters: Enabled: true # Align `when` with `case`. Style/CaseIndentation: Enabled: true # Align comments with method definitions. Style/CommentIndentation: Enabled: true # No extra empty lines. Style/EmptyLines: Enabled: true # In a regular class definition, no empty lines around the body. Style/EmptyLinesAroundClassBody: Enabled: true # In a regular module definition, no empty lines around the body. Style/EmptyLinesAroundModuleBody: Enabled: true # Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }. Style/HashSyntax: Enabled: true # Method definitions after `private` or `protected` isolated calls need one # extra level of indentation. Style/IndentationConsistency: Enabled: true EnforcedStyle: rails # Two spaces, no tabs (for indentation). Style/IndentationWidth: Enabled: true Style/SpaceAfterColon: Enabled: true Style/SpaceAfterComma: Enabled: true Style/SpaceAroundEqualsInParameterDefault: Enabled: true Style/SpaceAroundKeyword: Enabled: true Style/SpaceAroundOperators: Enabled: true Style/SpaceBeforeFirstArg: Enabled: true # Defining a method with parameters needs parentheses. Style/MethodDefParentheses: Enabled: true # Use `foo {}` not `foo{}`. Style/SpaceBeforeBlockBraces: Enabled: true # Use `foo { bar }` not `foo {bar}`. Style/SpaceInsideBlockBraces: Enabled: true # Use `{ a: 1 }` not `{a:1}`. Style/SpaceInsideHashLiteralBraces: Enabled: true Style/SpaceInsideParens: Enabled: true # Check quotes usage according to lint rule below. Style/StringLiterals: Enabled: true EnforcedStyle: double_quotes # Detect hard tabs, no hard tabs. Style/Tab: Enabled: true # Blank lines should not have any spaces. Style/TrailingBlankLines: Enabled: true # No trailing whitespace. Style/TrailingWhitespace: Enabled: true # Use quotes for string literals when they are enough. Style/UnneededPercentQ: Enabled: true # Align `end` with the matching keyword or starting expression except for # assignments, where it should be aligned with the LHS. Lint/EndAlignment: Enabled: true AlignWith: variable # Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg. Lint/RequireParentheses: Enabled: true actionpack-action_caching-1.2.0/test/0000755000004100000410000000000013055312543017565 5ustar www-datawww-dataactionpack-action_caching-1.2.0/test/fixtures/0000755000004100000410000000000013055312543021436 5ustar www-datawww-dataactionpack-action_caching-1.2.0/test/fixtures/layouts/0000755000004100000410000000000013055312543023136 5ustar www-datawww-dataactionpack-action_caching-1.2.0/test/fixtures/layouts/talk_from_action.html.erb0000644000004100000410000000006313055312543030105 0ustar www-datawww-data<%= params[:title] %> <%= yield -%> actionpack-action_caching-1.2.0/test/abstract_unit.rb0000644000004100000410000000044513055312543022757 0ustar www-datawww-datarequire "bundler/setup" require "minitest/autorun" require "action_controller" require "active_record" require "action_controller/action_caching" FIXTURE_LOAD_PATH = File.expand_path("../fixtures", __FILE__) if ActiveSupport.respond_to?(:test_order) ActiveSupport.test_order = :random end actionpack-action_caching-1.2.0/test/caching_test.rb0000644000004100000410000006311313055312543022551 0ustar www-datawww-datarequire "abstract_unit" require "mocha/setup" CACHE_DIR = "test_cache" # Don't change "../tmp" cavalierly or you might hose something you don't want hosed TEST_TMP_DIR = File.expand_path("../tmp", __FILE__) FILE_STORE_PATH = File.join(TEST_TMP_DIR, CACHE_DIR) class CachingController < ActionController::Base abstract! self.cache_store = :file_store, FILE_STORE_PATH end class CachePath def call(controller) ["controller", controller.params[:id]].compact.join("-") end end class ActionCachingTestController < CachingController rescue_from(Exception) { head 500 } rescue_from(ActionController::UnknownFormat) { head :not_acceptable } if defined? ActiveRecord rescue_from(ActiveRecord::RecordNotFound) { head :not_found } end self.view_paths = FIXTURE_LOAD_PATH before_action only: :with_symbol_format do request.params[:format] = :json end caches_action :index, :redirected, :forbidden, if: ->(c) { c.request.format && !c.request.format.json? }, expires_in: 1.hour caches_action :show, cache_path: "http://test.host/custom/show" caches_action :edit, cache_path: ->(c) { c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" } caches_action :custom_cache_path, cache_path: CachePath.new caches_action :symbol_cache_path, cache_path: :cache_path_protected_method caches_action :with_layout caches_action :with_format_and_http_param, cache_path: ->(c) { { key: "value" } } caches_action :with_symbol_format, cache_path: "http://test.host/action_caching_test/with_symbol_format" caches_action :not_url_cache_path, cache_path: ->(c) { "#{c.params[:action]}_key" } caches_action :not_url_cache_path_no_args, cache_path: -> { "#{params[:action]}_key" } caches_action :layout_false, layout: false caches_action :with_layout_proc_param, layout: ->(c) { c.params[:layout] != "false" } caches_action :with_layout_proc_param_no_args, layout: -> { params[:layout] != "false" } caches_action :record_not_found, :four_oh_four, :simple_runtime_error caches_action :streaming caches_action :invalid caches_action :accept layout "talk_from_action" def index @cache_this = MockTime.now.to_f.to_s render plain: @cache_this end def redirected redirect_to action: "index" end def forbidden render plain: "Forbidden" response.status = "403 Forbidden" end def with_layout @cache_this = MockTime.now.to_f.to_s render html: @cache_this, layout: true end def with_format_and_http_param @cache_this = MockTime.now.to_f.to_s render plain: @cache_this end def with_symbol_format @cache_this = MockTime.now.to_f.to_s render json: { timestamp: @cache_this } end def not_url_cache_path render plain: "cache_this" end alias_method :not_url_cache_path_no_args, :not_url_cache_path def record_not_found raise ActiveRecord::RecordNotFound, "oops!" end def four_oh_four render plain: "404'd!", status: 404 end def simple_runtime_error raise "oops!" end alias_method :show, :index alias_method :edit, :index alias_method :destroy, :index alias_method :custom_cache_path, :index alias_method :symbol_cache_path, :index alias_method :layout_false, :with_layout alias_method :with_layout_proc_param, :with_layout alias_method :with_layout_proc_param_no_args, :with_layout def expire expire_action controller: "action_caching_test", action: "index" head :ok end def expire_xml expire_action controller: "action_caching_test", action: "index", format: "xml" head :ok end def expire_with_url_string expire_action url_for(controller: "action_caching_test", action: "index") head :ok end def streaming render plain: "streaming", stream: true end def invalid @cache_this = MockTime.now.to_f.to_s respond_to do |format| format.json { render json: @cache_this } end end def accept @cache_this = MockTime.now.to_f.to_s respond_to do |format| format.html { render html: @cache_this } format.json { render json: @cache_this } end end def expire_accept if params.key?(:format) expire_action action: "accept", format: params[:format] elsif !request.format.html? expire_action action: "accept", format: request.format.to_sym else expire_action action: "accept" end head :ok end protected def cache_path_protected_method ["controller", params[:id]].compact.join("-") end if ActionPack::VERSION::STRING < "4.1" def render(options) if options.key?(:plain) super({ text: options.delete(:plain) }.merge(options)) response.content_type = "text/plain" elsif options.key?(:html) super({ text: options.delete(:html) }.merge(options)) response.content_type = "text/html" else super end end end end class MockTime < Time # Let Time spicy to assure that Time.now != Time.now def to_f super + rand end end class ActionCachingMockController attr_accessor :mock_url_for attr_accessor :mock_path def initialize yield self if block_given? end def url_for(*args) @mock_url_for end def params request.parameters end def request Object.new.instance_eval <<-EVAL def path; "#{@mock_path}" end def format; "all" end def parameters; { format: nil }; end self EVAL end end class ActionCacheTest < ActionController::TestCase tests ActionCachingTestController def setup super @routes = ActionDispatch::Routing::RouteSet.new @request.host = "hostname.com" FileUtils.mkdir_p(FILE_STORE_PATH) @path_class = ActionController::Caching::Actions::ActionCachePath @mock_controller = ActionCachingMockController.new end def teardown super FileUtils.rm_rf(File.dirname(FILE_STORE_PATH)) end def test_simple_action_cache_with_http_head draw do get "/action_caching_test", to: "action_caching_test#index" end head :index assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?("hostname.com/action_caching_test") head :index assert_response :success assert_equal cached_time, @response.body end def test_simple_action_cache draw do get "/action_caching_test", to: "action_caching_test#index" end get :index assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?("hostname.com/action_caching_test") get :index assert_response :success assert_equal cached_time, @response.body end def test_simple_action_not_cached draw do get "/action_caching_test/destroy", to: "action_caching_test#destroy" end get :destroy assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert !fragment_exist?("hostname.com/action_caching_test/destroy") get :destroy assert_response :success assert_not_equal cached_time, @response.body end def test_action_cache_with_layout draw do get "/action_caching_test/with_layout", to: "action_caching_test#with_layout" end get :with_layout assert_response :success cached_time = content_to_cache assert_not_equal cached_time, @response.body assert fragment_exist?("hostname.com/action_caching_test/with_layout") get :with_layout assert_response :success assert_not_equal cached_time, @response.body assert_equal @response.body, read_fragment("hostname.com/action_caching_test/with_layout") end def test_action_cache_with_layout_and_layout_cache_false draw do get "/action_caching_test/layout_false", to: "action_caching_test#layout_false" end get :layout_false, params: { title: "Request 1" } assert_response :success cached_time = content_to_cache assert_equal "Request 1\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/layout_false") get :layout_false, params: { title: "Request 2" } assert_response :success assert_equal "Request 2\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/layout_false") end def test_action_cache_with_layout_and_layout_cache_false_via_proc draw do get "/action_caching_test/with_layout_proc_param", to: "action_caching_test#with_layout_proc_param" end get :with_layout_proc_param, params: { title: "Request 1", layout: "false" } assert_response :success cached_time = content_to_cache assert_equal "Request 1\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/with_layout_proc_param") get :with_layout_proc_param, params: { title: "Request 2", layout: "false" } assert_response :success assert_equal "Request 2\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/with_layout_proc_param") end def test_action_cache_with_layout_and_layout_cache_true_via_proc draw do get "/action_caching_test/with_layout_proc_param", to: "action_caching_test#with_layout_proc_param" end get :with_layout_proc_param, params: { title: "Request 1", layout: "true" } assert_response :success cached_time = content_to_cache assert_equal "Request 1\n#{cached_time}", @response.body assert_equal "Request 1\n#{cached_time}", read_fragment("hostname.com/action_caching_test/with_layout_proc_param") get :with_layout_proc_param, params: { title: "Request 2", layout: "true" } assert_response :success assert_equal "Request 1\n#{cached_time}", @response.body assert_equal "Request 1\n#{cached_time}", read_fragment("hostname.com/action_caching_test/with_layout_proc_param") end def test_action_cache_conditional_options draw do get "/action_caching_test", to: "action_caching_test#index" end @request.accept = "application/json" get :index assert_response :success assert !fragment_exist?("hostname.com/action_caching_test") end def test_action_cache_with_format_and_http_param draw do get "/action_caching_test/with_format_and_http_param", to: "action_caching_test#with_format_and_http_param" end get :with_format_and_http_param, format: "json" assert_response :success assert !fragment_exist?("hostname.com/action_caching_test/with_format_and_http_param.json?key=value.json") assert fragment_exist?("hostname.com/action_caching_test/with_format_and_http_param.json?key=value") end def test_action_cache_with_symbol_format draw do get "/action_caching_test/with_symbol_format", to: "action_caching_test#with_symbol_format" end get :with_symbol_format assert_response :success assert !fragment_exist?("test.host/action_caching_test/with_symbol_format") assert fragment_exist?("test.host/action_caching_test/with_symbol_format.json") end def test_action_cache_not_url_cache_path draw do get "/action_caching_test/not_url_cache_path", to: "action_caching_test#not_url_cache_path" end get :not_url_cache_path assert_response :success assert !fragment_exist?("test.host/action_caching_test/not_url_cache_path") assert fragment_exist?("not_url_cache_path_key") end def test_action_cache_with_store_options draw do get "/action_caching_test", to: "action_caching_test#index" end MockTime.expects(:now).returns(12345).once @controller.expects(:read_fragment).with("hostname.com/action_caching_test", expires_in: 1.hour).once @controller.expects(:write_fragment).with("hostname.com/action_caching_test", "12345.0", expires_in: 1.hour).once get :index assert_response :success end def test_action_cache_with_custom_cache_path draw do get "/action_caching_test/show", to: "action_caching_test#show" end get :show assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?("test.host/custom/show") get :show assert_response :success assert_equal cached_time, @response.body end def test_action_cache_with_custom_cache_path_in_block draw do get "/action_caching_test/edit(/:id)", to: "action_caching_test#edit" end get :edit assert_response :success assert fragment_exist?("test.host/edit") get :edit, params: { id: 1 } assert_response :success assert fragment_exist?("test.host/1;edit") end def test_action_cache_with_custom_cache_path_with_custom_object draw do get "/action_caching_test/custom_cache_path(/:id)", to: "action_caching_test#custom_cache_path" end get :custom_cache_path assert_response :success assert fragment_exist?("controller") get :custom_cache_path, params: { id: 1 } assert_response :success assert fragment_exist?("controller-1") end def test_action_cache_with_symbol_cache_path draw do get "/action_caching_test/symbol_cache_path(/:id)", to: "action_caching_test#symbol_cache_path" end get :symbol_cache_path assert_response :success assert fragment_exist?("controller") get :symbol_cache_path, params: { id: 1 } assert_response :success assert fragment_exist?("controller-1") end def test_cache_expiration draw do get "/action_caching_test", to: "action_caching_test#index" get "/action_caching_test/expire", to: "action_caching_test#expire" end get :index assert_response :success cached_time = content_to_cache get :index assert_response :success assert_equal cached_time, @response.body get :expire assert_response :success get :index assert_response :success new_cached_time = content_to_cache assert_not_equal cached_time, @response.body get :index assert_response :success assert_equal new_cached_time, @response.body end def test_cache_expiration_isnt_affected_by_request_format draw do get "/action_caching_test", to: "action_caching_test#index" get "/action_caching_test/expire", to: "action_caching_test#expire" end get :index cached_time = content_to_cache @request.request_uri = "/action_caching_test/expire.xml" get :expire, format: :xml assert_response :success get :index assert_response :success assert_not_equal cached_time, @response.body end def test_cache_expiration_with_url_string draw do get "/action_caching_test", to: "action_caching_test#index" get "/action_caching_test/expire_with_url_string", to: "action_caching_test#expire_with_url_string" end get :index cached_time = content_to_cache @request.request_uri = "/action_caching_test/expire_with_url_string" get :expire_with_url_string assert_response :success get :index assert_response :success assert_not_equal cached_time, @response.body end def test_cache_is_scoped_by_subdomain draw do get "/action_caching_test", to: "action_caching_test#index" end @request.host = "jamis.hostname.com" get :index assert_response :success jamis_cache = content_to_cache @request.host = "david.hostname.com" get :index assert_response :success david_cache = content_to_cache assert_not_equal jamis_cache, @response.body @request.host = "jamis.hostname.com" get :index assert_response :success assert_equal jamis_cache, @response.body @request.host = "david.hostname.com" get :index assert_response :success assert_equal david_cache, @response.body end def test_redirect_is_not_cached draw do get "/action_caching_test", to: "action_caching_test#index" get "/action_caching_test/redirected", to: "action_caching_test#redirected" end get :redirected assert_response :redirect get :redirected assert_response :redirect end def test_forbidden_is_not_cached draw do get "/action_caching_test/forbidden", to: "action_caching_test#forbidden" end get :forbidden assert_response :forbidden get :forbidden assert_response :forbidden end def test_xml_version_of_resource_is_treated_as_different_cache draw do get "/action_caching_test/index", to: "action_caching_test#index" get "/action_caching_test/expire_xml", to: "action_caching_test#expire_xml" end get :index, format: "xml" assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?("hostname.com/action_caching_test/index.xml") get :index, format: "xml" assert_response :success assert_equal cached_time, @response.body assert_equal "application/xml", @response.content_type get :expire_xml assert_response :success get :index, format: "xml" assert_response :success assert_not_equal cached_time, @response.body end def test_correct_content_type_is_returned_for_cache_hit draw do get "/action_caching_test/index/:id", to: "action_caching_test#index" end # run it twice to cache it the first time get :index, params: { id: "content-type" }, format: "xml" get :index, params: { id: "content-type" }, format: "xml" assert_response :success assert_equal "application/xml", @response.content_type end def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key draw do get "/action_caching_test/show", to: "action_caching_test#show" end # run it twice to cache it the first time get :show, format: "xml" get :show, format: "xml" assert_response :success assert_equal "application/xml", @response.content_type end def test_correct_content_type_is_returned_for_cache_hit_on_action_with_string_key_from_proc draw do get "/action_caching_test/edit/:id", to: "action_caching_test#edit" end # run it twice to cache it the first time get :edit, params: { id: 1 }, format: "xml" get :edit, params: { id: 1 }, format: "xml" assert_response :success assert_equal "application/xml", @response.content_type end def test_empty_path_is_normalized @mock_controller.mock_url_for = "http://example.org/" @mock_controller.mock_path = "/" assert_equal "example.org/index", @path_class.new(@mock_controller, {}).path end def test_file_extensions draw do get "/action_caching_test/index/*id", to: "action_caching_test#index", format: false end get :index, params: { id: "kitten.jpg" } get :index, params: { id: "kitten.jpg" } assert_response :success end if defined? ActiveRecord def test_record_not_found_returns_404_for_multiple_requests draw do get "/action_caching_test/record_not_found", to: "action_caching_test#record_not_found" end get :record_not_found assert_response 404 get :record_not_found assert_response 404 end end def test_four_oh_four_returns_404_for_multiple_requests draw do get "/action_caching_test/four_oh_four", to: "action_caching_test#four_oh_four" end get :four_oh_four assert_response 404 get :four_oh_four assert_response 404 end def test_four_oh_four_renders_content draw do get "/action_caching_test/four_oh_four", to: "action_caching_test#four_oh_four" end get :four_oh_four assert_equal "404'd!", @response.body end def test_simple_runtime_error_returns_500_for_multiple_requests draw do get "/action_caching_test/simple_runtime_error", to: "action_caching_test#simple_runtime_error" end get :simple_runtime_error assert_response 500 get :simple_runtime_error assert_response 500 end def test_action_caching_plus_streaming draw do get "/action_caching_test/streaming", to: "action_caching_test#streaming" end get :streaming assert_response :success assert_match(/streaming/, @response.body) assert fragment_exist?("hostname.com/action_caching_test/streaming") end def test_invalid_format_returns_not_acceptable draw do get "/action_caching_test/invalid", to: "action_caching_test#invalid" end get :invalid, format: "json" assert_response :success cached_time = content_to_cache assert_equal cached_time, @response.body assert fragment_exist?("hostname.com/action_caching_test/invalid.json") get :invalid, format: "json" assert_response :success assert_equal cached_time, @response.body get :invalid, format: "xml" assert_response :not_acceptable get :invalid, format: "\xC3\x83" assert_response :not_acceptable end def test_format_from_accept_header draw do get "/action_caching_test/accept", to: "action_caching_test#accept" get "/action_caching_test/accept/expire", to: "action_caching_test#expire_accept" end # Cache the JSON format get_json :accept json_cached_time = content_to_cache assert_cached json_cached_time, "application/json" # Check that the JSON format is cached get_json :accept assert_cached json_cached_time, "application/json" # Cache the HTML format get_html :accept html_cached_time = content_to_cache assert_cached html_cached_time # Check that it's not the JSON format assert_not_equal json_cached_time, @response.body # Check that the HTML format is cached get_html :accept assert_cached html_cached_time # Check that the JSON format is still cached get_json :accept assert_cached json_cached_time, "application/json" # Expire the JSON format get_json :expire_accept assert_response :success # Check that the HTML format is still cached get_html :accept assert_cached html_cached_time # Check the JSON format was expired get_json :accept new_json_cached_time = content_to_cache assert_cached new_json_cached_time, "application/json" assert_not_equal json_cached_time, @response.body # Expire the HTML format get_html :expire_accept assert_response :success # Check that the JSON format is still cached get_json :accept assert_cached new_json_cached_time, "application/json" # Check the HTML format was expired get_html :accept new_html_cached_time = content_to_cache assert_cached new_html_cached_time assert_not_equal html_cached_time, @response.body end def test_explicit_html_format_is_used_for_fragment_path draw do get "/action_caching_test/accept", to: "action_caching_test#accept" get "/action_caching_test/accept/expire", to: "action_caching_test#expire_accept" end get :accept, format: "html" cached_time = content_to_cache assert_cached cached_time assert fragment_exist?("hostname.com/action_caching_test/accept.html") get :accept, format: "html" cached_time = content_to_cache assert_cached cached_time get :expire_accept, format: "html" assert_response :success assert !fragment_exist?("hostname.com/action_caching_test/accept.html") get :accept, format: "html" assert_not_cached cached_time end def test_lambda_arity_with_cache_path draw do get "/action_caching_test/not_url_cache_path_no_args", to: "action_caching_test#not_url_cache_path_no_args" end get :not_url_cache_path_no_args assert_response :success assert !fragment_exist?("test.host/action_caching_test/not_url_cache_path_no_args") assert fragment_exist?("not_url_cache_path_no_args_key") end def test_lambda_arity_with_layout draw do get "/action_caching_test/with_layout_proc_param_no_args", to: "action_caching_test#with_layout_proc_param_no_args" end get :with_layout_proc_param_no_args, params: { title: "Request 1", layout: "false" } assert_response :success cached_time = content_to_cache assert_equal "Request 1\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/with_layout_proc_param_no_args") get :with_layout_proc_param_no_args, params: { title: "Request 2", layout: "false" } assert_response :success assert_equal "Request 2\n#{cached_time}", @response.body assert_equal cached_time, read_fragment("hostname.com/action_caching_test/with_layout_proc_param_no_args") end private def get_html(*args) @request.accept = "text/html" get(*args) end def get_json(*args) @request.accept = "application/json" get(*args) end def assert_cached(cache_time, content_type = "text/html") assert_response :success assert_equal cache_time, @response.body assert_equal content_type, @response.content_type end def assert_not_cached(cache_time, content_type = "text/html") assert_response :success assert_not_equal cache_time, @response.body assert_equal content_type, @response.content_type end def content_to_cache @controller.instance_variable_get(:@cache_this) end def fragment_exist?(path) @controller.fragment_exist?(path) end def read_fragment(path) @controller.read_fragment(path) end def draw(&block) @routes = ActionDispatch::Routing::RouteSet.new @routes.draw(&block) @controller.extend(@routes.url_helpers) end if ActionPack::VERSION::STRING < "5.0" def get(action, options = {}) format = options.slice(:format) params = options[:params] || {} session = options[:session] || {} flash = options[:flash] || {} super(action, params.merge(format), session, flash) end end end actionpack-action_caching-1.2.0/.gitignore0000644000004100000410000000007213055312543020575 0ustar www-datawww-data.ruby-version Gemfile.lock gemfiles/*.lock pkg/* test/tmp actionpack-action_caching-1.2.0/CHANGELOG.md0000644000004100000410000000224013055312543020415 0ustar www-datawww-data## 1.2.0 (January 23, 2014) * Support proc options with zero arguments Fixes #40. *Andrew White* * The options `:layout` and `:cache_path` now behave the same when passed a `Symbol`, `Proc` or object that responds to call. *Andrew White* * Respect `Accept` header when caching actions Fixes #18. *Andrew White* * Support Rails 4.0, 4.1, 4.2, 5.0 and edge *Eileen Uchitelle*, *Andrew White* * Call `to_s` on the extension as it may be a symbol Fixes #10. *Andrew White* ## 1.1.1 (January 2, 2014) * Fix load order problem with other gems *Andrew White* ## 1.1.0 (November 1, 2013) * Allow to use non-proc object in `cache_path` option. You can pass an object that responds to a `call` method. Example: class CachePath def call(controller) controller.id end end class TestController < ApplicationController caches_action :index, :cache_path => CachePath.new def index; end end *Piotr Niełacny* ## 1.0.0 (February 28, 2013) * Extract Action Pack - Action Caching from Rails core. *Francesco Rodriguez* actionpack-action_caching-1.2.0/.codeclimate.yml0000644000004100000410000000010513055312543021654 0ustar www-datawww-dataengines: rubocop: enabled: true ratings: paths: - "**.rb" actionpack-action_caching-1.2.0/actionpack-action_caching.gemspec0000644000004100000410000000162313055312543025220 0ustar www-datawww-dataGem::Specification.new do |gem| gem.name = "actionpack-action_caching" gem.version = "1.2.0" gem.author = "David Heinemeier Hansson" gem.email = "david@loudthinking.com" gem.description = "Action caching for Action Pack (removed from core in Rails 4.0)" gem.summary = "Action caching for Action Pack (removed from core in Rails 4.0)" gem.homepage = "https://github.com/rails/actionpack-action_caching" gem.required_ruby_version = ">= 1.9.3" gem.files = `git ls-files`.split($/) gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) } gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.require_paths = ["lib"] gem.license = "MIT" gem.add_dependency "actionpack", ">= 4.0.0", "< 6" gem.add_development_dependency "mocha" gem.add_development_dependency "activerecord", ">= 4.0.0", "< 6" end actionpack-action_caching-1.2.0/README.md0000644000004100000410000001060013055312543020062 0ustar www-datawww-dataactionpack-action_caching ========================= Action caching for Action Pack (removed from core in Rails 4.0). Installation ------------ Add this line to your application's Gemfile: gem "actionpack-action_caching" And then execute: $ bundle Or install it yourself as: $ gem install actionpack-action_caching Usage ----- Action caching is similar to page caching by the fact that the entire output of the response is cached, but unlike page caching, every request still goes through Action Pack. The key benefit of this is that filters run before the cache is served, which allows for authentication and other restrictions on whether someone is allowed to execute such action. class ListsController < ApplicationController before_action :authenticate, except: :public caches_page :public caches_action :index, :show end In this example, the `public` action doesn't require authentication so it's possible to use the faster page caching. On the other hand `index` and `show` require authentication. They can still be cached, but we need action caching for them. Action caching uses fragment caching internally and an around filter to do the job. The fragment cache is named according to the host and path of the request. A page that is accessed at `http://david.example.com/lists/show/1` will result in a fragment named `david.example.com/lists/show/1`. This allows the cacher to differentiate between `david.example.com/lists/` and `jamis.example.com/lists/` -- which is a helpful way of assisting the subdomain-as-account-key pattern. Different representations of the same resource, e.g. `http://david.example.com/lists` and `http://david.example.com/lists.xml` are treated like separate requests and so are cached separately. Keep in mind when expiring an action cache that `action: "lists"` is not the same as `action: "list", format: :xml`. You can modify the default action cache path by passing a `:cache_path` option. This will be passed directly to `ActionCachePath.new`. This is handy for actions with multiple possible routes that should be cached differently. If a proc (or an object that responds to `to_proc`) is given, it is called with the current controller instance. And you can also use `:if` (or `:unless`) to control when the action should be cached, similar to how you use them with `before_action`. As of Rails 3.0, you can also pass `:expires_in` with a time interval (in seconds) to schedule expiration of the cached item. The following example depicts some of the points made above: class ListsController < ApplicationController before_action :authenticate, except: :public # simple fragment cache caches_action :current # expire cache after an hour caches_action :archived, expires_in: 1.hour # cache unless it's a JSON request caches_action :index, unless: -> { request.format.json? } # custom cache path caches_action :show, cache_path: { project: 1 } # custom cache path with a proc caches_action :history, cache_path: -> { request.domain } # custom cache path with a symbol caches_action :feed, cache_path: :user_cache_path protected def user_cache_path if params[:user_id] user_list_url(params[:user_id], params[:id]) else list_url(params[:id]) end end end If you pass `layout: false`, it will only cache your action content. That's useful when your layout has dynamic information. Note: Both the `:format` param and the `Accept` header are taken into account when caching the fragment with the `:format` having precedence. For backwards compatibility when the `Accept` header indicates a HTML request the fragment is stored without the extension but if an explicit `"html"` is passed in `:format` then that _is_ used for storing the fragment. Contributing ------------ 1. Fork it. 2. Create your feature branch (`git checkout -b my-new-feature`). 3. Commit your changes (`git commit -am 'Add some feature'`). 4. Push to the branch (`git push origin my-new-feature`). 5. Create a new Pull Request. Code Status ----------- * [![Build Status](https://travis-ci.org/rails/actionpack-action_caching.svg?branch=master)](https://travis-ci.org/rails/actionpack-action_caching) * [![Dependency Status](https://gemnasium.com/rails/actionpack-action_caching.svg)](https://gemnasium.com/rails/actionpack-action_caching)