From aad7fbde68684547959dcccc2102c978d5347a78 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 18 Feb 2007 23:54:20 +0000 Subject: Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [DHH] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6164 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- actionpack/lib/action_controller/base.rb | 2 +- .../lib/action_view/helpers/asset_tag_helper.rb | 171 ++++++++++++++++++--- 2 files changed, 154 insertions(+), 19 deletions(-) (limited to 'actionpack/lib') diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ecbe15bada..c012abc448 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -879,7 +879,7 @@ module ActionController #:nodoc: if text.is_a?(String) if response.headers['Status'][0..2] == '200' && !response.body.empty? - response.headers['Etag'] ||= %("#{Digest::MD5.hexdigest(text)}") + response.headers['Etag'] = %("#{Digest::MD5.hexdigest(text)}") if request.headers['HTTP_IF_NONE_MATCH'] == response.headers['Etag'] response.headers['Status'] = "304 Not Modified" diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 850615632f..e72e23b20b 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -28,6 +28,10 @@ module ActionView # for server load balancing. See http://www.die.net/musings/page_load_time/ # for background. module AssetTagHelper + ASSETS_DIR = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : "public" + JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts" + STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets" + # Returns a link tag that browsers and news readers can use to auto-detect # an RSS or ATOM feed. The +type+ can either be :rss (default) or # :atom. Control the link options in url_for format using the @@ -93,22 +97,70 @@ module ActionView # # ... # *see below + # + # * = The application.js file is only referenced if it exists + # + # You can also include all javascripts in the javascripts directory using :all as the source: + # + # javascript_include_tag :all # => + # + # + # ... + # + # + # + # + # Note that the default javascript files will be included first. So Prototype and Scriptaculous are available for + # all subsequently included files. They + # + # == Caching multiple javascripts into one + # + # You can also cache multiple javascripts into one file, which requires less HTTP connections and can better be + # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching + # is set to true (which is the case by default for the Rails production environment, but not for the development + # environment). Examples: + # + # javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is false => + # + # + # ... + # + # + # + # + # javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is true => + # + # + # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false => + # + # + # + # + # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false => + # def javascript_include_tag(*sources) options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { } + cache = options.delete("cache") - if sources.include?(:defaults) - sources = sources[0..(sources.index(:defaults))] + - @@javascript_default_sources.dup + - sources[(sources.index(:defaults) + 1)..sources.length] + if ActionController::Base.perform_caching && cache + joined_javascript_name = (cache == true ? "all" : cache) + ".js" + joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) - sources.delete(:defaults) - sources << "application" if defined?(RAILS_ROOT) && File.exists?("#{RAILS_ROOT}/public/javascripts/application.js") - end + if !File.exists?(joined_javascript_path) + File.open(joined_javascript_path, "w+") do |cache| + javascript_paths = expand_javascript_sources(sources).collect { |source| javascript_path(source) } + cache.write(join_asset_file_contents(javascript_paths)) + end + end - sources.collect do |source| - source = javascript_path(source) - content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options)) - end.join("\n") + content_tag("script", "", { + "type" => "text/javascript", "src" => javascript_path(joined_javascript_name) + }.merge(options)) + else + expand_javascript_sources(sources).collect do |source| + content_tag("script", "", { "type" => "text/javascript", "src" => javascript_path(source) }.merge(options)) + end.join("\n") + end end # Register one or more additional JavaScript files to be included when @@ -148,12 +200,64 @@ module ActionView # stylesheet_link_tag "random.styles", "/css/stylish" # => # # + # + # You can also include all styles in the stylesheet directory using :all as the source: + # + # stylesheet_link_tag :all # => + # + # + # + # + # == Caching multiple stylesheets into one + # + # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be + # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching + # is set to true (which is the case by default for the Rails production environment, but not for the development + # environment). Examples: + # + # stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false => + # + # + # + # + # stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true => + # + # + # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is false => + # + # + # + # + # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is true => + # def stylesheet_link_tag(*sources) options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { } - sources.collect do |source| - source = stylesheet_path(source) - tag("link", { "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source }.merge(options)) - end.join("\n") + cache = options.delete("cache") + + if ActionController::Base.perform_caching && cache + joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" + joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) + + if !File.exists?(joined_stylesheet_path) + File.open(joined_stylesheet_path, "w+") do |cache| + stylesheet_paths = expand_stylesheet_sources(sources).collect { |source| stylesheet_path(source) } + cache.write(join_asset_file_contents(stylesheet_paths)) + end + end + + tag("link", { + "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", + "href" => stylesheet_path(joined_stylesheet_name) + }.merge(options)) + else + options.delete("cache") + + expand_stylesheet_sources(sources).collect do |source| + tag("link", { + "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => stylesheet_path(source) + }.merge(options)) + end.join("\n") + end end # Computes the path to an image asset in the public images directory. @@ -216,6 +320,7 @@ module ActionView # request protocol. def compute_public_path(source, dir, ext) source += ".#{ext}" if File.extname(source).blank? + if source =~ %r{^[-a-z]+://} source else @@ -245,15 +350,45 @@ module ActionView # modification time as its cache-busting asset id. def rails_asset_id(source) ENV["RAILS_ASSET_ID"] || - File.mtime("#{RAILS_ROOT}/public/#{source}").to_i.to_s rescue "" + File.mtime(File.join(ASSETS_DIR, source)).to_i.to_s rescue "" end # Break out the asset path rewrite so you wish to put the asset id # someplace other than the query string. def rewrite_asset_path!(source) asset_id = rails_asset_id(source) - source << "?#{asset_id}" if defined?(RAILS_ROOT) && !asset_id.blank? + source << "?#{asset_id}" if !asset_id.blank? + end + + def expand_javascript_sources(sources) + case + when sources.include?(:all) + all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).split(".", 0).first } + sources = ((@@javascript_default_sources.dup & all_javascript_files) + all_javascript_files).uniq + + when sources.include?(:defaults) + sources = sources[0..(sources.index(:defaults))] + + @@javascript_default_sources.dup + + sources[(sources.index(:defaults) + 1)..sources.length] + + sources.delete(:defaults) + sources << "application" if File.exists?(File.join(JAVASCRIPTS_DIR, "application.js")) + end + + sources + end + + def expand_stylesheet_sources(sources) + if sources.first == :all + sources = Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).split(".", 1).first } + else + sources + end + end + + def join_asset_file_contents(paths) + paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n") end end end -end +end \ No newline at end of file -- cgit v1.2.3