turbo-sprockets-rails3-envaware 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +117 -0
  4. data/Rakefile +28 -0
  5. data/lib/sprockets/asset_with_dependencies.rb +134 -0
  6. data/lib/sprockets/static_non_digest_generator.rb +106 -0
  7. data/lib/sprockets/unprocessed_asset.rb +39 -0
  8. data/lib/turbo-sprockets-rails3.rb +15 -0
  9. data/lib/turbo-sprockets/helpers.rb +9 -0
  10. data/lib/turbo-sprockets/railtie.rb +26 -0
  11. data/lib/turbo-sprockets/sprockets_overrides/asset.rb +31 -0
  12. data/lib/turbo-sprockets/sprockets_overrides/base.rb +38 -0
  13. data/lib/turbo-sprockets/sprockets_overrides/bundled_asset.rb +33 -0
  14. data/lib/turbo-sprockets/sprockets_overrides/environment.rb +21 -0
  15. data/lib/turbo-sprockets/sprockets_overrides/index.rb +30 -0
  16. data/lib/turbo-sprockets/sprockets_overrides/processed_asset.rb +29 -0
  17. data/lib/turbo-sprockets/sprockets_overrides/static_compiler.rb +94 -0
  18. data/lib/turbo-sprockets/tasks/assets.rake +204 -0
  19. data/lib/turbo-sprockets/version.rb +3 -0
  20. data/test/abstract_unit.rb +143 -0
  21. data/test/assets_debugging_test.rb +65 -0
  22. data/test/assets_test.rb +542 -0
  23. data/test/fixtures/alternate/stylesheets/style.css +1 -0
  24. data/test/fixtures/app/fonts/dir/font.ttf +0 -0
  25. data/test/fixtures/app/fonts/font.ttf +0 -0
  26. data/test/fixtures/app/images/logo.png +0 -0
  27. data/test/fixtures/app/javascripts/application.js +1 -0
  28. data/test/fixtures/app/javascripts/dir/xmlhr.js +0 -0
  29. data/test/fixtures/app/javascripts/extra.js +0 -0
  30. data/test/fixtures/app/javascripts/foo.min.js +0 -0
  31. data/test/fixtures/app/javascripts/xmlhr.js +0 -0
  32. data/test/fixtures/app/stylesheets/application.css +1 -0
  33. data/test/fixtures/app/stylesheets/dir/style.css +0 -0
  34. data/test/fixtures/app/stylesheets/extra.css +0 -0
  35. data/test/fixtures/app/stylesheets/style.css +0 -0
  36. data/test/fixtures/app/stylesheets/style.ext +0 -0
  37. data/test/fixtures/app/stylesheets/style.min.css +0 -0
  38. data/test/sprockets_helper_test.rb +363 -0
  39. data/test/sprockets_helper_with_routes_test.rb +57 -0
  40. data/test/sprockets_helpers_abstract_unit.rb +358 -0
  41. metadata +139 -0
@@ -0,0 +1,15 @@
1
+ require 'sprockets/railtie'
2
+
3
+ module Sprockets
4
+ # Assets
5
+ autoload :UnprocessedAsset, "sprockets/unprocessed_asset"
6
+ autoload :AssetWithDependencies, "sprockets/asset_with_dependencies"
7
+ end
8
+
9
+ require 'sprockets'
10
+ Dir[File.expand_path('../turbo-sprockets/sprockets_overrides/*.rb', __FILE__)].each do |f|
11
+ require f
12
+ end
13
+
14
+ require 'turbo-sprockets/helpers'
15
+ require 'turbo-sprockets/railtie'
@@ -0,0 +1,9 @@
1
+ module TurboSprockets
2
+ def self.get_source_manifest_path(manifest_dir, filename="sources_manifest.yml")
3
+ "#{manifest_dir}/#{get_source_manifest_filename(filename)}"
4
+ end
5
+
6
+ def self.get_source_manifest_filename(filename="sources_manifest.yml")
7
+ ENV['RAILS_ENV'] ? "#{ENV['RAILS_ENV'].downcase}_#{filename}" : filename
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ require "action_controller/railtie"
2
+
3
+ module Sprockets
4
+ autoload :StaticNonDigestGenerator, "sprockets/static_non_digest_generator"
5
+ end
6
+
7
+ module TurboSprockets
8
+ class Railtie < ::Rails::Railtie
9
+ rake_tasks do
10
+ load "turbo-sprockets/tasks/assets.rake"
11
+ end
12
+
13
+ initializer "turbo-sprockets.environment", :after => "sprockets.environment", :group => :all do |app|
14
+ config = app.config
15
+
16
+ manifest_dir = config.assets.manifest || File.join(Rails.public_path, config.assets.prefix)
17
+ digests_manifest = File.join(manifest_dir, "manifest.yml")
18
+ sources_manifest = File.join(TurboSprockets.get_source_manifest_path(manifest_dir))
19
+ config.assets.digests = (File.exist?(digests_manifest) && YAML.load_file(digests_manifest)) || {}
20
+ config.assets.source_digests = (File.exist?(sources_manifest) && YAML.load_file(sources_manifest)) || {}
21
+
22
+ # Clear digests if loading previous manifest format
23
+ config.assets.digests = {} if config.assets.digests[:digest_files]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'sprockets/asset'
2
+
3
+ module Sprockets
4
+ Asset.class_eval do
5
+ # Internal initializer to load `Asset` from serialized `Hash`.
6
+ def self.from_hash(environment, hash)
7
+ return unless hash.is_a?(Hash)
8
+
9
+ klass = case hash['class']
10
+ when 'BundledAsset'
11
+ BundledAsset
12
+ when 'ProcessedAsset'
13
+ ProcessedAsset
14
+ when 'UnprocessedAsset'
15
+ UnprocessedAsset
16
+ when 'StaticAsset'
17
+ StaticAsset
18
+ else
19
+ nil
20
+ end
21
+
22
+ if klass
23
+ asset = klass.allocate
24
+ asset.init_with(environment, hash)
25
+ asset
26
+ end
27
+ rescue UnserializeError
28
+ nil
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'sprockets/base'
2
+
3
+ module Sprockets
4
+ Base.class_eval do
5
+ protected
6
+
7
+ def build_asset(logical_path, pathname, options)
8
+ pathname = Pathname.new(pathname)
9
+
10
+ # If there are any processors to run on the pathname, use
11
+ # `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
12
+ if attributes_for(pathname).processors.any?
13
+ if options[:bundle] == false
14
+ circular_call_protection(pathname.to_s) do
15
+ if options[:process] == false
16
+ UnprocessedAsset.new(index, logical_path, pathname)
17
+ else
18
+ ProcessedAsset.new(index, logical_path, pathname)
19
+ end
20
+ end
21
+ else
22
+ BundledAsset.new(index, logical_path, pathname, options)
23
+ end
24
+ else
25
+ StaticAsset.new(index, logical_path, pathname)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def cache_key_for(path, options)
32
+ options[:process] = true unless options.key?(:process)
33
+ key = "#{path}:#{options[:bundle] ? '1' : '0'}"
34
+ key << ":0" unless options[:process]
35
+ key
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ require 'sprockets/bundled_asset'
2
+
3
+ module Sprockets
4
+ BundledAsset.class_eval do
5
+
6
+ # Adds :process options
7
+
8
+ def initialize(environment, logical_path, pathname, options = {})
9
+ super(environment, logical_path, pathname)
10
+ @process = options.fetch(:process, true)
11
+
12
+ @processed_asset = environment.find_asset(pathname, :bundle => false, :process => @process)
13
+ @required_assets = @processed_asset.required_assets
14
+ @dependency_paths = @processed_asset.dependency_paths
15
+
16
+ @source = ""
17
+
18
+ # Explode Asset into parts and gather the dependency bodies
19
+ to_a.each { |dependency| @source << dependency.to_s }
20
+
21
+ if @process
22
+ # Run bundle processors on concatenated source
23
+ context = environment.context_class.new(environment, logical_path, pathname)
24
+ @source = context.evaluate(pathname, :data => @source,
25
+ :processors => environment.bundle_processors(content_type))
26
+ end
27
+
28
+ @mtime = to_a.map(&:mtime).max
29
+ @length = Rack::Utils.bytesize(source)
30
+ @digest = environment.digest.update(source).hexdigest
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,21 @@
1
+ require 'sprockets/environment'
2
+
3
+ module Sprockets
4
+ Environment.class_eval do
5
+
6
+ # Adds :process options
7
+
8
+ def find_asset(path, options = {})
9
+ options[:bundle] = true unless options.key?(:bundle)
10
+ options[:process] = true unless options.key?(:process)
11
+
12
+ # Ensure in-memory cached assets are still fresh on every lookup
13
+ if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
14
+ asset
15
+ elsif asset = index.find_asset(path, options)
16
+ # Cache is pushed upstream by Index#find_asset
17
+ asset
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ require 'sprockets/index'
2
+
3
+ module Sprockets
4
+ Index.class_eval do
5
+
6
+ # Adds :process options
7
+
8
+ def find_asset(path, options = {})
9
+ options[:bundle] = true unless options.key?(:bundle)
10
+ options[:process] = true unless options.key?(:process)
11
+
12
+ if asset = @assets[cache_key_for(path, options)]
13
+ asset
14
+ elsif asset = super
15
+ logical_path_cache_key = cache_key_for(path, options)
16
+ full_path_cache_key = cache_key_for(asset.pathname, options)
17
+
18
+ # Cache on Index
19
+ @assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
20
+
21
+ # Push cache upstream to Environment
22
+ @environment.instance_eval do
23
+ @assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
24
+ end
25
+
26
+ asset
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,29 @@
1
+ require 'sprockets/processed_asset'
2
+
3
+ module Sprockets
4
+
5
+ # Remove and redefine ProcessedAsset to inherit from AssetWithDependencies
6
+
7
+ remove_const :ProcessedAsset
8
+
9
+ class ProcessedAsset < AssetWithDependencies
10
+ def initialize(environment, logical_path, pathname)
11
+ super
12
+
13
+ start_time = Time.now.to_f
14
+
15
+ context = environment.context_class.new(environment, logical_path, pathname)
16
+ @source = context.evaluate(pathname)
17
+ @length = Rack::Utils.bytesize(source)
18
+ @digest = environment.digest.update(source).hexdigest
19
+
20
+ build_required_assets(environment, context)
21
+ build_dependency_paths(environment, context)
22
+
23
+ @dependency_digest = compute_dependency_digest(environment)
24
+
25
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
26
+ environment.logger.info "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,94 @@
1
+ begin
2
+ require 'sprockets/static_compiler'
3
+ rescue LoadError
4
+ end
5
+
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)$/
18
+
19
+ @current_source_digests = options.fetch(:source_digests, {})
20
+ @current_digests = options.fetch(:digests, {})
21
+
22
+ @digests = {}
23
+ @source_digests = {}
24
+ end
25
+
26
+ def compile
27
+ start_time = Time.now.to_f
28
+
29
+ env.each_logical_path(paths) do |logical_path|
30
+ # Fetch asset without any processing or compression,
31
+ # to calculate a digest of the concatenated source files
32
+ next unless asset = env.find_asset(logical_path, :process => false)
33
+ @source_digests[logical_path] = asset.digest
34
+
35
+ # Recompile if digest has changed or compiled digest file is missing
36
+ current_digest_file = @current_digests[logical_path]
37
+
38
+ if @source_digests[logical_path] != @current_source_digests[logical_path] ||
39
+ !(current_digest_file && File.exists?("#{@target}/#{current_digest_file}"))
40
+
41
+ if asset = env.find_asset(logical_path)
42
+ digest_path = write_asset(asset)
43
+ @digests[asset.logical_path] = digest_path
44
+ @digests[aliased_path_for(asset.logical_path)] = digest_path
45
+ # Update current_digests with new hash, for future assets to reference
46
+ @current_digests[asset.logical_path] = asset.digest_path
47
+ end
48
+ else
49
+ # Set asset file from manifest.yml
50
+ digest_path = @current_digests[logical_path]
51
+ @digests[logical_path] = digest_path
52
+ @digests[aliased_path_for(logical_path)] = digest_path
53
+
54
+ env.logger.debug "Not compiling #{logical_path}, sources digest has not changed " <<
55
+ "(#{@source_digests[logical_path][0...7]})"
56
+ end
57
+ end
58
+
59
+ # Encode all filenames & digests as UTF-8 for Ruby 1.9,
60
+ # otherwise YAML dumps other string encodings as !binary
61
+ if RUBY_VERSION.to_f >= 1.9
62
+ @source_digests = encode_hash_as_utf8 @source_digests
63
+ @digests = encode_hash_as_utf8 @digests
64
+ end
65
+
66
+ if @manifest
67
+ write_manifest(@digests)
68
+ write_sources_manifest(@source_digests)
69
+ end
70
+
71
+ # Store digests in Rails config. (Important if non-digest is run after primary)
72
+ config = ::Rails.application.config
73
+ config.assets.digests = @digests
74
+ config.assets.source_digests = @source_digests
75
+
76
+ elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
77
+ env.logger.debug "Processed #{'non-' unless @digest}digest assets in #{elapsed_time}ms"
78
+ end
79
+
80
+ def write_sources_manifest(source_digests)
81
+ FileUtils.mkdir_p(@manifest_path)
82
+ File.open(TurboSprockets.get_source_manifest_path(@manifest_path), 'wb') do |f|
83
+ YAML.dump(source_digests, f)
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def encode_hash_as_utf8(hash)
90
+ Hash[*hash.map {|k,v| [k.encode("UTF-8"), v.encode("UTF-8")] }.flatten]
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,204 @@
1
+ require "fileutils"
2
+ require 'shellwords'
3
+
4
+ # Clear all assets tasks from sprockets railtie,
5
+ # but preserve any extra actions added via 'enhance'
6
+ task_enhancements = {}
7
+ Rake::Task.tasks.each do |task|
8
+ if task.name.match '^assets:'
9
+ task_enhancements[task.name] = task.actions[1..-1] if task.actions.size > 1
10
+ task.clear
11
+ end
12
+ end
13
+
14
+ # Replace with our extended assets tasks
15
+ namespace :assets do
16
+ def ruby_rake_task(task, fork = true)
17
+ env = ENV['RAILS_ENV'] || 'production'
18
+ groups = ENV['RAILS_GROUPS'] || 'assets'
19
+ args = [$0, task,"RAILS_ENV=#{env}","RAILS_GROUPS=#{groups}"]
20
+ args << "--trace" if Rake.application.options.trace
21
+ if $0 =~ /rake\.bat\Z/i
22
+ Kernel.exec $0, *args
23
+ else
24
+ fork ? ruby(*args) : Kernel.exec(FileUtils::RUBY, *args)
25
+ end
26
+ end
27
+
28
+ # We are currently running with no explicit bundler group
29
+ # and/or no explicit environment - we have to reinvoke rake to
30
+ # execute this task.
31
+ def invoke_or_reboot_rake_task(task)
32
+ if ENV['RAILS_GROUPS'].to_s.empty? || ENV['RAILS_ENV'].to_s.empty?
33
+ ruby_rake_task task
34
+ else
35
+ Rake::Task[task].invoke
36
+ end
37
+ end
38
+
39
+ # Returns an array of assets recognized by config.assets.digests,
40
+ # including gzipped assets and manifests
41
+ def known_assets
42
+ assets = Rails.application.config.assets.digests.to_a.flatten.map do |asset|
43
+ [asset, "#{asset}.gz"]
44
+ end.flatten
45
+ assets + (%w(manifest.yml) << TurboSprockets.get_source_manifest_filename)
46
+ end
47
+
48
+ desc "Compile all the assets named in config.assets.precompile"
49
+ task :precompile do
50
+ invoke_or_reboot_rake_task "assets:precompile:all"
51
+ end
52
+
53
+ namespace :precompile do
54
+ def internal_precompile(digest=nil)
55
+ unless Rails.application.config.assets.enabled
56
+ warn "Cannot precompile assets if sprockets is disabled. Please set config.assets.enabled to true"
57
+ exit
58
+ end
59
+
60
+ # Ensure that action view is loaded and the appropriate
61
+ # sprockets hooks get executed
62
+ _ = ActionView::Base
63
+
64
+ config = Rails.application.config
65
+ config.assets.compile = true
66
+ config.assets.clean_after_precompile = false if config.assets.clean_after_precompile.nil?
67
+ config.assets.digest = digest unless digest.nil?
68
+ config.assets.digests ||= {}
69
+ config.assets.source_digests ||= {}
70
+ config.assets.handle_expiration = false if config.assets.handle_expiration.nil?
71
+
72
+ env = Rails.application.assets
73
+ target = File.join(::Rails.public_path, config.assets.prefix)
74
+
75
+ # This takes a long time to run if you aren't cleaning expired assets.
76
+ # You must call the assets:clean_expired rake task regularly if this is
77
+ # enabled
78
+ if config.assets.handle_expiration
79
+ # Before first compile, set the mtime of all current assets to current time.
80
+ # This time reflects the last time the assets were being used.
81
+ if digest.nil?
82
+ ::Rails.logger.debug "Updating mtimes for current assets..."
83
+ paths = known_assets.map { |asset| File.join(target, asset) }
84
+ paths.each_slice(100) do |slice|
85
+ # File.utime raises 'Operation not permitted' unless user is owner of file.
86
+ # Non-owners have permission to update mtime to the current time using 'touch'.
87
+ `touch -c #{slice.shelljoin}`
88
+ end
89
+ end
90
+ end
91
+
92
+ # If processing non-digest assets, and compiled digest files are
93
+ # present, then generate non-digest assets from existing assets.
94
+ # It is assumed that `assets:precompile:nondigest` won't be run manually
95
+ # if assets have been previously compiled with digests.
96
+ if !config.assets.digest && config.assets.digests.any?
97
+ generator = Sprockets::StaticNonDigestGenerator.new(env, target, config.assets.precompile,
98
+ :digests => config.assets.digests)
99
+ generator.generate
100
+ else
101
+ compiler = Sprockets::StaticCompiler.new(env, target, config.assets.precompile,
102
+ :digest => config.assets.digest,
103
+ :manifest => digest.nil?,
104
+ :manifest_path => config.assets.manifest,
105
+ :digests => config.assets.digests,
106
+ :source_digests => config.assets.source_digests
107
+ )
108
+ compiler.compile
109
+ end
110
+ end
111
+
112
+ task :all => ["assets:cache:clean"] do
113
+ # Other gems may want to add hooks to run after the 'assets:precompile:***' tasks.
114
+ # Since we aren't running separate rake tasks anymore, we manually invoke the extra actions.
115
+ internal_precompile
116
+ Rake::Task["assets:precompile:primary"].actions[1..-1].each &:call
117
+
118
+ if ::Rails.application.config.assets.digest
119
+ internal_precompile(false)
120
+ Rake::Task["assets:precompile:nondigest"].actions[1..-1].each &:call
121
+ end
122
+ end
123
+
124
+ task :primary => ["assets:cache:clean"] do
125
+ internal_precompile
126
+ end
127
+
128
+ task :nondigest => ["assets:cache:clean"] do
129
+ internal_precompile(false)
130
+ end
131
+ end
132
+
133
+ desc "Remove old assets that aren't referenced by manifest.yml"
134
+ task :clean_expired do
135
+ invoke_or_reboot_rake_task "assets:clean_expired:all"
136
+ end
137
+
138
+ # Remove assets that haven't been deployed since `config.assets.expire_after` (default 1 day).
139
+ # This provides a buffer between deploys, so that older assets can still be requested.
140
+ # The precompile task updates the mtime of the current assets before compiling,
141
+ # which indicates when they were last in use.
142
+ #
143
+ # The current assets are ignored, which is faster than the alternative of
144
+ # setting their mtimes only to check them again.
145
+ namespace :clean_expired do
146
+ task :all => ["assets:environment"] do
147
+ config = ::Rails.application.config
148
+ expire_after = config.assets.expire_after || 1.day
149
+ public_asset_path = File.join(::Rails.public_path, config.assets.prefix)
150
+
151
+ @known_assets = known_assets
152
+
153
+ Dir.glob(File.join(public_asset_path, '**/*')).each do |asset|
154
+ next if File.directory?(asset)
155
+ logical_path = asset.sub("#{public_asset_path}/", '')
156
+
157
+ unless logical_path.in?(@known_assets)
158
+ # Delete asset if not used for more than expire_after seconds
159
+ if File.mtime(asset) < (Time.now - expire_after)
160
+ ::Rails.logger.debug "Removing expired asset: #{logical_path}"
161
+ FileUtils.rm_f asset
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ desc "Remove compiled assets"
169
+ task :clean do
170
+ invoke_or_reboot_rake_task "assets:clean:all"
171
+ end
172
+
173
+ namespace :clean do
174
+ task :all => ["assets:cache:clean"] do
175
+ config = ::Rails.application.config
176
+ public_asset_path = File.join(::Rails.public_path, config.assets.prefix)
177
+ rm_rf public_asset_path, :secure => true
178
+ end
179
+ end
180
+
181
+ namespace :cache do
182
+ task :clean => ["assets:environment"] do
183
+ FileUtils.mkdir_p(File.join(::Rails.root.to_s, *%w(tmp cache assets)))
184
+ ::Rails.application.assets.cache.clear
185
+ end
186
+ end
187
+
188
+ task :environment do
189
+ if ::Rails.application.config.assets.initialize_on_precompile
190
+ Rake::Task["environment"].invoke
191
+ else
192
+ ::Rails.application.initialize!(:assets)
193
+ Sprockets::Bootstrap.new(Rails.application).run
194
+ end
195
+ end
196
+ end
197
+
198
+
199
+ # Append previous task enhancements to new Rake tasks.
200
+ task_enhancements.each do |task_name, actions|
201
+ actions.each do |proc|
202
+ Rake::Task[task_name].enhance &proc
203
+ end
204
+ end