sprockets 2.11.3 → 3.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +296 -0
  3. data/LICENSE +2 -2
  4. data/README.md +235 -242
  5. data/bin/sprockets +1 -0
  6. data/lib/rake/sprocketstask.rb +5 -4
  7. data/lib/sprockets/asset.rb +143 -210
  8. data/lib/sprockets/autoload/closure.rb +7 -0
  9. data/lib/sprockets/autoload/coffee_script.rb +7 -0
  10. data/lib/sprockets/autoload/eco.rb +7 -0
  11. data/lib/sprockets/autoload/ejs.rb +7 -0
  12. data/lib/sprockets/autoload/sass.rb +7 -0
  13. data/lib/sprockets/autoload/uglifier.rb +7 -0
  14. data/lib/sprockets/autoload/yui.rb +7 -0
  15. data/lib/sprockets/autoload.rb +11 -0
  16. data/lib/sprockets/base.rb +56 -393
  17. data/lib/sprockets/bower.rb +58 -0
  18. data/lib/sprockets/bundle.rb +69 -0
  19. data/lib/sprockets/cache/file_store.rb +168 -14
  20. data/lib/sprockets/cache/memory_store.rb +66 -0
  21. data/lib/sprockets/cache/null_store.rb +46 -0
  22. data/lib/sprockets/cache.rb +236 -0
  23. data/lib/sprockets/cached_environment.rb +69 -0
  24. data/lib/sprockets/closure_compressor.rb +35 -10
  25. data/lib/sprockets/coffee_script_processor.rb +25 -0
  26. data/lib/sprockets/coffee_script_template.rb +17 -0
  27. data/lib/sprockets/compressing.rb +44 -23
  28. data/lib/sprockets/configuration.rb +83 -0
  29. data/lib/sprockets/context.rb +86 -144
  30. data/lib/sprockets/dependencies.rb +73 -0
  31. data/lib/sprockets/deprecation.rb +90 -0
  32. data/lib/sprockets/digest_utils.rb +180 -0
  33. data/lib/sprockets/directive_processor.rb +207 -211
  34. data/lib/sprockets/eco_processor.rb +32 -0
  35. data/lib/sprockets/eco_template.rb +9 -30
  36. data/lib/sprockets/ejs_processor.rb +31 -0
  37. data/lib/sprockets/ejs_template.rb +9 -29
  38. data/lib/sprockets/encoding_utils.rb +261 -0
  39. data/lib/sprockets/engines.rb +53 -35
  40. data/lib/sprockets/environment.rb +17 -64
  41. data/lib/sprockets/erb_processor.rb +30 -0
  42. data/lib/sprockets/erb_template.rb +11 -0
  43. data/lib/sprockets/errors.rb +4 -13
  44. data/lib/sprockets/file_reader.rb +15 -0
  45. data/lib/sprockets/http_utils.rb +117 -0
  46. data/lib/sprockets/jst_processor.rb +35 -15
  47. data/lib/sprockets/legacy.rb +330 -0
  48. data/lib/sprockets/legacy_proc_processor.rb +35 -0
  49. data/lib/sprockets/legacy_tilt_processor.rb +29 -0
  50. data/lib/sprockets/loader.rb +325 -0
  51. data/lib/sprockets/manifest.rb +202 -127
  52. data/lib/sprockets/manifest_utils.rb +45 -0
  53. data/lib/sprockets/mime.rb +112 -31
  54. data/lib/sprockets/path_dependency_utils.rb +85 -0
  55. data/lib/sprockets/path_digest_utils.rb +47 -0
  56. data/lib/sprockets/path_utils.rb +287 -0
  57. data/lib/sprockets/paths.rb +42 -19
  58. data/lib/sprockets/processing.rb +178 -126
  59. data/lib/sprockets/processor_utils.rb +180 -0
  60. data/lib/sprockets/resolve.rb +211 -0
  61. data/lib/sprockets/sass_cache_store.rb +22 -17
  62. data/lib/sprockets/sass_compressor.rb +39 -15
  63. data/lib/sprockets/sass_functions.rb +2 -70
  64. data/lib/sprockets/sass_importer.rb +2 -29
  65. data/lib/sprockets/sass_processor.rb +292 -0
  66. data/lib/sprockets/sass_template.rb +12 -53
  67. data/lib/sprockets/server.rb +129 -84
  68. data/lib/sprockets/transformers.rb +145 -0
  69. data/lib/sprockets/uglifier_compressor.rb +39 -12
  70. data/lib/sprockets/unloaded_asset.rb +137 -0
  71. data/lib/sprockets/uri_tar.rb +98 -0
  72. data/lib/sprockets/uri_utils.rb +188 -0
  73. data/lib/sprockets/utils/gzip.rb +67 -0
  74. data/lib/sprockets/utils.rb +210 -44
  75. data/lib/sprockets/version.rb +1 -1
  76. data/lib/sprockets/yui_compressor.rb +39 -11
  77. data/lib/sprockets.rb +142 -81
  78. metadata +100 -80
  79. data/lib/sprockets/asset_attributes.rb +0 -137
  80. data/lib/sprockets/bundled_asset.rb +0 -78
  81. data/lib/sprockets/caching.rb +0 -96
  82. data/lib/sprockets/charset_normalizer.rb +0 -41
  83. data/lib/sprockets/index.rb +0 -100
  84. data/lib/sprockets/processed_asset.rb +0 -152
  85. data/lib/sprockets/processor.rb +0 -32
  86. data/lib/sprockets/safety_colons.rb +0 -28
  87. data/lib/sprockets/scss_template.rb +0 -13
  88. data/lib/sprockets/static_asset.rb +0 -58
@@ -1,32 +1,186 @@
1
- require 'digest/md5'
2
1
  require 'fileutils'
3
- require 'pathname'
2
+ require 'logger'
3
+ require 'sprockets/encoding_utils'
4
+ require 'sprockets/path_utils'
5
+ require 'zlib'
4
6
 
5
7
  module Sprockets
6
- module Cache
7
- # A simple file system cache store.
8
+ class Cache
9
+ # Public: A file system cache store that automatically cleans up old keys.
10
+ #
11
+ # Assign the instance to the Environment#cache.
8
12
  #
9
13
  # environment.cache = Sprockets::Cache::FileStore.new("/tmp")
10
14
  #
15
+ # See Also
16
+ #
17
+ # ActiveSupport::Cache::FileStore
18
+ #
11
19
  class FileStore
12
- def initialize(root)
13
- @root = Pathname.new(root)
20
+ # Internal: Default key limit for store.
21
+ DEFAULT_MAX_SIZE = 25 * 1024 * 1024
22
+
23
+ # Internal: Default standard error fatal logger.
24
+ #
25
+ # Returns a Logger.
26
+ def self.default_logger
27
+ logger = Logger.new($stderr)
28
+ logger.level = Logger::FATAL
29
+ logger
14
30
  end
15
31
 
16
- # Lookup value in cache
17
- def [](key)
18
- pathname = @root.join(key)
19
- pathname.exist? ? pathname.open('rb') { |f| Marshal.load(f) } : nil
32
+ # Public: Initialize the cache store.
33
+ #
34
+ # root - A String path to a directory to persist cached values to.
35
+ # max_size - A Integer of the maximum number of keys the store will hold.
36
+ # (default: 1000).
37
+ def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger)
38
+ @root = root
39
+ @max_size = max_size
40
+ @gc_size = max_size * 0.75
41
+ @logger = logger
42
+ end
43
+
44
+ # Public: Retrieve value from cache.
45
+ #
46
+ # This API should not be used directly, but via the Cache wrapper API.
47
+ #
48
+ # key - String cache key.
49
+ #
50
+ # Returns Object or nil or the value is not set.
51
+ def get(key)
52
+ path = File.join(@root, "#{key}.cache")
53
+
54
+ value = safe_open(path) do |f|
55
+ begin
56
+ EncodingUtils.unmarshaled_deflated(f.read, Zlib::MAX_WBITS)
57
+ rescue Exception => e
58
+ @logger.error do
59
+ "#{self.class}[#{path}] could not be unmarshaled: " +
60
+ "#{e.class}: #{e.message}"
61
+ end
62
+ nil
63
+ end
64
+ end
65
+
66
+ if value
67
+ FileUtils.touch(path)
68
+ value
69
+ end
20
70
  end
21
71
 
22
- # Save value to cache
23
- def []=(key, value)
72
+ # Public: Set a key and value in the cache.
73
+ #
74
+ # This API should not be used directly, but via the Cache wrapper API.
75
+ #
76
+ # key - String cache key.
77
+ # value - Object value.
78
+ #
79
+ # Returns Object value.
80
+ def set(key, value)
81
+ path = File.join(@root, "#{key}.cache")
82
+
24
83
  # Ensure directory exists
25
- FileUtils.mkdir_p @root.join(key).dirname
84
+ FileUtils.mkdir_p File.dirname(path)
85
+
86
+ # Check if cache exists before writing
87
+ exists = File.exist?(path)
88
+
89
+ # Serialize value
90
+ marshaled = Marshal.dump(value)
91
+
92
+ # Compress if larger than 4KB
93
+ if marshaled.bytesize > 4 * 1024
94
+ deflater = Zlib::Deflate.new(
95
+ Zlib::BEST_COMPRESSION,
96
+ Zlib::MAX_WBITS,
97
+ Zlib::MAX_MEM_LEVEL,
98
+ Zlib::DEFAULT_STRATEGY
99
+ )
100
+ deflater << marshaled
101
+ raw = deflater.finish
102
+ else
103
+ raw = marshaled
104
+ end
105
+
106
+ # Write data
107
+ PathUtils.atomic_write(path) do |f|
108
+ f.write(raw)
109
+ @size = size + f.size unless exists
110
+ end
111
+
112
+ # GC if necessary
113
+ gc! if size > @max_size
26
114
 
27
- @root.join(key).open('w') { |f| Marshal.dump(value, f)}
28
115
  value
29
116
  end
117
+
118
+ # Public: Pretty inspect
119
+ #
120
+ # Returns String.
121
+ def inspect
122
+ "#<#{self.class} size=#{size}/#{@max_size}>"
123
+ end
124
+
125
+ private
126
+ # Internal: Get all cache files along with stats.
127
+ #
128
+ # Returns an Array of [String filename, File::Stat] pairs sorted by
129
+ # mtime.
130
+ def find_caches
131
+ Dir.glob(File.join(@root, '**/*.cache')).reduce([]) { |stats, filename|
132
+ stat = safe_stat(filename)
133
+ # stat maybe nil if file was removed between the time we called
134
+ # dir.glob and the next stat
135
+ stats << [filename, stat] if stat
136
+ stats
137
+ }.sort_by { |_, stat| stat.mtime.to_i }
138
+ end
139
+
140
+ def size
141
+ @size ||= compute_size(find_caches)
142
+ end
143
+
144
+ def compute_size(caches)
145
+ caches.inject(0) { |sum, (_, stat)| sum + stat.size }
146
+ end
147
+
148
+ def safe_stat(fn)
149
+ File.stat(fn)
150
+ rescue Errno::ENOENT
151
+ nil
152
+ end
153
+
154
+ def safe_open(path, &block)
155
+ if File.exist?(path)
156
+ File.open(path, 'rb', &block)
157
+ end
158
+ rescue Errno::ENOENT
159
+ end
160
+
161
+ def gc!
162
+ start_time = Time.now
163
+
164
+ caches = find_caches
165
+ size = compute_size(caches)
166
+
167
+ delete_caches, keep_caches = caches.partition { |filename, stat|
168
+ deleted = size > @gc_size
169
+ size -= stat.size
170
+ deleted
171
+ }
172
+
173
+ return if delete_caches.empty?
174
+
175
+ FileUtils.remove(delete_caches.map(&:first), force: true)
176
+ @size = compute_size(keep_caches)
177
+
178
+ @logger.warn do
179
+ secs = Time.now.to_f - start_time.to_f
180
+ "#{self.class}[#{@root}] garbage collected " +
181
+ "#{delete_caches.size} files (#{(secs * 1000).to_i}ms)"
182
+ end
183
+ end
30
184
  end
31
185
  end
32
186
  end
@@ -0,0 +1,66 @@
1
+ module Sprockets
2
+ class Cache
3
+ # Public: Basic in memory LRU cache.
4
+ #
5
+ # Assign the instance to the Environment#cache.
6
+ #
7
+ # environment.cache = Sprockets::Cache::MemoryStore.new(1000)
8
+ #
9
+ # See Also
10
+ #
11
+ # ActiveSupport::Cache::MemoryStore
12
+ #
13
+ class MemoryStore
14
+ # Internal: Default key limit for store.
15
+ DEFAULT_MAX_SIZE = 1000
16
+
17
+ # Public: Initialize the cache store.
18
+ #
19
+ # max_size - A Integer of the maximum number of keys the store will hold.
20
+ # (default: 1000).
21
+ def initialize(max_size = DEFAULT_MAX_SIZE)
22
+ @max_size = max_size
23
+ @cache = {}
24
+ end
25
+
26
+ # Public: Retrieve value from cache.
27
+ #
28
+ # This API should not be used directly, but via the Cache wrapper API.
29
+ #
30
+ # key - String cache key.
31
+ #
32
+ # Returns Object or nil or the value is not set.
33
+ def get(key)
34
+ exists = true
35
+ value = @cache.delete(key) { exists = false }
36
+ if exists
37
+ @cache[key] = value
38
+ else
39
+ nil
40
+ end
41
+ end
42
+
43
+ # Public: Set a key and value in the cache.
44
+ #
45
+ # This API should not be used directly, but via the Cache wrapper API.
46
+ #
47
+ # key - String cache key.
48
+ # value - Object value.
49
+ #
50
+ # Returns Object value.
51
+ def set(key, value)
52
+ @cache.delete(key)
53
+ @cache[key] = value
54
+ @cache.shift if @cache.size > @max_size
55
+ value
56
+ end
57
+
58
+ # Public: Pretty inspect
59
+ #
60
+ # Returns String.
61
+ def inspect
62
+ "#<#{self.class} size=#{@cache.size}/#{@max_size}>"
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,46 @@
1
+ module Sprockets
2
+ class Cache
3
+ # Public: A compatible cache store that doesn't store anything. Used by
4
+ # default when no Environment#cache is configured.
5
+ #
6
+ # Assign the instance to the Environment#cache.
7
+ #
8
+ # environment.cache = Sprockets::Cache::NullStore.new
9
+ #
10
+ # See Also
11
+ #
12
+ # ActiveSupport::Cache::NullStore
13
+ #
14
+ class NullStore
15
+ # Public: Simulate a cache miss.
16
+ #
17
+ # This API should not be used directly, but via the Cache wrapper API.
18
+ #
19
+ # key - String cache key.
20
+ #
21
+ # Returns nil.
22
+ def get(key)
23
+ nil
24
+ end
25
+
26
+ # Public: Simulate setting a value in the cache.
27
+ #
28
+ # This API should not be used directly, but via the Cache wrapper API.
29
+ #
30
+ # key - String cache key.
31
+ # value - Object value.
32
+ #
33
+ # Returns Object value.
34
+ def set(key, value)
35
+ value
36
+ end
37
+
38
+ # Public: Pretty inspect
39
+ #
40
+ # Returns String.
41
+ def inspect
42
+ "#<#{self.class}>"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,236 @@
1
+ require 'logger'
2
+ require 'sprockets/digest_utils'
3
+
4
+ module Sprockets
5
+ # Public: Wrapper interface to backend cache stores. Ensures a consistent API
6
+ # even when the backend uses get/set or read/write.
7
+ #
8
+ # Public cache interface
9
+ #
10
+ # Always assign the backend store instance to Environment#cache=.
11
+ #
12
+ # environment.cache = Sprockets::Cache::MemoryStore.new(1000)
13
+ #
14
+ # Environment#cache will always return a wrapped Cache interface. See the
15
+ # methods marked public on this class.
16
+ #
17
+ #
18
+ # Backend cache interface
19
+ #
20
+ # The Backend cache store must implement two methods.
21
+ #
22
+ # get(key)
23
+ #
24
+ # key - An opaque String with a length less than 250 characters.
25
+ #
26
+ # Returns an JSON serializable object.
27
+ #
28
+ # set(key, value)
29
+ #
30
+ # Will only be called once per key. Setting a key "foo" with value "bar",
31
+ # then later key "foo" with value "baz" is an undefined behavior.
32
+ #
33
+ # key - An opaque String with a length less than 250 characters.
34
+ # value - A JSON serializable object.
35
+ #
36
+ # Returns argument value.
37
+ #
38
+ class Cache
39
+ # Builtin cache stores.
40
+ autoload :FileStore, 'sprockets/cache/file_store'
41
+ autoload :MemoryStore, 'sprockets/cache/memory_store'
42
+ autoload :NullStore, 'sprockets/cache/null_store'
43
+
44
+ # Internal: Cache key version for this class. Rarely should have to change
45
+ # unless the cache format radically changes. Will be bump on major version
46
+ # releases though.
47
+ VERSION = '3.0'
48
+
49
+ def self.default_logger
50
+ logger = Logger.new($stderr)
51
+ logger.level = Logger::FATAL
52
+ logger
53
+ end
54
+
55
+ # Internal: Wrap a backend cache store.
56
+ #
57
+ # Always assign a backend cache store instance to Environment#cache= and
58
+ # use Environment#cache to retreive a wrapped interface.
59
+ #
60
+ # cache - A compatible backend cache store instance.
61
+ def initialize(cache = nil, logger = self.class.default_logger)
62
+ @cache_wrapper = get_cache_wrapper(cache)
63
+ @fetch_cache = Cache::MemoryStore.new(1024)
64
+ @logger = logger
65
+ end
66
+
67
+ # Public: Prefer API to retrieve and set values in the cache store.
68
+ #
69
+ # key - JSON serializable key
70
+ # block -
71
+ # Must return a consistent JSON serializable object for the given key.
72
+ #
73
+ # Examples
74
+ #
75
+ # cache.fetch("foo") { "bar" }
76
+ #
77
+ # Returns a JSON serializable object.
78
+ def fetch(key)
79
+ start = Time.now.to_f
80
+ expanded_key = expand_key(key)
81
+ value = @fetch_cache.get(expanded_key)
82
+ if value.nil?
83
+ value = @cache_wrapper.get(expanded_key)
84
+ if value.nil?
85
+ value = yield
86
+ @cache_wrapper.set(expanded_key, value)
87
+ @logger.debug do
88
+ ms = "(#{((Time.now.to_f - start) * 1000).to_i}ms)"
89
+ "Sprockets Cache miss #{peek_key(key)} #{ms}"
90
+ end
91
+ end
92
+ @fetch_cache.set(expanded_key, value)
93
+ end
94
+ value
95
+ end
96
+
97
+ # Public: Low level API to retrieve item directly from the backend cache
98
+ # store.
99
+ #
100
+ # This API may be used publicly, but may have undefined behavior
101
+ # depending on the backend store being used. Prefer the
102
+ # Cache#fetch API over using this.
103
+ #
104
+ # key - JSON serializable key
105
+ # local - Check local cache first (default: false)
106
+ #
107
+ # Returns a JSON serializable object or nil if there was a cache miss.
108
+ def get(key, local = false)
109
+ expanded_key = expand_key(key)
110
+
111
+ if local && value = @fetch_cache.get(expanded_key)
112
+ return value
113
+ end
114
+
115
+ value = @cache_wrapper.get(expanded_key)
116
+ @fetch_cache.set(expanded_key, value) if local
117
+
118
+ value
119
+ end
120
+
121
+ # Public: Low level API to set item directly to the backend cache store.
122
+ #
123
+ # This API may be used publicly, but may have undefined behavior
124
+ # depending on the backend store being used. Prefer the
125
+ # Cache#fetch API over using this.
126
+ #
127
+ # key - JSON serializable key
128
+ # value - A consistent JSON serializable object for the given key. Setting
129
+ # a different value for the given key has undefined behavior.
130
+ # local - Set on local cache (default: false)
131
+ #
132
+ # Returns the value argument.
133
+ def set(key, value, local = false)
134
+ expanded_key = expand_key(key)
135
+ @fetch_cache.set(expanded_key, value) if local
136
+ @cache_wrapper.set(expanded_key, value)
137
+ end
138
+
139
+ # Public: Pretty inspect
140
+ #
141
+ # Returns String.
142
+ def inspect
143
+ "#<#{self.class} local=#{@fetch_cache.inspect} store=#{@cache_wrapper.cache.inspect}>"
144
+ end
145
+
146
+ private
147
+ # Internal: Expand object cache key into a short String key.
148
+ #
149
+ # The String should be under 250 characters so its compatible with
150
+ # Memcache.
151
+ #
152
+ # key - JSON serializable key
153
+ #
154
+ # Returns a String with a length less than 250 characters.
155
+ def expand_key(key)
156
+ digest_key = DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key))
157
+ namespace = digest_key[0, 2]
158
+ "sprockets/v#{VERSION}/#{namespace}/#{digest_key}"
159
+ end
160
+
161
+ PEEK_SIZE = 100
162
+
163
+ # Internal: Show first 100 characters of cache key for logging purposes.
164
+ #
165
+ # Returns a String with a length less than 100 characters.
166
+ def peek_key(key)
167
+ case key
168
+ when Integer
169
+ key.to_s
170
+ when String
171
+ key[0, PEEK_SIZE].inspect
172
+ when Array
173
+ str = []
174
+ key.each { |k| str << peek_key(k) }
175
+ str.join(':')[0, PEEK_SIZE]
176
+ else
177
+ peek_key(DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key)))
178
+ end
179
+ end
180
+
181
+ def get_cache_wrapper(cache)
182
+ if cache.is_a?(Cache)
183
+ cache
184
+
185
+ # `Cache#get(key)` for Memcache
186
+ elsif cache.respond_to?(:get)
187
+ GetWrapper.new(cache)
188
+
189
+ # `Cache#[key]` so `Hash` can be used
190
+ elsif cache.respond_to?(:[])
191
+ HashWrapper.new(cache)
192
+
193
+ # `Cache#read(key)` for `ActiveSupport::Cache` support
194
+ elsif cache.respond_to?(:read)
195
+ ReadWriteWrapper.new(cache)
196
+
197
+ else
198
+ cache = Sprockets::Cache::NullStore.new
199
+ GetWrapper.new(cache)
200
+ end
201
+ end
202
+
203
+ class Wrapper < Struct.new(:cache)
204
+ end
205
+
206
+ class GetWrapper < Wrapper
207
+ def get(key)
208
+ cache.get(key)
209
+ end
210
+
211
+ def set(key, value)
212
+ cache.set(key, value)
213
+ end
214
+ end
215
+
216
+ class HashWrapper < Wrapper
217
+ def get(key)
218
+ cache[key]
219
+ end
220
+
221
+ def set(key, value)
222
+ cache[key] = value
223
+ end
224
+ end
225
+
226
+ class ReadWriteWrapper < Wrapper
227
+ def get(key)
228
+ cache.read(key)
229
+ end
230
+
231
+ def set(key, value)
232
+ cache.write(key, value)
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,69 @@
1
+ require 'sprockets/base'
2
+
3
+ module Sprockets
4
+ # `Cached` is a special cached version of `Environment`.
5
+ #
6
+ # The expection is that all of its file system methods are cached
7
+ # for the instances lifetime. This makes `Cached` much faster. This
8
+ # behavior is ideal in production environments where the file system
9
+ # is immutable.
10
+ #
11
+ # `Cached` should not be initialized directly. Instead use
12
+ # `Environment#cached`.
13
+ class CachedEnvironment < Base
14
+ def initialize(environment)
15
+ initialize_configuration(environment)
16
+
17
+ @cache = environment.cache
18
+ @stats = Hash.new { |h, k| h[k] = _stat(k) }
19
+ @entries = Hash.new { |h, k| h[k] = _entries(k) }
20
+ @uris = Hash.new { |h, k| h[k] = _load(k) }
21
+
22
+ @processor_cache_keys = Hash.new { |h, k| h[k] = _processor_cache_key(k) }
23
+ @resolved_dependencies = Hash.new { |h, k| h[k] = _resolve_dependency(k) }
24
+ end
25
+
26
+ # No-op return self as cached environment.
27
+ def cached
28
+ self
29
+ end
30
+ alias_method :index, :cached
31
+
32
+ # Internal: Cache Environment#entries
33
+ alias_method :_entries, :entries
34
+ def entries(path)
35
+ @entries[path]
36
+ end
37
+
38
+ # Internal: Cache Environment#stat
39
+ alias_method :_stat, :stat
40
+ def stat(path)
41
+ @stats[path]
42
+ end
43
+
44
+ # Internal: Cache Environment#load
45
+ alias_method :_load, :load
46
+ def load(uri)
47
+ @uris[uri]
48
+ end
49
+
50
+ # Internal: Cache Environment#processor_cache_key
51
+ alias_method :_processor_cache_key, :processor_cache_key
52
+ def processor_cache_key(str)
53
+ @processor_cache_keys[str]
54
+ end
55
+
56
+ # Internal: Cache Environment#resolve_dependency
57
+ alias_method :_resolve_dependency, :resolve_dependency
58
+ def resolve_dependency(str)
59
+ @resolved_dependencies[str]
60
+ end
61
+
62
+ private
63
+ # Cache is immutable, any methods that try to change the runtime config
64
+ # should bomb.
65
+ def config=(config)
66
+ raise RuntimeError, "can't modify immutable cached environment"
67
+ end
68
+ end
69
+ end
@@ -1,22 +1,47 @@
1
- require 'tilt'
1
+ require 'sprockets/autoload'
2
+ require 'sprockets/digest_utils'
2
3
 
3
4
  module Sprockets
4
- class ClosureCompressor < Tilt::Template
5
- self.default_mime_type = 'application/javascript'
5
+ # Public: Closure Compiler minifier.
6
+ #
7
+ # To accept the default options
8
+ #
9
+ # environment.register_bundle_processor 'application/javascript',
10
+ # Sprockets::ClosureCompressor
11
+ #
12
+ # Or to pass options to the Closure::Compiler class.
13
+ #
14
+ # environment.register_bundle_processor 'application/javascript',
15
+ # Sprockets::ClosureCompressor.new({ ... })
16
+ #
17
+ class ClosureCompressor
18
+ VERSION = '1'
6
19
 
7
- def self.engine_initialized?
8
- defined?(::Closure::Compiler)
20
+ # Public: Return singleton instance with default options.
21
+ #
22
+ # Returns ClosureCompressor object.
23
+ def self.instance
24
+ @instance ||= new
9
25
  end
10
26
 
11
- def initialize_engine
12
- require_template_library 'closure-compiler'
27
+ def self.call(input)
28
+ instance.call(input)
13
29
  end
14
30
 
15
- def prepare
31
+ def self.cache_key
32
+ instance.cache_key
16
33
  end
17
34
 
18
- def evaluate(context, locals, &block)
19
- Closure::Compiler.new.compile(data)
35
+ attr_reader :cache_key
36
+
37
+ def initialize(options = {})
38
+ @options = options
39
+ @cache_key = "#{self.class.name}:#{Autoload::Closure::VERSION}:#{Autoload::Closure::COMPILER_VERSION}:#{VERSION}:#{DigestUtils.digest(options)}".freeze
40
+ end
41
+
42
+ def call(input)
43
+ @compiler ||= Autoload::Closure::Compiler.new(@options)
44
+ @compiler.compile(input[:data])
20
45
  end
21
46
  end
22
47
  end
@@ -0,0 +1,25 @@
1
+ require 'sprockets/autoload'
2
+
3
+ module Sprockets
4
+ # Processor engine class for the CoffeeScript compiler.
5
+ # Depends on the `coffee-script` and `coffee-script-source` gems.
6
+ #
7
+ # For more infomation see:
8
+ #
9
+ # https://github.com/josh/ruby-coffee-script
10
+ #
11
+ module CoffeeScriptProcessor
12
+ VERSION = '1'
13
+
14
+ def self.cache_key
15
+ @cache_key ||= "#{name}:#{Autoload::CoffeeScript::Source.version}:#{VERSION}".freeze
16
+ end
17
+
18
+ def self.call(input)
19
+ data = input[:data]
20
+ input[:cache].fetch([self.cache_key, data]) do
21
+ Autoload::CoffeeScript.compile(data)
22
+ end
23
+ end
24
+ end
25
+ end