Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source
Show
Ignore:
Timestamp:
02/18/07 23:54:20 (4 years ago)
Author:
david
Message:

Added caching option to AssetTagHelper#stylesheet_link_tag and AssetTagHelper#javascript_include_tag [DHH]

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/lib/action_view/helpers/asset_tag_helper.rb

    r6162 r6164  
    2929    # for background. 
    3030    module AssetTagHelper 
     31      ASSETS_DIR      = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/public" : "public" 
     32      JAVASCRIPTS_DIR = "#{ASSETS_DIR}/javascripts" 
     33      STYLESHEETS_DIR = "#{ASSETS_DIR}/stylesheets" 
     34       
    3135      # Returns a link tag that browsers and news readers can use to auto-detect 
    3236      # an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or 
     
    9498      #     ... 
    9599      #     <script type="text/javascript" src="/javascripts/application.js"></script> *see below 
     100      # 
     101      # * = The application.js file is only referenced if it exists 
     102      # 
     103      # You can also include all javascripts in the javascripts directory using :all as the source: 
     104      # 
     105      #   javascript_include_tag :all # => 
     106      #     <script type="text/javascript" src="/javascripts/prototype.js"></script> 
     107      #     <script type="text/javascript" src="/javascripts/effects.js"></script> 
     108      #     ... 
     109      #     <script type="text/javascript" src="/javascripts/application.js"></script> 
     110      #     <script type="text/javascript" src="/javascripts/shop.js"></script> 
     111      #     <script type="text/javascript" src="/javascripts/checkout.js"></script> 
     112      # 
     113      # Note that the default javascript files will be included first. So Prototype and Scriptaculous are available for 
     114      # all subsequently included files. They 
     115      # 
     116      # == Caching multiple javascripts into one 
     117      # 
     118      # You can also cache multiple javascripts into one file, which requires less HTTP connections and can better be 
     119      # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching 
     120      # is set to true (which is the case by default for the Rails production environment, but not for the development 
     121      # environment). Examples: 
     122      # 
     123      #   javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is false => 
     124      #     <script type="text/javascript" src="/javascripts/prototype.js"></script> 
     125      #     <script type="text/javascript" src="/javascripts/effects.js"></script> 
     126      #     ... 
     127      #     <script type="text/javascript" src="/javascripts/application.js"></script> 
     128      #     <script type="text/javascript" src="/javascripts/shop.js"></script> 
     129      #     <script type="text/javascript" src="/javascripts/checkout.js"></script> 
     130      # 
     131      #   javascript_include_tag :all, :cache => true # when ActionController::Base.perform_caching is true => 
     132      #     <script type="text/javascript" src="/javascripts/all.js"></script> 
     133      # 
     134      #   javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false => 
     135      #     <script type="text/javascript" src="/javascripts/prototype.js"></script> 
     136      #     <script type="text/javascript" src="/javascripts/cart.js"></script> 
     137      #     <script type="text/javascript" src="/javascripts/checkout.js"></script> 
     138      # 
     139      #   javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is false => 
     140      #     <script type="text/javascript" src="/javascripts/shop.js"></script> 
    96141      def javascript_include_tag(*sources) 
    97142        options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { } 
    98  
    99         if sources.include?(:defaults) 
    100           sources = sources[0..(sources.index(:defaults))] + 
    101             @@javascript_default_sources.dup + 
    102             sources[(sources.index(:defaults) + 1)..sources.length] 
    103  
    104           sources.delete(:defaults) 
    105           sources << "application" if defined?(RAILS_ROOT) && File.exists?("#{RAILS_ROOT}/public/javascripts/application.js") 
    106         end 
    107  
    108         sources.collect do |source| 
    109           source = javascript_path(source) 
    110           content_tag("script", "", { "type" => "text/javascript", "src" => source }.merge(options)) 
    111         end.join("\n") 
     143        cache   = options.delete("cache") 
     144 
     145        if ActionController::Base.perform_caching && cache 
     146          joined_javascript_name = (cache == true ? "all" : cache) + ".js" 
     147          joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) 
     148 
     149          if !File.exists?(joined_javascript_path) 
     150            File.open(joined_javascript_path, "w+") do |cache| 
     151              javascript_paths = expand_javascript_sources(sources).collect { |source| javascript_path(source) } 
     152              cache.write(join_asset_file_contents(javascript_paths)) 
     153            end 
     154          end 
     155 
     156          content_tag("script", "", {  
     157            "type" => "text/javascript", "src" => javascript_path(joined_javascript_name) 
     158          }.merge(options)) 
     159        else 
     160          expand_javascript_sources(sources).collect do |source| 
     161            content_tag("script", "", { "type" => "text/javascript", "src" => javascript_path(source) }.merge(options)) 
     162          end.join("\n") 
     163        end 
    112164      end 
    113165 
     
    149201      #     <link href="/stylesheets/random.styles" media="screen" rel="Stylesheet" type="text/css" /> 
    150202      #     <link href="/css/stylish.css" media="screen" rel="Stylesheet" type="text/css" /> 
     203      # 
     204      # You can also include all styles in the stylesheet directory using :all as the source: 
     205      # 
     206      #   stylesheet_link_tag :all # => 
     207      #     <link href="/stylesheets/style1.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     208      #     <link href="/stylesheets/styleB.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     209      #     <link href="/stylesheets/styleX2.css" media="screen" rel="Stylesheet" type="text/css" /> 
     210      # 
     211      # == Caching multiple stylesheets into one 
     212      # 
     213      # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be 
     214      # compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching 
     215      # is set to true (which is the case by default for the Rails production environment, but not for the development 
     216      # environment). Examples: 
     217      # 
     218      #   stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false => 
     219      #     <link href="/stylesheets/style1.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     220      #     <link href="/stylesheets/styleB.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     221      #     <link href="/stylesheets/styleX2.css" media="screen" rel="Stylesheet" type="text/css" /> 
     222      # 
     223      #   stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is true => 
     224      #     <link href="/stylesheets/all.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     225      # 
     226      #   stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is false => 
     227      #     <link href="/stylesheets/shop.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     228      #     <link href="/stylesheets/cart.css"  media="screen" rel="Stylesheet" type="text/css" /> 
     229      #     <link href="/stylesheets/checkout.css" media="screen" rel="Stylesheet" type="text/css" /> 
     230      # 
     231      #   stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is true => 
     232      #     <link href="/stylesheets/payment.css"  media="screen" rel="Stylesheet" type="text/css" /> 
    151233      def stylesheet_link_tag(*sources) 
    152234        options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { } 
    153         sources.collect do |source| 
    154           source = stylesheet_path(source) 
    155           tag("link", { "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => source }.merge(options)) 
    156         end.join("\n") 
     235        cache   = options.delete("cache") 
     236 
     237        if ActionController::Base.perform_caching && cache 
     238          joined_stylesheet_name = (cache == true ? "all" : cache) + ".css" 
     239          joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) 
     240 
     241          if !File.exists?(joined_stylesheet_path) 
     242            File.open(joined_stylesheet_path, "w+") do |cache| 
     243              stylesheet_paths = expand_stylesheet_sources(sources).collect { |source| stylesheet_path(source) } 
     244              cache.write(join_asset_file_contents(stylesheet_paths)) 
     245            end 
     246          end 
     247 
     248          tag("link", { 
     249            "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", 
     250            "href" => stylesheet_path(joined_stylesheet_name) 
     251          }.merge(options)) 
     252        else 
     253          options.delete("cache") 
     254 
     255          expand_stylesheet_sources(sources).collect do |source| 
     256            tag("link", {  
     257              "rel" => "Stylesheet", "type" => "text/css", "media" => "screen", "href" => stylesheet_path(source) 
     258            }.merge(options)) 
     259          end.join("\n") 
     260        end 
    157261      end 
    158262 
     
    217321        def compute_public_path(source, dir, ext) 
    218322          source += ".#{ext}" if File.extname(source).blank? 
     323 
    219324          if source =~ %r{^[-a-z]+://} 
    220325            source 
     
    246351        def rails_asset_id(source) 
    247352          ENV["RAILS_ASSET_ID"] || 
    248             File.mtime("#{RAILS_ROOT}/public/#{source}").to_i.to_s rescue "" 
     353            File.mtime(File.join(ASSETS_DIR, source)).to_i.to_s rescue "" 
    249354        end 
    250355 
     
    253358        def rewrite_asset_path!(source) 
    254359          asset_id = rails_asset_id(source) 
    255           source << "?#{asset_id}" if defined?(RAILS_ROOT) && !asset_id.blank? 
     360          source << "?#{asset_id}" if !asset_id.blank? 
     361        end 
     362 
     363        def expand_javascript_sources(sources)           
     364          case 
     365          when sources.include?(:all) 
     366            all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).split(".", 0).first } 
     367            sources = ((@@javascript_default_sources.dup & all_javascript_files) + all_javascript_files).uniq 
     368 
     369          when sources.include?(:defaults) 
     370            sources = sources[0..(sources.index(:defaults))] +  
     371              @@javascript_default_sources.dup +  
     372              sources[(sources.index(:defaults) + 1)..sources.length] 
     373 
     374            sources.delete(:defaults) 
     375            sources << "application" if File.exists?(File.join(JAVASCRIPTS_DIR, "application.js")) 
     376          end 
     377 
     378          sources 
     379        end 
     380 
     381        def expand_stylesheet_sources(sources) 
     382          if sources.first == :all 
     383            sources = Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).split(".", 1).first } 
     384          else 
     385            sources 
     386          end 
     387        end 
     388 
     389        def join_asset_file_contents(paths) 
     390          paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n")           
    256391        end 
    257392    end