turbo-sprockets-rails3 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,7 +1,9 @@
1
- # Turbo Sprockets for Rails 3
1
+ # Turbo Sprockets for Rails 3.2.x
2
2
 
3
- * Speeds up the Rails 3 asset pipeline by only recompiling changed assets
4
- * Generates non-digest assets from precompiled assets - Only compile once!
3
+ [![Build Status](https://secure.travis-ci.org/ndbroadbent/turbo-sprockets-rails3.png)](http://travis-ci.org/ndbroadbent/turbo-sprockets-rails3)
4
+
5
+ * Speeds up the Rails 3 asset pipeline by only recompiling changed assets, based on a hash of their source files
6
+ * Generates both non-fingerprinted and fingerprinted assets from a single compile
5
7
 
6
8
  This is a backport of the work I've done for Rails 4.0.0, released as
7
9
  a gem for Rails 3.2.x. (See [sprockets-rails #21](https://github.com/rails/sprockets-rails/pull/21) and [sprockets #367](https://github.com/sstephenson/sprockets/pull/367) for the Rails 4 pull requests.)
@@ -11,10 +13,16 @@ a gem for Rails 3.2.x. (See [sprockets-rails #21](https://github.com/rails/sproc
11
13
 
12
14
  Please test this out thoroughly on your local machine before deploying to a production site, and open an issue on GitHub if you have any problems. By using this software you agree to the terms and conditions in the [MIT license](https://github.com/ndbroadbent/turbo-sprockets-rails3/blob/master/MIT-LICENSE).
13
15
 
14
- ## Dependencies
16
+ ## Supported Versions
17
+
18
+ ### Ruby
19
+
20
+ All versions of Ruby that are supported by Rails `3.2.x`, including `1.9.3`, `1.9.2`, `1.8.7` and REE.
21
+
22
+ ### Rails
15
23
 
16
- * sprockets `~> 2.1.3`
17
- * railties `~> 3.2.0`
24
+ This gem only supports Rails `3.2.0` or higher.
25
+ Rails `3.1.x` support is not available at this time, because it depends on an outdated version of `sprockets`.
18
26
 
19
27
  ## Usage
20
28
 
@@ -43,12 +51,11 @@ Enjoy your lightning fast deploys!
43
51
 
44
52
  You won't be able to do an 'incremental update' on heroku, since your `public/assets`
45
53
  folder will be empty at the start of each push. However, this gem can still cut your
46
- precompile time in half, since it only compiles assets once to generate both digest and non-digest assets.
54
+ precompile time in half, since it only needs to compile assets once.
47
55
 
48
- If you want to make the most of `turbo-sprockets-rails3`, you can run `assets:precompile` on your local machine
49
- and commit the compiled assets. When you push compiled assets to Heroku, it automatically skips the `assets:precompile` task.
56
+ If you want to make the most of `turbo-sprockets-rails3`, you can run `assets:precompile` on your local machine and commit the compiled assets. When you push compiled assets to Heroku, it will automatically skip the `assets:precompile` task.
50
57
 
51
- I've automated this process in a Rake task for my own projects. It uses a temporary deploy repo so you can keep working, and it also rebases and amends the assets commit so your repo doesn't grow out of control. You can find my deploy task in a gist at https://gist.github.com/3802355. Save this file to `lib/tasks/deploy.rake`, and run `rake deploy` to deploy your app to Heroku.
58
+ I've automated this process in a Rake task for my own projects. My task creates a deployment repo at `tmp/heroku_deploy` so that you can keep working while deploying, and it also rebases and amends the assets commit to keep your repo's history from growing out of control. You can find the deploy task in a gist at https://gist.github.com/3802355. Save this file to `lib/tasks/deploy.rake`, and make sure you have added a `heroku` remote to your repo. You will now be able to run `rake deploy` to deploy your app to Heroku.
52
59
 
53
60
  ## Debugging
54
61
 
@@ -63,14 +63,17 @@ module Sprockets
63
63
  env.logger.debug "Stripped digests, copied to #{logical_path}, and created gzipped asset"
64
64
 
65
65
  else
66
- # Otherwise, treat file as binary and copy it
67
- FileUtils.cp_r abs_digest_path, abs_logical_path, :remove_destination => true
68
- env.logger.debug "Copied binary asset to #{logical_path}"
69
-
70
- # Copy gzipped asset if exists
71
- if File.exist? "#{abs_digest_path}.gz"
72
- FileUtils.cp_r "#{abs_digest_path}.gz", "#{abs_logical_path}.gz", :remove_destination => true
73
- env.logger.debug "Copied gzipped asset to #{logical_path}.gz"
66
+ # Otherwise, treat file as binary and copy it.
67
+ # Ignore paths that have no digests, such as READMEs
68
+ unless abs_digest_path == abs_logical_path
69
+ FileUtils.cp_r abs_digest_path, abs_logical_path, :remove_destination => true
70
+ env.logger.debug "Copied binary asset to #{logical_path}"
71
+
72
+ # Copy gzipped asset if exists
73
+ if File.exist? "#{abs_digest_path}.gz"
74
+ FileUtils.cp_r "#{abs_digest_path}.gz", "#{abs_logical_path}.gz", :remove_destination => true
75
+ env.logger.debug "Copied gzipped asset to #{logical_path}.gz"
76
+ end
74
77
  end
75
78
  end
76
79
  end
@@ -2,4 +2,6 @@ Dir[File.expand_path('../turbo-sprockets/sprockets_overrides/**/*.rb', __FILE__)
2
2
  require f
3
3
  end
4
4
 
5
+ require 'sprockets/railtie'
6
+ require 'sprockets/helpers'
5
7
  require 'turbo-sprockets/railtie'
@@ -1,74 +1,80 @@
1
- require 'sprockets/static_compiler'
1
+ begin
2
+ require 'sprockets/static_compiler'
3
+ rescue LoadError
4
+ end
2
5
 
3
- module Sprockets
4
- StaticCompiler.class_eval do
5
- def initialize(env, target, paths, options = {})
6
- @env = env
7
- @target = target
8
- @paths = paths
9
- @digest = options.fetch(:digest, true)
10
- @manifest = options.fetch(:manifest, true)
11
- @manifest_path = options.delete(:manifest_path) || target
12
- @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/
6
+ # Sprockets::StaticCompiler was only introduced in Rails 3.2.x
7
+ if defined?(Sprockets::StaticCompiler)
8
+ module Sprockets
9
+ StaticCompiler.class_eval do
10
+ def initialize(env, target, paths, options = {})
11
+ @env = env
12
+ @target = target
13
+ @paths = paths
14
+ @digest = options.fetch(:digest, true)
15
+ @manifest = options.fetch(:manifest, true)
16
+ @manifest_path = options.delete(:manifest_path) || target
17
+ @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/
13
18
 
14
- @current_source_digests = options.fetch(:source_digests, {})
15
- @current_digest_files = options.fetch(:digest_files, {})
19
+ @current_source_digests = options.fetch(:source_digests, {})
20
+ @current_digest_files = options.fetch(:digest_files, {})
16
21
 
17
- @digest_files = {}
18
- @source_digests = {}
19
- end
22
+ @digest_files = {}
23
+ @source_digests = {}
24
+ end
20
25
 
21
- def compile
22
- start_time = Time.now.to_f
26
+ def compile
27
+ start_time = Time.now.to_f
23
28
 
24
- env.each_logical_path do |logical_path|
25
- if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
26
- logical_path.sub!(/\/index\./, '.')
27
- end
28
- next unless compile_path?(logical_path)
29
+ env.each_logical_path do |logical_path|
30
+ if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
31
+ logical_path.sub!(/\/index\./, '.')
32
+ end
33
+ next unless compile_path?(logical_path)
29
34
 
30
- # Fetch asset without any processing or compression,
31
- # to calculate a digest of the concatenated source files
32
- asset = env.find_asset(logical_path, :process => false)
35
+ # Fetch asset without any processing or compression,
36
+ # to calculate a digest of the concatenated source files
37
+ asset = env.find_asset(logical_path, :process => false)
33
38
 
34
- # Force digest to UTF-8 for Ruby 1.9, otherwise YAML dumps ASCII-8BIT as !binary
35
- @source_digests[logical_path] = if RUBY_VERSION.to_f >= 1.9
36
- asset.digest.force_encoding("UTF-8")
37
- else
38
- asset.digest
39
- end
39
+ # Force digest to UTF-8 for Ruby 1.9, otherwise YAML dumps ASCII-8BIT as !binary
40
+ @source_digests[logical_path] = if RUBY_VERSION.to_f >= 1.9
41
+ asset.digest.force_encoding("UTF-8")
42
+ else
43
+ asset.digest
44
+ end
40
45
 
41
- # Recompile if digest has changed or compiled digest file is missing
42
- current_digest_file = @current_digest_files[logical_path]
46
+ # Recompile if digest has changed or compiled digest file is missing
47
+ current_digest_file = @current_digest_files[logical_path]
43
48
 
44
- if @source_digests[logical_path] != @current_source_digests[logical_path] ||
45
- !(current_digest_file && File.exists?("#{@target}/#{current_digest_file}"))
49
+ if @source_digests[logical_path] != @current_source_digests[logical_path] ||
50
+ !(current_digest_file && File.exists?("#{@target}/#{current_digest_file}"))
46
51
 
47
- if asset = env.find_asset(logical_path)
48
- @digest_files[logical_path] = write_asset(asset)
49
- end
52
+ if asset = env.find_asset(logical_path)
53
+ @digest_files[logical_path] = write_asset(asset)
54
+ end
50
55
 
51
- else
52
- # Set asset file from manifest.yml
53
- digest_file = @current_digest_files[logical_path]
54
- @digest_files[logical_path] = digest_file
56
+ else
57
+ # Set asset file from manifest.yml
58
+ digest_file = @current_digest_files[logical_path]
59
+ @digest_files[logical_path] = digest_file
55
60
 
56
- env.logger.debug "Not compiling #{logical_path}, sources digest has not changed " <<
57
- "(#{@source_digests[logical_path][0...7]})"
61
+ env.logger.debug "Not compiling #{logical_path}, sources digest has not changed " <<
62
+ "(#{@source_digests[logical_path][0...7]})"
63
+ end
58
64
  end
59
- end
60
65
 
61
- if @manifest
62
- write_manifest(:source_digests => @source_digests, :digest_files => @digest_files)
63
- end
66
+ if @manifest
67
+ write_manifest(:source_digests => @source_digests, :digest_files => @digest_files)
68
+ end
64
69
 
65
- # Store digests in Rails config. (Important if non-digest is run after primary)
66
- config = ::Rails.application.config
67
- config.assets.digest_files = @digest_files
68
- config.assets.source_digests = @source_digests
70
+ # Store digests in Rails config. (Important if non-digest is run after primary)
71
+ config = ::Rails.application.config
72
+ config.assets.digest_files = @digest_files
73
+ config.assets.source_digests = @source_digests
69
74
 
70
- elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
71
- env.logger.debug "Processed #{'non-' unless @digest}digest assets in #{elapsed_time}ms"
75
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
76
+ env.logger.debug "Processed #{'non-' unless @digest}digest assets in #{elapsed_time}ms"
77
+ end
72
78
  end
73
79
  end
74
80
  end
@@ -2,7 +2,7 @@ require "fileutils"
2
2
 
3
3
  # Clear all assets tasks from sprockets railtie
4
4
  Rake::Task.tasks.each do |task|
5
- if task.name.starts_with? 'assets:'
5
+ if task.name.match '^assets:'
6
6
  task.clear_prerequisites.clear_actions
7
7
  end
8
8
  end
@@ -1,3 +1,3 @@
1
1
  module TurboSprockets
2
- VERSION = "0.1.4"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -0,0 +1,143 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'turbo-sprockets-rails3'
5
+ require 'fileutils'
6
+
7
+ # Require minitest for 1.9 and test/unit for 1.8
8
+ # 'active_support/test_case' supports both.
9
+ begin
10
+ require 'minitest/autorun'
11
+ rescue LoadError
12
+ require 'test/unit'
13
+ end
14
+
15
+ require 'active_support/test_case'
16
+ require 'rails/generators'
17
+ require "active_support/testing/isolation"
18
+ require "active_support/core_ext/kernel/reporting"
19
+
20
+ module TestHelpers
21
+ module Paths
22
+ TMP_PATH = File.expand_path(File.join(File.dirname(__FILE__), *%w[.. tmp]))
23
+
24
+ def tmp_path(*args)
25
+ File.join(TMP_PATH, *args)
26
+ end
27
+
28
+ def app_path(*args)
29
+ tmp_path(*%w[app] + args)
30
+ end
31
+
32
+ def rails_root
33
+ app_path
34
+ end
35
+ end
36
+
37
+ module Rack
38
+ def app(env = "production")
39
+ old_env = ENV["RAILS_ENV"]
40
+ @app ||= begin
41
+ ENV["RAILS_ENV"] = env
42
+ require "#{app_path}/config/environment"
43
+ Rails.application
44
+ end
45
+ ensure
46
+ ENV["RAILS_ENV"] = old_env
47
+ end
48
+
49
+ def get(path)
50
+ @app.call(::Rack::MockRequest.env_for(path))
51
+ end
52
+ end
53
+
54
+ module Generation
55
+ # Build an application by invoking the generator and going through the whole stack.
56
+ def build_app(options = {})
57
+ @prev_rails_env = ENV['RAILS_ENV']
58
+ ENV['RAILS_ENV'] = 'development'
59
+
60
+ FileUtils.rm_rf(app_path)
61
+ FileUtils.cp_r(tmp_path('app_template'), app_path)
62
+
63
+ # Delete the initializers unless requested
64
+ unless options[:initializers]
65
+ Dir["#{app_path}/config/initializers/*.rb"].each do |initializer|
66
+ File.delete(initializer)
67
+ end
68
+ end
69
+
70
+ unless options[:gemfile]
71
+ File.delete"#{app_path}/Gemfile"
72
+ end
73
+
74
+ routes = File.read("#{app_path}/config/routes.rb")
75
+ if routes =~ /(\n\s*end\s*)\Z/
76
+ File.open("#{app_path}/config/routes.rb", 'w') do |f|
77
+ f.puts $` + "\nget ':controller(/:action(/:id))(.:format)'\n" + $1
78
+ end
79
+ end
80
+
81
+ add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"; config.active_support.deprecation = :log'
82
+ end
83
+
84
+ def teardown_app
85
+ ENV['RAILS_ENV'] = @prev_rails_env if @prev_rails_env
86
+ end
87
+
88
+ def add_to_config(str)
89
+ environment = File.read("#{app_path}/config/application.rb")
90
+ if environment =~ /(\n\s*end\s*end\s*)\Z/
91
+ File.open("#{app_path}/config/application.rb", 'w') do |f|
92
+ f.puts $` + "\n#{str}\n" + $1
93
+ end
94
+ end
95
+ end
96
+
97
+ def add_to_env_config(env, str)
98
+ environment = File.read("#{app_path}/config/environments/#{env}.rb")
99
+ if environment =~ /(\n\s*end\s*)\Z/
100
+ File.open("#{app_path}/config/environments/#{env}.rb", 'w') do |f|
101
+ f.puts $` + "\n#{str}\n" + $1
102
+ end
103
+ end
104
+ end
105
+
106
+ def app_file(path, contents)
107
+ FileUtils.mkdir_p File.dirname("#{app_path}/#{path}")
108
+ File.open("#{app_path}/#{path}", 'w') do |f|
109
+ f.puts contents
110
+ end
111
+ end
112
+
113
+ def boot_rails
114
+ require 'rubygems' unless defined? Gem
115
+ require 'bundler'
116
+ Bundler.setup
117
+ end
118
+ end
119
+ end
120
+
121
+ class ActiveSupport::TestCase
122
+ include TestHelpers::Paths
123
+ include TestHelpers::Rack
124
+ include TestHelpers::Generation
125
+ end
126
+
127
+ # Create a scope and build a fixture rails app
128
+ Module.new do
129
+ extend TestHelpers::Paths
130
+ # Build a rails app
131
+ if File.exist?(tmp_path)
132
+ FileUtils.rm_rf(tmp_path)
133
+ end
134
+ FileUtils.mkdir(tmp_path)
135
+
136
+ quietly do
137
+ Rails::Generators.invoke('app', ["#{tmp_path('app_template')}", "--skip-active-record", "--skip-test-unit"])
138
+ end
139
+
140
+ File.open("#{tmp_path}/app_template/config/boot.rb", 'w') do |f|
141
+ f.puts 'require "action_controller/railtie"'
142
+ end
143
+ end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/abstract_unit")
2
+ require 'rack/test'
3
+
4
+ module ApplicationTests
5
+ class AssetsDebuggingTest < ActiveSupport::TestCase
6
+ include ActiveSupport::Testing::Isolation
7
+ include Rack::Test::Methods
8
+
9
+ def setup
10
+ build_app(:initializers => true)
11
+
12
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
13
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
14
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
15
+
16
+ app_file "config/routes.rb", <<-RUBY
17
+ AppTemplate::Application.routes.draw do
18
+ get '/posts', :to => "posts#index"
19
+ end
20
+ RUBY
21
+
22
+ app_file "app/controllers/posts_controller.rb", <<-RUBY
23
+ class PostsController < ActionController::Base
24
+ end
25
+ RUBY
26
+
27
+ ENV["RAILS_ENV"] = "production"
28
+
29
+ boot_rails
30
+ end
31
+
32
+ def teardown
33
+ teardown_app
34
+ end
35
+
36
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
37
+ # config.assets.debug and config.assets.compile are false for production environment
38
+ ENV["RAILS_ENV"] = "production"
39
+ capture(:stdout) do
40
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
41
+ end
42
+ require "#{app_path}/config/environment"
43
+
44
+ class ::PostsController < ActionController::Base ; end
45
+
46
+ # the debug_assets params isn't used if compile is off
47
+ get '/posts?debug_assets=true'
48
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"/, last_response.body)
49
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"/, last_response.body)
50
+ end
51
+
52
+ test "assets aren't concatened when compile is true is on and debug_assets params is true" do
53
+ app_file "config/initializers/compile.rb", "Rails.application.config.assets.compile = true"
54
+
55
+ ENV["RAILS_ENV"] = "production"
56
+ require "#{app_path}/config/environment"
57
+
58
+ class ::PostsController < ActionController::Base ; end
59
+
60
+ get '/posts?debug_assets=true'
61
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js\?body=1"/, last_response.body)
62
+ assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1"/, last_response.body)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,542 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + "/abstract_unit")
3
+ require 'rack/test'
4
+
5
+ module ApplicationTests
6
+ class AssetsTest < ActiveSupport::TestCase
7
+ include ActiveSupport::Testing::Isolation
8
+ include Rack::Test::Methods
9
+
10
+ def setup
11
+ build_app(:initializers => true)
12
+ boot_rails
13
+ end
14
+
15
+ def teardown
16
+ teardown_app
17
+ end
18
+
19
+ def precompile!(env = nil)
20
+ quietly do
21
+ precompile_task = "bundle exec rake assets:precompile #{env} 2>&1"
22
+ output = Dir.chdir(app_path) { %x[ #{precompile_task} ] }
23
+ assert $?.success?, output
24
+ output
25
+ end
26
+ end
27
+
28
+ def clean_assets!
29
+ quietly do
30
+ assert Dir.chdir(app_path) { system('bundle exec rake assets:clean') }
31
+ end
32
+ end
33
+
34
+ def assert_file_exists(filename)
35
+ assert File.exists?(filename), "missing #{filename}"
36
+ end
37
+
38
+ def assert_no_file_exists(filename)
39
+ assert !File.exists?(filename), "#{filename} does exist"
40
+ end
41
+
42
+ test "assets routes have higher priority" do
43
+ app_file "app/assets/javascripts/demo.js.erb", "a = <%= image_path('rails.png').inspect %>;"
44
+
45
+ app_file 'config/routes.rb', <<-RUBY
46
+ AppTemplate::Application.routes.draw do
47
+ get '*path', :to => lambda { |env| [200, { "Content-Type" => "text/html" }, "Not an asset"] }
48
+ end
49
+ RUBY
50
+
51
+ require "#{app_path}/config/environment"
52
+
53
+ get "/assets/demo.js"
54
+ assert_equal 'a = "/assets/rails.png";', last_response.body.strip
55
+ end
56
+
57
+ test "assets do not require compressors until it is used" do
58
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
59
+ add_to_env_config "production", "config.assets.compile = true"
60
+
61
+ ENV["RAILS_ENV"] = "production"
62
+ require "#{app_path}/config/environment"
63
+
64
+ assert !defined?(Uglifier)
65
+ get "/assets/demo.js"
66
+ assert_match "alert()", last_response.body
67
+ assert defined?(Uglifier)
68
+ end
69
+
70
+ test "precompile creates the file, gives it the original asset's content and run in production as default" do
71
+ app_file "app/assets/javascripts/application.js", "alert();"
72
+ app_file "app/assets/javascripts/foo/application.js", "alert();"
73
+
74
+ ENV["RAILS_ENV"] = nil
75
+ precompile!
76
+
77
+ files = Dir["#{app_path}/public/assets/application-*.js"]
78
+ files << Dir["#{app_path}/public/assets/application.js"].first
79
+ files << Dir["#{app_path}/public/assets/foo/application-*.js"].first
80
+ files << Dir["#{app_path}/public/assets/foo/application.js"].first
81
+ files.each do |file|
82
+ assert_not_nil file, "Expected application.js asset to be generated, but none found"
83
+ assert_equal "alert();", File.read(file)
84
+ end
85
+ end
86
+
87
+ test "precompile application.js and application.css and all other non JS/CSS files" do
88
+ app_file "app/assets/javascripts/application.js", "alert();"
89
+ app_file "app/assets/stylesheets/application.css", "body{}"
90
+
91
+ app_file "app/assets/javascripts/someapplication.js", "alert();"
92
+ app_file "app/assets/stylesheets/someapplication.css", "body{}"
93
+
94
+ app_file "app/assets/javascripts/something.min.js", "alert();"
95
+ app_file "app/assets/stylesheets/something.min.css", "body{}"
96
+
97
+ app_file "app/assets/javascripts/something.else.js.erb", "alert();"
98
+ app_file "app/assets/stylesheets/something.else.css.erb", "body{}"
99
+
100
+ images_should_compile = ["a.png", "happyface.png", "happy_face.png", "happy.face.png",
101
+ "happy-face.png", "happy.happy_face.png", "happy_happy.face.png",
102
+ "happy.happy.face.png", "-happy.png", "-happy.face.png",
103
+ "_happy.face.png", "_happy.png"]
104
+
105
+ images_should_compile.each do |filename|
106
+ app_file "app/assets/images/#{filename}", "happy"
107
+ end
108
+
109
+ precompile!
110
+
111
+ images_should_compile.each do |filename|
112
+ assert_file_exists("#{app_path}/public/assets/#{filename}")
113
+ end
114
+
115
+ assert_file_exists("#{app_path}/public/assets/application.js")
116
+ assert_file_exists("#{app_path}/public/assets/application.css")
117
+
118
+ assert_no_file_exists("#{app_path}/public/assets/someapplication.js")
119
+ assert_no_file_exists("#{app_path}/public/assets/someapplication.css")
120
+
121
+ assert_no_file_exists("#{app_path}/public/assets/something.min.js")
122
+ assert_no_file_exists("#{app_path}/public/assets/something.min.css")
123
+
124
+ assert_no_file_exists("#{app_path}/public/assets/something.else.js")
125
+ assert_no_file_exists("#{app_path}/public/assets/something.else.css")
126
+ end
127
+
128
+ test "precompile something.js for directory containing index file" do
129
+ add_to_config "config.assets.precompile = [ 'something.js' ]"
130
+ app_file "app/assets/javascripts/something/index.js.erb", "alert();"
131
+
132
+ precompile!
133
+
134
+ assert_file_exists("#{app_path}/public/assets/something.js")
135
+ end
136
+
137
+ test "asset pipeline should use a Sprockets::Index when config.assets.digest is true" do
138
+ add_to_config "config.assets.digest = true"
139
+ add_to_config "config.action_controller.perform_caching = false"
140
+
141
+ ENV["RAILS_ENV"] = "production"
142
+ require "#{app_path}/config/environment"
143
+
144
+ assert_equal Sprockets::Index, Rails.application.assets.class
145
+ end
146
+
147
+ test "precompile creates a manifest file with all the assets listed" do
148
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
149
+ app_file "app/assets/javascripts/application.js", "alert();"
150
+ # digest is default in false, we must enable it for test environment
151
+ add_to_config "config.assets.digest = true"
152
+
153
+ precompile!
154
+ manifest = "#{app_path}/public/assets/manifest.yml"
155
+
156
+ digests = YAML.load_file(manifest)
157
+ digest_files, source_digests = digests[:digest_files], digests[:source_digests]
158
+
159
+ assert_match(/application-([0-z]+)\.js/, digest_files["application.js"])
160
+ assert_match(/application-([0-z]+)\.css/, digest_files["application.css"])
161
+
162
+ assert_match(/[0-z]+/, source_digests["application.js"])
163
+ assert_match(/[0-z]+/, source_digests["application.css"])
164
+ end
165
+
166
+ test "the manifest file should be saved by default in the same assets folder" do
167
+ app_file "app/assets/javascripts/application.js", "alert();"
168
+ # digest is default in false, we must enable it for test environment
169
+ add_to_config "config.assets.digest = true"
170
+ add_to_config "config.assets.prefix = '/x'"
171
+
172
+ precompile!
173
+
174
+ manifest = "#{app_path}/public/x/manifest.yml"
175
+ digest_files = YAML.load_file(manifest)[:digest_files]
176
+ assert_match(/application-([0-z]+)\.js/, digest_files["application.js"])
177
+ end
178
+
179
+ test "precompile does not append asset digests when config.assets.digest is false" do
180
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
181
+ app_file "app/assets/javascripts/application.js", "alert();"
182
+ add_to_config "config.assets.digest = false"
183
+
184
+ precompile!
185
+
186
+ assert_file_exists("#{app_path}/public/assets/application.js")
187
+ assert_file_exists("#{app_path}/public/assets/application.css")
188
+
189
+ manifest = "#{app_path}/public/assets/manifest.yml"
190
+
191
+ digest_files = YAML.load_file(manifest)[:digest_files]
192
+ assert_equal "application.js", digest_files["application.js"]
193
+ assert_equal "application.css", digest_files["application.css"]
194
+ end
195
+
196
+ test "assets do not require any assets group gem when manifest file is present" do
197
+ app_file "app/assets/javascripts/application.js", "alert();"
198
+ add_to_env_config "production", "config.serve_static_assets = true"
199
+
200
+ ENV["RAILS_ENV"] = "production"
201
+ precompile!
202
+
203
+ manifest = "#{app_path}/public/assets/manifest.yml"
204
+ digest_files = YAML.load_file(manifest)[:digest_files]
205
+ asset_path = digest_files["application.js"]
206
+
207
+ require "#{app_path}/config/environment"
208
+
209
+ # Checking if Uglifier is defined we can know if Sprockets was reached or not
210
+ assert !defined?(Uglifier)
211
+ get "/assets/#{asset_path}"
212
+ assert_match "alert()", last_response.body
213
+ assert !defined?(Uglifier)
214
+ end
215
+
216
+ test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled" do
217
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
218
+
219
+ app_file "config/routes.rb", <<-RUBY
220
+ AppTemplate::Application.routes.draw do
221
+ get '/posts', :to => "posts#index"
222
+ end
223
+ RUBY
224
+
225
+ ENV["RAILS_ENV"] = "production"
226
+ precompile!
227
+
228
+ # Create file after of precompile
229
+ app_file "app/assets/javascripts/app.js", "alert();"
230
+
231
+ require "#{app_path}/config/environment"
232
+ class ::PostsController < ActionController::Base
233
+ def show_detailed_exceptions?() true end
234
+ end
235
+
236
+ get '/posts'
237
+ assert_match(/AssetNotPrecompiledError/, last_response.body)
238
+ end
239
+
240
+ test "assets raise AssetNotPrecompiledError when manifest file is present and requested file isn't precompiled if digest is disabled" do
241
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'app' %>"
242
+ add_to_config "config.assets.compile = false"
243
+ add_to_config "config.assets.digest_files = false"
244
+
245
+ app_file "config/routes.rb", <<-RUBY
246
+ AppTemplate::Application.routes.draw do
247
+ get '/posts', :to => "posts#index"
248
+ end
249
+ RUBY
250
+
251
+ ENV["RAILS_ENV"] = "production"
252
+ precompile!
253
+
254
+ # Create file after of precompile
255
+ app_file "app/assets/javascripts/app.js", "alert();"
256
+
257
+ require "#{app_path}/config/environment"
258
+ class ::PostsController < ActionController::Base
259
+ def show_detailed_exceptions?() true end
260
+ end
261
+
262
+ get '/posts'
263
+ assert_match(/AssetNotPrecompiledError/, last_response.body)
264
+ end
265
+
266
+ test "precompile properly refers files referenced with asset_path and and run in the provided RAILS_ENV" do
267
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
268
+ # digest is default in false, we must enable it for test environment
269
+ add_to_env_config "test", "config.assets.digest = true"
270
+
271
+ precompile!('RAILS_ENV=test')
272
+
273
+ file = Dir["#{app_path}/public/assets/application.css"].first
274
+ assert_match(/\/assets\/rails\.png/, File.read(file))
275
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
276
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
277
+ end
278
+
279
+ test "precompile shouldn't use the digests present in manifest.yml" do
280
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
281
+
282
+ ENV["RAILS_ENV"] = "production"
283
+ precompile!
284
+
285
+ manifest = "#{app_path}/public/assets/manifest.yml"
286
+ assets = YAML.load_file(manifest)
287
+ asset_path = assets["application.css"]
288
+
289
+ app_file "app/assets/images/rails.png", "image changed"
290
+
291
+ precompile!
292
+ digest_files = YAML.load_file(manifest)[:digest_files]
293
+
294
+ assert_not_equal asset_path, digest_files["application.css"]
295
+ end
296
+
297
+ test "precompile appends the md5 hash to files referenced with asset_path and run in production as default even using RAILS_GROUPS=assets" do
298
+ app_file "app/assets/stylesheets/application.css.erb", "<%= asset_path('rails.png') %>"
299
+ add_to_config "config.assets.compile = true"
300
+
301
+ ENV["RAILS_ENV"] = nil
302
+
303
+ precompile!('RAILS_GROUPS=assets')
304
+
305
+ file = Dir["#{app_path}/public/assets/application-*.css"].first
306
+ assert_match(/\/assets\/rails-([0-z]+)\.png/, File.read(file))
307
+ end
308
+
309
+ test "precompile should handle utf8 filenames" do
310
+ filename = "レイルズ.png"
311
+ app_file "app/assets/images/#{filename}", "not a image really"
312
+ add_to_config "config.assets.precompile = [ /\.png$/, /application.(css|js)$/ ]"
313
+
314
+ precompile!
315
+ require "#{app_path}/config/environment"
316
+
317
+ get "/assets/#{URI.parser.escape(filename)}"
318
+ assert_match "not a image really", last_response.body
319
+ assert_file_exists("#{app_path}/public/assets/#{filename}")
320
+ end
321
+
322
+ test "assets are cleaned up properly" do
323
+ app_file "public/assets/application.js", "alert();"
324
+ app_file "public/assets/application.css", "a { color: green; }"
325
+ app_file "public/assets/subdir/broken.png", "not really an image file"
326
+
327
+ clean_assets!
328
+
329
+ files = Dir["#{app_path}/public/assets/**/*", "#{app_path}/tmp/cache/assets/*"]
330
+ assert_equal 0, files.length, "Expected no assets, but found #{files.join(', ')}"
331
+ end
332
+
333
+ test "assets routes are not drawn when compilation is disabled" do
334
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
335
+ add_to_config "config.assets.compile = false"
336
+
337
+ ENV["RAILS_ENV"] = "production"
338
+ require "#{app_path}/config/environment"
339
+
340
+ get "/assets/demo.js"
341
+ assert_equal 404, last_response.status
342
+ end
343
+
344
+ test "does not stream session cookies back" do
345
+ app_file "app/assets/javascripts/demo.js.erb", "<%= :alert %>();"
346
+
347
+ app_file "config/routes.rb", <<-RUBY
348
+ AppTemplate::Application.routes.draw do
349
+ get '/omg', :to => "omg#index"
350
+ end
351
+ RUBY
352
+
353
+ require "#{app_path}/config/environment"
354
+
355
+ class ::OmgController < ActionController::Base
356
+ def index
357
+ flash[:cool_story] = true
358
+ render :text => "ok"
359
+ end
360
+ end
361
+
362
+ get "/omg"
363
+ assert_equal 'ok', last_response.body
364
+
365
+ get "/assets/demo.js"
366
+ assert_match "alert()", last_response.body
367
+ assert_equal nil, last_response.headers["Set-Cookie"]
368
+ end
369
+
370
+ test "files in any assets/ directories are not added to Sprockets" do
371
+ %w[app lib vendor].each do |dir|
372
+ app_file "#{dir}/assets/#{dir}_test.erb", "testing"
373
+ end
374
+
375
+ app_file "app/assets/javascripts/demo.js", "alert();"
376
+
377
+ require "#{app_path}/config/environment"
378
+
379
+ get "/assets/demo.js"
380
+ assert_match "alert();", last_response.body
381
+ assert_equal 200, last_response.status
382
+ end
383
+
384
+ test "assets are concatenated when debug is off and compile is off either if debug_assets param is provided" do
385
+ app_with_assets_in_view
386
+
387
+ # config.assets.debug and config.assets.compile are false for production environment
388
+ ENV["RAILS_ENV"] = "production"
389
+ precompile!
390
+
391
+ require "#{app_path}/config/environment"
392
+
393
+ class ::PostsController < ActionController::Base ; end
394
+
395
+ # the debug_assets params isn't used if compile is off
396
+ get '/posts?debug_assets=true'
397
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js"/, last_response.body)
398
+ assert_no_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js"/, last_response.body)
399
+ end
400
+
401
+ test "assets aren't concatenated when compile is on and debug_assets params is true" do
402
+ app_with_assets_in_view
403
+ add_to_env_config "production", "config.assets.compile = true"
404
+ add_to_env_config "production", "config.assets.allow_debugging = true"
405
+
406
+ ENV["RAILS_ENV"] = "production"
407
+ require "#{app_path}/config/environment"
408
+
409
+ class ::PostsController < ActionController::Base ; end
410
+
411
+ get '/posts?debug_assets=true'
412
+ assert_match(/<script src="\/assets\/application-([0-z]+)\.js\?body=1"/, last_response.body)
413
+ assert_match(/<script src="\/assets\/xmlhr-([0-z]+)\.js\?body=1"/, last_response.body)
414
+ end
415
+
416
+ test "assets can access model information when precompiling" do
417
+ app_file "app/models/post.rb", "class Post; end"
418
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
419
+ app_file "app/assets/javascripts/xmlhr.js.erb", "<%= Post.name %>"
420
+
421
+ add_to_config "config.assets.digest = false"
422
+ precompile!
423
+ assert_equal "Post;\n", File.read("#{app_path}/public/assets/application.js")
424
+ end
425
+
426
+ test "assets can't access model information when precompiling if not initializing the app" do
427
+ app_file "app/models/post.rb", "class Post; end"
428
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
429
+ app_file "app/assets/javascripts/xmlhr.js.erb", "<%= defined?(Post) || :NoPost %>"
430
+
431
+ add_to_config "config.assets.digest = false"
432
+ add_to_config "config.assets.initialize_on_precompile = false"
433
+
434
+ precompile!
435
+ assert_equal "NoPost;\n", File.read("#{app_path}/public/assets/application.js")
436
+ end
437
+
438
+ test "initialization on the assets group should set assets_dir" do
439
+ require "#{app_path}/config/application"
440
+ Rails.application.initialize!(:assets)
441
+ assert_not_nil Rails.application.config.action_controller.assets_dir
442
+ end
443
+
444
+ test "enhancements to assets:precompile should only run once" do
445
+ app_file "lib/tasks/enhance.rake", "Rake::Task['assets:precompile'].enhance { puts 'enhancement' }"
446
+ output = precompile!
447
+ assert_equal 1, output.scan("enhancement").size
448
+ end
449
+
450
+ test "digested assets are not mistakenly removed" do
451
+ app_file "app/assets/application.js", "alert();"
452
+ add_to_config "config.assets.compile = true"
453
+ add_to_config "config.assets.digest = true"
454
+
455
+ precompile!
456
+
457
+ files = Dir["#{app_path}/public/assets/application-*.js"]
458
+ assert_equal 1, files.length, "Expected digested application.js asset to be generated, but none found"
459
+ end
460
+
461
+ test "digested assets are removed from configured path" do
462
+ app_file "public/production_assets/application.js", "alert();"
463
+ add_to_env_config "production", "config.assets.prefix = 'production_assets'"
464
+
465
+ ENV["RAILS_ENV"] = nil
466
+
467
+ clean_assets!
468
+
469
+ files = Dir["#{app_path}/public/production_assets/application.js"]
470
+ assert_equal 0, files.length, "Expected application.js asset to be removed, but still exists"
471
+ end
472
+
473
+ test "asset urls should use the request's protocol by default" do
474
+ app_with_assets_in_view
475
+ add_to_config "config.asset_host = 'example.com'"
476
+ require "#{app_path}/config/environment"
477
+ class ::PostsController < ActionController::Base; end
478
+
479
+ get '/posts', {}, {'HTTPS'=>'off'}
480
+ assert_match('src="http://example.com/assets/application.js', last_response.body)
481
+ get '/posts', {}, {'HTTPS'=>'on'}
482
+ assert_match('src="https://example.com/assets/application.js', last_response.body)
483
+ end
484
+
485
+ test "asset urls should be protocol-relative if no request is in scope" do
486
+ app_file "app/assets/javascripts/image_loader.js.erb", 'var src="<%= image_path("rails.png") %>";'
487
+ add_to_config "config.assets.precompile = %w{image_loader.js}"
488
+ add_to_config "config.asset_host = 'example.com'"
489
+ precompile!
490
+
491
+ assert_match 'src="//example.com/assets/rails.png"', File.read("#{app_path}/public/assets/image_loader.js")
492
+ end
493
+
494
+ test "asset paths should use RAILS_RELATIVE_URL_ROOT by default" do
495
+ ENV["RAILS_RELATIVE_URL_ROOT"] = "/sub/uri"
496
+
497
+ app_file "app/assets/javascripts/app.js.erb", 'var src="<%= image_path("rails.png") %>";'
498
+ add_to_config "config.assets.precompile = %w{app.js}"
499
+ precompile!
500
+
501
+ assert_match 'src="/sub/uri/assets/rails.png"', File.read("#{app_path}/public/assets/app.js")
502
+ end
503
+
504
+ test "html assets are compiled when executing precompile" do
505
+ app_file "app/assets/pages/page.html.erb", "<%= javascript_include_tag :application %>"
506
+ ENV["RAILS_ENV"] = "production"
507
+ ENV["RAILS_GROUP"] = "assets"
508
+
509
+ quietly do
510
+ Dir.chdir(app_path){ `bundle exec rake assets:precompile` }
511
+ end
512
+
513
+ assert_file_exists("#{app_path}/public/assets/page.html")
514
+ end
515
+
516
+ test "assets:cache:clean should clean cache" do
517
+ ENV["RAILS_ENV"] = "production"
518
+ precompile!
519
+
520
+ quietly do
521
+ Dir.chdir(app_path){ `bundle exec rake assets:cache:clean` }
522
+ end
523
+
524
+ require "#{app_path}/config/environment"
525
+ assert_equal 0, Dir.entries(Rails.application.assets.cache.cache_path).size - 2 # reject [".", ".."]
526
+ end
527
+
528
+ private
529
+
530
+ def app_with_assets_in_view
531
+ app_file "app/assets/javascripts/application.js", "//= require_tree ."
532
+ app_file "app/assets/javascripts/xmlhr.js", "function f1() { alert(); }"
533
+ app_file "app/views/posts/index.html.erb", "<%= javascript_include_tag 'application' %>"
534
+
535
+ app_file "config/routes.rb", <<-RUBY
536
+ AppTemplate::Application.routes.draw do
537
+ get '/posts', :to => "posts#index"
538
+ end
539
+ RUBY
540
+ end
541
+ end
542
+ end