eactionpack 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +7 -0
- data/MIT-LICENSE +21 -0
- data/README +469 -0
- data/RUNNING_UNIT_TESTS +24 -0
- data/Rakefile +146 -0
- data/install.rb +30 -0
- data/lib/action_controller.rb +79 -0
- data/lib/action_controller/assertions.rb +69 -0
- data/lib/action_controller/assertions/dom_assertions.rb +39 -0
- data/lib/action_controller/assertions/model_assertions.rb +20 -0
- data/lib/action_controller/assertions/response_assertions.rb +172 -0
- data/lib/action_controller/assertions/routing_assertions.rb +146 -0
- data/lib/action_controller/assertions/selector_assertions.rb +491 -0
- data/lib/action_controller/assertions/tag_assertions.rb +130 -0
- data/lib/action_controller/base.rb +1288 -0
- data/lib/action_controller/benchmarking.rb +94 -0
- data/lib/action_controller/caching.rb +72 -0
- data/lib/action_controller/caching/actions.rb +144 -0
- data/lib/action_controller/caching/fragments.rb +138 -0
- data/lib/action_controller/caching/pages.rb +154 -0
- data/lib/action_controller/caching/sql_cache.rb +18 -0
- data/lib/action_controller/caching/sweeping.rb +97 -0
- data/lib/action_controller/cgi_ext.rb +16 -0
- data/lib/action_controller/cgi_ext/cookie.rb +110 -0
- data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
- data/lib/action_controller/cgi_ext/session.rb +73 -0
- data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
- data/lib/action_controller/cgi_process.rb +223 -0
- data/lib/action_controller/components.rb +166 -0
- data/lib/action_controller/cookies.rb +96 -0
- data/lib/action_controller/dispatcher.rb +162 -0
- data/lib/action_controller/filters.rb +642 -0
- data/lib/action_controller/flash.rb +172 -0
- data/lib/action_controller/headers.rb +31 -0
- data/lib/action_controller/helpers.rb +221 -0
- data/lib/action_controller/http_authentication.rb +124 -0
- data/lib/action_controller/integration.rb +634 -0
- data/lib/action_controller/layout.rb +309 -0
- data/lib/action_controller/mime_responds.rb +173 -0
- data/lib/action_controller/mime_type.rb +186 -0
- data/lib/action_controller/mime_types.rb +20 -0
- data/lib/action_controller/polymorphic_routes.rb +191 -0
- data/lib/action_controller/record_identifier.rb +102 -0
- data/lib/action_controller/request.rb +764 -0
- data/lib/action_controller/request_forgery_protection.rb +140 -0
- data/lib/action_controller/request_profiler.rb +169 -0
- data/lib/action_controller/rescue.rb +258 -0
- data/lib/action_controller/resources.rb +572 -0
- data/lib/action_controller/response.rb +76 -0
- data/lib/action_controller/routing.rb +387 -0
- data/lib/action_controller/routing/builder.rb +203 -0
- data/lib/action_controller/routing/optimisations.rb +120 -0
- data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
- data/lib/action_controller/routing/route.rb +240 -0
- data/lib/action_controller/routing/route_set.rb +436 -0
- data/lib/action_controller/routing/routing_ext.rb +46 -0
- data/lib/action_controller/routing/segments.rb +283 -0
- data/lib/action_controller/session/active_record_store.rb +340 -0
- data/lib/action_controller/session/cookie_store.rb +166 -0
- data/lib/action_controller/session/drb_server.rb +32 -0
- data/lib/action_controller/session/drb_store.rb +35 -0
- data/lib/action_controller/session/mem_cache_store.rb +98 -0
- data/lib/action_controller/session_management.rb +158 -0
- data/lib/action_controller/status_codes.rb +88 -0
- data/lib/action_controller/streaming.rb +155 -0
- data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
- data/lib/action_controller/templates/rescues/_trace.erb +26 -0
- data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
- data/lib/action_controller/templates/rescues/layout.erb +29 -0
- data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
- data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
- data/lib/action_controller/templates/rescues/template_error.erb +21 -0
- data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
- data/lib/action_controller/test_case.rb +83 -0
- data/lib/action_controller/test_process.rb +526 -0
- data/lib/action_controller/url_rewriter.rb +142 -0
- data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
- data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
- data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
- data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
- data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
- data/lib/action_controller/verification.rb +130 -0
- data/lib/action_pack.rb +24 -0
- data/lib/action_pack/version.rb +9 -0
- data/lib/action_view.rb +44 -0
- data/lib/action_view/base.rb +335 -0
- data/lib/action_view/helpers/active_record_helper.rb +276 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
- data/lib/action_view/helpers/benchmark_helper.rb +33 -0
- data/lib/action_view/helpers/cache_helper.rb +40 -0
- data/lib/action_view/helpers/capture_helper.rb +161 -0
- data/lib/action_view/helpers/date_helper.rb +711 -0
- data/lib/action_view/helpers/debug_helper.rb +31 -0
- data/lib/action_view/helpers/form_helper.rb +767 -0
- data/lib/action_view/helpers/form_options_helper.rb +458 -0
- data/lib/action_view/helpers/form_tag_helper.rb +458 -0
- data/lib/action_view/helpers/javascript_helper.rb +148 -0
- data/lib/action_view/helpers/number_helper.rb +186 -0
- data/lib/action_view/helpers/record_identification_helper.rb +20 -0
- data/lib/action_view/helpers/record_tag_helper.rb +59 -0
- data/lib/action_view/helpers/sanitize_helper.rb +229 -0
- data/lib/action_view/helpers/tag_helper.rb +134 -0
- data/lib/action_view/helpers/text_helper.rb +507 -0
- data/lib/action_view/helpers/url_helper.rb +573 -0
- data/lib/action_view/inline_template.rb +20 -0
- data/lib/action_view/partial_template.rb +70 -0
- data/lib/action_view/partials.rb +158 -0
- data/lib/action_view/template.rb +125 -0
- data/lib/action_view/template_error.rb +110 -0
- data/lib/action_view/template_finder.rb +176 -0
- data/lib/action_view/template_handler.rb +34 -0
- data/lib/action_view/template_handlers/builder.rb +27 -0
- data/lib/action_view/template_handlers/compilable.rb +128 -0
- data/lib/action_view/template_handlers/erb.rb +56 -0
- data/lib/action_view/test_case.rb +58 -0
- data/lib/actionpack.rb +1 -0
- data/test/abstract_unit.rb +36 -0
- data/test/active_record_unit.rb +105 -0
- data/test/activerecord/active_record_store_test.rb +141 -0
- data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
- data/test/adv_attr_test.rb +20 -0
- data/test/controller/action_pack_assertions_test.rb +543 -0
- data/test/controller/addresses_render_test.rb +43 -0
- data/test/controller/assert_select_test.rb +331 -0
- data/test/controller/base_test.rb +219 -0
- data/test/controller/benchmark_test.rb +32 -0
- data/test/controller/caching_test.rb +581 -0
- data/test/controller/capture_test.rb +89 -0
- data/test/controller/cgi_test.rb +116 -0
- data/test/controller/components_test.rb +140 -0
- data/test/controller/content_type_test.rb +139 -0
- data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
- data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
- data/test/controller/cookie_test.rb +146 -0
- data/test/controller/custom_handler_test.rb +45 -0
- data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
- data/test/controller/dispatcher_test.rb +105 -0
- data/test/controller/fake_controllers.rb +33 -0
- data/test/controller/fake_models.rb +11 -0
- data/test/controller/filter_params_test.rb +49 -0
- data/test/controller/filters_test.rb +881 -0
- data/test/controller/flash_test.rb +146 -0
- data/test/controller/header_test.rb +14 -0
- data/test/controller/helper_test.rb +210 -0
- data/test/controller/html-scanner/cdata_node_test.rb +15 -0
- data/test/controller/html-scanner/document_test.rb +148 -0
- data/test/controller/html-scanner/node_test.rb +89 -0
- data/test/controller/html-scanner/sanitizer_test.rb +269 -0
- data/test/controller/html-scanner/tag_node_test.rb +238 -0
- data/test/controller/html-scanner/text_node_test.rb +50 -0
- data/test/controller/html-scanner/tokenizer_test.rb +131 -0
- data/test/controller/http_authentication_test.rb +54 -0
- data/test/controller/integration_test.rb +252 -0
- data/test/controller/integration_upload_test.rb +43 -0
- data/test/controller/layout_test.rb +255 -0
- data/test/controller/mime_responds_test.rb +514 -0
- data/test/controller/mime_type_test.rb +84 -0
- data/test/controller/new_render_test.rb +843 -0
- data/test/controller/polymorphic_routes_test.rb +174 -0
- data/test/controller/record_identifier_test.rb +139 -0
- data/test/controller/redirect_test.rb +289 -0
- data/test/controller/render_test.rb +484 -0
- data/test/controller/request_forgery_protection_test.rb +305 -0
- data/test/controller/request_test.rb +928 -0
- data/test/controller/rescue_test.rb +517 -0
- data/test/controller/resources_test.rb +873 -0
- data/test/controller/routing_test.rb +2464 -0
- data/test/controller/selector_test.rb +628 -0
- data/test/controller/send_file_test.rb +138 -0
- data/test/controller/session/cookie_store_test.rb +258 -0
- data/test/controller/session/mem_cache_store_test.rb +181 -0
- data/test/controller/session_fixation_test.rb +89 -0
- data/test/controller/session_management_test.rb +178 -0
- data/test/controller/test_test.rb +695 -0
- data/test/controller/url_rewriter_test.rb +310 -0
- data/test/controller/verification_test.rb +270 -0
- data/test/controller/view_paths_test.rb +140 -0
- data/test/controller/webservice_test.rb +229 -0
- data/test/fixtures/addresses/list.erb +1 -0
- data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
- data/test/fixtures/companies.yml +24 -0
- data/test/fixtures/company.rb +10 -0
- data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
- data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
- data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
- data/test/fixtures/customers/_customer.html.erb +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +49 -0
- data/test/fixtures/developer.rb +9 -0
- data/test/fixtures/developers.yml +21 -0
- data/test/fixtures/developers_projects.yml +13 -0
- data/test/fixtures/fun/games/hello_world.erb +1 -0
- data/test/fixtures/functional_caching/_partial.erb +3 -0
- data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
- data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
- data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
- data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
- data/test/fixtures/helpers/abc_helper.rb +5 -0
- data/test/fixtures/helpers/fun/games_helper.rb +3 -0
- data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
- data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
- data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
- data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
- data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
- data/test/fixtures/layouts/block_with_layout.erb +3 -0
- data/test/fixtures/layouts/builder.builder +3 -0
- data/test/fixtures/layouts/partial_with_layout.erb +3 -0
- data/test/fixtures/layouts/standard.erb +1 -0
- data/test/fixtures/layouts/talk_from_action.erb +2 -0
- data/test/fixtures/layouts/yield.erb +2 -0
- data/test/fixtures/mascot.rb +3 -0
- data/test/fixtures/mascots.yml +4 -0
- data/test/fixtures/mascots/_mascot.html.erb +1 -0
- data/test/fixtures/multipart/binary_file +0 -0
- data/test/fixtures/multipart/boundary_problem_file +10 -0
- data/test/fixtures/multipart/bracketed_param +5 -0
- data/test/fixtures/multipart/large_text_file +10 -0
- data/test/fixtures/multipart/mixed_files +0 -0
- data/test/fixtures/multipart/mona_lisa.jpg +0 -0
- data/test/fixtures/multipart/single_parameter +5 -0
- data/test/fixtures/multipart/text_file +10 -0
- data/test/fixtures/override/test/hello_world.erb +1 -0
- data/test/fixtures/override2/layouts/test/sub.erb +1 -0
- data/test/fixtures/post_test/layouts/post.html.erb +1 -0
- data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
- data/test/fixtures/post_test/post/index.html.erb +1 -0
- data/test/fixtures/post_test/post/index.iphone.erb +1 -0
- data/test/fixtures/post_test/super_post/index.html.erb +1 -0
- data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
- data/test/fixtures/project.rb +3 -0
- data/test/fixtures/projects.yml +7 -0
- data/test/fixtures/public/404.html +1 -0
- data/test/fixtures/public/500.html +1 -0
- data/test/fixtures/public/images/rails.png +0 -0
- data/test/fixtures/public/javascripts/application.js +1 -0
- data/test/fixtures/public/javascripts/bank.js +1 -0
- data/test/fixtures/public/javascripts/robber.js +1 -0
- data/test/fixtures/public/javascripts/version.1.0.js +1 -0
- data/test/fixtures/public/stylesheets/bank.css +1 -0
- data/test/fixtures/public/stylesheets/robber.css +1 -0
- data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
- data/test/fixtures/replies.yml +15 -0
- data/test/fixtures/reply.rb +7 -0
- data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
- data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
- data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
- data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
- data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
- data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
- data/test/fixtures/scope/test/modgreet.erb +1 -0
- data/test/fixtures/shared.html.erb +1 -0
- data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
- data/test/fixtures/test/_customer.erb +1 -0
- data/test/fixtures/test/_customer_counter.erb +1 -0
- data/test/fixtures/test/_customer_greeting.erb +1 -0
- data/test/fixtures/test/_form.erb +1 -0
- data/test/fixtures/test/_hash_greeting.erb +1 -0
- data/test/fixtures/test/_hash_object.erb +2 -0
- data/test/fixtures/test/_hello.builder +1 -0
- data/test/fixtures/test/_labelling_form.erb +1 -0
- data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
- data/test/fixtures/test/_partial.erb +1 -0
- data/test/fixtures/test/_partial.html.erb +1 -0
- data/test/fixtures/test/_partial.js.erb +1 -0
- data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
- data/test/fixtures/test/_partial_only.erb +1 -0
- data/test/fixtures/test/_person.erb +2 -0
- data/test/fixtures/test/_raise.html.erb +1 -0
- data/test/fixtures/test/action_talk_to_layout.erb +2 -0
- data/test/fixtures/test/block_content_for.erb +2 -0
- data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
- data/test/fixtures/test/capturing.erb +4 -0
- data/test/fixtures/test/content_for.erb +2 -0
- data/test/fixtures/test/content_for_concatenated.erb +3 -0
- data/test/fixtures/test/content_for_with_parameter.erb +2 -0
- data/test/fixtures/test/delete_with_js.rjs +2 -0
- data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/enum_rjs_test.rjs +6 -0
- data/test/fixtures/test/erb_content_for.erb +2 -0
- data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.builder +1 -0
- data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
- data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
- data/test/fixtures/test/greeting.erb +1 -0
- data/test/fixtures/test/greeting.js.rjs +1 -0
- data/test/fixtures/test/hello.builder +4 -0
- data/test/fixtures/test/hello_world.erb +1 -0
- data/test/fixtures/test/hello_world_container.builder +3 -0
- data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
- data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
- data/test/fixtures/test/hello_xml_world.builder +11 -0
- data/test/fixtures/test/list.erb +1 -0
- data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
- data/test/fixtures/test/potential_conflicts.erb +4 -0
- data/test/fixtures/test/render_file_from_template.html.erb +1 -0
- data/test/fixtures/test/render_file_with_ivar.erb +1 -0
- data/test/fixtures/test/render_file_with_locals.erb +1 -0
- data/test/fixtures/test/render_to_string_test.erb +1 -0
- data/test/fixtures/test/update_element_with_capture.erb +9 -0
- data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
- data/test/fixtures/topic.rb +3 -0
- data/test/fixtures/topics.yml +22 -0
- data/test/fixtures/topics/_topic.html.erb +1 -0
- data/test/template/active_record_helper_test.rb +268 -0
- data/test/template/asset_tag_helper_test.rb +514 -0
- data/test/template/atom_feed_helper_test.rb +179 -0
- data/test/template/benchmark_helper_test.rb +60 -0
- data/test/template/date_helper_test.rb +1791 -0
- data/test/template/deprecated_erb_variable_test.rb +9 -0
- data/test/template/erb_util_test.rb +24 -0
- data/test/template/form_helper_test.rb +885 -0
- data/test/template/form_options_helper_test.rb +1333 -0
- data/test/template/form_tag_helper_test.rb +272 -0
- data/test/template/javascript_helper_test.rb +73 -0
- data/test/template/number_helper_test.rb +97 -0
- data/test/template/record_tag_helper_test.rb +54 -0
- data/test/template/sanitize_helper_test.rb +48 -0
- data/test/template/tag_helper_test.rb +77 -0
- data/test/template/template_finder_test.rb +73 -0
- data/test/template/template_object_test.rb +95 -0
- data/test/template/test_test.rb +56 -0
- data/test/template/text_helper_test.rb +367 -0
- data/test/template/url_helper_test.rb +544 -0
- data/test/testing_sandbox.rb +15 -0
- metadata +469 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
module ActionController #:nodoc:
|
|
5
|
+
module Caching
|
|
6
|
+
# Page caching is an approach to caching where the entire action output of is stored as a HTML file that the web server
|
|
7
|
+
# can serve without going through Action Pack. This is the fastest way to cache your content as opposed to going dynamically
|
|
8
|
+
# through the process of generating the content. Unfortunately, this incredible speed-up is only available to stateless pages
|
|
9
|
+
# where all visitors are treated the same. Content management systems -- including weblogs and wikis -- have many pages that are
|
|
10
|
+
# a great fit for this approach, but account-based systems where people log in and manipulate their own data are often less
|
|
11
|
+
# likely candidates.
|
|
12
|
+
#
|
|
13
|
+
# Specifying which actions to cache is done through the <tt>caches_page</tt> class method:
|
|
14
|
+
#
|
|
15
|
+
# class WeblogController < ActionController::Base
|
|
16
|
+
# caches_page :show, :new
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# This will generate cache files such as <tt>weblog/show/5.html</tt> and <tt>weblog/new.html</tt>,
|
|
20
|
+
# which match the URLs used to trigger the dynamic generation. This is how the web server is able
|
|
21
|
+
# pick up a cache file when it exists and otherwise let the request pass on to Action Pack to generate it.
|
|
22
|
+
#
|
|
23
|
+
# Expiration of the cache is handled by deleting the cached file, which results in a lazy regeneration approach where the cache
|
|
24
|
+
# is not restored before another hit is made against it. The API for doing so mimics the options from +url_for+ and friends:
|
|
25
|
+
#
|
|
26
|
+
# class WeblogController < ActionController::Base
|
|
27
|
+
# def update
|
|
28
|
+
# List.update(params[:list][:id], params[:list])
|
|
29
|
+
# expire_page :action => "show", :id => params[:list][:id]
|
|
30
|
+
# redirect_to :action => "show", :id => params[:list][:id]
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
|
|
35
|
+
# expired.
|
|
36
|
+
#
|
|
37
|
+
# == Setting the cache directory
|
|
38
|
+
#
|
|
39
|
+
# The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
|
|
40
|
+
# For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
|
|
41
|
+
# this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
|
|
42
|
+
# web server to look in the new location for cached files.
|
|
43
|
+
#
|
|
44
|
+
# == Setting the cache extension
|
|
45
|
+
#
|
|
46
|
+
# Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
|
|
47
|
+
# order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
|
|
48
|
+
# If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
|
|
49
|
+
# extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
|
|
50
|
+
module Pages
|
|
51
|
+
def self.included(base) #:nodoc:
|
|
52
|
+
base.extend(ClassMethods)
|
|
53
|
+
base.class_eval do
|
|
54
|
+
@@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
|
|
55
|
+
cattr_accessor :page_cache_directory
|
|
56
|
+
|
|
57
|
+
@@page_cache_extension = '.html'
|
|
58
|
+
cattr_accessor :page_cache_extension
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
module ClassMethods
|
|
63
|
+
# Expires the page that was cached with the +path+ as a key. Example:
|
|
64
|
+
# expire_page "/lists/show"
|
|
65
|
+
def expire_page(path)
|
|
66
|
+
return unless perform_caching
|
|
67
|
+
|
|
68
|
+
benchmark "Expired page: #{page_cache_file(path)}" do
|
|
69
|
+
File.delete(page_cache_path(path)) if File.exist?(page_cache_path(path))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Manually cache the +content+ in the key determined by +path+. Example:
|
|
74
|
+
# cache_page "I'm the cached content", "/lists/show"
|
|
75
|
+
def cache_page(content, path)
|
|
76
|
+
return unless perform_caching
|
|
77
|
+
|
|
78
|
+
benchmark "Cached page: #{page_cache_file(path)}" do
|
|
79
|
+
FileUtils.makedirs(File.dirname(page_cache_path(path)))
|
|
80
|
+
File.open(page_cache_path(path), "wb+") { |f| f.write(content) }
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
|
|
85
|
+
# matches the triggering url.
|
|
86
|
+
#
|
|
87
|
+
# Usage:
|
|
88
|
+
#
|
|
89
|
+
# # cache the index action
|
|
90
|
+
# caches_page :index
|
|
91
|
+
#
|
|
92
|
+
# # cache the index action except for JSON requests
|
|
93
|
+
# caches_page :index, :if => Proc.new { |c| !c.request.format.json? }
|
|
94
|
+
def caches_page(*actions)
|
|
95
|
+
return unless perform_caching
|
|
96
|
+
options = actions.extract_options!
|
|
97
|
+
after_filter({:only => actions}.merge(options)) { |c| c.cache_page }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
def page_cache_file(path)
|
|
102
|
+
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
|
|
103
|
+
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
|
104
|
+
return name
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def page_cache_path(path)
|
|
108
|
+
page_cache_directory + page_cache_file(path)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Expires the page that was cached with the +options+ as a key. Example:
|
|
113
|
+
# expire_page :controller => "lists", :action => "show"
|
|
114
|
+
def expire_page(options = {})
|
|
115
|
+
return unless perform_caching
|
|
116
|
+
|
|
117
|
+
if options.is_a?(Hash)
|
|
118
|
+
if options[:action].is_a?(Array)
|
|
119
|
+
options[:action].dup.each do |action|
|
|
120
|
+
self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
|
|
121
|
+
end
|
|
122
|
+
else
|
|
123
|
+
self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
|
|
124
|
+
end
|
|
125
|
+
else
|
|
126
|
+
self.class.expire_page(options)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Manually cache the +content+ in the key determined by +options+. If no content is provided, the contents of response.body is used
|
|
131
|
+
# If no options are provided, the requested url is used. Example:
|
|
132
|
+
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
|
|
133
|
+
def cache_page(content = nil, options = nil)
|
|
134
|
+
return unless perform_caching && caching_allowed
|
|
135
|
+
|
|
136
|
+
path = case options
|
|
137
|
+
when Hash
|
|
138
|
+
url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
|
|
139
|
+
when String
|
|
140
|
+
options
|
|
141
|
+
else
|
|
142
|
+
request.path
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
self.class.cache_page(content || response.body, path)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
def caching_allowed
|
|
150
|
+
request.get? && response.headers['Status'].to_i == 200
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module ActionController #:nodoc:
|
|
2
|
+
module Caching
|
|
3
|
+
module SqlCache
|
|
4
|
+
def self.included(base) #:nodoc:
|
|
5
|
+
if defined?(ActiveRecord) && ActiveRecord::Base.respond_to?(:cache)
|
|
6
|
+
base.alias_method_chain :perform_action, :caching
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
def perform_action_with_caching
|
|
12
|
+
ActiveRecord::Base.cache do
|
|
13
|
+
perform_action_without_caching
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
module ActionController #:nodoc:
|
|
2
|
+
module Caching
|
|
3
|
+
# Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change.
|
|
4
|
+
# They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
|
|
5
|
+
#
|
|
6
|
+
# class ListSweeper < ActionController::Caching::Sweeper
|
|
7
|
+
# observe List, Item
|
|
8
|
+
#
|
|
9
|
+
# def after_save(record)
|
|
10
|
+
# list = record.is_a?(List) ? record : record.list
|
|
11
|
+
# expire_page(:controller => "lists", :action => %w( show public feed ), :id => list.id)
|
|
12
|
+
# expire_action(:controller => "lists", :action => "all")
|
|
13
|
+
# list.shares.each { |share| expire_page(:controller => "lists", :action => "show", :id => share.url_key) }
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# The sweeper is assigned in the controllers that wish to have its job performed using the <tt>cache_sweeper</tt> class method:
|
|
18
|
+
#
|
|
19
|
+
# class ListsController < ApplicationController
|
|
20
|
+
# caches_action :index, :show, :public, :feed
|
|
21
|
+
# cache_sweeper :list_sweeper, :only => [ :edit, :destroy, :share ]
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# In the example above, four actions are cached and three actions are responsible for expiring those caches.
|
|
25
|
+
#
|
|
26
|
+
# You can also name an explicit class in the declaration of a sweeper, which is needed if the sweeper is in a module:
|
|
27
|
+
#
|
|
28
|
+
# class ListsController < ApplicationController
|
|
29
|
+
# caches_action :index, :show, :public, :feed
|
|
30
|
+
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
|
|
31
|
+
# end
|
|
32
|
+
module Sweeping
|
|
33
|
+
def self.included(base) #:nodoc:
|
|
34
|
+
base.extend(ClassMethods)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
module ClassMethods #:nodoc:
|
|
38
|
+
def cache_sweeper(*sweepers)
|
|
39
|
+
configuration = sweepers.extract_options!
|
|
40
|
+
|
|
41
|
+
sweepers.each do |sweeper|
|
|
42
|
+
ActiveRecord::Base.observers << sweeper if defined?(ActiveRecord) and defined?(ActiveRecord::Base)
|
|
43
|
+
sweeper_instance = (sweeper.is_a?(Symbol) ? Object.const_get(sweeper.to_s.classify) : sweeper).instance
|
|
44
|
+
|
|
45
|
+
if sweeper_instance.is_a?(Sweeper)
|
|
46
|
+
around_filter(sweeper_instance, :only => configuration[:only])
|
|
47
|
+
else
|
|
48
|
+
after_filter(sweeper_instance, :only => configuration[:only])
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if defined?(ActiveRecord) and defined?(ActiveRecord::Observer)
|
|
56
|
+
class Sweeper < ActiveRecord::Observer #:nodoc:
|
|
57
|
+
attr_accessor :controller
|
|
58
|
+
|
|
59
|
+
def before(controller)
|
|
60
|
+
self.controller = controller
|
|
61
|
+
callback(:before) if controller.perform_caching
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def after(controller)
|
|
65
|
+
callback(:after) if controller.perform_caching
|
|
66
|
+
# Clean up, so that the controller can be collected after this request
|
|
67
|
+
self.controller = nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
protected
|
|
71
|
+
# gets the action cache path for the given options.
|
|
72
|
+
def action_path_for(options)
|
|
73
|
+
ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Retrieve instance variables set in the controller.
|
|
77
|
+
def assigns(key)
|
|
78
|
+
controller.instance_variable_get("@#{key}")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
def callback(timing)
|
|
83
|
+
controller_callback_method_name = "#{timing}_#{controller.controller_name.underscore}"
|
|
84
|
+
action_callback_method_name = "#{controller_callback_method_name}_#{controller.action_name}"
|
|
85
|
+
|
|
86
|
+
send!(controller_callback_method_name) if respond_to?(controller_callback_method_name, true)
|
|
87
|
+
send!(action_callback_method_name) if respond_to?(action_callback_method_name, true)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def method_missing(method, *arguments)
|
|
91
|
+
return if @controller.nil?
|
|
92
|
+
@controller.send!(method, *arguments)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'action_controller/cgi_ext/stdinput'
|
|
2
|
+
require 'action_controller/cgi_ext/query_extension'
|
|
3
|
+
require 'action_controller/cgi_ext/cookie'
|
|
4
|
+
require 'action_controller/cgi_ext/session'
|
|
5
|
+
|
|
6
|
+
class CGI #:nodoc:
|
|
7
|
+
include ActionController::CgiExt::Stdinput
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
alias :escapeHTML_fail_on_nil :escapeHTML
|
|
11
|
+
|
|
12
|
+
def escapeHTML(string)
|
|
13
|
+
escapeHTML_fail_on_nil(string) unless string.nil?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
CGI.module_eval { remove_const "Cookie" }
|
|
2
|
+
|
|
3
|
+
# TODO: document how this differs from stdlib CGI::Cookie
|
|
4
|
+
class CGI #:nodoc:
|
|
5
|
+
class Cookie < DelegateClass(Array)
|
|
6
|
+
attr_accessor :name, :value, :path, :domain, :expires
|
|
7
|
+
attr_reader :secure, :http_only
|
|
8
|
+
|
|
9
|
+
# Creates a new CGI::Cookie object.
|
|
10
|
+
#
|
|
11
|
+
# The contents of the cookie can be specified as a +name+ and one
|
|
12
|
+
# or more +value+ arguments. Alternatively, the contents can
|
|
13
|
+
# be specified as a single hash argument. The possible keywords of
|
|
14
|
+
# this hash are as follows:
|
|
15
|
+
#
|
|
16
|
+
# * <tt>:name</tt> - The name of the cookie. Required.
|
|
17
|
+
# * <tt>:value</tt> - The cookie's value or list of values.
|
|
18
|
+
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the
|
|
19
|
+
# base directory of the CGI script.
|
|
20
|
+
# * <tt>:domain</tt> - The domain for which this cookie applies.
|
|
21
|
+
# * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
|
|
22
|
+
# * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
|
|
23
|
+
# +false+). Secure cookies are only transmitted to HTTPS servers.
|
|
24
|
+
# * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
|
|
25
|
+
# More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+.
|
|
26
|
+
#
|
|
27
|
+
# These keywords correspond to attributes of the cookie object.
|
|
28
|
+
def initialize(name = '', *value)
|
|
29
|
+
if name.kind_of?(String)
|
|
30
|
+
@name = name
|
|
31
|
+
@value = Array(value)
|
|
32
|
+
@domain = nil
|
|
33
|
+
@expires = nil
|
|
34
|
+
@secure = false
|
|
35
|
+
@http_only = false
|
|
36
|
+
@path = nil
|
|
37
|
+
else
|
|
38
|
+
@name = name['name']
|
|
39
|
+
@value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
|
|
40
|
+
@domain = name['domain']
|
|
41
|
+
@expires = name['expires']
|
|
42
|
+
@secure = name['secure'] || false
|
|
43
|
+
@http_only = name['http_only'] || false
|
|
44
|
+
@path = name['path']
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
raise ArgumentError, "`name' required" unless @name
|
|
48
|
+
|
|
49
|
+
# simple support for IE
|
|
50
|
+
unless @path
|
|
51
|
+
%r|^(.*/)|.match(ENV['SCRIPT_NAME'])
|
|
52
|
+
@path = ($1 or '')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
super(@value)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Sets whether the Cookie is a secure cookie or not.
|
|
59
|
+
def secure=(val)
|
|
60
|
+
@secure = val == true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Sets whether the Cookie is an HTTP only cookie or not.
|
|
64
|
+
def http_only=(val)
|
|
65
|
+
@http_only = val == true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Converts the Cookie to its string representation.
|
|
69
|
+
def to_s
|
|
70
|
+
buf = ''
|
|
71
|
+
buf << @name << '='
|
|
72
|
+
buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
|
|
73
|
+
buf << '; domain=' << @domain if @domain
|
|
74
|
+
buf << '; path=' << @path if @path
|
|
75
|
+
buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
|
|
76
|
+
buf << '; secure' if @secure
|
|
77
|
+
buf << '; HttpOnly' if @http_only
|
|
78
|
+
buf
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# FIXME: work around broken 1.8.7 DelegateClass#respond_to?
|
|
82
|
+
def respond_to?(method, include_private = false)
|
|
83
|
+
return true if super(method)
|
|
84
|
+
return __getobj__.respond_to?(method, include_private)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
|
|
88
|
+
# pairs.
|
|
89
|
+
#
|
|
90
|
+
# cookies = CGI::Cookie::parse("raw_cookie_string")
|
|
91
|
+
# # => { "name1" => cookie1, "name2" => cookie2, ... }
|
|
92
|
+
#
|
|
93
|
+
def self.parse(raw_cookie)
|
|
94
|
+
cookies = Hash.new([])
|
|
95
|
+
|
|
96
|
+
if raw_cookie
|
|
97
|
+
raw_cookie.split(/;\s?/).each do |pairs|
|
|
98
|
+
name, value = pairs.split('=',2)
|
|
99
|
+
next unless name and value
|
|
100
|
+
name = CGI::unescape(name)
|
|
101
|
+
unless cookies.has_key?(name)
|
|
102
|
+
cookies[name] = new(name, CGI::unescape(value))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
cookies
|
|
108
|
+
end
|
|
109
|
+
end # class Cookie
|
|
110
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
3
|
+
class CGI #:nodoc:
|
|
4
|
+
module QueryExtension
|
|
5
|
+
# Remove the old initialize_query method before redefining it.
|
|
6
|
+
remove_method :initialize_query
|
|
7
|
+
|
|
8
|
+
# Neuter CGI parameter parsing.
|
|
9
|
+
def initialize_query
|
|
10
|
+
# Fix some strange request environments.
|
|
11
|
+
env_table['REQUEST_METHOD'] ||= 'GET'
|
|
12
|
+
|
|
13
|
+
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
|
|
14
|
+
if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
|
|
15
|
+
env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
|
|
19
|
+
@params = {}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
require 'cgi/session'
|
|
3
|
+
require 'cgi/session/pstore'
|
|
4
|
+
|
|
5
|
+
class CGI #:nodoc:
|
|
6
|
+
# * Expose the CGI instance to session stores.
|
|
7
|
+
# * Don't require 'digest/md5' whenever a new session id is generated.
|
|
8
|
+
class Session #:nodoc:
|
|
9
|
+
begin
|
|
10
|
+
require 'securerandom'
|
|
11
|
+
|
|
12
|
+
# Generate a 32-character unique id using SecureRandom.
|
|
13
|
+
# This is used to generate session ids but may be reused elsewhere.
|
|
14
|
+
def self.generate_unique_id(constant = nil)
|
|
15
|
+
SecureRandom.hex(16)
|
|
16
|
+
end
|
|
17
|
+
rescue LoadError
|
|
18
|
+
# Generate an 32-character unique id based on a hash of the current time,
|
|
19
|
+
# a random number, the process id, and a constant string. This is used
|
|
20
|
+
# to generate session ids but may be reused elsewhere.
|
|
21
|
+
def self.generate_unique_id(constant = 'foobar')
|
|
22
|
+
md5 = Digest::MD5.new
|
|
23
|
+
now = Time.now
|
|
24
|
+
md5 << now.to_s
|
|
25
|
+
md5 << String(now.usec)
|
|
26
|
+
md5 << String(rand(0))
|
|
27
|
+
md5 << String($$)
|
|
28
|
+
md5 << constant
|
|
29
|
+
md5.hexdigest
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Make the CGI instance available to session stores.
|
|
34
|
+
attr_reader :cgi
|
|
35
|
+
attr_reader :dbman
|
|
36
|
+
alias_method :initialize_without_cgi_reader, :initialize
|
|
37
|
+
def initialize(cgi, options = {})
|
|
38
|
+
@cgi = cgi
|
|
39
|
+
initialize_without_cgi_reader(cgi, options)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
# Create a new session id.
|
|
44
|
+
def create_new_id
|
|
45
|
+
@new_session = true
|
|
46
|
+
self.class.generate_unique_id
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# * Don't require 'digest/md5' whenever a new session is started.
|
|
50
|
+
class PStore #:nodoc:
|
|
51
|
+
def initialize(session, option={})
|
|
52
|
+
dir = option['tmpdir'] || Dir::tmpdir
|
|
53
|
+
prefix = option['prefix'] || ''
|
|
54
|
+
id = session.session_id
|
|
55
|
+
md5 = Digest::MD5.hexdigest(id)[0,16]
|
|
56
|
+
path = dir+"/"+prefix+md5
|
|
57
|
+
path.untaint
|
|
58
|
+
if File::exist?(path)
|
|
59
|
+
@hash = nil
|
|
60
|
+
else
|
|
61
|
+
unless session.new_session
|
|
62
|
+
raise CGI::Session::NoSession, "uninitialized session"
|
|
63
|
+
end
|
|
64
|
+
@hash = {}
|
|
65
|
+
end
|
|
66
|
+
@p = ::PStore.new(path)
|
|
67
|
+
@p.transaction do |p|
|
|
68
|
+
File.chmod(0600, p.path)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|