turbo-sprockets-rails4 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -10
  3. data/lib/turbo-sprockets-rails4.rb +52 -3
  4. data/lib/turbo-sprockets/asset_resolver.rb +82 -0
  5. data/lib/turbo-sprockets/parallel_compiler.rb +2 -4
  6. data/lib/turbo-sprockets/parallel_preloader.rb +89 -0
  7. data/lib/turbo-sprockets/railtie.rb +24 -2
  8. data/lib/turbo-sprockets/version.rb +1 -1
  9. data/spec/asset_resolver_spec.rb +154 -0
  10. data/spec/log/test.log +88 -0
  11. data/spec/parallel_compiler_spec.rb +22 -5
  12. data/spec/parallel_preloader_spec.rb +31 -0
  13. data/spec/spec_helper.rb +13 -4
  14. data/spec/tmp/cache/assets/sprockets/v3.0/Le/LeawzTm7RwKtrjUFVCR7o9JVRLjcbCfbykBpzdSZxtM.cache +1 -0
  15. data/spec/tmp/cache/assets/sprockets/v3.0/Ok/OkAys_2Q1L3vb5MfExaVwWECnkhnpaauOKQ4K5h2EQM.cache +1 -0
  16. data/spec/tmp/cache/assets/sprockets/v3.0/{rs/rsmYKfF63AJstBxp9BfJQ_Alz93hJXeRW6AlZasgAg8.cache → P5/P5S3gl7IvP4a1i2vH3aGqb7MG5quRXURszmF4Jo-exM.cache} +0 -0
  17. data/spec/tmp/cache/assets/sprockets/v3.0/{F9/F9uLDkuCpwXDtwzIob92yhwmsuBy0NUF2GnJGRiyWF4.cache → Tj/TjacfODSnBaChvUCg9VRCYGvF9N9D1BFEYa_LOY0Myo.cache} +0 -0
  18. data/spec/tmp/cache/assets/sprockets/v3.0/{-m/-mYOrHQQw4eQz_Ht_LzLRCkKUaiPsHUZnmBchCOMAQU.cache → YF/YFli9jLfluQ3HEM4JQkUGPnhKztRu1QjsR-krCrHnDQ.cache} +0 -0
  19. data/spec/tmp/cache/assets/sprockets/v3.0/_T/_T7w3tcNAR1OjzPNFDxGLBsotNkwlubZL18upfeyLOk.cache +1 -0
  20. data/spec/tmp/cache/assets/sprockets/v3.0/{FA/FA7o1GffPYj_2A2EsHqBM8vMKfdSdGYJzMYpjhkXAxE.cache → aH/aHkgzJaZpAnMTLP7DvYxN89R8XHqtxL4DrC2d90U3Z8.cache} +0 -0
  21. data/spec/tmp/cache/assets/sprockets/v3.0/zI/zIeYCT_q2r-xVxHddTUvci_yk-5Aa8M6wKAaxpqoNt0.cache +1 -0
  22. data/turbo-sprockets-rails4.gemspec +1 -1
  23. metadata +15 -23
  24. data/spec/public/assets/file1-9461265d541718e5776632b202c4ccdd21f13d9cd7d0b4d92c1b43131292749a.js +0 -4
  25. data/spec/public/assets/file1-9461265d541718e5776632b202c4ccdd21f13d9cd7d0b4d92c1b43131292749a.js.gz +0 -0
  26. data/spec/public/assets/file2-e2a24754a5ca8cb2f9c645a80ad4d4ca375c4d9914f1d962b4fb3e779dc4ad34.js +0 -4
  27. data/spec/public/assets/file2-e2a24754a5ca8cb2f9c645a80ad4d4ca375c4d9914f1d962b4fb3e779dc4ad34.js.gz +0 -0
  28. data/spec/tmp/cache/assets/sprockets/v3.0/0g/0geAvurz1AX0xI67Ou6WBG0HEnHLFZG5Rbvog7JuFh8.cache +0 -1
  29. data/spec/tmp/cache/assets/sprockets/v3.0/Ny/Ny4Hng5x4A-TVYWIKr0AjzqV9U32nMfeROjIYX3t1zc.cache +0 -1
  30. data/spec/tmp/cache/assets/sprockets/v3.0/Py/PyP_Z86btB-ByMXeasgN-buL98MfdATp8WPzc8AWy6c.cache +0 -1
  31. data/spec/tmp/cache/assets/sprockets/v3.0/Yi/Yi_k91msAv7kbymP50BwVfhkKwo_XMEZIJmMf6LgTAA.cache +0 -1
  32. data/spec/tmp/cache/assets/sprockets/v3.0/b8/b8XCYwFHJAQi_UQnYIe_rPgE6W5gMlmY69smkT7lUUg.cache +0 -1
  33. data/spec/tmp/cache/assets/sprockets/v3.0/sk/skLSgxSN-gAyttjzKsGZfvS6147mrFxxK3ztXCqNNP8.cache +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1854ff556095ce844de7a1c0a0220bc604343cff
4
- data.tar.gz: 0f526c593fc25dee2804ec227fa4da3924262015
3
+ metadata.gz: 93dc4b63a52cdc1eac49f1cb2465a380d547ae89
4
+ data.tar.gz: 35b1991e4acb678231774f80f13ef1cdf707c4bc
5
5
  SHA512:
6
- metadata.gz: 9d634af0655938051494b90372b40a497248c443c548f232cab6c813603b0cad5e3f3c16edd810a4fa32dd17df168e90ce0292e070953c79f46f55a00ee19591
7
- data.tar.gz: 94c3356a12c24d286825a77f8d2ff2bc985cac3552019a5040f84875648ae50ea7e77e990c6cd847400be3b60f21be6f5da22f113bb7a73d56fce7d24a9f6771
6
+ metadata.gz: 06571631ba75f30ee811bca5c54fdb3de1fb8d2e2aa69e7910b44db0ef7e5c427bedcbf52a658cf42dd402af406fa331d88e153098d50af2758d221fb20b9ff8
7
+ data.tar.gz: 455c9c46852d8e3acc202e8c1891ae36694ce2a5592b1ab4d6b1c701ec4d373342ceaf52f921035f8c0489666794d1a8bd83c3c183e0d3ac2d890e831f6f6fa5
data/Gemfile CHANGED
@@ -1,10 +1,2 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
4
-
5
- group :development, :test do
6
- gem 'pry-byebug'
7
- gem 'rails'
8
- gem 'rake'
9
- gem 'rspec', '~> 3.0'
10
- end
1
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../Gemfile-rails-4.2.x', __FILE__)
2
+ Bundler.load
@@ -1,9 +1,58 @@
1
1
  require 'turbo-sprockets/railtie'
2
2
 
3
3
  module TurboSprockets
4
- autoload :ParallelCompiler, 'turbo-sprockets/parallel_compiler'
4
+ autoload :AssetResolver, 'turbo-sprockets/asset_resolver'
5
+ autoload :ParallelCompiler, 'turbo-sprockets/parallel_compiler'
6
+ autoload :ParallelPreloader, 'turbo-sprockets/parallel_preloader'
5
7
 
6
- def self.logger
7
- Rails.application.assets_manifest.send(:logger)
8
+ class Config
9
+ attr_reader :precompiler, :preloader
10
+
11
+ def initialize(options = {})
12
+ @precompiler = ComponentConfig.new(options.fetch(:precompiler, {}))
13
+ @preloader = ComponentConfig.new(options.fetch(:preloader, {}))
14
+ end
15
+ end
16
+
17
+ class ComponentConfig
18
+ DEFAULT_WORKER_COUNT = 2
19
+
20
+ attr_accessor :enabled, :worker_count, :logger
21
+ alias_method :enabled?, :enabled
22
+
23
+ def initialize(options = {})
24
+ options.each_pair do |key, value|
25
+ send("#{key}=", value)
26
+ end
27
+ end
28
+
29
+ def worker_count
30
+ worker_count_from_env || @worker_count || DEFAULT_WORKER_COUNT
31
+ end
32
+
33
+ def logger
34
+ @logger || Rails.logger
35
+ end
36
+
37
+ private
38
+
39
+ def worker_count_from_env
40
+ if count = ENV['TURBO_SPROCKETS_WORKER_COUNT']
41
+ count.to_i
42
+ end
43
+ end
44
+ end
45
+
46
+ class << self
47
+ def configure
48
+ yield configuration
49
+ end
50
+
51
+ def configuration
52
+ @configuration ||= Config.new(
53
+ precompiler: { enabled: true },
54
+ preloader: { enabled: true }
55
+ )
56
+ end
8
57
  end
9
58
  end
@@ -0,0 +1,82 @@
1
+ module TurboSprockets
2
+ class AssetResolver
3
+ class << self
4
+ # Returns a relative, digested asset path for the given logical path.
5
+ #
6
+ # For example:
7
+ # AssetResolver.resolve('lux/checkmark_2x.png')
8
+ # => lux/checkmark_2x-7177b0151ec35ffb6d.png
9
+ #
10
+ def resolve(logical_path)
11
+ # If compile is true we're running in development and have access to the
12
+ # sprockets environment. If compile is false the sprockets environment
13
+ # will not be available, so we have to fall back to the asset manifest.
14
+ if Rails.application.config.assets.compile
15
+ asset = Rails.application.assets.find_asset(logical_path)
16
+
17
+ if Rails.application.config.assets.digest
18
+ asset.try(&:digest_path)
19
+ else
20
+ asset.try(&:logical_path)
21
+ end
22
+ else
23
+ Rails.application.assets_manifest.assets[logical_path]
24
+ end
25
+ end
26
+
27
+ # Returns an absolute, undigested asset path for the given logical path.
28
+ # This is the original asset path from app/assets or similar.
29
+ #
30
+ # For example:
31
+ # AssetResolver.resolve_naked('lux/checkmark_2x.png')
32
+ # => /data/lumoslabs/shared/bundle/ruby/2.4.0/gems/lux-2.11.0/app/assets/images/lux/checkmark_2x.png
33
+ #
34
+ def resolve_naked(logical_path)
35
+ # check to see if logical_path exists within any of the current asset paths
36
+ Rails.application.config.assets.paths.each do |path|
37
+ candidate = File.join(path, logical_path)
38
+ return candidate if File.exist?(candidate)
39
+ end
40
+ end
41
+
42
+ # Returns the absolute, digested URL for the given logical path. This
43
+ # includes important stuff like the CDN host/port and asset prefix
44
+ # (i.e. /compiled).
45
+ #
46
+ # For example:
47
+ # AssetResolver.resolve_url_for('lux/checkmark_2x.png')
48
+ # => https://asset.lumosity.com:443/compiled/lux/checkmark_2x-7177b0151ec35ffb6d.png
49
+ #
50
+ def resolve_url_for(logical_path)
51
+ url_join(
52
+ Rails.application.config.action_controller.asset_host,
53
+ Rails.application.config.assets.prefix,
54
+ resolve(logical_path)
55
+ )
56
+ end
57
+
58
+ # Operates like File.join, but on segments of a URL. Specifically, it makes
59
+ # sure the segments are conjoined by only one forward slash. The URI class
60
+ # is also capable of doing this, but is much more difficult to use. Note
61
+ # that this method doesn't consider whether or not the segments are relative
62
+ # or absolute - it simply joins them.
63
+ #
64
+ # Examples:
65
+ # UrlMethods.join('/foo/', '/bar', '/baz/')
66
+ # => 'foo/bar/baz'
67
+ #
68
+ # UrlMethods.join('http://foo.com', 'bar/', '/baz')
69
+ # # => 'http://foo.com/bar/baz'
70
+ #
71
+ def url_join(*segments)
72
+ segments.compact!
73
+
74
+ # this regex strips off leading and trailing forward slashes
75
+ joined = segments.map { |p| p.sub(/\A\/?(.*?)\/?\z/, "\\1") }.join('/')
76
+
77
+ # handle absolute URLs
78
+ segments.first.start_with?('/') ? "/#{joined}" : joined
79
+ end
80
+ end
81
+ end
82
+ end
@@ -4,8 +4,6 @@ require 'json'
4
4
 
5
5
  module TurboSprockets
6
6
  class ParallelCompiler
7
- DEFAULT_WORKER_COUNT = 2
8
-
9
7
  attr_reader :manifest
10
8
 
11
9
  def initialize(manifest)
@@ -86,7 +84,7 @@ module TurboSprockets
86
84
  end
87
85
 
88
86
  def worker_count
89
- @worker_count ||= ENV.fetch('SPROCKETS_WORKER_COUNT', DEFAULT_WORKER_COUNT).to_i
87
+ TurboSprockets.configuration.precompiler.worker_count
90
88
  end
91
89
 
92
90
  def environment
@@ -94,7 +92,7 @@ module TurboSprockets
94
92
  end
95
93
 
96
94
  def logger
97
- ::TurboSprockets.logger
95
+ TurboSprockets.configuration.precompiler.logger
98
96
  end
99
97
  end
100
98
  end
@@ -0,0 +1,89 @@
1
+ require 'parallel'
2
+
3
+ # The asset preloader is designed to precompute and cache all precompilable
4
+ # assets in parallel to avoid doing it in serial on the first request. As of
5
+ # Sprockets 3, all assets on the precompile list (i.e. config.assets.precompile)
6
+ # are compiled on the first request whether the current page has asked for them
7
+ # or not. Obviously such behavior can mean a very slow initial request (we were
8
+ # seeing load times on the order of 10-11 minutes). By preloading, or warming the
9
+ # sprockets cache, initial page load times can be reduced to ~15 seconds (with
10
+ # an additional ~2 minutes spent during boot). Preloading only happens once, so
11
+ # subsequent requests should be fast. Preloading is different from precompiling,
12
+ # as the latter does not appear to cache the assets once they've been compiled.
13
+ # Generally speaking, preloading should be done in development and precompiling
14
+ # should be done in production.
15
+ module TurboSprockets
16
+ class ParallelPreloader
17
+ class << self
18
+ def preload!
19
+ list = precompile_list
20
+
21
+ count = 0
22
+ count_mutex = Mutex.new
23
+
24
+ options = {
25
+ in_processes: worker_count,
26
+ finish: -> (*) {
27
+ count_mutex.synchronize { count += 1 }
28
+
29
+ if count % 10 == 0 || count == list.size
30
+ logger.info("#{count}/#{list.size} preloading assets... ")
31
+ end
32
+ }
33
+ }
34
+
35
+ Parallel.each(list, options) do |path|
36
+ # compat: false causes #resolve to return fully resolved asset URIs,
37
+ # complete with correct MIME type, etc
38
+ uri, _ = assets.resolve(path, compat: false)
39
+ next unless uri
40
+
41
+ # only load the asset if it hasn't been cached yet
42
+ unloaded = Sprockets::UnloadedAsset.new(uri, assets)
43
+ key = unloaded.dependency_history_key
44
+ assets.load(uri) unless assets.cache.get(key)
45
+
46
+ nil
47
+ end
48
+
49
+ logger.info('done')
50
+ end
51
+
52
+ private
53
+
54
+ # find all assets that have been added to the precompile list
55
+ # code adapted from Sprockets::Manifest and Sprockets::Legacy
56
+ def precompile_list
57
+ paths, filters = config.assets.precompile.partition do |arg|
58
+ Sprockets::Manifest.simple_logical_path?(arg)
59
+ end
60
+
61
+ filters = filters.map do |arg|
62
+ Sprockets::Manifest.compile_match_filter(arg)
63
+ end
64
+
65
+ paths + assets.logical_paths.each_with_object([]) do |(logical_path, filename), ret|
66
+ if filters.any? { |f| f.call(logical_path, filename) }
67
+ ret << logical_path
68
+ end
69
+ end
70
+ end
71
+
72
+ def worker_count
73
+ TurboSprockets.configuration.preloader.worker_count
74
+ end
75
+
76
+ def config
77
+ Rails.application.config
78
+ end
79
+
80
+ def assets
81
+ Rails.application.assets
82
+ end
83
+
84
+ def logger
85
+ TurboSprockets.configuration.preloader.logger
86
+ end
87
+ end
88
+ end
89
+ end
@@ -4,10 +4,32 @@ module TurboSprockets
4
4
  unless Sprockets::Manifest.method_defined?(:compile_with_parallelism)
5
5
  Sprockets::Manifest.class_eval do
6
6
  def compile_with_parallelism(*args)
7
- ::TurboSprockets::ParallelCompiler.new(self).compile(*args)
7
+ if TurboSprockets.configuration.precompiler.enabled?
8
+ TurboSprockets::ParallelCompiler.new(self).compile(*args)
9
+ else
10
+ compile_without_parallelism
11
+ end
8
12
  end
9
13
 
10
- alias_method_chain :compile, :parallelism
14
+ alias_method :compile_without_parallelism, :compile
15
+ alias_method :compile, :compile_with_parallelism
16
+ end
17
+ end
18
+ end
19
+
20
+ config.after_initialize do
21
+ if ::TurboSprockets.configuration.preloader.enabled?
22
+ # make sure routes are available before attempting to preload, since
23
+ # assets may make use of route helpers
24
+ Rails.application.reload_routes!
25
+
26
+ # actually do the preloading
27
+ TurboSprockets::ParallelPreloader.preload!
28
+
29
+ # for some reason parallel operations may cause activerecord to
30
+ # disconnect
31
+ if const_defined?(:ActiveRecord)
32
+ ActiveRecord::Base.connection.reconnect!
11
33
  end
12
34
  end
13
35
  end
@@ -1,3 +1,3 @@
1
1
  module TurboSprockets
2
- VERSION = '1.0.0'
2
+ VERSION = '1.1.0'
3
3
  end
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ describe TurboSprockets::AssetResolver do
5
+ RSpec::Matchers.define :match_digested do |expected|
6
+ match do |actual|
7
+ extname = File.extname(expected)
8
+ re = /#{expected.chomp(extname)}-[a-f0-9]+#{extname}/
9
+ !!(re =~ actual)
10
+ end
11
+ end
12
+
13
+ let(:environment) { Sprockets::Environment.new(Rails.root) }
14
+ let(:manifest_path) { 'manifest.json' }
15
+ let(:logical_path) { 'file1.js' }
16
+ let(:asset_host) { 'http://localhost:3000' }
17
+
18
+ # sprockets options
19
+ let(:prefix) { '' }
20
+ let(:digest) { true }
21
+ let(:compile) { true }
22
+
23
+ # adapted from:
24
+ # https://github.com/rails/sprockets-rails/blob/v3.2.0/lib/sprockets/railtie.rb#L198
25
+ let(:manifest) do
26
+ path = File.join('public', prefix)
27
+ Sprockets::Manifest.new(environment, path, manifest_path)
28
+ end
29
+
30
+ def logical_to_digested_path(logical_path)
31
+ extname = File.extname(logical_path)
32
+ digest_hash = SecureRandom.hex
33
+ "#{logical_path.chomp(extname)}-#{digest_hash}#{extname}"
34
+ end
35
+
36
+ before do
37
+ allow(Rails.application).to receive(:assets).and_return(environment)
38
+ allow(Rails.application).to receive(:assets_manifest).and_return(manifest)
39
+ allow(Rails.application.config.assets).to receive(:digest).and_return(digest)
40
+ allow(Rails.application.config.assets).to receive(:compile).and_return(compile)
41
+ allow(Rails.application.config.assets).to receive(:prefix).and_return(prefix)
42
+ allow(Rails.application.config.action_controller).to receive(:asset_host).and_return(asset_host)
43
+ end
44
+
45
+ describe '.url_join' do
46
+ it 'joins url segments with mismatched leading/trailing slashes' do
47
+ expect(described_class.url_join('foo/', '/bar/', '/baz')).to eq('foo/bar/baz')
48
+ end
49
+
50
+ it 'keeps absolute URLs absolute' do
51
+ expect(described_class.url_join('/foo', '/bar/', '/baz')).to eq('/foo/bar/baz')
52
+ end
53
+
54
+ it "doesn't modify domain names and schemes" do
55
+ expect(described_class.url_join('https://foo.com', '/bar/', '/baz')).to(
56
+ eq('https://foo.com/bar/baz')
57
+ )
58
+ end
59
+ end
60
+
61
+ context 'with a sprockets environment' do
62
+ before do
63
+ Rails.application.config.assets.paths.each do |path|
64
+ environment.append_path(path)
65
+ environment.context_class.send(:include, ::Sprockets::Rails::Context)
66
+ environment.context_class.digest_assets = digest
67
+ environment.context_class.assets_prefix = prefix
68
+ end
69
+ end
70
+
71
+ context 'when digesting is disabled' do
72
+ let(:digest) { false }
73
+
74
+ describe '.resolve' do
75
+ it 'returns a relative undigested path' do
76
+ expect(described_class.resolve(logical_path)).to eq(logical_path)
77
+ end
78
+ end
79
+
80
+ describe '.resolve_naked' do
81
+ it 'returns the original, absolute, undigested asset path' do
82
+ expect(described_class.resolve_naked(logical_path)).to eq(
83
+ Rails.root.join(File.join(%w[app assets javascripts])).join(logical_path).to_s
84
+ )
85
+ end
86
+ end
87
+
88
+ describe '.resolve_url_for' do
89
+ it 'returns the full undigested URL' do
90
+ expect(described_class.resolve_url_for(logical_path)).to eq(
91
+ "#{asset_host}/#{prefix}/#{logical_path}"
92
+ )
93
+ end
94
+ end
95
+ end
96
+
97
+ context 'when digesting is enabled' do
98
+ describe '.resolve' do
99
+ it 'returns a relative digested asset path' do
100
+ result = described_class.resolve(logical_path)
101
+ expect(result).to match_digested(logical_path)
102
+ end
103
+ end
104
+
105
+ describe '.resolve_naked' do
106
+ it 'returns the original, absolute, undigested asset path' do
107
+ expect(described_class.resolve_naked(logical_path)).to eq(
108
+ Rails.root.join(File.join(%w[app assets javascripts])).join(logical_path).to_s
109
+ )
110
+ end
111
+ end
112
+
113
+ describe '.resolve_url_for' do
114
+ it 'returns the full digested URL' do
115
+ uri = URI.parse(described_class.resolve_url_for(logical_path))
116
+ expect(uri.path.sub(/\/?#{prefix}\/?/, '')).to match_digested(logical_path)
117
+ expect(uri.to_s).to start_with(asset_host)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ context 'without a sprockets environment' do
124
+ let(:environment) { nil }
125
+ let(:compile) { false }
126
+
127
+ before do
128
+ manifest.assets[logical_path] = logical_to_digested_path(logical_path)
129
+ end
130
+
131
+ describe '.resolve' do
132
+ it 'returns the relative digested path from the manifest' do
133
+ result = described_class.resolve(logical_path)
134
+ expect(result).to eq(manifest.assets[logical_path])
135
+ end
136
+ end
137
+
138
+ describe '.resolve_naked' do
139
+ it 'returns the original, absolute, undigested asset path' do
140
+ expect(described_class.resolve_naked(logical_path)).to eq(
141
+ Rails.root.join(File.join(%w[app assets javascripts])).join(logical_path).to_s
142
+ )
143
+ end
144
+ end
145
+
146
+ describe '.resolve_url_for' do
147
+ it 'returns the full digested URL' do
148
+ uri = URI.parse(described_class.resolve_url_for(logical_path))
149
+ expect(uri.path.sub(/\/?#{prefix}\/?/, '')).to match_digested(logical_path)
150
+ expect(uri.to_s).to start_with(asset_host)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,88 @@
1
+ 1/2 preloading assets...
2
+ 2/2 preloading assets...
3
+ done
4
+ 1/2 preloading assets...
5
+ 2/2 preloading assets...
6
+ done
7
+ 1/2 preloading assets...
8
+ 2/2 preloading assets...
9
+ done
10
+ 1/2 preloading assets...
11
+ 2/2 preloading assets...
12
+ done
13
+ 1/2 preloading assets...
14
+ 2/2 preloading assets...
15
+ done
16
+ 1/2 preloading assets...
17
+ 2/2 preloading assets...
18
+ done
19
+ 1/2 preloading assets...
20
+ 2/2 preloading assets...
21
+ done
22
+ 1/2 preloading assets...
23
+ 2/2 preloading assets...
24
+ done
25
+ 1/2 preloading assets...
26
+ 2/2 preloading assets...
27
+ done
28
+ 1/2 preloading assets...
29
+ 2/2 preloading assets...
30
+ done
31
+ 2/2 preloading assets...
32
+ done
33
+ 2/2 preloading assets...
34
+ done
35
+ 2/2 preloading assets...
36
+ done
37
+ 2/2 preloading assets...
38
+ done
39
+ 2/2 preloading assets...
40
+ done
41
+ 2/2 preloading assets...
42
+ done
43
+ 2/2 preloading assets...
44
+ done
45
+ 2/2 preloading assets...
46
+ done
47
+ 2/2 preloading assets...
48
+ done
49
+ 2/2 preloading assets...
50
+ done
51
+ 2/2 preloading assets...
52
+ done
53
+ 2/2 preloading assets...
54
+ done
55
+ 2/2 preloading assets...
56
+ done
57
+ 2/2 preloading assets...
58
+ done
59
+ 2/2 preloading assets...
60
+ done
61
+ 2/2 preloading assets...
62
+ done
63
+ 2/2 preloading assets...
64
+ done
65
+ 2/2 preloading assets...
66
+ done
67
+ 2/2 preloading assets...
68
+ done
69
+ 2/2 preloading assets...
70
+ done
71
+ 2/2 preloading assets...
72
+ done
73
+ 2/2 preloading assets...
74
+ done
75
+ 2/2 preloading assets...
76
+ done
77
+ 2/2 preloading assets...
78
+ done
79
+ 2/2 preloading assets...
80
+ done
81
+ 2/2 preloading assets...
82
+ done
83
+ 2/2 preloading assets...
84
+ done
85
+ 2/2 preloading assets...
86
+ done
87
+ 2/2 preloading assets...
88
+ done
@@ -42,14 +42,31 @@ describe TurboSprockets::ParallelCompiler do
42
42
  expect(manifest['assets']).to eq(files)
43
43
  end
44
44
 
45
- it 'uses the specified number of workers' do
46
- logger = CapturingLogger.new
47
- allow(TurboSprockets).to receive(:logger).and_return(logger)
48
-
49
- with_env('SPROCKETS_WORKER_COUNT' => '4') do
45
+ it 'uses the specified number of workers from the ENV' do
46
+ with_env('TURBO_SPROCKETS_WORKER_COUNT' => '4') do
50
47
  precompile!
51
48
  end
52
49
 
53
50
  expect(logger.messages).to include([:warn, 'Precompiling with 4 workers'])
54
51
  end
52
+
53
+ it 'uses the specified number of workers from the config' do
54
+ TurboSprockets.configure do |config|
55
+ config.precompiler.worker_count = 5
56
+ end
57
+
58
+ precompile!
59
+
60
+ expect(logger.messages).to include([:warn, 'Precompiling with 5 workers'])
61
+ end
62
+
63
+ it 'does not compile in parallel when configured not to' do
64
+ TurboSprockets.configure do |config|
65
+ config.precompiler.enabled = false
66
+ end
67
+
68
+ precompile!
69
+
70
+ expect(logger.messages).to be_empty
71
+ end
55
72
  end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe TurboSprockets::ParallelPreloader do
4
+ let(:logical_paths) { %w(file1.js file2.js) }
5
+
6
+ def get_cached_asset(path)
7
+ uri, _ = assets.resolve(path, compat: false)
8
+ key = Sprockets::UnloadedAsset.new(uri, assets).dependency_history_key
9
+ assets.cache.get(key)
10
+ end
11
+
12
+ describe '.preload!' do
13
+ it 'preloads all assets' do
14
+ logical_paths.each do |path|
15
+ expect(get_cached_asset(path)).to be_nil
16
+ end
17
+
18
+ described_class.preload!
19
+
20
+ logical_paths.each do |path|
21
+ expect(get_cached_asset(path)).to_not be_nil
22
+ end
23
+ end
24
+
25
+ it 'parallelizes the preload' do
26
+ # make sure we have _some_ way of knowing parallel compliation is happening
27
+ expect(Parallel).to receive(:each).and_call_original
28
+ described_class.preload!
29
+ end
30
+ end
31
+ end
@@ -22,9 +22,9 @@ end
22
22
  module LetDeclarations
23
23
  extend RSpec::SharedContext
24
24
 
25
- let(:app) do
26
- Rails.application
27
- end
25
+ let(:app) { Rails.application }
26
+ let(:assets) { app.assets }
27
+ let(:logger) { CapturingLogger.new }
28
28
 
29
29
  let(:assets_dir) do
30
30
  TurboSprockets::DummyApplication.root.join('public/assets')
@@ -58,9 +58,18 @@ end
58
58
  RSpec.configure do |config|
59
59
  config.include(LetDeclarations)
60
60
 
61
- config.before(:each) do
61
+ config.before do
62
62
  FileUtils.rm_rf(tmp_dir)
63
63
  FileUtils.rm_rf(assets_dir)
64
64
  FileUtils.mkdir_p(assets_dir)
65
+
66
+ TurboSprockets.configure do |config|
67
+ config.preloader.logger = logger
68
+ config.precompiler.logger = logger
69
+ end
70
+ end
71
+
72
+ config.after(:each) do
73
+ TurboSprockets.instance_variable_set(:@configuration, nil)
65
74
  end
66
75
  end
@@ -0,0 +1 @@
1
+ I"app/assets/javascripts/file1.js?type=application/javascript&id=f8187f06aff5f6971815ff0ee8806c8ed5eb9924fdc8094fea7c73134ed73bce:ET
@@ -0,0 +1 @@
1
+ I"�app/assets/javascripts/file1.js?type=application/javascript&pipeline=self&id=4a6526d450ce35992b00a9ae6913b6ef20260999c28f6b3bca28b79353e33be5:ET
@@ -0,0 +1 @@
1
+ I"�app/assets/javascripts/file2.js?type=application/javascript&pipeline=self&id=c0f02293ed827ef09372ec69f2eacc7cc884fb685310c441a3a49a09eae13970:ET
@@ -0,0 +1 @@
1
+ I"app/assets/javascripts/file2.js?type=application/javascript&id=035933ec486cdea65727bf40150c9be42540a7243591294b9d7aa9466c5be4be:ET
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.has_rdoc = true
15
15
 
16
16
  s.add_dependency 'parallel', '~> 1.0'
17
- s.add_dependency 'railties', '>= 4', '< 5'
17
+ s.add_dependency 'railties', '>= 4'
18
18
  s.add_dependency 'sprockets', '~> 3.0'
19
19
 
20
20
  s.require_path = 'lib'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-sprockets-rails4
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cameron Dutro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-07 00:00:00.000000000 Z
11
+ date: 2017-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel
@@ -31,9 +31,6 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '4'
34
- - - "<"
35
- - !ruby/object:Gem::Version
36
- version: '5'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
@@ -41,9 +38,6 @@ dependencies:
41
38
  - - ">="
42
39
  - !ruby/object:Gem::Version
43
40
  version: '4'
44
- - - "<"
45
- - !ruby/object:Gem::Version
46
- version: '5'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: sprockets
49
43
  requirement: !ruby/object:Gem::Requirement
@@ -69,33 +63,31 @@ files:
69
63
  - README.md
70
64
  - Rakefile
71
65
  - lib/turbo-sprockets-rails4.rb
66
+ - lib/turbo-sprockets/asset_resolver.rb
72
67
  - lib/turbo-sprockets/parallel_compiler.rb
68
+ - lib/turbo-sprockets/parallel_preloader.rb
73
69
  - lib/turbo-sprockets/railtie.rb
74
70
  - lib/turbo-sprockets/version.rb
75
71
  - spec/app/assets/javascripts/file1.js
76
72
  - spec/app/assets/javascripts/file2.js
73
+ - spec/asset_resolver_spec.rb
77
74
  - spec/config/application.rb
78
75
  - spec/log/test.log
79
76
  - spec/parallel_compiler_spec.rb
80
- - spec/public/assets/file1-9461265d541718e5776632b202c4ccdd21f13d9cd7d0b4d92c1b43131292749a.js
81
- - spec/public/assets/file1-9461265d541718e5776632b202c4ccdd21f13d9cd7d0b4d92c1b43131292749a.js.gz
82
- - spec/public/assets/file2-e2a24754a5ca8cb2f9c645a80ad4d4ca375c4d9914f1d962b4fb3e779dc4ad34.js
83
- - spec/public/assets/file2-e2a24754a5ca8cb2f9c645a80ad4d4ca375c4d9914f1d962b4fb3e779dc4ad34.js.gz
77
+ - spec/parallel_preloader_spec.rb
84
78
  - spec/spec_helper.rb
85
- - spec/tmp/cache/assets/sprockets/v3.0/-m/-mYOrHQQw4eQz_Ht_LzLRCkKUaiPsHUZnmBchCOMAQU.cache
86
- - spec/tmp/cache/assets/sprockets/v3.0/0g/0geAvurz1AX0xI67Ou6WBG0HEnHLFZG5Rbvog7JuFh8.cache
87
- - spec/tmp/cache/assets/sprockets/v3.0/F9/F9uLDkuCpwXDtwzIob92yhwmsuBy0NUF2GnJGRiyWF4.cache
88
- - spec/tmp/cache/assets/sprockets/v3.0/FA/FA7o1GffPYj_2A2EsHqBM8vMKfdSdGYJzMYpjhkXAxE.cache
89
- - spec/tmp/cache/assets/sprockets/v3.0/Ny/Ny4Hng5x4A-TVYWIKr0AjzqV9U32nMfeROjIYX3t1zc.cache
79
+ - spec/tmp/cache/assets/sprockets/v3.0/Le/LeawzTm7RwKtrjUFVCR7o9JVRLjcbCfbykBpzdSZxtM.cache
80
+ - spec/tmp/cache/assets/sprockets/v3.0/Ok/OkAys_2Q1L3vb5MfExaVwWECnkhnpaauOKQ4K5h2EQM.cache
90
81
  - spec/tmp/cache/assets/sprockets/v3.0/Oo/OoRDv9ysw5g18-975bcUUGTFBnb0-ShhbDgUqo-VuOU.cache
91
- - spec/tmp/cache/assets/sprockets/v3.0/Py/PyP_Z86btB-ByMXeasgN-buL98MfdATp8WPzc8AWy6c.cache
92
- - spec/tmp/cache/assets/sprockets/v3.0/Yi/Yi_k91msAv7kbymP50BwVfhkKwo_XMEZIJmMf6LgTAA.cache
82
+ - spec/tmp/cache/assets/sprockets/v3.0/P5/P5S3gl7IvP4a1i2vH3aGqb7MG5quRXURszmF4Jo-exM.cache
83
+ - spec/tmp/cache/assets/sprockets/v3.0/Tj/TjacfODSnBaChvUCg9VRCYGvF9N9D1BFEYa_LOY0Myo.cache
84
+ - spec/tmp/cache/assets/sprockets/v3.0/YF/YFli9jLfluQ3HEM4JQkUGPnhKztRu1QjsR-krCrHnDQ.cache
93
85
  - spec/tmp/cache/assets/sprockets/v3.0/_4/_4pm9XADf-SWLKsx4Qea0XRAZetvbGWa8MQf_aMqcm0.cache
94
- - spec/tmp/cache/assets/sprockets/v3.0/b8/b8XCYwFHJAQi_UQnYIe_rPgE6W5gMlmY69smkT7lUUg.cache
86
+ - spec/tmp/cache/assets/sprockets/v3.0/_T/_T7w3tcNAR1OjzPNFDxGLBsotNkwlubZL18upfeyLOk.cache
87
+ - spec/tmp/cache/assets/sprockets/v3.0/aH/aHkgzJaZpAnMTLP7DvYxN89R8XHqtxL4DrC2d90U3Z8.cache
95
88
  - spec/tmp/cache/assets/sprockets/v3.0/c5/c5fWyhSDQYpk5wBXxoIj4L8d8VRI5fx8BA-Vusc-Tm8.cache
96
89
  - spec/tmp/cache/assets/sprockets/v3.0/fi/fide8IvH1RAoWxoK8yipcMFLPqQym3YmbZs93J5nyGM.cache
97
- - spec/tmp/cache/assets/sprockets/v3.0/rs/rsmYKfF63AJstBxp9BfJQ_Alz93hJXeRW6AlZasgAg8.cache
98
- - spec/tmp/cache/assets/sprockets/v3.0/sk/skLSgxSN-gAyttjzKsGZfvS6147mrFxxK3ztXCqNNP8.cache
90
+ - spec/tmp/cache/assets/sprockets/v3.0/zI/zIeYCT_q2r-xVxHddTUvci_yk-5Aa8M6wKAaxpqoNt0.cache
99
91
  - turbo-sprockets-rails4.gemspec
100
92
  homepage: http://github.com/camertron
101
93
  licenses: []
@@ -116,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
108
  version: '0'
117
109
  requirements: []
118
110
  rubyforge_project:
119
- rubygems_version: 2.5.2
111
+ rubygems_version: 2.6.11
120
112
  signing_key:
121
113
  specification_version: 4
122
114
  summary: Speed up asset precompliation by compiling assets in parallel.
@@ -1 +0,0 @@
1
- "%h,t�[�J�!�R����S$I�9Q�F2��^g�
@@ -1 +0,0 @@
1
- I"app/assets/javascripts/file1.js?type=application/javascript&id=fd26267e172201fa2d3b128fce4cb1a53c227f28aa0b693ac2708a9ab0b44ea2:ET
@@ -1 +0,0 @@
1
- I"app/assets/javascripts/file2.js?type=application/javascript&id=8e7ad17b3e9d5262767b265f3b265c1f5fc9742a2782ce3232b8087632963d4d:ET
@@ -1 +0,0 @@
1
- "%N�a�t�%�چ�e�o/���H .����t⠝
@@ -1 +0,0 @@
1
- I"�app/assets/javascripts/file2.js?type=application/javascript&pipeline=self&id=93fac2a2152bd5b89fdb8f97fd3fac83e8b82a3621681c1f077981a977775fcf:ET
@@ -1 +0,0 @@
1
- I"�app/assets/javascripts/file1.js?type=application/javascript&pipeline=self&id=18ce48382a6f42ab81da8c115f1e4c686ad07b2fec30a431a20e627dd718ae7f:ET