turbo-sprockets-rails3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Nathan D. Broadbent
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.
data/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # Turbo Sprockets for Rails 3
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!
5
+
6
+ This is a backport of the work I've done for Rails 4.0.0, released as
7
+ 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.)
8
+
9
+ ### Disclaimer
10
+
11
+ 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).
12
+
13
+ ### Dependencies
14
+
15
+ * sprockets `~> 2.1.3`
16
+ * railties `~> 3.2.0`
17
+
18
+ ### Usage
19
+
20
+ Just drop the gem in your `Gemfile`, in the `:assets` group:
21
+
22
+ ```ruby
23
+ group :assets do
24
+ ...
25
+
26
+ gem 'turbo-sprockets-rails3'
27
+ end
28
+ ```
29
+
30
+ Run `bundle`, and you're done!
31
+
32
+ Test it out by running `rake assets:precompile`. When it's finished, your `public/assets/manifest.yml` file should include a `:source_digests` hash for your assets.
33
+
34
+ Go on, run `rake assets:precompile` again, and it should be a whole lot faster than before.
35
+
36
+ Enjoy your lightning fast deploys!
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ ENV["TEST_CORES"] = "1"
4
+
5
+ namespace :test do
6
+ task :isolated do
7
+ Dir["test/assets*_test.rb"].each do |file|
8
+ dash_i = [
9
+ 'test',
10
+ 'lib',
11
+ ]
12
+ ruby "-I#{dash_i.join ':'}", file
13
+ end
14
+ end
15
+ end
16
+
17
+ Rake::TestTask.new("test:regular") do |t|
18
+ t.libs << 'lib'
19
+ t.libs << 'test'
20
+ t.pattern = 'test/**/sprockets*_test.rb'
21
+ t.verbose = true
22
+ end
23
+
24
+ task :test => ['test:isolated', 'test:regular']
25
+ task :default => :test
@@ -0,0 +1,134 @@
1
+ require 'sprockets/asset'
2
+ require 'sprockets/utils'
3
+
4
+ module Sprockets
5
+ # `AssetWithDependencies` is the base class for `ProcessedAsset` and `UnprocessedAsset`.
6
+ class AssetWithDependencies < Asset
7
+
8
+ # :dependency_digest is used internally to check equality
9
+ attr_reader :dependency_digest, :source
10
+
11
+
12
+ # Initialize asset from serialized hash
13
+ def init_with(environment, coder, asset_options = {})
14
+ asset_options[:bundle] = false
15
+
16
+ super(environment, coder)
17
+
18
+ @source = coder['source']
19
+ @dependency_digest = coder['dependency_digest']
20
+
21
+ @required_assets = coder['required_paths'].map { |p|
22
+ p = expand_root_path(p)
23
+
24
+ unless environment.paths.detect { |path| p[path] }
25
+ raise UnserializeError, "#{p} isn't in paths"
26
+ end
27
+
28
+ p == pathname.to_s ? self : environment.find_asset(p, asset_options)
29
+ }
30
+ @dependency_paths = coder['dependency_paths'].map { |h|
31
+ DependencyFile.new(expand_root_path(h['path']), h['mtime'], h['digest'])
32
+ }
33
+ end
34
+
35
+ # Serialize custom attributes.
36
+ def encode_with(coder)
37
+ super
38
+
39
+ coder['source'] = source
40
+ coder['dependency_digest'] = dependency_digest
41
+
42
+ coder['required_paths'] = required_assets.map { |a|
43
+ relativize_root_path(a.pathname).to_s
44
+ }
45
+ coder['dependency_paths'] = dependency_paths.map { |d|
46
+ { 'path' => relativize_root_path(d.pathname).to_s,
47
+ 'mtime' => d.mtime.iso8601,
48
+ 'digest' => d.digest }
49
+ }
50
+ end
51
+
52
+ # Checks if Asset is stale by comparing the actual mtime and
53
+ # digest to the inmemory model.
54
+ def fresh?(environment)
55
+ # Check freshness of all declared dependencies
56
+ @dependency_paths.all? { |dep| dependency_fresh?(environment, dep) }
57
+ end
58
+
59
+ protected
60
+ class DependencyFile < Struct.new(:pathname, :mtime, :digest)
61
+ def initialize(pathname, mtime, digest)
62
+ pathname = Pathname.new(pathname) unless pathname.is_a?(Pathname)
63
+ mtime = Time.parse(mtime) if mtime.is_a?(String)
64
+ super
65
+ end
66
+
67
+ def eql?(other)
68
+ other.is_a?(DependencyFile) &&
69
+ pathname.eql?(other.pathname) &&
70
+ mtime.eql?(other.mtime) &&
71
+ digest.eql?(other.digest)
72
+ end
73
+
74
+ def hash
75
+ pathname.to_s.hash
76
+ end
77
+ end
78
+
79
+ private
80
+ def build_required_assets(environment, context, asset_options = {})
81
+ asset_options[:bundle] = false
82
+ @required_assets = []
83
+ required_assets_cache = {}
84
+
85
+ (context._required_paths + [pathname.to_s]).each do |path|
86
+ if path == self.pathname.to_s
87
+ unless required_assets_cache[self]
88
+ required_assets_cache[self] = true
89
+ @required_assets << self
90
+ end
91
+ elsif asset = environment.find_asset(path, asset_options)
92
+ asset.required_assets.each do |asset_dependency|
93
+ unless required_assets_cache[asset_dependency]
94
+ required_assets_cache[asset_dependency] = true
95
+ @required_assets << asset_dependency
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ required_assets_cache.clear
102
+ required_assets_cache = nil
103
+ end
104
+
105
+ def build_dependency_paths(environment, context, asset_options = {})
106
+ asset_options[:bundle] = false
107
+ dependency_paths = {}
108
+
109
+ context._dependency_paths.each do |path|
110
+ dep = DependencyFile.new(path, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
111
+ dependency_paths[dep] = true
112
+ end
113
+
114
+ context._dependency_assets.each do |path|
115
+ if path == self.pathname.to_s
116
+ dep = DependencyFile.new(pathname, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
117
+ dependency_paths[dep] = true
118
+ elsif asset = environment.find_asset(path, asset_options)
119
+ asset.dependency_paths.each do |d|
120
+ dependency_paths[d] = true
121
+ end
122
+ end
123
+ end
124
+
125
+ @dependency_paths = dependency_paths.keys
126
+ end
127
+
128
+ def compute_dependency_digest(environment)
129
+ required_assets.inject(environment.digest) { |digest, asset|
130
+ digest.update asset.digest
131
+ }.hexdigest
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+
3
+ module Sprockets
4
+ class StaticCleaner
5
+ attr_accessor :env, :target
6
+
7
+ def initialize(env, target, digest_files)
8
+ @env = env
9
+ @target = File.join(target, '') # Make sure target ends with trailing /
10
+ @digest_files = digest_files
11
+ end
12
+
13
+ # Remove all files from `config.assets.prefix` that are not found in manifest.yml
14
+ def remove_old_assets!
15
+ known_files = @digest_files.flatten
16
+ known_files += known_files.map {|f| "#{f}.gz" } # Recognize gzipped files
17
+ known_files << 'manifest.yml'
18
+
19
+ assets_prefix = ::Rails.application.config.assets.prefix
20
+
21
+ Dir[File.join(target, "**/*")].each do |path|
22
+ unless File.directory?(path)
23
+ logical_path = path.sub(target, '')
24
+ unless logical_path.in? known_files
25
+ FileUtils.rm path
26
+ env.logger.debug "Deleted old asset at public#{assets_prefix}/#{logical_path}"
27
+ end
28
+ end
29
+ end
30
+
31
+ # Remove empty directories (reversed to delete top-level empty dirs first)
32
+ Dir[File.join(target, "**/*")].reverse.each do |path|
33
+ if File.exists?(path) && File.directory?(path) && (Dir.entries(path) - %w(. ..)).empty?
34
+ FileUtils.rmdir path
35
+ logical_path = path.sub(target, '')
36
+ env.logger.debug "Deleted empty directory at public#{assets_prefix}/#{logical_path}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,99 @@
1
+ require 'fileutils'
2
+
3
+ module Sprockets
4
+ class StaticNonDigestGenerator
5
+
6
+ DIGEST_REGEX = /-([0-9a-f]{32})/
7
+
8
+ attr_accessor :env, :target, :paths
9
+
10
+ def initialize(env, target, paths, options = {})
11
+ @env = env
12
+ @target = target
13
+ @paths = paths
14
+ @digest_files = options.fetch(:digest_files, {})
15
+
16
+ # Parse digests from digest_files hash
17
+ @asset_digests = Hash[*@digest_files.map {|file, digest_file|
18
+ [file, digest_file[DIGEST_REGEX, 1]]
19
+ }.flatten]
20
+ end
21
+
22
+
23
+ # Generate non-digest assets by making a copy of the digest asset,
24
+ # with digests stripped from js and css. The new files are also gzipped.
25
+ # Other assets are copied verbatim.
26
+ def generate
27
+ start_time = Time.now.to_f
28
+
29
+ env.each_logical_path do |logical_path|
30
+ next unless compile_path?(logical_path)
31
+
32
+ digest_path = @digest_files[logical_path]
33
+ abs_digest_path = "#{@target}/#{digest_path}"
34
+ abs_logical_path = "#{@target}/#{logical_path}"
35
+
36
+ mtime = File.mtime(abs_digest_path)
37
+
38
+ # Remove known digests from css & js
39
+ if abs_digest_path.match(/\.(?:js|css)$/)
40
+ asset_body = File.read(abs_digest_path)
41
+
42
+ # Find all hashes in the asset body with a leading '-'
43
+ asset_body.gsub!(DIGEST_REGEX) do |match|
44
+ # Only remove if known digest
45
+ $1.in?(@asset_digests.values) ? '' : match
46
+ end
47
+
48
+ # Write non-digest file
49
+ File.open abs_logical_path, 'w' do |f|
50
+ f.write asset_body
51
+ end
52
+ # Set modification and access times
53
+ File.utime(File.atime(abs_digest_path), mtime, abs_logical_path)
54
+
55
+ # Also write gzipped asset
56
+ File.open("#{abs_logical_path}.gz", 'wb') do |f|
57
+ gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
58
+ gz.mtime = mtime.to_i
59
+ gz.write asset_body
60
+ gz.close
61
+ end
62
+
63
+ env.logger.debug "Stripped digests, copied to #{logical_path}, and created gzipped asset"
64
+
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"
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
80
+ env.logger.debug "Generated non-digest assets in #{elapsed_time}ms"
81
+ end
82
+
83
+ private
84
+
85
+ def compile_path?(logical_path)
86
+ paths.each do |path|
87
+ case path
88
+ when Regexp
89
+ return true if path.match(logical_path)
90
+ when Proc
91
+ return true if path.call(logical_path)
92
+ else
93
+ return true if File.fnmatch(path.to_s, logical_path)
94
+ end
95
+ end
96
+ false
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,28 @@
1
+ require 'sprockets/asset_with_dependencies'
2
+
3
+ module Sprockets
4
+ class UnprocessedAsset < AssetWithDependencies
5
+ def initialize(environment, logical_path, pathname)
6
+ super
7
+
8
+ context = environment.context_class.new(environment, logical_path, pathname)
9
+ attributes = environment.attributes_for(pathname)
10
+ processors = attributes.processors
11
+
12
+ # Remove all engine processors except ERB to return unprocessed source file
13
+ processors -= (attributes.engines - [Tilt::ERBTemplate])
14
+
15
+ @source = context.evaluate(pathname, :processors => processors)
16
+
17
+ build_required_assets(environment, context, :process => false)
18
+ build_dependency_paths(environment, context, :process => false)
19
+
20
+ @dependency_digest = compute_dependency_digest(environment)
21
+ end
22
+ end
23
+
24
+ # Return unprocessed dependencies when initializing asset from serialized hash
25
+ def init_with(environment, coder)
26
+ super(environment, coder, :process => false)
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require "action_controller/railtie"
2
+
3
+ module Sprockets
4
+ autoload :StaticCleaner, "sprockets/static_cleaner"
5
+ autoload :StaticNonDigestGenerator, "sprockets/static_non_digest_generator"
6
+ end
7
+
8
+ module TurboSprockets
9
+ class Railtie < ::Rails::Railtie
10
+ rake_tasks do
11
+ load "turbo-sprockets/tasks/assets.rake"
12
+ end
13
+
14
+ initializer "turbo-sprockets.environment", :after => "sprockets.environment", :group => :all do |app|
15
+ config = app.config
16
+
17
+ if config.assets.manifest
18
+ manifest_path = File.join(config.assets.manifest, "manifest.yml")
19
+ else
20
+ manifest_path = File.join(Rails.public_path, config.assets.prefix, "manifest.yml")
21
+ end
22
+
23
+ if File.exist?(manifest_path)
24
+ manifest = YAML.load_file(manifest_path)
25
+ config.assets.digest_files = manifest[:digest_files] || {}
26
+ config.assets.source_digests = manifest[:source_digests] || {}
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'sprockets/asset'
2
+
3
+ Sprockets::Asset.class_eval do
4
+ # Internal initializer to load `Asset` from serialized `Hash`.
5
+ def self.from_hash(environment, hash)
6
+ return unless hash.is_a?(Hash)
7
+
8
+ klass = case hash['class']
9
+ when 'BundledAsset'
10
+ BundledAsset
11
+ when 'ProcessedAsset'
12
+ ProcessedAsset
13
+ when 'UnprocessedAsset'
14
+ UnprocessedAsset
15
+ when 'StaticAsset'
16
+ StaticAsset
17
+ else
18
+ nil
19
+ end
20
+
21
+ if klass
22
+ asset = klass.allocate
23
+ asset.init_with(environment, hash)
24
+ asset
25
+ end
26
+ rescue UnserializeError
27
+ nil
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ require 'sprockets/base'
2
+ require 'sprockets/unprocessed_asset'
3
+
4
+ module Sprockets
5
+ Base.class_eval do
6
+ protected
7
+
8
+ def build_asset(logical_path, pathname, options)
9
+ pathname = Pathname.new(pathname)
10
+
11
+ # If there are any processors to run on the pathname, use
12
+ # `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
13
+ if attributes_for(pathname).processors.any?
14
+ if options[:bundle] == false
15
+ circular_call_protection(pathname.to_s) do
16
+ if options[:process] == false
17
+ UnprocessedAsset.new(index, logical_path, pathname)
18
+ else
19
+ ProcessedAsset.new(index, logical_path, pathname)
20
+ end
21
+ end
22
+ else
23
+ BundledAsset.new(index, logical_path, pathname, options)
24
+ end
25
+ else
26
+ StaticAsset.new(index, logical_path, pathname)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def cache_key_for(path, options)
33
+ options[:process] = true unless options.key?(:process)
34
+ "#{path}:#{options[:bundle] ? '1' : '0'}:#{options[:process] ? '1' : '0'}"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ require 'sprockets/bundled_asset'
2
+
3
+ Sprockets::BundledAsset.class_eval do
4
+
5
+ # Adds :process options
6
+
7
+ def initialize(environment, logical_path, pathname, options = {})
8
+ super(environment, logical_path, pathname)
9
+ @process = options.fetch(:process, true)
10
+
11
+ @processed_asset = environment.find_asset(pathname, :bundle => false, :process => @process)
12
+ @required_assets = @processed_asset.required_assets
13
+ @dependency_paths = @processed_asset.dependency_paths
14
+
15
+ @source = ""
16
+
17
+ # Explode Asset into parts and gather the dependency bodies
18
+ to_a.each { |dependency| @source << dependency.to_s }
19
+
20
+ if @process
21
+ # Run bundle processors on concatenated source
22
+ context = environment.context_class.new(environment, logical_path, pathname)
23
+ @source = context.evaluate(pathname, :data => @source,
24
+ :processors => environment.bundle_processors(content_type))
25
+ end
26
+
27
+ @mtime = to_a.map(&:mtime).max
28
+ @length = Rack::Utils.bytesize(source)
29
+ @digest = environment.digest.update(source).hexdigest
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'sprockets/environment'
2
+
3
+ Sprockets::Environment.class_eval do
4
+
5
+ # Adds :process options
6
+
7
+ def find_asset(path, options = {})
8
+ options[:bundle] = true unless options.key?(:bundle)
9
+ options[:process] = true unless options.key?(:process)
10
+
11
+ # Ensure in-memory cached assets are still fresh on every lookup
12
+ if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
13
+ asset
14
+ elsif asset = index.find_asset(path, options)
15
+ # Cache is pushed upstream by Index#find_asset
16
+ asset
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,42 @@
1
+ require 'sprockets/helpers/rails_helper'
2
+
3
+ module Sprockets
4
+ module Helpers
5
+ RailsHelper.module_eval do
6
+ def asset_paths
7
+ @asset_paths ||= begin
8
+ paths = RailsHelper::AssetPaths.new(config, controller)
9
+ paths.asset_environment = asset_environment
10
+ paths.digest_files = digest_files
11
+ paths.compile_assets = compile_assets?
12
+ paths.digest_assets = digest_assets?
13
+ paths
14
+ end
15
+ end
16
+
17
+ private
18
+ def digest_files
19
+ Rails.application.config.assets.digest_files
20
+ end
21
+ end
22
+
23
+ RailsHelper::AssetPaths.class_eval do
24
+ attr_accessor :digest_files
25
+
26
+ def digest_for(logical_path)
27
+ if digest_assets && digest_files && (digest = digest_files[logical_path])
28
+ return digest
29
+ end
30
+
31
+ if compile_assets
32
+ if digest_assets && asset = asset_environment[logical_path]
33
+ return asset.digest_path
34
+ end
35
+ return logical_path
36
+ else
37
+ raise AssetNotPrecompiledError.new("#{logical_path} isn't precompiled")
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,28 @@
1
+ require 'sprockets/index'
2
+
3
+ Sprockets::Index.class_eval do
4
+
5
+ # Adds :process options
6
+
7
+ def find_asset(path, options = {})
8
+ options[:bundle] = true unless options.key?(:bundle)
9
+ options[:process] = true unless options.key?(:process)
10
+
11
+ if asset = @assets[cache_key_for(path, options)]
12
+ asset
13
+ elsif asset = super
14
+ logical_path_cache_key = cache_key_for(path, options)
15
+ full_path_cache_key = cache_key_for(asset.pathname, options)
16
+
17
+ # Cache on Index
18
+ @assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
19
+
20
+ # Push cache upstream to Environment
21
+ @environment.instance_eval do
22
+ @assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
23
+ end
24
+
25
+ asset
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'sprockets/processed_asset'
2
+ require 'sprockets/asset_with_dependencies'
3
+
4
+ module Sprockets
5
+
6
+ # Remove and redefine ProcessedAsset to inherit from AssetWithDependencies
7
+
8
+ remove_const :ProcessedAsset
9
+
10
+ class ProcessedAsset < AssetWithDependencies
11
+ def initialize(environment, logical_path, pathname)
12
+ super
13
+
14
+ start_time = Time.now.to_f
15
+
16
+ context = environment.context_class.new(environment, logical_path, pathname)
17
+ @source = context.evaluate(pathname)
18
+ @length = Rack::Utils.bytesize(source)
19
+ @digest = environment.digest.update(source).hexdigest
20
+
21
+ build_required_assets(environment, context)
22
+ build_dependency_paths(environment, context)
23
+
24
+ @dependency_digest = compute_dependency_digest(environment)
25
+
26
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
27
+ environment.logger.info "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,68 @@
1
+ require 'sprockets/static_compiler'
2
+
3
+ Sprockets::StaticCompiler.class_eval do
4
+ def initialize(env, target, paths, options = {})
5
+ @env = env
6
+ @target = target
7
+ @paths = paths
8
+ @digest = options.fetch(:digest, true)
9
+ @manifest = options.fetch(:manifest, true)
10
+ @manifest_path = options.delete(:manifest_path) || target
11
+ @zip_files = options.delete(:zip_files) || /\.(?:css|html|js|svg|txt|xml)$/
12
+
13
+ @current_source_digests = options.fetch(:source_digests, {})
14
+ @current_digest_files = options.fetch(:digest_files, {})
15
+
16
+ @digest_files = {}
17
+ @source_digests = {}
18
+ end
19
+
20
+ def compile
21
+ start_time = Time.now.to_f
22
+
23
+ env.each_logical_path do |logical_path|
24
+ if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
25
+ logical_path.sub!(/\/index\./, '.')
26
+ end
27
+ next unless compile_path?(logical_path)
28
+
29
+ # Fetch asset without any processing or compression,
30
+ # to calculate a digest of the concatenated source files
31
+ asset = env.find_asset(logical_path, :process => false)
32
+
33
+ # Force digest to UTF-8, otherwise YAML dumps ASCII-8BIT as !binary
34
+ @source_digests[logical_path] = asset.digest.force_encoding("UTF-8")
35
+
36
+ # Recompile if digest has changed or compiled digest file is missing
37
+ current_digest_file = @current_digest_files[logical_path]
38
+
39
+ if @source_digests[logical_path] != @current_source_digests[logical_path] ||
40
+ !(current_digest_file && File.exists?("#{@target}/#{current_digest_file}"))
41
+
42
+ if asset = env.find_asset(logical_path)
43
+ @digest_files[logical_path] = write_asset(asset)
44
+ end
45
+
46
+ else
47
+ # Set asset file from manifest.yml
48
+ digest_file = @current_digest_files[logical_path]
49
+ @digest_files[logical_path] = digest_file
50
+
51
+ env.logger.debug "Not compiling #{logical_path}, sources digest has not changed " <<
52
+ "(#{@source_digests[logical_path][0...7]})"
53
+ end
54
+ end
55
+
56
+ if @manifest
57
+ write_manifest(source_digests: @source_digests, digest_files: @digest_files)
58
+ end
59
+
60
+ # Store digests in Rails config. (Important if non-digest is run after primary)
61
+ config = ::Rails.application.config
62
+ config.assets.digest_files = @digest_files
63
+ config.assets.source_digests = @source_digests
64
+
65
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
66
+ env.logger.debug "Processed #{'non-' unless @digest}digest assets in #{elapsed_time}ms"
67
+ end
68
+ end
@@ -0,0 +1,128 @@
1
+ require "fileutils"
2
+
3
+ # Clear all assets tasks from sprockets railtie
4
+ Rake::Task.tasks.each do |task|
5
+ if task.name.starts_with? 'assets:'
6
+ task.clear_prerequisites.clear_actions
7
+ end
8
+ end
9
+
10
+ # Replace with our extended assets tasks
11
+ namespace :assets do
12
+ def ruby_rake_task(task, fork = true)
13
+ env = ENV['RAILS_ENV'] || 'production'
14
+ groups = ENV['RAILS_GROUPS'] || 'assets'
15
+ args = [$0, task,"RAILS_ENV=#{env}","RAILS_GROUPS=#{groups}"]
16
+ args << "--trace" if Rake.application.options.trace
17
+ if $0 =~ /rake\.bat\Z/i
18
+ Kernel.exec $0, *args
19
+ else
20
+ fork ? ruby(*args) : Kernel.exec(FileUtils::RUBY, *args)
21
+ end
22
+ end
23
+
24
+ # We are currently running with no explicit bundler group
25
+ # and/or no explicit environment - we have to reinvoke rake to
26
+ # execute this task.
27
+ def invoke_or_reboot_rake_task(task)
28
+ if ENV['RAILS_GROUPS'].to_s.empty? || ENV['RAILS_ENV'].to_s.empty?
29
+ ruby_rake_task task
30
+ else
31
+ Rake::Task[task].invoke
32
+ end
33
+ end
34
+
35
+ desc "Compile all the assets named in config.assets.precompile"
36
+ task :precompile do
37
+ invoke_or_reboot_rake_task "assets:precompile:all"
38
+ end
39
+
40
+ namespace :precompile do
41
+ def internal_precompile(digest=nil)
42
+ unless Rails.application.config.assets.enabled
43
+ warn "Cannot precompile assets if sprockets is disabled. Please set config.assets.enabled to true"
44
+ exit
45
+ end
46
+
47
+ # Ensure that action view is loaded and the appropriate
48
+ # sprockets hooks get executed
49
+ _ = ActionView::Base
50
+
51
+ config = Rails.application.config
52
+ config.assets.compile = true
53
+ config.assets.digest = digest unless digest.nil?
54
+ config.assets.digest_files ||= {}
55
+ config.assets.source_digests ||= {}
56
+
57
+ env = Rails.application.assets
58
+ target = File.join(::Rails.public_path, config.assets.prefix)
59
+
60
+ # If processing non-digest assets, and compiled digest files are
61
+ # present, then generate non-digest assets from existing assets.
62
+ # It is assumed that `assets:precompile:nondigest` won't be run manually
63
+ # if assets have been previously compiled with digests.
64
+ if !config.assets.digest && config.assets.digest_files.any?
65
+ generator = Sprockets::StaticNonDigestGenerator.new(env, target, config.assets.precompile,
66
+ :digest_files => config.assets.digest_files,
67
+ :source_digests => config.assets.source_digests
68
+ )
69
+ generator.generate
70
+ else
71
+ compiler = Sprockets::StaticCompiler.new(env, target, config.assets.precompile,
72
+ :digest => config.assets.digest,
73
+ :manifest => digest.nil?,
74
+ :manifest_path => config.assets.manifest,
75
+ :digest_files => config.assets.digest_files,
76
+ :source_digests => config.assets.source_digests
77
+ )
78
+ compiler.compile
79
+ end
80
+
81
+ unless config.assets.clean_after_precompile == false
82
+ cleaner = Sprockets::StaticCleaner.new(env, target, config.assets.digest_files)
83
+ cleaner.remove_old_assets!
84
+ end
85
+ end
86
+
87
+ task :all => ["assets:cache:clean"] do
88
+ internal_precompile
89
+ internal_precompile(false) if ::Rails.application.config.assets.digest
90
+ end
91
+
92
+ task :primary => ["assets:cache:clean"] do
93
+ internal_precompile
94
+ end
95
+
96
+ task :nondigest => ["assets:cache:clean"] do
97
+ internal_precompile(false)
98
+ end
99
+ end
100
+
101
+ desc "Remove compiled assets"
102
+ task :clean do
103
+ invoke_or_reboot_rake_task "assets:clean:all"
104
+ end
105
+
106
+ namespace :clean do
107
+ task :all => ["assets:cache:clean"] do
108
+ config = ::Rails.application.config
109
+ public_asset_path = File.join(::Rails.public_path, config.assets.prefix)
110
+ rm_rf public_asset_path, :secure => true
111
+ end
112
+ end
113
+
114
+ namespace :cache do
115
+ task :clean => ['tmp:create', "assets:environment"] do
116
+ ::Rails.application.assets.cache.clear
117
+ end
118
+ end
119
+
120
+ task :environment do
121
+ if ::Rails.application.config.assets.initialize_on_precompile
122
+ Rake::Task["environment"].invoke
123
+ else
124
+ ::Rails.application.initialize!(:assets)
125
+ Sprockets::Bootstrap.new(Rails.application).run
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,3 @@
1
+ module TurboSprockets
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,5 @@
1
+ Dir[File.expand_path('../turbo-sprockets/sprockets_overrides/**/*.rb', __FILE__)].each do |f|
2
+ require f
3
+ end
4
+
5
+ require 'turbo-sprockets/railtie'
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turbo-sprockets-rails3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nathan Broadbent
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sprockets
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.1.3
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.1.3
30
+ - !ruby/object:Gem::Dependency
31
+ name: railties
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.2.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.2.0
46
+ description: Speeds up the Rails 3 asset pipeline by only recompiling changed assets
47
+ email:
48
+ - nathan.f77@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - lib/turbo-sprockets-rails3.rb
54
+ - lib/turbo-sprockets/version.rb
55
+ - lib/turbo-sprockets/tasks/assets.rake
56
+ - lib/turbo-sprockets/railtie.rb
57
+ - lib/turbo-sprockets/sprockets_overrides/helpers/rails_helper.rb
58
+ - lib/turbo-sprockets/sprockets_overrides/static_compiler.rb
59
+ - lib/turbo-sprockets/sprockets_overrides/base.rb
60
+ - lib/turbo-sprockets/sprockets_overrides/bundled_asset.rb
61
+ - lib/turbo-sprockets/sprockets_overrides/index.rb
62
+ - lib/turbo-sprockets/sprockets_overrides/asset.rb
63
+ - lib/turbo-sprockets/sprockets_overrides/environment.rb
64
+ - lib/turbo-sprockets/sprockets_overrides/processed_asset.rb
65
+ - lib/sprockets/static_non_digest_generator.rb
66
+ - lib/sprockets/asset_with_dependencies.rb
67
+ - lib/sprockets/static_cleaner.rb
68
+ - lib/sprockets/unprocessed_asset.rb
69
+ - MIT-LICENSE
70
+ - Rakefile
71
+ - README.md
72
+ homepage: https://github.com/ndbroadbent/turbo-sprockets-rails3
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 1.8.24
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Supercharge your Rails 3 asset pipeline
96
+ test_files: []