Your assets are covered.
With Rails’ default asset caching, CSS and JS are concatenated (not even minified) at runtime when that bundle is first requested. Not good enough. AssetHat can automatically:
- Easily minify and bundle CSS and JS on deploy to reduce file sizes and HTTP requests.
- Load popular third-party JS (like jQuery and Prototype) from Google's CDN when in production, or from localhost in development.
- Force image URLs in your CSS to use CDN subdomains, not just the current host.
- Add an image’s last Git commit ID to its CSS URLs to bust browser caches (e.g., /images/foo.png?ab12cd34e).
After setup, you can use these in your layouts and views:
<%= include_css :bundle => 'application' %> # => <link href="/stylesheets/bundles/application.min.css" # media="screen,projection" rel="stylesheet" type="text/css" /> <%= include_js :bundles => ['plugins', 'common'] %> # => <script src="/javascripts/bundles/plugins.min.js" # type="text/javascript"></script> # <script src="/javascripts/bundles/common.min.js" # type="text/javascript"></script>
And this in your deploy script:
rake asset_hat:minify
See README.rdoc for more.
Methods
public class
Constants
VERSION | = | self.version | This gem’s version number. | |
TYPES | = | [:css, :js] | Types of supported assets; currently [:css, :js]. | |
ASSETS_DIR | = | defined?(Rails.public_path) ? Rails.public_path : 'public' | Base directory in which all assets are kept, e.g., ‘public/’. | |
STYLESHEETS_DIR | = | "#{ASSETS_DIR}/stylesheets" | Directory in which all stylesheets are kept, e.g., ‘public/stylesheets/’. | |
JAVASCRIPTS_DIR | = | "#{ASSETS_DIR}/javascripts" | Directory in which all JavaScripts are kept, e.g., ‘public/javascripts/’. | |
RELATIVE_CONFIG_FILEPATH | = | File.join('config', 'assets.yml') | Relative path for the config file. | |
CONFIG_FILEPATH | = | File.join(RAILS_ROOT, RELATIVE_CONFIG_FILEPATH) | Absolute path for the config file. |
Public class methods
Returns true if the specified asset exists in the file system:
AssetHat.asset_exists?('application', :css) # => true if /public/stylesheets/application.css exists AssetHat.asset_exists?('some-plugin', :js) # => true if /public/javascripts/some-plugin.js exists
See also AssetHat::STYLESHEETS_DIR and AssetHat::JAVASCRIPTS_DIR.
# File lib/asset_hat.rb, line 92 92: def self.asset_exists?(filename, type) 93: # Process arguments 94: type = type.to_sym 95: unless TYPES.include?(type) 96: raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}." 97: return 98: end 99: 100: @asset_exists ||= TYPES.inject({}) do |hsh, known_type| 101: hsh.merge!(known_type => {}) 102: end 103: if @asset_exists[type][filename].nil? 104: @asset_exists[type][filename] = 105: File.exist?(File.join(self.assets_dir(type), filename)) 106: end 107: @asset_exists[type][filename] 108: end
# File lib/asset_hat.rb, line 75 75: def self.assets_dir(type) 76: case type.to_sym 77: when :css ; STYLESHEETS_DIR 78: when :js ; JAVASCRIPTS_DIR 79: else nil 80: end 81: end
Returns the extension-less names of files in the given bundle:
AssetHat.bundle_filenames('application', :css) # => ['reset', 'application', 'clearfix'] AssetHat.bundle_filenames('non-existent-file', :css) # => nil
# File lib/asset_hat.rb, line 134 134: def self.bundle_filenames(bundle, type) 135: # Process arguments 136: unless TYPES.include?(type.to_sym) 137: raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}." 138: return 139: end 140: 141: self.config[type.to_s]['bundles'][bundle] rescue nil 142: end
Returns the full paths of files in the given bundle:
AssetHat.bundle_filenames('application', :css) # => ['/path/to/app/public/stylesheets/reset.css', '/path/to/app/public/stylesheets/application.css', '/path/to/app/public/stylesheets/clearfix.css'] AssetHat.bundle_filenames('non-existent-file', :css) # => nil
# File lib/asset_hat.rb, line 152 152: def self.bundle_filepaths(bundle, type) 153: # Process arguments 154: unless TYPES.include?(type.to_sym) 155: raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}." 156: return 157: end 158: 159: dir = self.assets_dir(type) 160: filenames = self.bundle_filenames(bundle, type) 161: filepaths = filenames.present? ? 162: filenames.map { |fn| File.join(dir, "#{fn}.#{type}") } : nil 163: end
Returns true if bundles should be included as single minified files (e.g., in production), or false if bundles should be included as separate, unminified files (e.g., in development). To modify this value, set config.action_controller.perform_caching = true in your environment.
# File lib/asset_hat.rb, line 115 115: def self.cache? ; ActionController::Base.perform_caching ; end
# File lib/asset_hat.rb, line 165 165: def self.clear_html_cache 166: html_cache = {} 167: end
Nested-hash version of config/assets.yml.
# File lib/asset_hat.rb, line 65 65: def self.config 66: if !cache? || @config.blank? 67: @config = YAML.load(File.open(CONFIG_FILEPATH, 'r')) 68: end 69: @config 70: end
Usage:
AssetHat.last_bundle_commit_id('application', :css)
Returns a string of the latest commit ID for the given bundle, based on which of its files were most recently modified in the repository. If no ID can be found, `nil` is returned.
# File lib/asset_hat/vcs.rb, line 45 45: def self.last_bundle_commit_id(bundle, type) 46: # Process arguments 47: type = type.to_sym 48: unless TYPES.include?(type) 49: raise "Unknown type \"#{type}\"; should be one of: #{TYPES.join(', ')}." 50: return 51: end 52: 53: # Default to `{:css => {}, :js => {}}` 54: @last_bundle_commit_ids ||= 55: TYPES.inject({}) { |hsh, t| hsh.merge(t => {}) } 56: 57: if @last_bundle_commit_ids[type][bundle].blank? 58: dir = self.assets_dir(type) 59: filepaths = self.bundle_filepaths(bundle, type) 60: if filepaths.present? 61: @last_bundle_commit_ids[type][bundle] = self.last_commit_id(*filepaths) 62: end 63: end 64: 65: @last_bundle_commit_ids[type][bundle] 66: end
Usage:
AssetHat.last_commit_id('public/stylesheets/application.css') AssetHat.last_commit_id('public/stylesheets/ie.css', 'public/stylesheets/ie7.css', 'public/stylesheets/ie6.css')
Returns a string of the commit ID for the file with the most recent commit. If the file(s) cannot be found, `nil` is returned. Options:
- vcs
- Version control system. Currently, the only supported value is :git.
# File lib/asset_hat/vcs.rb, line 18 18: def self.last_commit_id(*args) 19: # Process arguments 20: options = args.extract_options! 21: options = options.symbolize_keys.reverse_merge(:vcs => :git) 22: filepaths = args.join(' ') 23: 24: # Validate options 25: if options[:vcs] != :git 26: raise 'Git is currently the only supported VCS.' and return 27: end 28: 29: @last_commit_ids ||= {} 30: if @last_commit_ids[filepaths].blank? 31: h = `git log -1 --pretty=format:%h #{filepaths} 2>/dev/null` 32: # `h` has either the abbreviated Git commit hash or an empty string 33: @last_commit_ids[filepaths] = h if h.present? 34: end 35: @last_commit_ids[filepaths] 36: end
Returns the expected path for the minified version of an asset:
AssetHat.min_filepath('public/stylesheets/bundles/application.css', 'css') # => 'public/stylesheets/bundles/application.min.css'
See also AssetHat::CSS.min_filepath and AssetHat::JS.min_filepath.
# File lib/asset_hat.rb, line 124 124: def self.min_filepath(filepath, extension) 125: filepath.sub(/([^\.]*).#{extension}$/, "\\1.min.#{extension}") 126: end
Returns this gem’s version number. See also VERSION.
# File lib/asset_hat/version.rb, line 3 3: def self.version 4: data_filepath = File.join(File.dirname(__FILE__), %w[.. .. VERSION.yml]) 5: data = YAML.load(File.open(data_filepath, 'r')) 6: [:major, :minor, :patch, :build]. 7: map { |x| data[x] }.reject(&:blank?).join('.') 8: end