├── .gemtest ├── .gitignore ├── Gemfile ├── doc ├── created.rid ├── index.html ├── fr_class_index.html ├── files │ ├── lib │ │ ├── asset_hat │ │ │ ├── vcs_rb.html │ │ │ ├── tasks │ │ │ │ ├── css_rb.html │ │ │ │ └── js_rb.html │ │ │ ├── version_rb.html │ │ │ ├── js │ │ │ │ └── vendors_rb.html │ │ │ ├── capistrano_rb.html │ │ │ ├── initializers │ │ │ │ ├── action_view_rb.html │ │ │ │ └── cache_last_commit_ids_rb.html │ │ │ ├── js_rb.html │ │ │ ├── css_rb.html │ │ │ ├── tasks_rb.html │ │ │ ├── railtie_rb.html │ │ │ └── unicorn_rb.html │ │ ├── tasks │ │ │ └── asset_hat_rake.html │ │ ├── asset_hat_helper_rb.html │ │ └── asset_hat_rb.html │ ├── LICENSE.html │ └── HISTORY.html ├── fr_file_index.html ├── classes │ └── AssetHat │ │ ├── CSS │ │ └── Engines.html │ │ ├── JS │ │ ├── Engines.html │ │ └── Vendors.html │ │ ├── JS.html │ │ └── CSS.html └── rdoc-style.css ├── lib ├── tasks │ └── asset_hat.rake ├── asset_hat │ ├── initializers │ │ ├── action_view.rb │ │ └── cache_last_commit_ids.rb │ ├── version.rb │ ├── unicorn.rb │ ├── railtie.rb │ ├── capistrano.rb │ ├── tasks.rb │ ├── js.rb │ ├── vcs.rb │ ├── tasks │ │ ├── js.rb │ │ └── css.rb │ ├── css.rb │ └── js │ │ └── vendors.rb └── asset_hat.rb ├── public ├── javascripts │ ├── js-file-1-1.js │ ├── js-file-1-2.js │ ├── js-file-1-3.js │ ├── js-file-2-1.js │ ├── js-file-2-2.js │ ├── js-file-2-3.js │ └── bundles │ │ ├── js-bundle-1.min.js │ │ └── js-bundle-2.min.js └── stylesheets │ ├── css-file-1-1.css │ ├── css-file-1-2.css │ ├── css-file-1-3.css │ ├── css-file-2-1.css │ ├── css-file-2-2.css │ ├── css-file-2-3.css │ └── bundles │ ├── css-bundle-1.min.css │ ├── css-bundle-2.min.css │ └── ssl │ ├── css-bundle-1.min.css │ ├── css-bundle-2.min.css │ └── css-bundle-3.min.css ├── VERSION.yml ├── rails └── init.rb ├── test ├── test_helper.rb └── asset_hat_test.rb ├── LICENSE ├── Rakefile ├── config └── assets.yml ├── HISTORY ├── asset_hat.gemspec └── README.rdoc /.gemtest: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site/ 2 | pkg/ 3 | tmp/ 4 | Gemfile.lock -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /doc/created.rid: -------------------------------------------------------------------------------- 1 | Mon, 08 Aug 2011 14:30:15 -0400 2 | -------------------------------------------------------------------------------- /lib/tasks/asset_hat.rake: -------------------------------------------------------------------------------- 1 | require 'asset_hat/tasks' 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-1-1.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-1-2.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-1-3.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-2-1.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-2-2.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/javascripts/js-file-2-3.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-1-1.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-1-2.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-1-3.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-2-1.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-2-2.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /public/stylesheets/css-file-2-3.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | -------------------------------------------------------------------------------- /VERSION.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :major: 0 3 | :build: 4 | :minor: 4 5 | :patch: 2 6 | -------------------------------------------------------------------------------- /lib/asset_hat/initializers/action_view.rb: -------------------------------------------------------------------------------- 1 | ActionView::Base.send(:include, ::AssetHatHelper) 2 | -------------------------------------------------------------------------------- /lib/asset_hat/initializers/cache_last_commit_ids.rb: -------------------------------------------------------------------------------- 1 | AssetHat.cache_last_commit_ids unless defined?(::IRB) 2 | -------------------------------------------------------------------------------- /public/stylesheets/bundles/css-bundle-1.min.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | /* [placeholder] */ 3 | /* [placeholder] */ 4 | -------------------------------------------------------------------------------- /public/stylesheets/bundles/css-bundle-2.min.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | /* [placeholder] */ 3 | /* [placeholder] */ 4 | -------------------------------------------------------------------------------- /public/stylesheets/bundles/ssl/css-bundle-1.min.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | /* [placeholder] */ 3 | /* [placeholder] */ 4 | -------------------------------------------------------------------------------- /public/stylesheets/bundles/ssl/css-bundle-2.min.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | /* [placeholder] */ 3 | /* [placeholder] */ 4 | -------------------------------------------------------------------------------- /public/stylesheets/bundles/ssl/css-bundle-3.min.css: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | /* [placeholder] */ 3 | /* [placeholder] */ 4 | -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | require 'asset_hat/initializers/action_view' 2 | require 'asset_hat/initializers/cache_last_commit_ids' 3 | -------------------------------------------------------------------------------- /public/javascripts/bundles/js-bundle-1.min.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | 3 | /* [placeholder] */ 4 | 5 | /* [placeholder] */ 6 | 7 | -------------------------------------------------------------------------------- /public/javascripts/bundles/js-bundle-2.min.js: -------------------------------------------------------------------------------- 1 | /* [placeholder] */ 2 | 3 | /* [placeholder] */ 4 | 5 | /* [placeholder] */ 6 | 7 | -------------------------------------------------------------------------------- /lib/asset_hat/version.rb: -------------------------------------------------------------------------------- 1 | module AssetHat 2 | # Returns this gem's version number. See also VERSION. 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 9 | 10 | # This gem's version number. 11 | VERSION = self.version 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/asset_hat/unicorn.rb: -------------------------------------------------------------------------------- 1 | # Add the code below to your app's unicorn.conf.rb or similar so that assets' 2 | # commit IDs are precached only once by the Unicorn master, then propagated 3 | # out to workers. (Otherwise, each worker will precache commit IDs, which 4 | # wastes resources.) More on configuring Unicorn: 5 | # http://unicorn.bogomips.org/Unicorn/Configurator.html 6 | 7 | before_fork do |server, worker| 8 | AssetHat.cache_last_commit_ids if defined?(AssetHat) 9 | end 10 | -------------------------------------------------------------------------------- /lib/asset_hat/railtie.rb: -------------------------------------------------------------------------------- 1 | require 'asset_hat' 2 | require 'asset_hat_helper' 3 | require 'rails' 4 | 5 | module AssetHat 6 | class Railtie < Rails::Railtie #:nodoc: 7 | initializer 'asset_hat.action_view' do |app| 8 | require 'asset_hat/initializers/action_view' 9 | end 10 | 11 | initializer 'asset_hat.cache_last_commit_ids' do |app| 12 | require 'asset_hat/initializers/cache_last_commit_ids' 13 | end 14 | 15 | rake_tasks do 16 | load 'tasks/asset_hat.rake' 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/asset_hat/capistrano.rb: -------------------------------------------------------------------------------- 1 | Capistrano::Configuration.instance(:must_exist).load do 2 | after 'deploy:update_code', 'deploy:asset_hat:minify' 3 | 4 | namespace :deploy do 5 | namespace :asset_hat do 6 | desc 'Minify all CSS/JS with AssetHat' 7 | task :minify, :roles => :assets, :except => {:no_release => true} do 8 | rake = fetch(:rake, "rake") 9 | env = fetch(:environment, fetch(:rails_env, "production")) 10 | run "cd #{release_path} ; " + 11 | "#{rake} RAILS_ENV=#{env} FORMAT=short asset_hat:minify" 12 | end 13 | end 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'test/unit' 3 | require 'active_support' 4 | require 'action_controller' 5 | require 'action_view/test_case' 6 | 7 | require 'shoulda' 8 | require 'flexmock/test_unit' 9 | require 'asset_hat' 10 | require 'asset_hat_helper' 11 | 12 | 13 | 14 | ActionController::Base.perform_caching = false 15 | 16 | unless defined?(Rails) 17 | module Rails 18 | class << self 19 | # Enable `Rails.env.test?`, `Rails.env.development?`, etc. 20 | def env; ActiveSupport::StringInquirer.new('test'); end 21 | end 22 | end 23 | end 24 | 25 | class ActionView::TestCase 26 | teardown :clear_html_cache 27 | 28 | def clear_html_cache 29 | AssetHat.clear_html_cache 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AssetHat 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Ron DeVera, Mint Digital 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /doc/fr_class_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Classes [AssetHat] 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Classes

12 |
    13 |
  1. AssetHat 14 |
    1. AssetHat::CSS 15 |
      1. AssetHat::CSS::Engines
      2. 16 |
    2. AssetHat::JS 17 |
      1. AssetHat::JS::Engines
      2. AssetHat::JS::Vendors
      3. 18 |
    3. 19 |
  2. AssetHatHelper
  3. 20 |
21 |
22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/asset_hat/tasks.rb: -------------------------------------------------------------------------------- 1 | require 'asset_hat/tasks/css' 2 | require 'asset_hat/tasks/js' 3 | 4 | namespace :asset_hat do 5 | 6 | desc 'Minifies all CSS and JS bundles' 7 | task :minify => :environment do 8 | format = ENV['FORMAT'] 9 | print 'Minifying CSS/JS...' 10 | puts unless format == 'dot' 11 | 12 | %w[css js].each do |type| 13 | task = Rake::Task["asset_hat:#{type}:minify"] 14 | task.reenable 15 | task.invoke(:show_intro => false, :show_outro => false) 16 | end 17 | puts unless format == 'short' 18 | puts 'Done.' 19 | end 20 | 21 | namespace :minify do 22 | %w[css js].each do |type| 23 | desc "Alias for asset_hat:#{type}:minify" 24 | task type.to_sym => "asset_hat:#{type}:minify" 25 | end 26 | end 27 | 28 | desc 'Prepare configuration file' 29 | task :config do 30 | require 'asset_hat' 31 | 32 | template_filepath = File.join(File.dirname(__FILE__), '..', '..', 33 | AssetHat::RELATIVE_CONFIG_FILEPATH) 34 | target_filepath = AssetHat::CONFIG_FILEPATH 35 | 36 | if File.exists?(target_filepath) 37 | print "Replace #{target_filepath}? (y/n) " 38 | response = $stdin.gets.chomp 39 | unless response.downcase == 'y' 40 | puts 'Aborted.' ; exit 41 | end 42 | end 43 | 44 | FileUtils.cp(template_filepath, target_filepath) 45 | puts "\nWrote to #{target_filepath}. Next, open this file in your editor" 46 | puts 'and set up your CSS/JS bundles.' 47 | end 48 | 49 | end # namespace :asset_hat 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/vcs_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: vcs.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

vcs.rb

29 |
30 | lib/asset_hat/vcs.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/tasks/css_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: css.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

css.rb

29 |
30 | lib/asset_hat/tasks/css.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/tasks/js_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: js.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

js.rb

29 |
30 | lib/asset_hat/tasks/js.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/version_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: version.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

version.rb

29 |
30 | lib/asset_hat/version.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/js/vendors_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: vendors.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

vendors.rb

29 |
30 | lib/asset_hat/js/vendors.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/capistrano_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: capistrano.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

capistrano.rb

29 |
30 | lib/asset_hat/capistrano.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/initializers/action_view_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: action_view.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

action_view.rb

29 |
30 | lib/asset_hat/initializers/action_view.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/initializers/cache_last_commit_ids_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: cache_last_commit_ids.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

cache_last_commit_ids.rb

29 |
30 | lib/asset_hat/initializers/cache_last_commit_ids.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /doc/files/lib/tasks/asset_hat_rake.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: asset_hat.rake [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

asset_hat.rake

29 |
30 | lib/tasks/asset_hat.rake 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |

41 | require ‘asset_hat/tasks‘ 42 |

43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/js_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: js.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

js.rb

29 |
30 | lib/asset_hat/js.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |

Required files

42 |
    43 |
  1. jsmin
  2. 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 |
53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/css_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: css.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

css.rb

29 |
30 | lib/asset_hat/css.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |

Required files

42 |
    43 |
  1. cssmin
  2. 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 |
53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat_helper_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: asset_hat_helper.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

asset_hat_helper.rb

29 |
30 | lib/asset_hat_helper.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |

41 | Helpers for use in layouts for global includes, and in views for 42 | view-specific includes. 43 |

44 |
45 |
46 |
47 |
48 |
49 | 50 |
51 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: asset_hat.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

asset_hat.rb

29 |
30 | lib/asset_hat.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |

Required files

42 |
    43 |
  1. asset_hat/railtie
  2. 44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | 52 |
53 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | # require 'rake/rdoctask' 3 | require 'hanna/rdoctask' 4 | 5 | begin 6 | require 'jeweler' 7 | Jeweler::Tasks.new do |gemspec| 8 | gemspec.name = 'asset_hat' 9 | gemspec.summary = 'Your assets are covered.' 10 | gemspec.description = %{ 11 | Load CSS and JS faster. Minifies, bundles, and optimizes CSS/JS assets 12 | ahead of time (e.g., on deploy), not at runtime. Loads popular 13 | third-party JS (like jQuery, YUI, and Dojo) from localhost in 14 | development, and auto-switches to Google's CDN in production. Lets you 15 | switch on LABjs mode to load more scripts in parallel. Can rewrite 16 | stylesheets to use CDN hosts (not just your web server) and 17 | cache-busting hashes for updated images. 18 | }.strip.split.join(' ') 19 | gemspec.homepage = 'http://mintdigital.github.com/asset_hat' 20 | 21 | gemspec.authors = ['Ron DeVera', 'Mint Digital'] 22 | gemspec.email = 'hello@rondevera.com' 23 | 24 | gemspec.add_development_dependency 'flexmock', '~> 0.8.6' 25 | gemspec.add_development_dependency 'hanna', '~> 0.1.12' 26 | gemspec.add_development_dependency 'jeweler', '~> 1.6.0' 27 | gemspec.add_development_dependency 'shoulda', '~> 2.10.2' 28 | gemspec.add_runtime_dependency 'cssmin', '~> 1.0.2' 29 | gemspec.add_runtime_dependency 'jsmin', '~> 1.0.1' 30 | end 31 | Jeweler::GemcutterTasks.new 32 | rescue LoadError 33 | puts 'Jeweler is not available. Install it with: `gem install jeweler`' 34 | end 35 | 36 | Rake::TestTask.new(:test) do |t| 37 | t.libs << 'lib' << 'test' 38 | t.pattern = 'test/*_test.rb' 39 | t.verbose = true 40 | end 41 | 42 | task :default => :test 43 | 44 | desc 'Generate documentation' 45 | Rake::RDocTask.new(:rdoc) do |rdoc| 46 | rdoc.rdoc_dir = 'doc' 47 | rdoc.title = 'AssetHat' 48 | rdoc.main = 'README.rdoc' 49 | rdoc.options += %w[--line-numbers --inline-source] 50 | %w[README.rdoc HISTORY LICENSE lib/*].each do |path| 51 | rdoc.rdoc_files.include(path) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/tasks_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: tasks.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

tasks.rb

29 |
30 | lib/asset_hat/tasks.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |

Required files

42 |
    43 |
  1. asset_hat/tasks/css
  2. 44 |
  3. asset_hat/tasks/js
  4. 45 |
  5. asset_hat
  6. 46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/railtie_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: railtie.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

railtie.rb

29 |
30 | lib/asset_hat/railtie.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |
41 |

Required files

42 |
    43 |
  1. asset_hat
  2. 44 |
  3. asset_hat_helper
  4. 45 |
  5. rails
  6. 46 |
  7. asset_hat/initializers/action_view
  8. 47 |
  9. asset_hat/initializers/cache_last_commit_ids
  10. 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 56 |
57 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /doc/files/lib/asset_hat/unicorn_rb.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: unicorn.rb [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

unicorn.rb

29 |
30 | lib/asset_hat/unicorn.rb 31 |
32 |
33 | Last Update: 34 | Fri May 06 17:23:46 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |

41 | Add the code below to your app’s unicorn.conf.rb or similar so that 42 | assets’ commit IDs are precached only once by the Unicorn master, 43 | then propagated out to workers. (Otherwise, each worker will precache 44 | commit IDs, which wastes resources.) More on configuring Unicorn: unicorn.bogomips.org/Unicorn/Configurator.html 46 |

47 |
48 |
49 |
50 |
51 |
52 | 53 |
54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /config/assets.yml: -------------------------------------------------------------------------------- 1 | # Here, you can upgrade your layouts and views to AssetHat. For example, find 2 | # this sort of code in your app: 3 | # 4 | # stylesheet_link_tag 'reset', 'application' 5 | # javascript_include_tag( 6 | # Rails.env.production? ? '' : 'jquery-1.6.1') 7 | # javascript_include_tag 'utilities', 'application' 8 | # 9 | # Create matching bundles in this file (no `.css` or `.js` suffixes needed; 10 | # ERb is supported if you need it): 11 | # 12 | # css: 13 | # bundles: 14 | # application: 15 | # - reset 16 | # - application 17 | # js: 18 | # vendors: 19 | # jquery: 20 | # version: 1.6.1 21 | # bundles: 22 | # application: 23 | # - utilities 24 | # - application 25 | # 26 | # Then, simplify your layouts and views to match your bundles: 27 | # 28 | # include_css :bundle => 'application' 29 | # include_js :jquery, :bundle => 'application' 30 | # 31 | # This makes it really easy to reuse sets of CSS/JS files across layouts. 32 | # 33 | # Lastly, set up your deployment script to run `rake asset_hat:minify` 34 | # automatically. This creates minified versions of your CSS/JS; your original 35 | # code is untouched. If you deploy with Capistrano, here's an example recipe: 36 | # https://github.com/mintdigital/asset_hat/blob/master/lib/asset_hat/capistrano.rb 37 | # 38 | # That's it! In development, individual CSS/JS files are loaded separately, 39 | # and your local copy of jQuery is used. In production, your minified CSS/JS 40 | # bundles are loaded, and jQuery is loaded from Google's CDN. 41 | # 42 | # More info: https://github.com/mintdigital/asset_hat/#readme 43 | 44 | css: 45 | engine: cssmin 46 | # Available minification engines: 47 | # http://mintdigital.github.com/asset_hat/doc/classes/AssetHat/CSS/Engines.html 48 | 49 | bundles: 50 | css-bundle-1: 51 | - css-file-1-1 52 | - css-file-1-2 53 | - css-file-1-3 54 | css-bundle-2: 55 | - css-file-2-1 56 | - css-file-2-2 57 | - css-file-2-3 58 | 59 | js: 60 | engine: jsmin 61 | # Available minification engines: 62 | # http://mintdigital.github.com/asset_hat/doc/classes/AssetHat/JS/Engines.html 63 | 64 | # Enable if you're using jQuery or other popular JS: 65 | # 66 | # vendors: 67 | # jquery: 68 | # version: 1.6.1 69 | # 70 | # Supported vendors: 71 | # http://mintdigital.github.com/asset_hat/doc/classes/AssetHat/JS/Vendors.html 72 | 73 | bundles: 74 | js-bundle-1: 75 | - js-file-1-1 76 | - js-file-1-2 77 | - js-file-1-3 78 | js-bundle-2: 79 | - js-file-2-1 80 | - js-file-2-2 81 | - js-file-2-3 82 | -------------------------------------------------------------------------------- /doc/fr_file_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Files [AssetHat] 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Files

12 |
    13 |
  1. HISTORY
  2. 14 |
  3. LICENSE
  4. 15 |
  5. README.rdoc
  6. 16 |
  7. lib/asset_hat.rb
  8. 17 |
  9. lib/asset_hat/capistrano.rb
  10. 18 |
  11. lib/asset_hat/css.rb
  12. 19 |
  13. lib/asset_hat/initializers/action_view.rb
  14. 20 |
  15. lib/asset_hat/initializers/cache_last_commit_ids.rb
  16. 21 |
  17. lib/asset_hat/js.rb
  18. 22 |
  19. lib/asset_hat/js/vendors.rb
  20. 23 |
  21. lib/asset_hat/railtie.rb
  22. 24 |
  23. lib/asset_hat/tasks.rb
  24. 25 |
  25. lib/asset_hat/tasks/css.rb
  26. 26 |
  27. lib/asset_hat/tasks/js.rb
  28. 27 |
  29. lib/asset_hat/unicorn.rb
  30. 28 |
  31. lib/asset_hat/vcs.rb
  32. 29 |
  33. lib/asset_hat/version.rb
  34. 30 |
  35. lib/asset_hat_helper.rb
  36. 31 |
  37. lib/tasks/asset_hat.rake
  38. 32 |
  39. 33 | show all 34 |
  40. 35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/asset_hat/js.rb: -------------------------------------------------------------------------------- 1 | require 'jsmin' 2 | require File.join(File.dirname(__FILE__), 'js', 'vendors') 3 | 4 | module AssetHat 5 | # Methods for minifying JavaScript. 6 | module JS 7 | # A list of supported minification 8 | # engine names. 9 | ENGINES = [:weak, :jsmin] 10 | 11 | # A list of supported 12 | # 3rd-party JavaScript plugin/vendor names. 13 | VENDORS = Vendors::VENDORS 14 | 15 | # Returns the expected path for the minified version of a JS asset: 16 | # 17 | # AssetHat::JS.min_filepath('public/javascripts/bundles/application.js') 18 | # # => 'public/javascripts/bundles/application.min.js' 19 | def self.min_filepath(filepath) 20 | AssetHat.min_filepath(filepath, 'js') 21 | end 22 | 23 | # Accepts a string of JS, and returns that JS minified. Options: 24 | # 25 | # [engine] Default is :jsmin; see 26 | # Engines.jsmin. 27 | # Allowed values are in ENGINES. 28 | def self.minify(input_string, options={}) 29 | options.reverse_merge!(:engine => :jsmin) 30 | 31 | engine = options[:engine].to_sym 32 | unless ENGINES.include?(engine) 33 | raise %{ 34 | Unknown JS minification engine '#{engine}'. 35 | Allowed: #{ENGINES.map{ |e| "'#{e}'" }.join(', ')} 36 | }.strip.gsub(/\s+/, ' ') and return 37 | end 38 | 39 | AssetHat::JS::Engines.send(engine, input_string).strip 40 | end 41 | 42 | # Swappable JavaScript minification engines. 43 | module Engines 44 | # Barebones JavaScript minification engine that: 45 | # - Skips leading/trailing whitespace for each line, excluding line 46 | # breaks; and 47 | # - Removes one-line comments that had no actual code on that line. 48 | def self.weak(input_string) 49 | input = StringIO.new(input_string) 50 | output = StringIO.new 51 | 52 | input.each do |line| 53 | # Remove indentation and trailing whitespace 54 | line.strip! 55 | next if line.blank? 56 | 57 | # Skip single-line comments 58 | next if !(line =~ /^\/\//).nil? 59 | # TODO: Also skip single-line comments that began mid-line, but not 60 | # inside a string or regex 61 | 62 | # TODO: Skip multi-line comments 63 | # - Should not strip from within a string or regex 64 | # - Should not strip comments that begin with `/*!` (e.g., licenses) 65 | 66 | output.write(line + "\n") 67 | end 68 | 69 | output.rewind 70 | output.read 71 | end 72 | 73 | # JavaScript minification engine that simply uses the JSMin gem, a Ruby 74 | # port of Crockford's JSMin. 75 | # 76 | # Sources: 77 | # - http://github.com/rgrove/jsmin 78 | # - http://rubygems.org/gems/jsmin 79 | def self.jsmin(input_string) 80 | JSMin.minify(input_string + "\n") 81 | end 82 | end # module Engines 83 | 84 | end 85 | 86 | end 87 | -------------------------------------------------------------------------------- /doc/files/LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: LICENSE [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

LICENSE

29 |
30 | LICENSE 31 |
32 |
33 | Last Update: 34 | Mon Aug 08 12:28:21 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |

41 | Copyright © 2011 Ron DeVera, Mint Digital 42 |

43 |

44 | Permission is hereby granted, free of charge, to any person obtaining a 45 | copy of this software and associated documentation files (the 46 | “Software”), to deal in the Software without restriction, 47 | including without limitation the rights to use, copy, modify, merge, 48 | publish, distribute, sublicense, and/or sell copies of the Software, and to 49 | permit persons to whom the Software is furnished to do so, subject to the 50 | following conditions: 51 |

52 |

53 | The above copyright notice and this permission notice shall be included in 54 | all copies or substantial portions of the Software. 55 |

56 |

57 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 58 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 59 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 60 | NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 61 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 62 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 63 | USE OR OTHER DEALINGS IN THE SOFTWARE. 64 |

65 |
66 |
67 |
68 |
69 |
70 | 71 |
72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /lib/asset_hat/vcs.rb: -------------------------------------------------------------------------------- 1 | module AssetHat 2 | class << self 3 | attr_accessor :last_commit_ids, :last_bundle_commit_ids #:nodoc: 4 | end 5 | 6 | # Usage: 7 | # 8 | # AssetHat.last_commit_id('public/stylesheets/application.css') 9 | # AssetHat.last_commit_id('public/stylesheets/ie.css', 10 | # 'public/stylesheets/ie7.css', 11 | # 'public/stylesheets/ie6.css') 12 | # 13 | # Returns a string of the commit ID for the file with the most recent 14 | # commit. If the file(s) cannot be found, `nil` is returned. Options: 15 | # 16 | # [vcs] Version control system. Currently, the only supported value is 17 | # :git. 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 37 | 38 | # Usage: 39 | # 40 | # AssetHat.last_bundle_commit_id('application', :css) 41 | # 42 | # Returns a string of the latest commit ID for the given bundle, based 43 | # on which of its files were most recently modified in the repository. If 44 | # no ID can be found, `nil` is returned. 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] = 62 | self.last_commit_id(*filepaths) 63 | end 64 | end 65 | 66 | @last_bundle_commit_ids[type][bundle] 67 | end 68 | 69 | def self.last_commit_ids #:nodoc: 70 | @last_commit_ids 71 | end 72 | 73 | # Precomputes and caches the last commit ID for all bundles. Your web server 74 | # process(es) should run this at boot to avoid overhead during user runtime. 75 | def self.cache_last_commit_ids 76 | AssetHat::TYPES.each do |type| 77 | next if AssetHat.config[type.to_s].blank? || 78 | AssetHat.config[type.to_s]['bundles'].blank? 79 | 80 | AssetHat.config[type.to_s]['bundles'].keys.each do |bundle| 81 | # Memoize commit ID for this bundle 82 | AssetHat.last_bundle_commit_id(bundle, type) if AssetHat.cache? 83 | 84 | # Memoize commit IDs for each file in this bundle 85 | AssetHat.bundle_filepaths(bundle, type).each do |filepath| 86 | AssetHat.last_commit_id(filepath) 87 | end 88 | end 89 | end 90 | end 91 | 92 | end 93 | -------------------------------------------------------------------------------- /HISTORY: -------------------------------------------------------------------------------- 1 | = HISTORY 2 | 3 | == Version 0.4.2 (2011-08-08) 4 | * FIX: Changed Capistrano task to minify assets inside latest release path, 5 | not previous release path. (Thanks, sauliusg[https://github.com/sauliusg]!) 6 | * FIX: Fixed deprecation warnings when running Rake tasks. (Thanks, 7 | jsonperl[https://github.com/jsonperl]!) 8 | 9 | == Version 0.4.1 (2011-05-06) 10 | * FIX: Stopped `gem install asset_hat` from foolishly requiring itself. 11 | 12 | == Version 0.4.0 (2011-05-06) 13 | * FEATURE: Added Rails 3 support. 14 | * FEATURE: Added support for loading JavaScript files via LABjs: 15 | `<%= include_js :jquery, :bundles => %[plugins app], :loader => :lab_js %>`. 16 | (Provides only basic LABjs support. To build custom JS logic, use the new 17 | `:only_url` option.) 18 | * FEATURE: Added support for getting asset URLs, e.g.: 19 | `<%= include_css 'foo', :bundle => 'bar', :only_url => true %>`, 20 | `<%= include_js :jquery, 'foo', :bundle => 'bar', :only_url => true %>`. 21 | 22 | == Version 0.3.1 (2011-04-02) 23 | * IMPROVEMENT: Added tolerance for `.css` and `.js` extensions in assets.yml, 24 | even though you don't need them. 25 | * IMPROVEMENT: Added support for symbols as bundle names, e.g., 26 | `include_js :bundle => :application`. Thanks, 27 | daphonz[https://github.com/daphonz]! 28 | * FIX: Worked around a JSMin bug that causes an error when JS ends with a 29 | comment and no line break. 30 | 31 | == Version 0.3.0 (2010-12-08) 32 | * FEATURE: Added ERb support in `config/assets.yml`. 33 | * FEATURE: Added support for auto-creating SSL versions of every stylesheet. 34 | * FEATURE: Added support for loading remote JS via SSL. Includes loading 35 | popular JS from `https://ajax.googleapis.com` or your own CDN. 36 | * FEATURE: Added support for `FORMAT=long|short|dot` flags for 37 | `rake asset_hat:minify`. Useful for shortening output from deployment 38 | scripts. 39 | * FEATURE: Added support for absolute paths in `config/assets.yml`. Allows 40 | pointing to assets directly inside `public/`. 41 | * FEATURE: Added `asset_hat:minify:css` and `asset_hat:minify:js` Rake task 42 | aliases. 43 | * IMPROVEMENT: Updated default CSS minification engine to remove rules that 44 | have empty declaration blocks. 45 | * IMPROVEMENT: Stopped pre-caching asset commit IDs when launching console. 46 | * IMPROVEMENT: Added support for single/double quotation marks in `url()` CSS 47 | when adding asset hosts and cache busters 48 | * FIX: Fixed adding asset commit IDs to URLs containing "?" in stylesheets. 49 | 50 | == Version 0.2.1 (2010-07-21) 51 | * FEATURE: Added WebFont Loader to supported JS vendors. 52 | * FIX: Stopped adding asset hosts to `url(/htc/...)` URLs because IE 6, by 53 | default, refuses to run .htc files from other domains, including CDN 54 | subdomains. 55 | 56 | == Version 0.2.0 (2010-06-10) 57 | * FEATURE: Added support for loading many more JS vendors from Google's CDN, 58 | including Prototype, MooTools, and SWFObject. 59 | * FEATURE: Cleaned up existing docs, and added complete RDoc documentation. 60 | * FEATURE: Added example integration script for Capistrano deployments. 61 | 62 | == Version 0.1.5 (2010-03-11) 63 | * FIX: Fixed fetching asset commit IDs in some environments. The bug was 64 | possibly caused by older versions of Git, which fail to read logs properly 65 | when given absolute paths, rather than relative paths. 66 | * FIX: Stopped app tests from running twice in some environments. 67 | 68 | == Version 0.1.4 (2010-03-03) 69 | * FIX: Fixed config filepaths in `asset_hat:config` task. 70 | 71 | == Version 0.1.3 (2010-03-03) 72 | * FIX: Allowed adding commit IDs and asset hosts to `url(/htc/...)` URLs 73 | (e.g., `/htc/iepngfix.htc`) in CSS, not just images. 74 | * FIX: Changed `AssetHat.config` to memoize only if `cache?` is true. In 75 | development environments, this means the config file will be reread with 76 | every request. 77 | 78 | == Version 0.1.2 (2010-01-27) 79 | * IMPROVEMENT: Memoized HTML output from `include_css` and `include_js` when 80 | `AssetHat.cache?` is true. 81 | 82 | == Version 0.1.1 (2010-01-20) 83 | * FIX: Rewrote `AssetHat::VERSION` to be based on `VERSION.yml`. 84 | * FIX: Prefixed `AssetHat::CONFIG_FILEPATH` with `RAILS_ROOT`, which fixes 85 | ability to run an app's individual test files. 86 | 87 | == Version 0.1.0 (2010-01-19) 88 | * Initial release. 89 | -------------------------------------------------------------------------------- /lib/asset_hat/tasks/js.rb: -------------------------------------------------------------------------------- 1 | namespace :asset_hat do 2 | namespace :js do 3 | 4 | desc 'Minifies one JS file' 5 | task :minify_file, [:filepath] => :environment do |t, args| 6 | type = 'js' 7 | 8 | if args.filepath.blank? 9 | raise "Usage: rake asset_hat:#{type}:" + 10 | "minify_file[filepath.#{type}]" and return 11 | end 12 | 13 | verbose = (ENV['VERBOSE'] != 'false') # Defaults to `true` 14 | min_options = { 15 | :engine => AssetHat.config[type]['engine'] 16 | }.reject { |k,v| v.blank? } 17 | 18 | if verbose && args.filepath.match(/\.min\.#{type}$/) 19 | raise "#{args.filepath} is already minified." and return 20 | end 21 | 22 | input = File.open(args.filepath, 'r').read 23 | output = AssetHat::JS.minify(input, min_options) 24 | 25 | # Write minified content to file 26 | target_filepath = AssetHat::JS.min_filepath(args.filepath) 27 | File.open(target_filepath, 'w') { |f| f.write output } 28 | 29 | # Print results 30 | puts "- Minified to #{target_filepath}" if verbose 31 | end 32 | 33 | desc 'Minifies one JS bundle' 34 | task :minify_bundle, [:bundle] => :environment do |t, args| 35 | type = 'js' 36 | 37 | if args.bundle.blank? 38 | raise "Usage: rake asset_hat:#{type}:" + 39 | "minify_bundle[application]" and return 40 | end 41 | 42 | config = AssetHat.config[type] 43 | report_format = ([ENV['FORMAT']] & %w[long short dot])[0] || 'long' 44 | $stdout.sync = true if report_format == 'dot' # Output immediately 45 | min_options = { 46 | :engine => config['engine'] 47 | }.reject { |k,v| v.blank? } 48 | 49 | # Get bundle filenames 50 | filenames = config['bundles'][args.bundle].select(&:present?) 51 | if filenames.empty? 52 | raise "No #{type.upcase} files are specified for the " + 53 | "#{args.bundle} bundle in #{AssetHat::CONFIG_FILEPATH}." and return 54 | end 55 | filepaths = filenames.map do |filename| 56 | parts = filename.split(File::SEPARATOR) 57 | parts.last << '.' << type unless parts.last =~ /\.#{type}$/ 58 | File.join( 59 | (parts.first.present? ? 60 | AssetHat.assets_dir(type) : # Given path was relative 61 | AssetHat::ASSETS_DIR), # Given path was absolute 62 | parts 63 | ) 64 | end 65 | bundle_filepath = AssetHat::JS.min_filepath(File.join( 66 | AssetHat.bundles_dir(type), "#{args.bundle}.#{type}")) 67 | 68 | # Concatenate and process output 69 | output = '' 70 | old_bundle_size = 0.0 71 | new_bundle_size = 0.0 72 | filepaths.each do |filepath| 73 | file_output = File.open(filepath, 'r').read 74 | old_bundle_size += file_output.size 75 | unless filepath =~ /\.min\.#{type}$/ # Already minified 76 | file_output = AssetHat::JS.minify(file_output, min_options) 77 | end 78 | new_bundle_size += file_output.size 79 | output << file_output + "\n" 80 | end 81 | FileUtils.makedirs(File.dirname(bundle_filepath)) 82 | File.open(bundle_filepath, 'w') { |f| f.write output } 83 | 84 | # Print results 85 | percent_saved = 86 | "#{'%.1f' % ((1 - (new_bundle_size / old_bundle_size)) * 100)}%" 87 | bundle_filepath.sub!(/^#{Rails.root}\//, '') 88 | case report_format 89 | when 'dot' 90 | print '.' 91 | when 'short' 92 | puts "Minified #{percent_saved.rjust(6)}: #{bundle_filepath}" 93 | else # 'long' 94 | puts "\n Wrote #{type.upcase} bundle: #{bundle_filepath}" 95 | filepaths.each do |filepath| 96 | puts " contains: #{filepath.sub(/^#{Rails.root}\//, '')}" 97 | end 98 | if old_bundle_size > 0 99 | puts " MINIFIED: #{percent_saved}" + 100 | (" (empty!)" if new_bundle_size == 0).to_s + 101 | " (Engine: #{min_options[:engine]})" 102 | end 103 | end 104 | end 105 | 106 | desc 'Concatenates and minifies all JS bundles' 107 | task :minify, [:opts] => :environment do |t, args| 108 | args.with_defaults(:opts => {}) 109 | opts = args.opts.reverse_merge(:show_intro => true, :show_outro => true) 110 | type = 'js' 111 | report_format = ENV['FORMAT'] 112 | 113 | if opts[:show_intro] 114 | print "Minifying #{type.upcase}..." 115 | if report_format == 'dot' 116 | $stdout.sync = true # Output immediately 117 | else 118 | puts 119 | end 120 | end 121 | 122 | # Get input bundles 123 | config = AssetHat.config[type] 124 | if config.blank? || config['bundles'].blank? 125 | raise "You need to set up #{type.upcase} bundles " + 126 | "in #{AssetHat::CONFIG_FILEPATH}." and return 127 | end 128 | bundles = config['bundles'].keys 129 | 130 | # Minify bundles 131 | bundles.each do |bundle| 132 | task = Rake::Task["asset_hat:#{type}:minify_bundle"] 133 | task.reenable 134 | task.invoke(bundle) 135 | end 136 | 137 | if opts[:show_outro] 138 | puts unless report_format == 'short' 139 | puts 'Done.' 140 | end 141 | end 142 | 143 | end # namespace :js 144 | end # namespace :asset_hat 145 | -------------------------------------------------------------------------------- /asset_hat.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{asset_hat} 8 | s.version = "0.4.2" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Ron DeVera", "Mint Digital"] 12 | s.date = %q{2011-08-08} 13 | s.description = %q{Load CSS and JS faster. Minifies, bundles, and optimizes CSS/JS assets ahead of time (e.g., on deploy), not at runtime. Loads popular third-party JS (like jQuery, YUI, and Dojo) from localhost in development, and auto-switches to Google's CDN in production. Lets you switch on LABjs mode to load more scripts in parallel. Can rewrite stylesheets to use CDN hosts (not just your web server) and cache-busting hashes for updated images.} 14 | s.email = %q{hello@rondevera.com} 15 | s.extra_rdoc_files = [ 16 | "LICENSE", 17 | "README.rdoc" 18 | ] 19 | s.files = [ 20 | ".gemtest", 21 | "Gemfile", 22 | "HISTORY", 23 | "LICENSE", 24 | "README.rdoc", 25 | "Rakefile", 26 | "VERSION.yml", 27 | "asset_hat.gemspec", 28 | "config/assets.yml", 29 | "doc/classes/AssetHat.html", 30 | "doc/classes/AssetHat/CSS.html", 31 | "doc/classes/AssetHat/CSS/Engines.html", 32 | "doc/classes/AssetHat/JS.html", 33 | "doc/classes/AssetHat/JS/Engines.html", 34 | "doc/classes/AssetHat/JS/Vendors.html", 35 | "doc/classes/AssetHatHelper.html", 36 | "doc/created.rid", 37 | "doc/files/HISTORY.html", 38 | "doc/files/LICENSE.html", 39 | "doc/files/README_rdoc.html", 40 | "doc/files/lib/asset_hat/capistrano_rb.html", 41 | "doc/files/lib/asset_hat/css_rb.html", 42 | "doc/files/lib/asset_hat/initializers/action_view_rb.html", 43 | "doc/files/lib/asset_hat/initializers/cache_last_commit_ids_rb.html", 44 | "doc/files/lib/asset_hat/js/vendors_rb.html", 45 | "doc/files/lib/asset_hat/js_rb.html", 46 | "doc/files/lib/asset_hat/railtie_rb.html", 47 | "doc/files/lib/asset_hat/tasks/css_rb.html", 48 | "doc/files/lib/asset_hat/tasks/js_rb.html", 49 | "doc/files/lib/asset_hat/tasks_rb.html", 50 | "doc/files/lib/asset_hat/unicorn_rb.html", 51 | "doc/files/lib/asset_hat/vcs_rb.html", 52 | "doc/files/lib/asset_hat/version_rb.html", 53 | "doc/files/lib/asset_hat_helper_rb.html", 54 | "doc/files/lib/asset_hat_rb.html", 55 | "doc/files/lib/tasks/asset_hat_rake.html", 56 | "doc/fr_class_index.html", 57 | "doc/fr_file_index.html", 58 | "doc/fr_method_index.html", 59 | "doc/index.html", 60 | "doc/rdoc-style.css", 61 | "lib/asset_hat.rb", 62 | "lib/asset_hat/capistrano.rb", 63 | "lib/asset_hat/css.rb", 64 | "lib/asset_hat/initializers/action_view.rb", 65 | "lib/asset_hat/initializers/cache_last_commit_ids.rb", 66 | "lib/asset_hat/js.rb", 67 | "lib/asset_hat/js/vendors.rb", 68 | "lib/asset_hat/railtie.rb", 69 | "lib/asset_hat/tasks.rb", 70 | "lib/asset_hat/tasks/css.rb", 71 | "lib/asset_hat/tasks/js.rb", 72 | "lib/asset_hat/unicorn.rb", 73 | "lib/asset_hat/vcs.rb", 74 | "lib/asset_hat/version.rb", 75 | "lib/asset_hat_helper.rb", 76 | "lib/tasks/asset_hat.rake", 77 | "public/javascripts/bundles/js-bundle-1.min.js", 78 | "public/javascripts/bundles/js-bundle-2.min.js", 79 | "public/javascripts/js-file-1-1.js", 80 | "public/javascripts/js-file-1-2.js", 81 | "public/javascripts/js-file-1-3.js", 82 | "public/javascripts/js-file-2-1.js", 83 | "public/javascripts/js-file-2-2.js", 84 | "public/javascripts/js-file-2-3.js", 85 | "public/stylesheets/bundles/css-bundle-1.min.css", 86 | "public/stylesheets/bundles/css-bundle-2.min.css", 87 | "public/stylesheets/bundles/ssl/css-bundle-1.min.css", 88 | "public/stylesheets/bundles/ssl/css-bundle-2.min.css", 89 | "public/stylesheets/bundles/ssl/css-bundle-3.min.css", 90 | "public/stylesheets/css-file-1-1.css", 91 | "public/stylesheets/css-file-1-2.css", 92 | "public/stylesheets/css-file-1-3.css", 93 | "public/stylesheets/css-file-2-1.css", 94 | "public/stylesheets/css-file-2-2.css", 95 | "public/stylesheets/css-file-2-3.css", 96 | "rails/init.rb", 97 | "test/asset_hat_helper_test.rb", 98 | "test/asset_hat_test.rb", 99 | "test/test_helper.rb" 100 | ] 101 | s.homepage = %q{http://mintdigital.github.com/asset_hat} 102 | s.require_paths = ["lib"] 103 | s.rubygems_version = %q{1.4.2} 104 | s.summary = %q{Your assets are covered.} 105 | 106 | if s.respond_to? :specification_version then 107 | s.specification_version = 3 108 | 109 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 110 | s.add_development_dependency(%q, ["~> 0.8.6"]) 111 | s.add_development_dependency(%q, ["~> 0.1.12"]) 112 | s.add_development_dependency(%q, ["~> 1.6.0"]) 113 | s.add_development_dependency(%q, ["~> 2.10.2"]) 114 | s.add_runtime_dependency(%q, ["~> 1.0.2"]) 115 | s.add_runtime_dependency(%q, ["~> 1.0.1"]) 116 | else 117 | s.add_dependency(%q, ["~> 0.8.6"]) 118 | s.add_dependency(%q, ["~> 0.1.12"]) 119 | s.add_dependency(%q, ["~> 1.6.0"]) 120 | s.add_dependency(%q, ["~> 2.10.2"]) 121 | s.add_dependency(%q, ["~> 1.0.2"]) 122 | s.add_dependency(%q, ["~> 1.0.1"]) 123 | end 124 | else 125 | s.add_dependency(%q, ["~> 0.8.6"]) 126 | s.add_dependency(%q, ["~> 0.1.12"]) 127 | s.add_dependency(%q, ["~> 1.6.0"]) 128 | s.add_dependency(%q, ["~> 2.10.2"]) 129 | s.add_dependency(%q, ["~> 1.0.2"]) 130 | s.add_dependency(%q, ["~> 1.0.1"]) 131 | end 132 | end 133 | 134 | -------------------------------------------------------------------------------- /lib/asset_hat/css.rb: -------------------------------------------------------------------------------- 1 | require 'cssmin' 2 | 3 | module AssetHat 4 | # Methods for minifying and optimizing CSS. 5 | module CSS 6 | # A list of supported minification 7 | # engine names. 8 | ENGINES = [:weak, :cssmin] 9 | 10 | # Returns the expected path for the minified version of a CSS asset: 11 | # 12 | # AssetHat::CSS.min_filepath('public/stylesheets/bundles/application.css') 13 | # # => 'public/stylesheets/bundles/application.min.css' 14 | def self.min_filepath(filepath) 15 | AssetHat.min_filepath(filepath, 'css') 16 | end 17 | 18 | # Accepts a string of CSS, and returns that CSS minified. Options: 19 | # 20 | # [engine] Default is :cssmin; see 21 | # Engines.cssmin. 22 | # Allowed values are in ENGINES. 23 | def self.minify(input_string, options={}) 24 | options.reverse_merge!(:engine => :cssmin) 25 | 26 | engine = options[:engine].to_sym 27 | unless ENGINES.include?(engine) 28 | raise %{ 29 | Unknown CSS minification engine '#{engine}'. 30 | Allowed: #{ENGINES.map{ |e| "'#{e}'" }.join(', ')} 31 | }.strip.gsub(/\s+/, ' ') and return 32 | end 33 | 34 | AssetHat::CSS::Engines.send(engine, input_string).strip 35 | end 36 | 37 | # Given a string containing CSS, appends each referenced asset's last 38 | # commit ID to its URL, e.g., 39 | # background: url(/images/foo.png?ab12cd3). This enables 40 | # cache busting: If the user's browser has cached a copy of foo.png from a 41 | # previous deployment, this new URL forces the browser to ignore that 42 | # cache and request the latest version. 43 | def self.add_asset_commit_ids(css) 44 | update_css_urls(css, %w[images htc]) do |src, quote| 45 | # Get absolute path 46 | filepath = File.join(ASSETS_DIR, src) 47 | 48 | # Convert to relative path 49 | filepath.sub!(/^#{FileUtils.pwd}#{File::SEPARATOR}/, '') 50 | 51 | commit_id = AssetHat.last_commit_id(filepath) 52 | if commit_id.present? 53 | "url(#{quote}#{src}#{src =~ /\?/ ? '&' : '?'}#{commit_id}#{quote})" 54 | else 55 | "url(#{quote}#{src}#{quote})" 56 | end 57 | end 58 | end 59 | 60 | # Arguments: 61 | # 62 | # - A string containing CSS; 63 | # - A string containing the app's asset host, e.g., 64 | # 'http\://cdn%d.example.com'. This value is typically taken from 65 | # config.action_controller.asset_host in 66 | # the app's config/environments/production.rb. 67 | # 68 | # An asset host is added to every image URL in the CSS, e.g., 69 | # background: url(http\://cdn2.example.com/images/foo.png); 70 | # if %d in the asset host, it is replaced with an arbitrary 71 | # number in 0-3, inclusive. 72 | # 73 | # Options: 74 | # 75 | # [ssl] Set to true to simulate a request via SSL. Defaults 76 | # to false. 77 | def self.add_asset_hosts(css, asset_host, options={}) 78 | return css if asset_host.blank? 79 | 80 | options.reverse_merge!(:ssl => false) 81 | 82 | update_css_urls(css, %w[images]) do |src, quote| 83 | computed_asset_host = AssetHat.compute_asset_host( 84 | asset_host, src, options.slice(:ssl)) 85 | "url(#{quote}#{computed_asset_host}#{src}#{quote})" 86 | end 87 | end 88 | 89 | # Swappable CSS minification engines. Each accepts and returns a string. 90 | module Engines 91 | # Barebones CSS minification engine that only strips whitespace from the 92 | # start and end of every line, including linebreaks. For safety, doesn't 93 | # attempt to parse the CSS itself. 94 | def self.weak(input_string) 95 | input = StringIO.new(input_string) 96 | output = StringIO.new 97 | 98 | input.each do |line| 99 | # Remove indentation and trailing whitespace (including line breaks) 100 | line.strip! 101 | next if line.blank? 102 | 103 | output.write line 104 | end 105 | 106 | output.rewind 107 | output.read 108 | end 109 | 110 | # CSS minification engine that mostly uses the CSSMin gem, a Ruby port 111 | # of Lecomte's YUI Compressor and Schlueter's PHP cssmin. 112 | # 113 | # Sources: 114 | # - http://github.com/rgrove/cssmin 115 | # - http://rubygems.org/gems/cssmin 116 | def self.cssmin(input_string) 117 | output = CSSMin.minify(input_string) 118 | 119 | # Remove rules that have empty declaration blocks 120 | output.gsub!(/\}([^\}]+\{;\}){1,}/, '}') 121 | 122 | output 123 | end 124 | end 125 | 126 | 127 | 128 | private 129 | 130 | # Strips any balanced quotation marks from `src`. Returns `src` and an 131 | # instance of the quotation mark as two separate strings. 132 | def self.separate_src_and_quotes(src) 133 | quote = src[0, 1] 134 | 135 | if %w[' "].include?(quote) && quote == src[-1, 1] 136 | src = src[1, src.length - 2] # Strip quotes 137 | else 138 | quote = nil # No quotes in original CSS 139 | end 140 | 141 | [src, quote] 142 | end 143 | 144 | def self.update_css_urls(css, dirs, &css_block) 145 | new_css = css.dup 146 | dirs = dirs.join('|') 147 | 148 | gsub_block = lambda do |match| 149 | src, quote = separate_src_and_quotes($1) 150 | css_block.call(src, quote) 151 | end 152 | 153 | # Match without quotes 154 | new_css.gsub!(/url[\s]*\((\/(#{dirs})\/[^)'"]+)\)/, &gsub_block) 155 | 156 | # Match with single/double quotes 157 | new_css.gsub!(/url[\s]*\(((['"])\/(#{dirs})\/[^)]+\2)\)/, &gsub_block) 158 | 159 | new_css 160 | end 161 | 162 | end 163 | 164 | end 165 | -------------------------------------------------------------------------------- /lib/asset_hat/tasks/css.rb: -------------------------------------------------------------------------------- 1 | namespace :asset_hat do 2 | namespace :css do 3 | 4 | desc 'Adds commit IDs to asset URLs in CSS for cache busting' 5 | task :add_asset_commit_ids, [:filename] => :environment do |t, args| 6 | if args.filename.blank? 7 | raise 'Usage: rake asset_hat:css:' + 8 | 'add_asset_commit_ids[filename.css]' and return 9 | end 10 | 11 | verbose = (ENV['VERBOSE'] != 'false') # Defaults to `true` 12 | 13 | css = File.open(args.filename, 'r') { |f| f.read } 14 | css = AssetHat::CSS.add_asset_commit_ids(css) 15 | File.open(args.filename, 'w') { |f| f.write css } 16 | 17 | puts "- Added asset commit IDs to #{args.filename}" if verbose 18 | end 19 | 20 | desc 'Adds hosts to asset URLs in CSS' 21 | task :add_asset_hosts, [:filename] => :environment do |t, args| 22 | if args.filename.blank? 23 | raise 'Usage: rake asset_hat:css:' + 24 | 'add_asset_hosts[filename.css]' and return 25 | end 26 | 27 | verbose = (ENV['VERBOSE'] != 'false') # Defaults to `true` 28 | 29 | asset_host = ActionController::Base.asset_host 30 | if asset_host.blank? 31 | raise "This environment (#{ENV['RAILS_ENV']}) " + 32 | "doesn't have an `asset_host` configured." and return 33 | end 34 | 35 | css = File.open(args.filename, 'r') { |f| f.read } 36 | css = AssetHat::CSS.add_asset_hosts(css, asset_host) 37 | File.open(args.filename, 'w') { |f| f.write css } 38 | 39 | puts "- Added asset hosts to #{args.filename}" if verbose 40 | end 41 | 42 | desc 'Minifies one CSS file' 43 | task :minify_file, [:filepath] => :environment do |t, args| 44 | type = 'css' 45 | 46 | if args.filepath.blank? 47 | raise "Usage: rake asset_hat:#{type}:" + 48 | "minify_file[path/to/filename.#{type}]" and return 49 | end 50 | 51 | verbose = (ENV['VERBOSE'] != 'false') # Defaults to `true` 52 | min_options = { 53 | :engine => AssetHat.config[type]['engine'] 54 | }.reject { |k,v| v.blank? } 55 | 56 | input = File.open(args.filepath, 'r').read 57 | output = AssetHat::CSS.minify(input, min_options) 58 | 59 | # Write minified content to file 60 | target_filepath = AssetHat::CSS.min_filepath(args.filepath) 61 | File.open(target_filepath, 'w') { |f| f.write output } 62 | 63 | # Print results 64 | puts "- Minified to #{target_filepath}" if verbose 65 | end 66 | 67 | desc 'Minifies one CSS bundle' 68 | task :minify_bundle, [:bundle] => :environment do |t, args| 69 | type = 'css' 70 | 71 | if args.bundle.blank? 72 | raise "Usage: rake asset_hat:#{type}:" + 73 | "minify_bundle[application]" and return 74 | end 75 | 76 | config = AssetHat.config[type] 77 | report_format = ([ENV['FORMAT']] & %w[long short dot])[0] || 'long' 78 | $stdout.sync = (report_format == 'dot') 79 | min_options = { 80 | :engine => config['engine'] 81 | }.reject { |k,v| v.blank? } 82 | 83 | # Get bundle filenames 84 | filenames = config['bundles'][args.bundle].select(&:present?) 85 | if filenames.empty? 86 | raise "No #{type.upcase} files are specified for the " + 87 | "#{args.bundle} bundle in #{AssetHat::CONFIG_FILEPATH}." and return 88 | end 89 | filepaths = filenames.map do |filename| 90 | parts = filename.split(File::SEPARATOR) 91 | parts.last << '.' << type unless parts.last =~ /\.#{type}$/ 92 | File.join( 93 | (parts.first.present? ? 94 | AssetHat.assets_dir(type) : # Given path was relative 95 | AssetHat::ASSETS_DIR), # Given path was absolute 96 | parts 97 | ) 98 | end 99 | 100 | # Check whether app has special SSL asset host 101 | asset_host = ActionController::Base.asset_host 102 | output_options_array = [{:ssl => false}] 103 | if AssetHat.ssl_asset_host_differs? 104 | # The bundle needs a second version, which uses the asset host via SSL 105 | output_options_array << {:ssl => true} 106 | end 107 | 108 | output_options_array.each do |output_options| 109 | 110 | # Concatenate and process output 111 | bundle_filepath = AssetHat::CSS.min_filepath(File.join( 112 | AssetHat.bundles_dir(type, output_options.slice(:ssl)), 113 | "#{args.bundle}.#{type}")) 114 | old_bundle_size = 0.0 115 | new_bundle_size = 0.0 116 | output = '' 117 | filepaths.each do |filepath| 118 | file_output = File.open(filepath, 'r').read 119 | old_bundle_size += file_output.size 120 | 121 | file_output = AssetHat::CSS.minify(file_output, min_options) 122 | file_output = AssetHat::CSS.add_asset_commit_ids(file_output) 123 | if asset_host.present? 124 | file_output = AssetHat::CSS.add_asset_hosts( 125 | file_output, asset_host, output_options.slice(:ssl)) 126 | end 127 | 128 | new_bundle_size += file_output.size 129 | output << file_output + "\n" 130 | end 131 | FileUtils.makedirs(File.dirname(bundle_filepath)) 132 | File.open(bundle_filepath, 'w') { |f| f.write output } 133 | 134 | # Print results 135 | percent_saved = 136 | "#{'%.1f' % ((1 - (new_bundle_size / old_bundle_size)) * 100)}%" 137 | bundle_filepath.sub!(/^#{Rails.root}\//, '') 138 | case report_format 139 | when 'dot' 140 | print '.' 141 | when 'short' 142 | puts "Minified #{percent_saved.rjust(6)}: #{bundle_filepath}" 143 | else # 'long' 144 | puts "\nWrote #{type.upcase} bundle: #{bundle_filepath}" 145 | filepaths.each do |filepath| 146 | puts " contains: #{filepath.sub(/^#{Rails.root}\//, '')}" 147 | end 148 | if old_bundle_size > 0 149 | puts " MINIFIED: #{percent_saved}" + 150 | (" (empty!)" if new_bundle_size == 0).to_s + 151 | " (Engine: #{min_options[:engine]})" 152 | end 153 | end 154 | end 155 | end 156 | 157 | desc 'Concatenates and minifies all CSS bundles' 158 | task :minify, [:opts] => :environment do |t, args| 159 | args.with_defaults(:opts => {}) 160 | opts = args.opts.reverse_merge(:show_intro => true, :show_outro => true) 161 | type = 'css' 162 | report_format = ENV['FORMAT'] 163 | 164 | if opts[:show_intro] 165 | print "Minifying #{type.upcase}..." 166 | if report_format == 'dot' 167 | $stdout.sync = true # Output immediately 168 | else 169 | puts 170 | end 171 | end 172 | 173 | # Get input bundles 174 | config = AssetHat.config[type] 175 | if config.blank? || config['bundles'].blank? 176 | raise "You need to set up #{type.upcase} bundles " + 177 | "in #{AssetHat::CONFIG_FILEPATH}." and return 178 | end 179 | bundles = config['bundles'].keys 180 | 181 | # Minify bundles 182 | bundles.each do |bundle| 183 | task = Rake::Task["asset_hat:#{type}:minify_bundle"] 184 | task.reenable 185 | task.invoke(bundle) 186 | end 187 | 188 | if opts[:show_outro] 189 | puts unless report_format == 'short' 190 | puts 'Done.' 191 | end 192 | end 193 | 194 | end # namespace :css 195 | end # namespace :asset_hat 196 | -------------------------------------------------------------------------------- /doc/classes/AssetHat/CSS/Engines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : AssetHat::CSS::Engines [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

29 | Module 30 | AssetHat::CSS::Engines 31 |

32 |
    33 |
  1. 34 | lib/asset_hat/css.rb 35 |
  2. 36 |
37 |
38 |
39 |
40 |
41 |

42 | Swappable CSS minification engines. Each accepts 43 | and returns a string. 44 |

45 |
46 |
47 |

Methods

48 |

public class

49 |
    50 |
  1. cssmin
  2. 51 |
  3. weak
  4. 52 |
53 |
54 |
55 |
56 |

Public class methods

57 |
58 | 59 |
60 | cssmin 61 | (input_string) 62 |
63 |
64 |

65 | CSS minification engine that mostly uses the 66 | CSSMin gem, a Ruby port of Lecomte’s YUI Compressor and 67 | Schlueter’s PHP cssmin. 68 |

69 |

70 | Sources: 71 |

72 | 80 |
81 |
82 | 83 | [show source] 84 | 85 |
     # File lib/asset_hat/css.rb, line 116
116:       def self.cssmin(input_string)
117:         output = CSSMin.minify(input_string)
118: 
119:         # Remove rules that have empty declaration blocks
120:         output.gsub!(/\}([^\}]+\{;\}){1,}/, '}')
121: 
122:         output
123:       end
86 |
87 |
88 |
89 | 90 |
91 | weak 92 | (input_string) 93 |
94 |
95 |

96 | Barebones CSS minification engine that only 97 | strips whitespace from the start and end of every line, including 98 | linebreaks. For safety, doesn’t attempt to parse the CSS itself. 100 |

101 |
102 |
103 | 104 | [show source] 105 | 106 |
     # File lib/asset_hat/css.rb, line 94
 94:       def self.weak(input_string)
 95:         input   = StringIO.new(input_string)
 96:         output  = StringIO.new
 97: 
 98:         input.each do |line|
 99:           # Remove indentation and trailing whitespace (including line breaks)
100:           line.strip!
101:           next if line.blank?
102: 
103:           output.write line
104:         end
105: 
106:         output.rewind
107:         output.read
108:       end
107 |
108 |
109 |
110 |
111 |
112 |
113 | 114 |
115 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /doc/classes/AssetHat/JS/Engines.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : AssetHat::JS::Engines [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

29 | Module 30 | AssetHat::JS::Engines 31 |

32 |
    33 |
  1. 34 | lib/asset_hat/js.rb 35 |
  2. 36 |
37 |
38 |
39 |
40 |
41 |

42 | Swappable JavaScript minification engines. 43 |

44 |
45 |
46 |

Methods

47 |

public class

48 |
    49 |
  1. jsmin
  2. 50 |
  3. weak
  4. 51 |
52 |
53 |
54 |
55 |

Public class methods

56 |
57 | 58 |
59 | jsmin 60 | (input_string) 61 |
62 |
63 |

64 | JavaScript minification engine that simply uses the JSMin gem, a Ruby port 65 | of Crockford’s JSMin. 66 |

67 |

68 | Sources: 69 |

70 | 78 |
79 |
80 | 81 | [show source] 82 | 83 |
    # File lib/asset_hat/js.rb, line 79
79:       def self.jsmin(input_string)
80:         JSMin.minify(input_string + "\n")
81:       end
84 |
85 |
86 |
87 | 88 |
89 | weak 90 | (input_string) 91 |
92 |
93 |

94 | Barebones JavaScript minification engine that: 95 |

96 |
    97 |
  • Skips leading/trailing whitespace for each line, excluding line breaks; and 98 | 99 |
  • 100 |
  • Removes one-line comments that had no actual code on that line. 101 | 102 |
  • 103 |
104 |
105 |
106 | 107 | [show source] 108 | 109 |
    # File lib/asset_hat/js.rb, line 48
48:       def self.weak(input_string)
49:         input   = StringIO.new(input_string)
50:         output  = StringIO.new
51: 
52:         input.each do |line|
53:           # Remove indentation and trailing whitespace
54:           line.strip!
55:           next if line.blank?
56: 
57:           # Skip single-line comments
58:           next if !(line =~ /^\/\//).nil?
59:           # TODO: Also skip single-line comments that began mid-line, but not
60:           #       inside a string or regex
61: 
62:           # TODO: Skip multi-line comments
63:           # - Should not strip from within a string or regex
64:           # - Should not strip comments that begin with `/*!` (e.g., licenses)
65: 
66:           output.write(line + "\n")
67:         end
68: 
69:         output.rewind
70:         output.read
71:       end
110 |
111 |
112 |
113 |
114 |
115 |
116 | 117 |
118 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /doc/files/HISTORY.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | File: HISTORY [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |
HISTORY
29 |
30 | HISTORY 31 |
32 |
33 | Last Update: 34 | Mon Aug 08 13:47:59 -0400 2011 35 |
36 |
37 |
38 |
39 |
40 |

HISTORY

41 |

Version 0.4.2 (2011-08-08)

42 |
    43 |
  • FIX: Changed Capistrano task to minify assets inside latest release path, 44 | not previous release path. (Thanks, sauliusg!) 46 | 47 |
  • 48 |
  • FIX: Fixed deprecation warnings when running Rake tasks. (Thanks, jsonperl!) 50 | 51 |
  • 52 |
53 |

Version 0.4.1 (2011-05-06)

54 |
    55 |
  • FIX: Stopped `gem install asset_hat` from foolishly requiring itself. 56 | 57 |
  • 58 |
59 |

Version 0.4.0 (2011-05-06)

60 |
    61 |
  • FEATURE: Added Rails 3 support. 62 | 63 |
  • 64 |
  • FEATURE: Added support for loading JavaScript files via LABjs: `<%= 65 | include_js :jquery, :bundles => %[plugins app], :loader => :lab_js %>`. 66 | (Provides only basic LABjs support. To build custom JS logic, use the new 67 | `:only_url` option.) 68 | 69 |
  • 70 |
  • FEATURE: Added support for getting asset URLs, e.g.: `<%= include_css 71 | ‘foo’, :bundle => ‘bar’, :only_url => true %>`, 72 | `<%= include_js :jquery, ‘foo’, :bundle => ‘bar’, 73 | :only_url => true %>`. 74 | 75 |
  • 76 |
77 |

Version 0.3.1 (2011-04-02)

78 |
    79 |
  • IMPROVEMENT: Added tolerance for `.css` and `.js` extensions in assets.yml, 80 | even though you don’t need them. 81 | 82 |
  • 83 |
  • IMPROVEMENT: Added support for symbols as bundle names, e.g., `include_js 84 | :bundle => :application`. Thanks, daphonz! 86 | 87 |
  • 88 |
  • FIX: Worked around a JSMin bug that causes an error when JS ends with a 89 | comment and no line break. 90 | 91 |
  • 92 |
93 |

Version 0.3.0 (2010-12-08)

94 |
    95 |
  • FEATURE: Added ERb support in `config/assets.yml`. 96 | 97 |
  • 98 |
  • FEATURE: Added support for auto-creating SSL versions of every stylesheet. 99 | 100 |
  • 101 |
  • FEATURE: Added support for loading remote JS via SSL. Includes loading 102 | popular JS from `ajax.googleapis.com` or your own 104 | CDN. 105 | 106 |
  • 107 |
  • FEATURE: Added support for `FORMAT=long|short|dot` flags for `rake 108 | asset_hat:minify`. Useful for shortening output from deployment scripts. 109 | 110 |
  • 111 |
  • FEATURE: Added support for absolute paths in `config/assets.yml`. Allows 112 | pointing to assets directly inside `public/`. 113 | 114 |
  • 115 |
  • FEATURE: Added `asset_hat:minify:css` and `asset_hat:minify:js` Rake task 116 | aliases. 117 | 118 |
  • 119 |
  • IMPROVEMENT: Updated default CSS minification engine to remove rules that 120 | have empty declaration blocks. 121 | 122 |
  • 123 |
  • IMPROVEMENT: Stopped pre-caching asset commit IDs when launching console. 124 | 125 |
  • 126 |
  • IMPROVEMENT: Added support for single/double quotation marks in `url()` CSS 127 | when adding asset hosts and cache busters 128 | 129 |
  • 130 |
  • FIX: Fixed adding asset commit IDs to URLs containing “?” in 131 | stylesheets. 132 | 133 |
  • 134 |
135 |

Version 0.2.1 (2010-07-21)

136 |
    137 |
  • FEATURE: Added WebFont Loader to supported JS vendors. 138 | 139 |
  • 140 |
  • FIX: Stopped adding asset hosts to `url(/htc/...)` URLs because IE 6, by 141 | default, refuses to run .htc files from other domains, including CDN 142 | subdomains. 143 | 144 |
  • 145 |
146 |

Version 0.2.0 (2010-06-10)

147 |
    148 |
  • FEATURE: Added support for loading many more JS vendors from Google’s 149 | CDN, including Prototype, MooTools, and SWFObject. 150 | 151 |
  • 152 |
  • FEATURE: Cleaned up existing docs, and added complete RDoc documentation. 153 | 154 |
  • 155 |
  • FEATURE: Added example integration script for Capistrano deployments. 156 | 157 |
  • 158 |
159 |

Version 0.1.5 (2010-03-11)

160 |
    161 |
  • FIX: Fixed fetching asset commit IDs in some environments. The bug was 162 | possibly caused by older versions of Git, which fail to read logs properly 163 | when given absolute paths, rather than relative paths. 164 | 165 |
  • 166 |
  • FIX: Stopped app tests from running twice in some environments. 167 | 168 |
  • 169 |
170 |

Version 0.1.4 (2010-03-03)

171 |
    172 |
  • FIX: Fixed config filepaths in `asset_hat:config` task. 173 | 174 |
  • 175 |
176 |

Version 0.1.3 (2010-03-03)

177 |
    178 |
  • FIX: Allowed adding commit IDs and asset hosts to `url(/htc/...)` URLs 179 | (e.g., `/htc/iepngfix.htc`) in CSS, not just images. 180 | 181 |
  • 182 |
  • FIX: Changed `AssetHat.config` to memoize 184 | only if `cache?` is true. In development environments, this means the 185 | config file will be reread with every request. 186 | 187 |
  • 188 |
189 |

Version 0.1.2 (2010-01-27)

190 |
    191 |
  • IMPROVEMENT: Memoized HTML output from `include_css` and `include_js` when 192 | `AssetHat.cache?` is true. 193 | 194 |
  • 195 |
196 |

Version 0.1.1 (2010-01-20)

197 |
    198 |
  • FIX: Rewrote `AssetHat::VERSION` to be based on `VERSION.yml`. 199 | 200 |
  • 201 |
  • FIX: Prefixed `AssetHat::CONFIG_FILEPATH` with `RAILS_ROOT`, which fixes 202 | ability to run an app’s individual test files. 203 | 204 |
  • 205 |
206 |

Version 0.1.0 (2010-01-19)

207 |
    208 |
  • Initial release. 209 | 210 |
  • 211 |
212 |
213 |
214 |
215 |
216 |
217 | 218 |
219 | 222 | 223 | 224 | -------------------------------------------------------------------------------- /doc/classes/AssetHat/JS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : AssetHat::JS [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

29 | Module 30 | AssetHat::JS 31 |

32 |
    33 |
  1. 34 | lib/asset_hat/js/vendors.rb 35 |
  2. 36 |
  3. 37 | lib/asset_hat/js.rb 38 |
  4. 39 |
  5. 40 | show all 41 |
  6. 42 |
43 |
44 |
45 |
46 |
47 |

48 | Methods for minifying JavaScript. 49 |

50 |
51 |
52 |

Methods

53 |

public class

54 |
    55 |
  1. min_filepath
  2. 56 |
  3. minify
  4. 57 |
58 |
59 |
60 |
61 |

Classes and Modules

62 | Module AssetHat::JS::Engines
63 | Module AssetHat::JS::Vendors
64 |
65 |
66 |

Constants

67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 89 | 90 |
ENGINES=[:weak, :jsmin]  75 | 76 | A list of supported minification engine names. 77 |
VENDORS=Vendors::VENDORS  85 | 86 | A list of supported 3rd-party JavaScript 87 | plugin/vendor names. 88 |
91 |
92 |
93 |
94 |

Public class methods

95 |
96 | 97 |
98 | min_filepath 99 | (filepath) 100 |
101 |
102 |

103 | Returns the expected path for the minified version of a JS asset: 105 |

106 |
AssetHat::JS.min_filepath('public/javascripts/bundles/application.js')
  # => 'public/javascripts/bundles/application.min.js'
107 |
108 |
109 | 110 | [show source] 111 | 112 |
    # File lib/asset_hat/js.rb, line 19
19:     def self.min_filepath(filepath)
20:       AssetHat.min_filepath(filepath, 'js')
21:     end
113 |
114 |
115 |
116 | 117 |
118 | minify 119 | (input_string, options={}) 120 |
121 |
122 |

123 | Accepts a string of JS, and returns that JS minified. Options: 125 |

126 |
127 |
engine
Default is :jsmin; see Engines.jsmin. Allowed values are in 129 | ENGINES. 130 | 131 |
132 |
133 |
134 |
135 | 136 | [show source] 137 | 138 |
    # File lib/asset_hat/js.rb, line 28
28:     def self.minify(input_string, options={})
29:       options.reverse_merge!(:engine => :jsmin)
30: 
31:       engine = options[:engine].to_sym
32:       unless ENGINES.include?(engine)
33:         raise %{
34:           Unknown JS minification engine '#{engine}'.
35:           Allowed: #{ENGINES.map{ |e| "'#{e}'" }.join(', ')}
36:         }.strip.gsub(/\s+/, ' ') and return
37:       end
38: 
39:       AssetHat::JS::Engines.send(engine, input_string).strip
40:     end
139 |
140 |
141 |
142 |
143 |
144 |
145 | 146 |
147 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /test/asset_hat_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class AssetHatTest < ActiveSupport::TestCase 4 | context 'AssetHat' do 5 | should 'know where to find/store assets in the file system' do 6 | assert_equal 'public/stylesheets', AssetHat.assets_dir(:css) 7 | assert_equal 'public/javascripts', AssetHat.assets_dir(:js) 8 | 9 | assert_equal 'bundles', AssetHat.bundles_dir 10 | assert_equal 'bundles/ssl', AssetHat.bundles_dir(:ssl => true) 11 | assert_equal 'public/stylesheets/bundles', AssetHat.bundles_dir(:css) 12 | assert_equal 'public/javascripts/bundles', AssetHat.bundles_dir(:js) 13 | assert_equal 'public/stylesheets/bundles/ssl', 14 | AssetHat.bundles_dir(:css, :ssl => true) 15 | assert_equal 'public/javascripts/bundles/ssl', 16 | AssetHat.bundles_dir(:js, :ssl => true) 17 | end 18 | 19 | should 'know whether the config file needs to be generated' do 20 | flexmock(File).should_receive(:exists?). 21 | with(AssetHat::CONFIG_FILEPATH).and_return(false) 22 | 23 | assert_raise RuntimeError do 24 | AssetHat.config 25 | end 26 | end 27 | 28 | should 'know where to find assets via a URL' do 29 | assert_equal '/stylesheets', AssetHat.assets_path(:css) 30 | assert_equal '/javascripts', AssetHat.assets_path(:js) 31 | 32 | assert_equal '/stylesheets/bundles', AssetHat.bundles_path(:css) 33 | assert_equal '/stylesheets/bundles/ssl', 34 | AssetHat.bundles_path(:css, :ssl => true) 35 | assert_equal '/javascripts/bundles', AssetHat.bundles_path(:js) 36 | assert_equal '/javascripts/bundles/ssl', 37 | AssetHat.bundles_path(:js, :ssl => true) 38 | end 39 | 40 | context 'with caching enabled' do 41 | setup do 42 | flexmock(AssetHat, :cache? => true) 43 | end 44 | 45 | should 'memoize config' do 46 | AssetHat.config 47 | flexmock(YAML).should_receive(:load).never 48 | 3.times { AssetHat.config } 49 | end 50 | end # context 'with caching enabled' 51 | 52 | context 'with caching disabled' do 53 | setup do 54 | flexmock(AssetHat, :cache? => false) 55 | end 56 | 57 | should 'not memoize config' do 58 | AssetHat.config 59 | flexmock(YAML).should_receive(:load).times(3) 60 | 3.times { AssetHat.config } 61 | end 62 | end # context 'with caching disabled' 63 | 64 | should 'recognize existing assets' do 65 | assert AssetHat.asset_exists?('css-file-1-1.css', :css) 66 | assert !AssetHat.asset_exists?('non-existent-css', :css) 67 | assert AssetHat.asset_exists?('js-file-1-1.js', :js) 68 | assert !AssetHat.asset_exists?('non-existent-js', :js) 69 | end 70 | 71 | should "return a bundle's filenames" do 72 | assert_equal %w[css-file-1-1 css-file-1-2 css-file-1-3], 73 | AssetHat.bundle_filenames('css-bundle-1', :css) 74 | end 75 | 76 | should "return a bundle's filepaths" do 77 | expected = [1,2,3].map { |i| "public/stylesheets/css-file-1-#{i}.css" } 78 | assert_equal expected, AssetHat.bundle_filepaths('css-bundle-1', :css) 79 | end 80 | 81 | context 'with asset host' do 82 | should 'compute asset host from a String' do 83 | asset_host = 'http://cdn%d.example.com' 84 | flexmock(ActionController::Base, :asset_host => asset_host) 85 | assert_match /http:\/\/cdn\d\.example\.com/, 86 | AssetHat.compute_asset_host(asset_host, 'x.png') 87 | end 88 | 89 | should 'compute asset host from a Proc' do 90 | asset_host = Proc.new do |source, request| 91 | if request.ssl? 92 | "#{request.protocol}ssl.cdn#{source.hash % 4}.example.com" 93 | else 94 | "#{request.protocol}cdn#{source.hash % 4}.example.com" 95 | end 96 | end 97 | flexmock(ActionController::Base, :asset_host => asset_host) 98 | 99 | assert_match /http:\/\/cdn\d\.example\.com/, 100 | AssetHat.compute_asset_host(asset_host, 'x.png') 101 | assert_match /https:\/\/ssl\.cdn\d\.example\.com/, 102 | AssetHat.compute_asset_host(asset_host, 'x.png', :ssl => true) 103 | end 104 | 105 | should 'know that asset host is same between SSL and non-SSL URLs' do 106 | asset_host = 'http://cdn%d.example.com' 107 | flexmock(ActionController::Base, :asset_host => asset_host) 108 | assert !AssetHat.ssl_asset_host_differs? 109 | end 110 | 111 | should 'know that asset host differs between SSL and non-SSL URLs' do 112 | asset_host = Proc.new do |source, request| 113 | if request.ssl? 114 | "#{request.protocol}ssl.cdn#{source.hash % 4}.example.com" 115 | else 116 | "#{request.protocol}cdn#{source.hash % 4}.example.com" 117 | end 118 | end 119 | flexmock(ActionController::Base, :asset_host => asset_host) 120 | assert AssetHat.ssl_asset_host_differs? 121 | end 122 | end # context 'with asset host' 123 | end # context 'AssetHat' 124 | 125 | context 'AssetHat::CSS' do 126 | should 'return path to minified file' do 127 | assert_equal 'foo/bar/baz.min.css', 128 | AssetHat::CSS.min_filepath('foo/bar/baz.css') 129 | end 130 | 131 | should 'add image asset commit IDs' do 132 | commit_id = 111 133 | flexmock(AssetHat, :last_commit_id => commit_id) 134 | flexmock(Rails, :public_path => '') 135 | 136 | # No/single/double quotes: 137 | ['', "'", '"'].each do |quote| 138 | img = '/images/foo.png' 139 | assert_equal( 140 | "p{background:url(#{quote}#{img}?#{commit_id}#{quote})}", 141 | AssetHat::CSS.add_asset_commit_ids( 142 | "p{background:url(#{quote}#{img}#{quote})}") 143 | ) 144 | 145 | img = '/images/?id=foo.png' 146 | assert_equal( 147 | "p{background:url(#{quote}#{img}&#{commit_id}#{quote})}", 148 | AssetHat::CSS.add_asset_commit_ids( 149 | "p{background:url(#{quote}#{img}#{quote})}") 150 | ) 151 | end 152 | 153 | # Mismatched quotes (should remain untouched): 154 | %w[ 155 | '/images/foo.png 156 | '/images/?id=foo.png 157 | /images/foo.png' 158 | /images/?id=foo.png' 159 | "/images/foo.png 160 | "/images/?id=foo.png 161 | /images/foo.png" 162 | /images/?id=foo.png" 163 | '/images/foo.png" 164 | '/images/?id=foo.png" 165 | "/images/foo.png' 166 | "/images/?id=foo.png' 167 | ].each do |bad_url| 168 | assert_equal "p{background:url(#{bad_url})}", 169 | AssetHat::CSS.add_asset_commit_ids("p{background:url(#{bad_url})}") 170 | end 171 | end 172 | 173 | should 'add .htc asset commit IDs' do 174 | commit_id = 111 175 | flexmock(AssetHat, :last_commit_id => commit_id) 176 | flexmock(Rails, :public_path => '') 177 | 178 | assert_equal "p{background:url(/htc/iepngfix.htc?#{commit_id})}", 179 | AssetHat::CSS.add_asset_commit_ids( 180 | "p{background:url(/htc/iepngfix.htc)}") 181 | assert_equal "p{background:url(/htc/?id=iepngfix&#{commit_id})}", 182 | AssetHat::CSS.add_asset_commit_ids( 183 | "p{background:url(/htc/?id=iepngfix)}") 184 | end 185 | 186 | should 'add image asset hosts' do 187 | asset_host = 'http://cdn%d.example.com' 188 | asset_host_regex = 'http://cdn\d\.example\.com' 189 | 190 | # No/single/double quotes: 191 | ['', "'", '"'].each do |quote| 192 | img = '/images/foo.png' 193 | assert_match( 194 | Regexp.new("^p\\{background:url\\(#{quote}" + 195 | "#{asset_host_regex}#{Regexp.escape(img)}#{quote}\\)\\}$"), 196 | AssetHat::CSS.add_asset_hosts( 197 | "p{background:url(#{quote}#{img}#{quote})}", asset_host) 198 | ) 199 | end 200 | 201 | # Mismatched quotes (should remain untouched): 202 | %w[ 203 | '/images/foo.png 204 | /images/foo.png' 205 | "/images/foo.png 206 | /images/foo.png" 207 | '/images/foo.png" 208 | "/images/foo.png' 209 | ].each do |bad_url| 210 | assert_equal "p{background:url(#{bad_url})}", 211 | AssetHat::CSS.add_asset_hosts("p{background:url(#{bad_url})}", 212 | asset_host) 213 | end 214 | end 215 | 216 | should 'not add .htc asset hosts' do 217 | asset_host = 'http://media%d.example.com' 218 | assert_match( 219 | /^p\{background:url\(\/htc\/iepngfix.htc\)\}$/, 220 | AssetHat::CSS.add_asset_hosts( 221 | 'p{background:url(/htc/iepngfix.htc)}', asset_host) 222 | ) 223 | end 224 | 225 | context 'when minifying' do 226 | setup do 227 | @input = %{ 228 | .foo { width: 1px; } 229 | .bar {} 230 | .baz{ width : 2px; } 231 | .qux {} 232 | .quux { } 233 | .corge {/* ! */} 234 | } 235 | end 236 | 237 | context 'with cssmin engine' do 238 | should 'remove rules that have empty declaration blocks' do 239 | assert_equal '.foo{width:1px;}.baz{width:2px;}', 240 | AssetHat::CSS.minify(@input, :engine => :cssmin) 241 | end 242 | end 243 | end # context 'when minifying' 244 | 245 | end # context 'AssetHat::CSS' 246 | 247 | context 'AssetHat::JS' do 248 | should 'return path to minified file' do 249 | assert_equal 'foo/bar/baz.min.js', 250 | AssetHat::JS.min_filepath('foo/bar/baz.js') 251 | end 252 | 253 | context 'with minifying' do 254 | should 'minify if a string ends with a comment and no line break' do 255 | input = 'foo(); // bar' 256 | assert_equal 'foo();', AssetHat::JS.minify(input, :engine => :jsmin) 257 | end 258 | end # context 'with minifying' 259 | end # context 'AssetHat::JS' 260 | 261 | end 262 | -------------------------------------------------------------------------------- /doc/rdoc-style.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; } 3 | 4 | body { 5 | font-family: Lucida Grande, Verdana, Arial, Helvetica, sans-serif; 6 | font-size: 90%; 7 | margin: 0; 8 | padding: 0; 9 | background: white; 10 | color: black; } 11 | 12 | #wrapper { 13 | min-height: 100%; 14 | height: auto !important; 15 | height: 100%; 16 | margin: 0 auto -43px; } 17 | 18 | #footer-push { 19 | height: 43px; } 20 | 21 | div.header, #footer { 22 | background: #eee; } 23 | 24 | #footer { 25 | border-top: 1px solid silver; 26 | margin-top: 12px; 27 | padding: 0 2em; 28 | line-height: 30px; 29 | text-align: center; 30 | font-variant: small-caps; 31 | font-size: 95%; } 32 | 33 | .clearing:after { 34 | content: "."; 35 | visibility: hidden; 36 | height: 0; 37 | display: block; 38 | clear: both; } 39 | * html .clearing { 40 | height: 1px; } 41 | .clearing *:first-child + html { 42 | overflow: hidden; } 43 | 44 | h1, h2, h3, h4, h5, h6 { 45 | margin: 0; 46 | font-weight: normal; } 47 | 48 | a { 49 | color: #0b3e71; } 50 | a:hover { 51 | background: #336699; 52 | text-decoration: none; 53 | color: #eef; } 54 | 55 | #diagram img { 56 | border: 0; } 57 | 58 | #description a, .method .description a, .header a { 59 | color: #336699; } 60 | #description a:hover, .method .description a:hover, .header a:hover { 61 | color: #eee; } 62 | #description h1 a, #description h2 a, #description h3 a, #description h4 a, #description h5 a, #description h6 a, .method .description h1 a, .method .description h2 a, .method .description h3 a, .method .description h4 a, .method .description h5 a, .method .description h6 a, .header h1 a, .header h2 a, .header h3 a, .header h4 a, .header h5 a, .header h6 a { 63 | color: #0b3e71; } 64 | 65 | ol { 66 | margin: 0; 67 | padding: 0; 68 | list-style: none; } 69 | ol li { 70 | margin-left: 0; 71 | white-space: nowrap; } 72 | ol li.other { 73 | display: none; } 74 | 75 | ol.expanded li.other { 76 | display: list-item; } 77 | 78 | table { 79 | margin-bottom: 1em; 80 | font-size: 1em; 81 | border-collapse: collapse; } 82 | table td, table th { 83 | padding: .4em .8em; } 84 | table thead { 85 | background-color: #e8e8e8; } 86 | table thead th { 87 | font-variant: small-caps; 88 | color: #666666; } 89 | table tr { 90 | border-bottom: 1px solid silver; } 91 | 92 | #index a.show, div.header a.show { 93 | text-decoration: underline; 94 | font-style: italic; 95 | color: #666666; } 96 | #index a.show:after, div.header a.show:after { 97 | content: " ..."; } 98 | #index a.show:hover, div.header a.show:hover { 99 | color: black; 100 | background: #ffe; } 101 | 102 | #index { 103 | font: 85%/1.2 Arial, Helvetica, sans-serif; } 104 | #index a { 105 | text-decoration: none; } 106 | #index h1 { 107 | padding: .2em .5em .1em; 108 | background: #ccc; 109 | font: small-caps 1.2em Georgia, serif; 110 | color: #333; 111 | border-bottom: 1px solid gray; } 112 | #index form { 113 | margin: 0; 114 | padding: 0; } 115 | #index form input { 116 | margin: .4em 3px 0 .4em; 117 | width: 80%; } 118 | #index form #search.untouched { 119 | color: #777777; } 120 | #index form .clear_button { 121 | -moz-border-radius: 7px; 122 | -webkit-border-radius: 7px; 123 | background: #AAA; 124 | color: white; 125 | padding: 0 5px 1px 5px; 126 | cursor: pointer; } 127 | #index ol { 128 | padding: .4em .5em; } 129 | #index ol li { 130 | white-space: nowrap; } 131 | #index #index-entries li a { 132 | padding: 1px 2px; } 133 | #index #index-entries.classes { 134 | font-size: 1.1em; } 135 | #index #index-entries.classes ol { 136 | padding: 0; } 137 | #index #index-entries.classes span.nodoc { 138 | display: none; } 139 | #index #index-entries.classes span.nodoc, #index #index-entries.classes a { 140 | font-weight: bold; } 141 | #index #index-entries.classes .parent { 142 | font-weight: normal; } 143 | #index #index-entries.methods li, #index #search-results.methods li { 144 | margin-bottom: 0.2em; } 145 | #index #index-entries.methods li a .method_name, #index #search-results.methods li a .method_name { 146 | margin-right: 0.25em; } 147 | #index #index-entries.methods li a .module_name, #index #search-results.methods li a .module_name { 148 | color: #666666; } 149 | #index #index-entries.methods li a:hover .module_name, #index #search-results.methods li a:hover .module_name { 150 | color: #ddd; } 151 | 152 | #attribute-list .context-item-name { 153 | font-weight: bold; } 154 | 155 | div.header { 156 | font-size: 80%; 157 | padding: .5em 2%; 158 | font-family: Arial, Helvetica, sans-serif; 159 | border-bottom: 1px solid silver; } 160 | div.header .name { 161 | font-size: 1.6em; 162 | font-family: Georgia, serif; } 163 | div.header .name .type { 164 | color: #666666; 165 | font-size: 80%; 166 | font-variant: small-caps; } 167 | div.header h1.name { 168 | font-size: 2.2em; } 169 | div.header .paths, div.header .last-update, div.header .parent { 170 | color: #666666; } 171 | div.header .last-update .datetime { 172 | color: #484848; } 173 | div.header .parent { 174 | margin-top: .3em; } 175 | div.header .parent strong { 176 | font-weight: normal; 177 | color: #484848; } 178 | 179 | #content { 180 | padding: 12px 2%; } 181 | div.class #content { 182 | position: relative; 183 | width: 72%; } 184 | #content pre, #content .method .synopsis { 185 | font: 14px Monaco, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; } 186 | #content pre { 187 | color: black; 188 | background: #eee; 189 | border: 1px solid silver; 190 | padding: .5em .8em; 191 | overflow: auto; } 192 | #content p code, #content p tt, #content li code, #content li tt, #content dl code, #content dl tt { 193 | font: 14px Monaco, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace; 194 | background: #ffffe3; 195 | padding: 2px 3px; 196 | line-height: 1.4; } 197 | #content h1 code, #content h1 tt, #content h2 code, #content h2 tt, #content h3 code, #content h3 tt, #content h4 code, #content h4 tt, #content h5 code, #content h5 tt, #content h6 code, #content h6 tt { 198 | font-size: 1.1em; } 199 | #content #text { 200 | position: relative; } 201 | #content #description p { 202 | margin-top: .5em; } 203 | #content #description h1, #content #description h2, #content #description h3, #content #description h4, #content #description h5, #content #description h6 { 204 | font-family: Georgia, serif; } 205 | #content #description h1 { 206 | font-size: 2.2em; 207 | margin-bottom: .2em; 208 | border-bottom: 3px double #d8d8d8; 209 | padding-bottom: .1em; } 210 | #content #description h2 { 211 | font-size: 1.8em; 212 | color: #0e3062; 213 | margin: .8em 0 .3em 0; } 214 | #content #description h3 { 215 | font-size: 1.6em; 216 | margin: .8em 0 .3em 0; 217 | color: #666666; } 218 | #content #description h4 { 219 | font-size: 1.4em; 220 | margin: .8em 0 .3em 0; } 221 | #content #description h5 { 222 | font-size: 1.2em; 223 | margin: .8em 0 .3em 0; 224 | color: #0e3062; } 225 | #content #description h6 { 226 | font-size: 1.0em; 227 | margin: .8em 0 .3em 0; 228 | color: #666666; } 229 | #content #description ul, #content #description ol, #content .method .description ul, #content .method .description ol { 230 | margin: .8em 0; 231 | padding-left: 1.5em; } 232 | #content #description ol, #content .method .description ol { 233 | list-style: decimal; } 234 | #content #description ol li, #content .method .description ol li { 235 | white-space: normal; } 236 | 237 | #method-list { 238 | position: absolute; 239 | top: 0px; 240 | right: -33%; 241 | width: 28%; 242 | background: #eee; 243 | border: 1px solid silver; 244 | padding: .4em 1%; 245 | overflow: hidden; } 246 | #method-list h2 { 247 | font-size: 1.3em; } 248 | #method-list h3 { 249 | font-variant: small-caps; 250 | text-transform: capitalize; 251 | font-family: Georgia, serif; 252 | color: #666; 253 | font-size: 1.1em; } 254 | #method-list ol { 255 | padding: 0 0 .5em .5em; } 256 | 257 | #context { 258 | border-top: 1px dashed silver; 259 | margin-top: 1em; 260 | margin-bottom: 1em; } 261 | 262 | #context h2, #section h2 { 263 | font: small-caps 1.2em Georgia, serif; 264 | color: #444; 265 | margin: 1em 0 .2em 0; } 266 | 267 | #methods .method { 268 | border: 1px solid silver; 269 | margin-top: .5em; 270 | background: #eee; } 271 | #methods .method .synopsis { 272 | color: black; 273 | background: silver; 274 | padding: .2em 1em; } 275 | #methods .method .synopsis .name { 276 | font-weight: bold; } 277 | #methods .method .synopsis a { 278 | text-decoration: none; } 279 | #methods .method .description { 280 | padding: 0 1em; } 281 | #methods .method .description pre { 282 | background: #f8f8f8; } 283 | #methods .method .source { 284 | margin: .5em 0; } 285 | #methods .method .source-toggle { 286 | font-size: 85%; 287 | margin-left: 1em; } 288 | #methods .public-class { 289 | background: #ffffe4; } 290 | #methods .public-instance .synopsis { 291 | color: #eee; 292 | background: #336699; } 293 | 294 | #content .method .source pre { 295 | background: #262626; 296 | color: #ffdead; 297 | margin: 1em; 298 | padding: 0.5em; 299 | border: 1px dashed #999; 300 | overflow: auto; } 301 | #content .method .source pre .ruby-constant { 302 | color: #7fffd4; 303 | background: transparent; } 304 | #content .method .source pre .ruby-keyword { 305 | color: #00ffff; 306 | background: transparent; } 307 | #content .method .source pre .ruby-ivar { 308 | color: #eedd82; 309 | background: transparent; } 310 | #content .method .source pre .ruby-operator { 311 | color: #00ffee; 312 | background: transparent; } 313 | #content .method .source pre .ruby-identifier { 314 | color: #ffdead; 315 | background: transparent; } 316 | #content .method .source pre .ruby-node { 317 | color: #ffa07a; 318 | background: transparent; } 319 | #content .method .source pre .ruby-comment { 320 | color: #b22222; 321 | font-weight: bold; 322 | background: transparent; } 323 | #content .method .source pre .ruby-regexp { 324 | color: #ffa07a; 325 | background: transparent; } 326 | #content .method .source pre .ruby-value { 327 | color: #7fffd4; 328 | background: transparent; } 329 | -------------------------------------------------------------------------------- /lib/asset_hat/js/vendors.rb: -------------------------------------------------------------------------------- 1 | module AssetHat 2 | module JS 3 | # For working with supported 3rd-party JavaScript 4 | # plugin/framework/library vendors. 5 | module Vendors 6 | # A list of supported 3rd-party JavaScript plugin/vendor names. 7 | # Homepages: 8 | # 9 | # * Frameworks/libraries: 10 | # * {Dojo}[http://dojotoolkit.org/] 11 | # * {Ext Core}[http://extjs.com/products/extcore/] 12 | # * {jQuery UI}[http://jqueryui.com/] 13 | # * {jQuery}[http://jquery.com/] 14 | # * {MooTools}[http://mootools.net/] 15 | # * {Prototype}[http://www.prototypejs.org/] 16 | # * {script.aculo.us}[http://script.aculo.us/] 17 | # * {SWFObject}[http://code.google.com/p/swfobject/] 18 | # * {WebFont Loader}[http://code.google.com/apis/webfonts/docs/webfont_loader.html] 19 | # * {YUI}[http://developer.yahoo.com/yui/] 20 | # * Loaders: 21 | # * {LABjs}[http://labjs.com] 22 | VENDORS_ON_GOOGLE_CDN = [ 23 | :dojo, 24 | :ext_core, 25 | :jquery, 26 | :jquery_ui, 27 | :mootools, 28 | :prototype, 29 | :scriptaculous, 30 | :swfobject, 31 | :webfont, 32 | :yui 33 | ] 34 | VENDORS_ON_CDNJS = [ 35 | :lab_js 36 | ] 37 | VENDORS = VENDORS_ON_GOOGLE_CDN + VENDORS_ON_CDNJS 38 | 39 | # Accepts an item from `VENDORS`, and returns the URL at which that 40 | # vendor asset can be found. The URL is either local (relative) or 41 | # remote, depending on the environment configuration: 42 | # 43 | # - If `AssetHat.consider_all_requests_local?` is true: 44 | # - The local file takes precedence. 45 | # - If the local file is missing, the remote URL in assets.yml is 46 | # used as a fallback. 47 | # - If there is no remote URL in assets.yml, the Google CDN URL is 48 | # used as a fallback. (This makes setup easier: If the app doesn't 49 | # already have a local copy of the vendor file, then it's instead 50 | # loaded remotely.) 51 | # - If `AssetHat.consider_all_requests_local?` is false: 52 | # - The remote URL in assets.yml takes precedence. 53 | # - The {Google CDN}[http://code.google.com/apis/ajaxlibs/] URL is 54 | # used as a fallback, but only if a version number can be found 55 | # (either in assets.yml or via the helper's `:version` option). If 56 | # no version number is found, the remote URL cannot be built, so 57 | # the local file (if any) is used as a fallback. 58 | # 59 | # Options: 60 | # 61 | # [ssl] Boolean for whether to include vendor JS via HTTPS. Defaults 62 | # to false. 63 | # [version] The vendor version, e.g., '1.6.0' for jQuery 1.6. By 64 | # default, each vendor version is taken from 65 | # config/assets.yml; use this option to override 66 | # the configuration. 67 | def self.source_for(vendor, options={}) 68 | vendor_config = 69 | AssetHat.config['js']['vendors'][vendor.to_s] rescue nil 70 | use_local = AssetHat.consider_all_requests_local? 71 | use_ssl = !!options[:ssl] 72 | version = options[:version] || vendor_config['version'] rescue nil 73 | 74 | # Prepare local path and default remote URL 75 | srcs = Vendors.vendor_uris(vendor, 76 | :use_ssl => use_ssl, :version => version) 77 | local_src, remote_src = srcs[:local], srcs[:remote] 78 | 79 | # Using the local URL requires that the vendor file exists locally. If 80 | # the vendor file doesn't exist, use the remote URL as fallback. 81 | use_local &&= AssetHat.asset_exists?(local_src, :js) 82 | 83 | # If no version given, can't determine the remote URL; use the local 84 | # URL as fallback. 85 | use_local ||= version.blank? 86 | 87 | if use_local 88 | src = local_src 89 | else 90 | # To ease setup, if no local copy of the vendor code is found, 91 | # use a remote URL as a fallback. 92 | 93 | # Give precedence to configured remote URLs 94 | src = vendor_config.try(:[], 'remote_ssl_url') if use_ssl 95 | src ||= vendor_config.try(:[], 'remote_url') 96 | 97 | # Use default remote URL as fallback 98 | src ||= remote_src 99 | 100 | # Use local URL as final resort, even though the file doesn't 101 | # exist, in hopes that the app maintainer finds the 404 (or the 102 | # warning below) in the logs. This needs to be fixed in the app, 103 | # rather than relying on a CDN to dynamically provide the latest 104 | # stable vendor version. 105 | if src.blank? 106 | src = local_src 107 | Rails.logger.warn "\n\nAssetHat WARNING (#{Time.now}):\n" + %{ 108 | Tried to reference the vendor JS `:#{vendor}`, but 109 | #{AssetHat.assets_dir(:js)}/#{local_src} couldn't be found, and 110 | couldn't use a remote fallback because no vendor version was 111 | given in #{AssetHat::RELATIVE_CONFIG_FILEPATH}. 112 | }.squish! 113 | # TODO: Create `AssetHat::Logger.warn`, etc. methods 114 | end 115 | end 116 | 117 | src 118 | end 119 | 120 | 121 | 122 | private 123 | 124 | def self.vendor_uris(vendor, options={}) 125 | # Returns a hash with keys `:local` and `:remote`. 126 | 127 | uris = {} 128 | use_ssl = options[:use_ssl] 129 | version = options[:version] 130 | 131 | case vendor 132 | when :jquery 133 | uris[:local ] = "#{['jquery', version].compact.join('-')}.min.js" 134 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jquery/#{version}/jquery.min.js" 135 | when :jquery_ui 136 | uris[:local ] = "#{['jquery-ui', version].compact.join('-')}.min.js" 137 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/jqueryui/#{version}/jquery-ui.min.js" 138 | when :prototype 139 | # Prototype currently doesn't provide an official minified version. 140 | uris[:local ] = "#{['prototype', version].compact.join('-')}.js" 141 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/prototype/#{version}/prototype.js" 142 | when :scriptaculous 143 | # script.aculo.us currently doesn't provide an official minified version. 144 | uris[:local ] = "#{['scriptaculous', version].compact.join('-')}.js" 145 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/scriptaculous/#{version}/scriptaculous.js" 146 | when :mootools 147 | uris[:local ] = "#{['mootools', version].compact.join('-')}.min.js" 148 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/mootools/#{version}/mootools-yui-compressed.js" 149 | when :dojo 150 | uris[:local ] = "#{['dojo', version].compact.join('-')}.min.js" 151 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/dojo/#{version}/dojo/dojo.xd.js" 152 | when :swfobject 153 | uris[:local ] = "#{['swfobject', version].compact.join('-')}.min.js" 154 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/swfobject/#{version}/swfobject.js" 155 | when :yui 156 | uris[:local ] = "#{['yui', version].compact.join('-')}.min.js" 157 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/yui/#{version}/build/yuiloader/yuiloader-min.js" 158 | when :ext_core 159 | uris[:local ] = "#{['ext-core', version].compact.join('-')}.min.js" 160 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/ext-core/#{version}/ext-core.js" 161 | when :webfont 162 | uris[:local ] = "#{['webfont', version].compact.join('-')}.min.js" 163 | uris[:remote] = "http#{'s' if use_ssl}://ajax.googleapis.com/ajax/libs/webfont/#{version}/webfont.js" 164 | when :lab_js 165 | uris[:local ] = "#{['LAB', version].compact.join('-')}.min.js" 166 | 167 | remote_host = 168 | if use_ssl 169 | 'https://d3eee1nukb5wg.cloudfront.net/' 170 | # This must match the value in the cdnjs repo: 171 | # https://github.com/cdnjs/cdnjs/raw/master/https_location 172 | # 173 | # Amazon CloudFront doesn't support SSL, as discussed here: 174 | # - http://www.cdnjs.com/#IDComment130405257 175 | # - https://forums.aws.amazon.com/message.jspa?messageID=141951 176 | # As a result, the SSL certificate at is 177 | # invalid. To work around this, we instead load assets via 178 | # cdnjs's CloudFront bucket ID. The bucket ID may change in 179 | # the future, so it should be synced with the host published 180 | # in the cdnjs repo, as noted above. 181 | # 182 | # For complete control over this, you can simply download the 183 | # vendor JS and host it on a server where you can maintain 184 | # SSL certificates. 185 | else 186 | 'http://ajax.cdnjs.com' 187 | end 188 | uris[:remote] = "#{remote_host}/ajax/libs/labjs/#{version}/LAB.min.js" 189 | else nil # TODO: Write to log 190 | end 191 | 192 | # The remote URL can only be properly determined if the version number 193 | # is known; otherwise, discard. 194 | uris.delete(:remote) if version.blank? 195 | 196 | uris 197 | end 198 | 199 | # Usage (currently only supports LABjs): 200 | # 201 | # AssetHat::JS::Vendors.loader_js :lab_js, 202 | # :urls => ['/javascripts/app.js', 203 | # 'http://cdn.example.com/jquery.js'] 204 | # 205 | # Returns a string of JS: 206 | # 207 | # window.$LABinst=$LAB. 208 | # script('/javascripts/app.js').wait(). 209 | # script('http://cdn.example.com/jquery.js').wait(); 210 | def self.loader_js(loader, opts) 211 | return nil unless opts[:urls] 212 | 213 | case loader 214 | when :lab_js 215 | urls = opts[:urls] 216 | lines = ['window.$LABinst=$LAB.'] 217 | urls.each_with_index do |url, i| 218 | is_last = i >= urls.length - 1 219 | lines << " script('#{url}').wait()#{is_last ? ';' : '.'}" 220 | # Use `wait()` for each script to load in parallel, but 221 | # preserve execution order by default. Alternatively, call 222 | # `$LAB.setOptions({AlwaysPreserveOrder:true})` at the start 223 | # of the chain, but if the list of bundles to include is short, 224 | # this may use even more bytes. 225 | end 226 | lines.join("\n") 227 | end 228 | end 229 | 230 | end 231 | 232 | end 233 | 234 | end 235 | -------------------------------------------------------------------------------- /lib/asset_hat.rb: -------------------------------------------------------------------------------- 1 | %w[css js vcs].each do |filename| 2 | require File.join(File.dirname(__FILE__), 'asset_hat', filename) 3 | end 4 | 5 | # Your assets are covered. See README.rdoc for more. 6 | module AssetHat 7 | if defined?(Rails) && Rails::VERSION::MAJOR >= 3 8 | RAILS_ROOT = Rails.root || '.' #:nodoc: 9 | require 'asset_hat/railtie' 10 | else 11 | RAILS_ROOT = File.join(File.dirname(__FILE__), '..') unless 12 | defined?(RAILS_ROOT) #:nodoc: 13 | end 14 | 15 | # Types of supported assets; currently [:css, :js]. 16 | TYPES = [:css, :js] 17 | 18 | # Base directory in which all assets are kept, e.g., 'public/'. 19 | ASSETS_DIR = defined?(Rails.public_path) && Rails.public_path.present? ? 20 | Rails.public_path : 'public' 21 | 22 | # Root URL path for all stylesheets. For public-facing use. 23 | STYLESHEETS_PATH = '/stylesheets' 24 | 25 | # Root URL path for all JavaScripts. For public-facing use. 26 | JAVASCRIPTS_PATH = '/javascripts' 27 | 28 | # Directory in which all stylesheets are kept, e.g., 'public/stylesheets'. 29 | # For internal filesystem use. 30 | STYLESHEETS_DIR = File.join(ASSETS_DIR, 'stylesheets') 31 | 32 | # Directory in which all JavaScripts are kept, e.g., 'public/javascripts'. 33 | # For internal filesystem use. 34 | JAVASCRIPTS_DIR = File.join(ASSETS_DIR, 'javascripts') 35 | 36 | # Relative path for the config file. 37 | RELATIVE_CONFIG_FILEPATH = File.join('config', 'assets.yml') 38 | 39 | # Absolute path for the config file. 40 | CONFIG_FILEPATH = File.join(RAILS_ROOT, RELATIVE_CONFIG_FILEPATH) 41 | 42 | class << self 43 | attr_accessor :config, :asset_exists, :html_cache #:nodoc: 44 | end 45 | 46 | # Nested-hash version of config/assets.yml. 47 | def self.config 48 | unless File.exists?(CONFIG_FILEPATH) 49 | raise '`config/assets.yml` is missing! ' + 50 | 'Run `rake asset_hat:config` to generate it.' and return 51 | end 52 | 53 | if !cache? || @config.blank? 54 | @config = YAML.load(ERB.new(File.read(CONFIG_FILEPATH)).result) 55 | end 56 | @config 57 | end 58 | 59 | # Returns the relative path to the directory where the original CSS or JS 60 | # files are stored. For internal filesystem use. 61 | # 62 | # type argument: :css or :js 63 | def self.assets_dir(type) 64 | case type.to_sym 65 | when :css ; STYLESHEETS_DIR 66 | when :js ; JAVASCRIPTS_DIR 67 | else 68 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 69 | nil 70 | end 71 | end 72 | 73 | # Returns the root URL path where the original CSS or JS files are stored. 74 | # For external URL-building use. 75 | # 76 | # type argument: :css or :js 77 | def self.assets_path(type) 78 | case type.to_sym 79 | when :css ; STYLESHEETS_PATH 80 | when :js ; JAVASCRIPTS_PATH 81 | else 82 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 83 | nil 84 | end 85 | end 86 | 87 | # Returns the relative path to the directory where CSS or JS bundles are 88 | # stored. For internal filesystem use. 89 | # 90 | # Usage: 91 | # 92 | # AssetHat.bundles_dir 93 | # # => 'bundles' 94 | # AssetHat.bundles_dir(:ssl => true) 95 | # # => 'bundles/ssl' 96 | # AssetHat.bundles_dir(:css) 97 | # # => 'public/stylesheets/bundles' 98 | # AssetHat.bundles_dir(:js, :ssl => true) 99 | # # => 'public/javascripts/bundles/ssl 100 | # 101 | # Options: 102 | # 103 | # [ssl] Set this to true if the stylesheet references images 104 | # via SSL. Defaults to false. 105 | def self.bundles_dir(*args) 106 | options = args.extract_options! 107 | options.symbolize_keys!.reverse_merge!(:ssl => false) 108 | type = args.first 109 | 110 | dir = type.present? ? File.join(assets_dir(type), 'bundles') : 'bundles' 111 | dir = File.join(dir, 'ssl') if options[:ssl] 112 | dir 113 | end 114 | 115 | # Returns the root URL path where CSS or JS bundles are stored. For external 116 | # URL-building use. 117 | # 118 | # Usage: 119 | # 120 | # AssetHat.bundles_path(:css) 121 | # # => 'public/stylesheets/bundles' 122 | # AssetHat.bundles_path(:js, :ssl => true) 123 | # # => 'public/javascripts/bundles/ssl 124 | # 125 | # Options: 126 | # 127 | # [ssl] Set this to true if the stylesheet references images 128 | # via SSL. Defaults to false. 129 | def self.bundles_path(type, options={}) 130 | type = type.to_sym 131 | unless TYPES.include?(type) 132 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 133 | return 134 | end 135 | 136 | path = case type 137 | when :css ; STYLESHEETS_PATH 138 | when :js ; JAVASCRIPTS_PATH 139 | else nil 140 | end 141 | path += '/bundles' 142 | path += '/ssl' if options[:ssl] 143 | path 144 | end 145 | 146 | # Returns true if the specified asset exists in the file system: 147 | # 148 | # AssetHat.asset_exists?('application.css', :css) 149 | # # => true if public/stylesheets/application.css exists 150 | # AssetHat.asset_exists?('some-plugin.js', :js) 151 | # # => true if public/javascripts/some-plugin.js exists 152 | # 153 | # See also AssetHat.assets_dir. 154 | def self.asset_exists?(filename, type) 155 | # Process arguments 156 | type = type.to_sym 157 | unless TYPES.include?(type) 158 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 159 | return 160 | end 161 | 162 | # Default to `{:css => {}, :js => {}}` 163 | @asset_exists ||= TYPES.inject({}) { |hsh, t| hsh.merge(t => {}) } 164 | 165 | if @asset_exists[type][filename].nil? 166 | @asset_exists[type][filename] = 167 | File.exist?(File.join(self.assets_dir(type), filename)) 168 | end 169 | @asset_exists[type][filename] 170 | end 171 | 172 | # Returns true if bundles should be included as single minified 173 | # files (e.g., in production), or false if bundles should be 174 | # included as separate, unminified files (e.g., in development). To modify 175 | # this value, set 176 | # config.action_controller.perform_caching (boolean) 177 | # in your environment. 178 | def self.cache? ; ActionController::Base.perform_caching ; end 179 | 180 | # Returns the value of 181 | # Rails.application.config.consider_all_requests_local or its 182 | # equivalent in older versions of Rails. To modify this value, set 183 | # config.consider_all_requests_local (boolean) in your 184 | # environment. 185 | def self.consider_all_requests_local? 186 | if defined?(Rails) && Rails.respond_to?(:application) 187 | Rails.application.config.consider_all_requests_local 188 | else # Rails 2.x 189 | ActionController::Base.consider_all_requests_local 190 | end 191 | end 192 | 193 | # Returns the expected path for the minified version of an asset: 194 | # 195 | # AssetHat.min_filepath('public/stylesheets/bundles/application.css', 'css') 196 | # # => 'public/stylesheets/bundles/application.min.css' 197 | # 198 | # See also AssetHat::CSS.min_filepath and 199 | # AssetHat::JS.min_filepath. 200 | def self.min_filepath(filepath, extension) 201 | filepath.sub(/([^\.]*).#{extension}$/, "\\1.min.#{extension}") 202 | end 203 | 204 | # Returns the extension-less names of files in the given bundle: 205 | # 206 | # AssetHat.bundle_filenames('application', :css) 207 | # # => ['reset', 'application'] 208 | # AssetHat.bundle_filenames('non-existent-file', :css) 209 | # # => nil 210 | def self.bundle_filenames(bundle, type) 211 | # Process arguments 212 | unless TYPES.include?(type.to_sym) 213 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 214 | return 215 | end 216 | 217 | self.config[type.to_s]['bundles'][bundle.to_s] rescue nil 218 | end 219 | 220 | # Returns the full paths of files in the given bundle: 221 | # 222 | # AssetHat.bundle_filenames('application', :css) 223 | # # => ['/path/to/app/public/stylesheets/reset.css', 224 | # '/path/to/app/public/stylesheets/application.css'] 225 | # AssetHat.bundle_filenames('non-existent-file', :css) 226 | # # => nil 227 | def self.bundle_filepaths(bundle, type) 228 | # Process arguments 229 | unless TYPES.include?(type.to_sym) 230 | raise %{Unknown type "#{type}"; should be one of: #{TYPES.join(', ')}.} 231 | return 232 | end 233 | 234 | dir = self.assets_dir(type) 235 | filenames = self.bundle_filenames(bundle, type) 236 | filepaths = filenames.present? ? 237 | filenames.map { |fn| File.join(dir, "#{fn}.#{type}") } : nil 238 | end 239 | 240 | # Reads ActionController::Base.asset_host, which can be a 241 | # String or Proc, and returns a String. Should behave just like Rails 242 | # 2.3.x's private `compute_asset_host` method, but with a simulated request. 243 | # 244 | # Example environment config for CDN support via SSL: 245 | # 246 | # # In config/environments/production.rb: 247 | # config.action_controller.asset_host = Proc.new do |source, request| 248 | # "#{request.protocol}cdn#{source.hash % 4}.example.com" 249 | # # => 'http://cdn0.example.com', 'https://cdn1.example.com', etc. 250 | # end 251 | # 252 | # If your CDN doesn't have SSL support, you can instead revert SSL pages to 253 | # serving assets from your web server: 254 | # 255 | # config.action_controller.asset_host = Proc.new do |source, request| 256 | # request.ssl? ? nil : "http://cdn#{source.hash % 4}.example.com" 257 | # end 258 | # 259 | # Options: 260 | # 261 | # [ssl] Set to true to simulate a request via SSL. Defaults to 262 | # false. 263 | def self.compute_asset_host(asset_host, source, options={}) 264 | host = asset_host 265 | if host.is_a?(Proc) || host.respond_to?(:call) 266 | case host.is_a?(Proc) ? 267 | host.arity : host.method(:call).arity 268 | when 2 269 | if defined?(ActionDispatch) 270 | request_class = ActionDispatch::Request 271 | else # Rails 2.x 272 | request_class = ActionController::Request 273 | end 274 | request = request_class.new( 275 | 'HTTPS' => options[:ssl] ? 'on' : 'off') 276 | host = host.call(source, request) 277 | else 278 | host = host.call(source) 279 | end 280 | else 281 | host %= (source.hash % 4) if host =~ /%d/ 282 | end 283 | host 284 | end 285 | 286 | # Returns true if the asset host differs between SSL and 287 | # non-SSL pages, or false if the asset host doesn't change. 288 | def self.ssl_asset_host_differs? 289 | asset_host = ActionController::Base.asset_host 290 | AssetHat.compute_asset_host(asset_host, 'x.png') != 291 | AssetHat.compute_asset_host(asset_host, 'x.png', :ssl => true) 292 | end 293 | 294 | def self.clear_html_cache 295 | html_cache = {} 296 | end 297 | 298 | end 299 | -------------------------------------------------------------------------------- /doc/classes/AssetHat/CSS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : AssetHat::CSS [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

29 | Module 30 | AssetHat::CSS 31 |

32 |
    33 |
  1. 34 | lib/asset_hat/css.rb 35 |
  2. 36 |
37 |
38 |
39 |
40 |
41 |

42 | Methods for minifying and optimizing CSS. 43 |

44 |
45 |
46 |

Methods

47 |

public class

48 |
    49 |
  1. add_asset_commit_ids
  2. 50 |
  3. add_asset_hosts
  4. 51 |
  5. min_filepath
  6. 52 |
  7. minify
  8. 53 |
54 |
55 |
56 |
57 |

Classes and Modules

58 | Module AssetHat::CSS::Engines
59 |
60 |
61 |

Constants

62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 73 | 74 |
ENGINES=[:weak, :cssmin]  70 | 71 | A list of supported minification engine names. 72 |
75 |
76 |
77 |
78 |

Public class methods

79 |
80 | 81 |
82 | add_asset_commit_ids 83 | (css) 84 |
85 |
86 |

87 | Given a string containing CSS, appends each 88 | referenced asset’s last commit ID to its URL, e.g., background: 89 | url(/images/foo.png?ab12cd3). This enables cache busting: If the 90 | user’s browser has cached a copy of foo.png from a previous 91 | deployment, this new URL forces the browser to ignore that cache and 92 | request the latest version. 93 |

94 |
95 |
96 | 97 | [show source] 98 | 99 |
    # File lib/asset_hat/css.rb, line 43
43:     def self.add_asset_commit_ids(css)
44:       update_css_urls(css, %w[images htc]) do |src, quote|
45:         # Get absolute path
46:         filepath = File.join(ASSETS_DIR, src)
47: 
48:         # Convert to relative path
49:         filepath.sub!(/^#{FileUtils.pwd}#{File::SEPARATOR}/, '')
50: 
51:         commit_id = AssetHat.last_commit_id(filepath)
52:         if commit_id.present?
53:           "url(#{quote}#{src}#{src =~ /\?/ ? '&' : '?'}#{commit_id}#{quote})"
54:         else
55:           "url(#{quote}#{src}#{quote})"
56:         end
57:       end
58:     end
100 |
101 |
102 |
103 | 104 |
105 | add_asset_hosts 106 | (css, asset_host, options={}) 107 |
108 |
109 |

110 | Arguments: 111 |

112 |
    113 |
  • A string containing CSS; 114 | 115 |
  • 116 |
  • A string containing the app’s asset host, e.g., 117 | ‘http://cdn%d.example.com’. This value is typically taken from 118 | config.action_controller.asset_host in the app’s 119 | config/environments/production.rb. 120 | 121 |
  • 122 |
123 |

124 | An asset host is added to every image URL in the CSS, e.g., background: 126 | url(http://cdn2.example.com/images/foo.png); if %d in the 127 | asset host, it is replaced with an arbitrary number in 0-3, inclusive. 128 |

129 |

130 | Options: 131 |

132 |
133 |
ssl
Set to true to simulate a request via SSL. Defaults to 134 | false. 135 | 136 |
137 |
138 |
139 |
140 | 141 | [show source] 142 | 143 |
    # File lib/asset_hat/css.rb, line 77
77:     def self.add_asset_hosts(css, asset_host, options={})
78:       return css if asset_host.blank?
79: 
80:       options.reverse_merge!(:ssl => false)
81: 
82:       update_css_urls(css, %w[images]) do |src, quote|
83:         computed_asset_host = AssetHat.compute_asset_host(
84:           asset_host, src, options.slice(:ssl))
85:         "url(#{quote}#{computed_asset_host}#{src}#{quote})"
86:       end
87:     end
144 |
145 |
146 |
147 | 148 |
149 | min_filepath 150 | (filepath) 151 |
152 |
153 |

154 | Returns the expected path for the minified version of a CSS asset: 156 |

157 |
AssetHat::CSS.min_filepath('public/stylesheets/bundles/application.css')
  # => 'public/stylesheets/bundles/application.min.css'
158 |
159 |
160 | 161 | [show source] 162 | 163 |
    # File lib/asset_hat/css.rb, line 14
14:     def self.min_filepath(filepath)
15:       AssetHat.min_filepath(filepath, 'css')
16:     end
164 |
165 |
166 |
167 | 168 |
169 | minify 170 | (input_string, options={}) 171 |
172 |
173 |

174 | Accepts a string of CSS, and returns that CSS minified. Options: 176 |

177 |
178 |
engine
Default is :cssmin; see Engines.cssmin. Allowed values are 181 | in ENGINES. 182 | 183 |
184 |
185 |
186 |
187 | 188 | [show source] 189 | 190 |
    # File lib/asset_hat/css.rb, line 23
23:     def self.minify(input_string, options={})
24:       options.reverse_merge!(:engine => :cssmin)
25: 
26:       engine = options[:engine].to_sym
27:       unless ENGINES.include?(engine)
28:         raise %{
29:           Unknown CSS minification engine '#{engine}'.
30:           Allowed: #{ENGINES.map{ |e| "'#{e}'" }.join(', ')}
31:         }.strip.gsub(/\s+/, ' ') and return
32:       end
33: 
34:       AssetHat::CSS::Engines.send(engine, input_string).strip
35:     end
191 |
192 |
193 |
194 |
195 |
196 |
197 | 198 |
199 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | = AssetHat 2 | 3 | Load CSS and JS faster. Your assets are covered. 4 | 5 | With Rails' default asset caching, CSS and JS are concatenated (not even 6 | minified) at runtime when that bundle is first requested. Not good enough. 7 | To make your pages load faster, AssetHat can automatically: 8 | 9 | * Easily *minify* and *bundle* CSS and JS to reduce file sizes and 10 | HTTP requests. Do this on deploy, and avoid any performance hit at runtime. 11 | * Load popular libraries from the JS community -- like jQuery, YUI, and 12 | Dojo -- from {Google's CDN}[http://code.google.com/apis/ajaxlibs/] 13 | when in production, or from localhost in development. It's as simple as 14 | <%= include_js :jquery %> to load straight from Google. 15 | * Load plenty of JS files in parallel in {LABjs}[http://labjs.com/] 16 | mode. When calling include_js, just add :loader => 17 | :lab_js. 18 | * Force image URLs in your CSS to use CDN subdomains 19 | (including SSL support), not just the current host. 20 | * Add an image's last Git[http://git-scm.com/] commit ID to its CSS URLs to 21 | bust browser caches (e.g., 22 | /images/foo.png?ab12cd3). 23 | 24 | After setup, you can use this in your layouts and views: 25 | 26 | <%= include_css :bundle => 'application' %> 27 | <%= include_js :jquery, :bundles => ['plugins', 'common'] %> 28 | 29 | Which expands into: 30 | 31 | 33 | 35 | 36 | 38 | 40 | 41 | Don't have your own copy of jQuery? AssetHat detects this and knows how to 42 | load jQuery from Google's CDN instead, whether you're in development or 43 | production. 44 | 45 | Add this to your deploy script, and your CSS/JS will be optimized 46 | automatically: 47 | 48 | rake asset_hat:minify 49 | 50 | Tested with Rails 3 and Rails 2.3.x (with Bundler). For a quick overview, see 51 | {the AssetHat website}[http://mintdigital.github.com/asset_hat/]. To see how 52 | AssetHat performs in production, 53 | {check some stats}[http://logicalfriday.com/2011/05/06/assethat-0-4-load-css-and-js-faster-your-assets-are-covered/]. 54 | For the gritty details, check the 55 | {complete docs}[http://mintdigital.github.com/asset_hat/doc/] and 56 | {change history}[http://mintdigital.github.com/asset_hat/doc/files/HISTORY.html]. 57 | 58 | 59 | 60 | == Installation 61 | 62 | === Rails 3.x 63 | 64 | 1. Add to your app's Gemfile: gem 'asset_hat', '0.x.x' 65 | 2. Command-line: bundle install 66 | 67 | === Rails 2.3.x 68 | 69 | 1. Add the gem: 70 | 71 | * If you're using {Bundler 0.9+}[http://github.com/carlhuda/bundler]: 72 | 73 | 1. Add to your app's Gemfile: gem 'asset_hat', '0.x.x' 74 | 2. Command-line: bundle install 75 | 76 | * If you're using Rails 2.3.x's config.gem: 77 | 78 | 1. Add to your app's config/environment.rb: 79 | config.gem 'asset_hat', :version => '0.x.x' 80 | 2. Command-line: gem install asset_hat 81 | 82 | 2. Add to your app's Rakefile: require 'asset_hat/tasks' 83 | 84 | 85 | 86 | == Quick start & configuration 87 | 88 | 1. In all of your layouts and views, change stylesheet_link_tag 89 | to include_css, and change 90 | javascript_include_tag to include_js. (Don't 91 | worry, these helpers use the same arguments as Rails' helpers. Nothing 92 | will break.) 93 | 94 | 2. Create the default config file: 95 | 96 | rake asset_hat:config 97 | 98 | 3. In your app, open the new file at config/assets.yml, and set 99 | up your CSS/JS bundles according to that file's example. 100 | 101 | 4. In your layouts and views, switch to the new bundles. For example, if you 102 | originally had this: 103 | 104 | <%# app/views/layouts/application.html.erb: %> 105 | <%= stylesheet_include_tag 'reset', 'application' %> 106 | 107 | Then you'll now have: 108 | 109 | # config/assets.yml: 110 | css: 111 | bundles: 112 | application: ['reset', 'application'] 113 | 114 | <%# app/views/layouts/application.html.erb: %> 115 | <%= include_css :bundle => 'application' %> 116 | 117 | 5. Add this to your deployment script: 118 | 119 | rake asset_hat:minify 120 | 121 | This minifies all of the CSS/JS files listed in 122 | config/assets.yml, concatenates the minified code into bundle 123 | files, and adds CDN asset hosts and cache-busting commit IDs to image URLs 124 | in your CSS. 125 | 126 | Any previously minified bundles are overwritten; your original 127 | CSS/JS files remain untouched. Bundles are created as new files in 128 | public/stylesheets/bundles/ and 129 | public/javascripts/bundles/. 130 | 131 | If you're using a CSS/JS layer like SASS or CoffeeScript, be sure to 132 | compile/transpile to regular CSS/JS before running 133 | rake asset_hat:minify. (When AssetHat is ready for Rails 3.1, 134 | rake asset_hat:minify will automatically start with 135 | rake assets:precompile.) 136 | 137 | === Advanced configuration 138 | 139 | If you manage deployments with Capistrano[http://www.capify.org/], see the 140 | gem's packaged example at 141 | lib/asset_hat/capistrano.rb[https://github.com/mintdigital/asset_hat/blob/master/lib/asset_hat/capistrano.rb]. 142 | 143 | If your stack uses Unicorn[http://unicorn.bogomips.org/], you'll want to 144 | configure it so that assets' commit IDs are precached only once. See the gem's 145 | packaged example at 146 | lib/asset_hat/unicorn.rb[https://github.com/mintdigital/asset_hat/blob/master/lib/asset_hat/unicorn.rb]. 147 | 148 | If you want shorter output during deployments, you can use 149 | `rake asset_hat:minify FORMAT=short` (one output line per bundle) or 150 | `FORMAT=dot` (one output line total) in your deploy script. 151 | 152 | Additional settings are supported in config/assets.yml: 153 | 154 | * engine: Indicates how CSS and JS are minified; omit this 155 | setting to use the defaults. By default, CSS is minified with 156 | rgrove/cssmin[http://github.com/rgrove/cssmin] (a Ruby port of Lecomte's 157 | YUI Compressor and Schlueter's PHP cssmin), and JS is minified with 158 | rgrove/jsmin[http://github.com/rgrove/jsmin] (a Ruby port of Crockford's 159 | JSMin). 160 | 161 | If you'd prefer to have slightly more readable code for debugging purposes, 162 | you can switch both the CSS and JS engines to weak. However, 163 | the weak engines don't save as many bytes. 164 | 165 | * vendors: Configures third-party JS that's loaded from a CDN or 166 | other external source. The following example configures jQuery and jQuery UI 167 | for use throughout the app: 168 | 169 | js: 170 | vendors: 171 | jquery: 172 | version: 1.5.2 173 | jquery_ui: 174 | version: 1.8.12 175 | remote_url: http://cdn.example.com/js/jquery-ui-1.8.12.min.js 176 | remote_ssl_url: https://cdn-ssl.example.com/js/jquery-ui-1.8.12.min.js 177 | 178 | Configuration keys per vendor: 179 | 180 | * version: Sets the default version across the app. In the 181 | example above, <%= include_js :jquery %> uses version 1.5.2 182 | by default. You can override this for special layouts/views with 183 | <%= include_js :jquery, :version => '1.3.2' %>. 184 | * remote_url, remote_ssl_url: By default, vendor 185 | JS will load from {Google's CDN}[http://code.google.com/apis/ajaxlibs/] 186 | in production (or any environment where 187 | config.action_controller.consider_all_requests_local is set 188 | to false). If the original request to your app used SSL, the 189 | vendor JS will also load from Google's CDN via SSL. If you prefer to use a 190 | different CDN, specify its SSL/non-SSL URLs, and the appropriate URL will 191 | be used per request. 192 | 193 | A full list of supported vendors is in the 194 | AssetHat::JS::Vendors module. 195 | 196 | === SSL configuration 197 | 198 | When you request a page via SSL, some browsers (euphemism for "IE") show 199 | errors if any assets -- stylesheets, JS files, images -- are served _without_ 200 | SSL. 201 | 202 | AssetHat plays well with SSL pages that load assets from a CDN. Put this in 203 | config/environments/production.rb or similar: 204 | 205 | config.action_controller.asset_host = Proc.new do |source, request| 206 | "#{request.protocol}cdn#{source.hash % 4}.example.com" 207 | # => 'http://cdn0.example.com', 'https://cdn1.example.com', etc. 208 | end 209 | 210 | This switches to a different CDN URL if the request used SSL. When you run 211 | rake asset_hat:minify at deploy time, AssetHat detects this 212 | configuration, and generates special SSL versions of each stylesheet that also 213 | load images from CDN via SSL. (Non-SSL pages still get non-SSL stylesheets.) 214 | 215 | 216 | 217 | == Usage 218 | 219 | In your layouts and views, instead of these: 220 | 221 | <%= stylesheet_link_tag 'reset', 'application', 'clearfix', 222 | :media => 'screen,projection', 223 | :cache => 'bundles/application' %> 224 | <%= javascript_include_tag 'plugin-1', 'plugin-2', 'plugin-3', 225 | :cache => 'bundles/application' %> 226 | 227 | Use these: 228 | 229 | <%= include_css :bundle => 'application' %> 230 | <%= include_js :bundle => 'application' %> 231 | 232 | These turn into: 233 | 234 | 236 | 238 | 239 | Have an enormous app? You can integrate gradually, using AssetHat alongside 240 | Rails' default asset caching. 241 | 242 | If your environment has config.action_controller.perform_caching 243 | set to true (e.g., in production), the layout/view will include 244 | minified bundle files. Otherwise, the separate, unminified files will be 245 | included, based on the bundle contents you define in 246 | config/assets.yml. 247 | 248 | If your environment has config.action_controller.asset_host 249 | pointing to a CDN, your CSS/JS files will load from there. If your 250 | configuration supports using the CDN via SSL (see the section "SSL 251 | configuration"), SSL requests will also load CSS/JS files via SSL. 252 | 253 | === Advanced usage 254 | 255 | You can also include single files as expected: 256 | 257 | <%= include_css 'reset', 'application' %> 258 | <%= include_js 'plugin.min', 'application' %> 259 | 260 | Or include multiple bundles at once: 261 | 262 | <%= include_js :bundles => %w[plugins common] %> 263 | 264 | When including multiple bundles at once, this yields one 265 | or 281 | 282 | What a hassle. With AssetHat, just set up a bundle in 283 | config/assets.yml: 284 | 285 | js: 286 | bundles: 287 | app: 288 | - common 289 | - search 290 | - app 291 | 292 | Ready to go. Here's how to load jQuery and your bundle normally: 293 | 294 | <%= include_js :jquery, :bundle => 'app' %> 295 | 296 | And here's how to switch on LABjs mode: 297 | 298 | <%= include_js :jquery, :bundle => 'app', 299 | :loader => :lab_js %> 300 | 301 | Add your preferred jQuery and LABjs versions to the config file if you haven't 302 | already, and that's it. If you don't have a copy of LABjs locally, AssetHat 303 | knows how to instead load it from {cdnjs}[http://cdnjs.com/], which uses 304 | high-speed Amazon Cloudfront servers. 305 | 306 | This is just the most common LABjs use case. If you want to fine-tune it even 307 | further, you can have the best of both worlds: 308 | 309 | <%= include_js :lab_js %> 310 | 317 | 318 | In this example, common is not a dependency for 319 | search, so allow either to execute as soon as possible -- 320 | whichever happens to load first -- rather than always forcing 321 | common to execute first. 322 | 323 | === Bundle tips 324 | 325 | Don't go overboard with huge bundles: 326 | 327 | * Mobile browsers may not cache CSS/JS files that are too large, regardless of 328 | gzipping. Check the latest specs for each mobile browser you support. 329 | * You might want to put plugins (rarely changed) in one bundle, and 330 | application code (frequently changed) in another bundle. This way, when the 331 | app code changes, the browser re-downloads only the new app code, and uses 332 | the cached plugin code. 333 | * Regardless of code-change frequency, it's sometimes faster to split a bundle 334 | in half, and load each half in parallel (i.e., two HTTP requests instead of 335 | one). LABjs mode can help with loading several smaller bundles in parallel. 336 | Your own tests will tell what's optimal for your situation. 337 | 338 | 339 | 340 | == More info 341 | 342 | * {Official website}[http://mintdigital.github.com/asset_hat/] 343 | * {Full documentation}[http://mintdigital.github.com/asset_hat/doc/] 344 | * {History/changelog}[http://mintdigital.github.com/asset_hat/doc/files/HISTORY.html] 345 | * Released under the 346 | {MIT license}[https://github.com/mintdigital/asset_hat/blob/master/LICENSE]. 347 | 348 | === Contributing 349 | 350 | Have an idea, problem, or bug report? 351 | {Send a pull request}[http://help.github.com/send-pull-requests/]! Please base 352 | pull requests on the `development` branch, not the `master` branch. 353 | 354 | Contributors: 355 | 356 | * {rondevera}[https://github.com/rondevera] 357 | (maintainer; Twitter: {@ronalddevera}[https://twitter.com/#!/ronalddevera]) 358 | * {philnash}[https://github.com/philnash] 359 | * {dstrelau}[https://github.com/dstrelau] 360 | * {daphonz}[https://github.com/daphonz] 361 | * {sauliusg}[https://github.com/sauliusg] 362 | * {jsonperl}[https://github.com/jsonperl] 363 | 364 | 365 | 366 | == {What is best in AssetHat?}[http://www.youtube.com/watch?v=V30tyaXv6EI] 367 | 368 | * To crush your assets; 369 | * See them bundled before you; and 370 | * Hear no more lamentation about slow page loads. 371 | -------------------------------------------------------------------------------- /doc/classes/AssetHat/JS/Vendors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | : AssetHat::JS::Vendors [AssetHat] 5 | 6 | 7 | 24 | 25 | 26 |
27 |
28 |

29 | Module 30 | AssetHat::JS::Vendors 31 |

32 |
    33 |
  1. 34 | lib/asset_hat/js/vendors.rb 35 |
  2. 36 |
37 |
38 |
39 |
40 |
41 |

42 | For working with supported 3rd-party JavaScript plugin/framework/library 43 | vendors. 44 |

45 |
46 |
47 |

Methods

48 |

public class

49 |
    50 |
  1. source_for
  2. 51 |
52 |
53 |
54 |
55 |

Constants

56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 |
VENDORS_ON_GOOGLE_CDN=[ :dojo, :ext_core, :jquery, :jquery_ui, :mootools, :prototype, :scriptaculous, :swfobject, :webfont, :yui ]  64 | 65 | A list of supported 3rd-party JavaScript plugin/vendor names. Homepages: 66 | 67 | 114 |
VENDORS_ON_CDNJS=[ :lab_js ]
VENDORS=VENDORS_ON_GOOGLE_CDN + VENDORS_ON_CDNJS
127 |
128 |
129 |
130 |

Public class methods

131 |
132 | 133 |
134 | source_for 135 | (vendor, options={}) 136 |
137 |
138 |

139 | Accepts an item from `VENDORS`, and returns the URL at which that vendor 140 | asset can be found. The URL is either local (relative) or remote, depending 141 | on the environment configuration: 142 |

143 |
    144 |
  • If `AssetHat.consider_all_requests_local?` 146 | is true: 147 | 148 |
      149 |
    • The local file takes precedence. 150 | 151 |
    • 152 |
    • If the local file is missing, the remote URL in assets.yml is used as a 153 | fallback. 154 | 155 |
    • 156 |
    • If there is no remote URL in assets.yml, the Google CDN URL is used as a 157 | fallback. (This makes setup easier: If the app doesn’t already have a 158 | local copy of the vendor file, then it’s instead loaded remotely.) 159 | 160 |
    • 161 |
    162 |
  • 163 |
  • If `AssetHat.consider_all_requests_local?` 165 | is false: 166 | 167 |
      168 |
    • The remote URL in assets.yml takes precedence. 169 | 170 |
    • 171 |
    • The Google CDN URL is 172 | used as a fallback, but only if a version number can be found (either in 173 | assets.yml or via the helper’s `:version` option). If no version 174 | number is found, the remote URL cannot be built, so the local file (if any) 175 | is used as a fallback. 176 | 177 |
    • 178 |
    179 |
  • 180 |
181 |

182 | Options: 183 |

184 |
185 |
ssl
Boolean for whether to include vendor JS via 186 | HTTPS. Defaults to false. 187 | 188 |
189 |
version
The vendor version, e.g., ‘1.6.0’ for jQuery 1.6. By default, 190 | each vendor version is taken from config/assets.yml; use this 191 | option to override the configuration. 192 | 193 |
194 |
195 |
196 |
197 | 198 | [show source] 199 | 200 |
     # File lib/asset_hat/js/vendors.rb, line 67
 67:       def self.source_for(vendor, options={})
 68:         vendor_config =
 69:           AssetHat.config['js']['vendors'][vendor.to_s] rescue nil
 70:         use_local = AssetHat.consider_all_requests_local?
 71:         use_ssl   = !!options[:ssl]
 72:         version   = options[:version] || vendor_config['version'] rescue nil
 73: 
 74:         # Prepare local path and default remote URL
 75:         srcs = Vendors.vendor_uris(vendor,
 76:           :use_ssl => use_ssl, :version => version)
 77:         local_src, remote_src = srcs[:local], srcs[:remote]
 78: 
 79:         # Using the local URL requires that the vendor file exists locally. If
 80:         # the vendor file doesn't exist, use the remote URL as fallback.
 81:         use_local &&= AssetHat.asset_exists?(local_src, :js)
 82: 
 83:         # If no version given, can't determine the remote URL; use the local
 84:         # URL as fallback.
 85:         use_local ||= version.blank?
 86: 
 87:         if use_local
 88:           src = local_src
 89:         else
 90:           # To ease setup, if no local copy of the vendor code is found,
 91:           # use a remote URL as a fallback.
 92: 
 93:           # Give precedence to configured remote URLs
 94:           src   = vendor_config.try(:[], 'remote_ssl_url') if use_ssl
 95:           src ||= vendor_config.try(:[], 'remote_url')
 96: 
 97:           # Use default remote URL as fallback
 98:           src ||= remote_src
 99: 
100:           # Use local URL as final resort, even though the file doesn't
101:           # exist, in hopes that the app maintainer finds the 404 (or the
102:           # warning below) in the logs. This needs to be fixed in the app,
103:           # rather than relying on a CDN to dynamically provide the latest
104:           # stable vendor version.
105:           if src.blank?
106:             src = local_src
107:             Rails.logger.warn "\n\nAssetHat WARNING (#{Time.now}):\n" + %{
108:               Tried to reference the vendor JS `:#{vendor}`, but
109:               #{AssetHat.assets_dir(:js)}/#{local_src} couldn't be found, and
110:               couldn't use a remote fallback because no vendor version was
111:               given in #{AssetHat::RELATIVE_CONFIG_FILEPATH}.
112:             }.squish!
113:               # TODO: Create `AssetHat::Logger.warn`, etc. methods
114:           end
115:         end
116: 
117:         src
118:       end
201 |
202 |
203 |
204 |
205 |
206 |
207 | 208 |
209 | 212 | 213 | 214 | --------------------------------------------------------------------------------