sprockets 3.0.0.beta.3 → 3.0.0.beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
1
  require 'sprockets/encoding_utils'
2
+ require 'sprockets/http_utils'
2
3
 
3
4
  module Sprockets
4
5
  module Mime
@@ -119,10 +120,6 @@ module Sprockets
119
120
  end
120
121
 
121
122
  private
122
- def read_input(input)
123
- read_file(input[:filename], input[:content_type])
124
- end
125
-
126
123
  # Internal: Get a postprocessor to perform the encoding.
127
124
  #
128
125
  # encoding - String encoding.
@@ -0,0 +1,47 @@
1
+ require 'sprockets/digest_utils'
2
+ require 'sprockets/path_utils'
3
+
4
+ module Sprockets
5
+ # Internal: Crossover of path and digest utilities functions.
6
+ module PathDigestUtils
7
+ include DigestUtils, PathUtils
8
+
9
+ # Internal: Compute digest for file stat.
10
+ #
11
+ # path - String filename
12
+ # stat - File::Stat
13
+ #
14
+ # Returns String digest bytes.
15
+ def stat_digest(path, stat)
16
+ if stat.directory?
17
+ # If its a directive, digest the list of filenames
18
+ digest_class.digest(self.entries(path).join(','))
19
+ elsif stat.file?
20
+ # If its a file, digest the contents
21
+ digest_class.file(path.to_s).digest
22
+ else
23
+ raise TypeError, "stat was not a directory or file: #{stat.ftype}"
24
+ end
25
+ end
26
+
27
+ # Internal: Compute digest for path.
28
+ #
29
+ # path - String filename or directory path.
30
+ #
31
+ # Returns String digest bytes or nil.
32
+ def file_digest(path)
33
+ if stat = self.stat(path)
34
+ self.stat_digest(path, stat)
35
+ end
36
+ end
37
+
38
+ # Internal: Compute digest for a set of paths.
39
+ #
40
+ # paths - Array of filename or directory paths.
41
+ #
42
+ # Returns String digest bytes.
43
+ def files_digest(paths)
44
+ self.digest(paths.map { |path| self.file_digest(path) })
45
+ end
46
+ end
47
+ end
@@ -195,6 +195,28 @@ module Sprockets
195
195
  nil
196
196
  end
197
197
 
198
+ # Internal: Recursive stat all the files under a directory in alphabetical
199
+ # order.
200
+ #
201
+ # dir - A String directory
202
+ #
203
+ # Returns an Enumerator of [path, stat].
204
+ def stat_sorted_tree(dir, &block)
205
+ return to_enum(__method__, dir) unless block_given?
206
+
207
+ self.stat_directory(dir).sort_by { |path, stat|
208
+ stat.directory? ? "#{path}/" : path
209
+ }.each do |path, stat|
210
+ yield path, stat
211
+
212
+ if stat.directory?
213
+ stat_sorted_tree(path, &block)
214
+ end
215
+ end
216
+
217
+ nil
218
+ end
219
+
198
220
  # Internal: Write to a file atomically. Useful for situations where you
199
221
  # don't want other processes or threads to see half-written files.
200
222
  #
@@ -1,5 +1,9 @@
1
+ require 'sprockets/path_utils'
2
+
1
3
  module Sprockets
2
4
  module Paths
5
+ include PathUtils
6
+
3
7
  # Returns `Environment` root.
4
8
  #
5
9
  # All relative paths are expanded with root as its base. To be
@@ -41,5 +45,22 @@ module Sprockets
41
45
  paths.clear
42
46
  end
43
47
  end
48
+
49
+ # Public: Iterate over every file under all load paths.
50
+ #
51
+ # Returns Enumerator if no block is given.
52
+ def each_file
53
+ return to_enum(__method__) unless block_given?
54
+
55
+ paths.each do |root|
56
+ stat_tree(root).each do |filename, stat|
57
+ if stat.file?
58
+ yield filename
59
+ end
60
+ end
61
+ end
62
+
63
+ nil
64
+ end
44
65
  end
45
66
  end
@@ -156,61 +156,17 @@ module Sprockets
156
156
  end
157
157
  end
158
158
 
159
- # Internal: Run processors on filename and data.
160
- #
161
- # Returns Hash.
162
- def process(processors, uri, filename, load_path, name, content_type)
163
- data, metadata = nil, {}
164
-
165
- input = {
166
- environment: self,
167
- cache: cache,
168
- uri: uri,
169
- filename: filename,
170
- load_path: load_path,
171
- name: name,
172
- content_type: content_type,
173
- metadata: metadata
174
- }
175
-
176
- processors.each do |processor|
177
- begin
178
- result = processor.call(input.merge(data: data, metadata: metadata))
179
- case result
180
- when NilClass
181
- # noop
182
- when Hash
183
- data = result[:data] if result.key?(:data)
184
- metadata = metadata.merge(result)
185
- metadata.delete(:data)
186
- when String
187
- data = result
188
- else
189
- raise Error, "invalid processor return type: #{result.class}"
190
- end
191
- end
192
- end
193
-
194
- {
195
- source: data,
196
- charset: data.encoding.name.downcase,
197
- length: data.bytesize,
198
- digest: digest(data),
199
- metadata: metadata
200
- }
201
- end
202
-
203
159
  # Internal: Two dimensional Hash of reducer functions for a given mime type
204
160
  # and asset metadata key.
205
161
  attr_reader :bundle_reducers
206
162
 
207
- # Public: Register bundle reducer function.
163
+ # Public: Register bundle metadata reducer function.
208
164
  #
209
165
  # Examples
210
166
  #
211
- # Sprockets.register_bundle_reducer 'application/javascript', :jshint_errors, [], :+
167
+ # Sprockets.register_bundle_metadata_reducer 'application/javascript', :jshint_errors, [], :+
212
168
  #
213
- # Sprockets.register_bundle_reducer 'text/css', :selector_count, 0 { |total, count|
169
+ # Sprockets.register_bundle_metadata_reducer 'text/css', :selector_count, 0 { |total, count|
214
170
  # total + count
215
171
  # }
216
172
  #
@@ -220,7 +176,7 @@ module Sprockets
220
176
  # block - Proc accepting the memo accumulator and current value
221
177
  #
222
178
  # Returns nothing.
223
- def register_bundle_reducer(mime_type, key, *args, &block)
179
+ def register_bundle_metadata_reducer(mime_type, key, *args, &block)
224
180
  case args.size
225
181
  when 0
226
182
  reducer = block
@@ -1,106 +1,57 @@
1
+ require 'sprockets/asset_uri'
2
+
1
3
  module Sprockets
2
4
  module Resolve
3
- # Public: Iterate over every file under all load paths.
4
- #
5
- # Returns Enumerator if no block is given.
6
- def each_file
7
- return to_enum(__method__) unless block_given?
8
-
9
- paths.each do |root|
10
- stat_tree(root).each do |filename, stat|
11
- if stat.file?
12
- yield filename
13
- end
14
- end
15
- end
16
-
17
- nil
18
- end
19
-
20
- # Finds the expanded real path for a given logical path by
21
- # searching the environment's paths.
5
+ # Public: Finds the absolute path for a given logical path by searching the
6
+ # environment's load paths.
22
7
  #
23
8
  # resolve("application.js")
24
- # # => "/path/to/app/javascripts/application.js.coffee"
9
+ # # => "/path/to/app/javascripts/application.js"
10
+ #
11
+ # An accept content type can be given if the logical path doesn't have a
12
+ # format extension.
25
13
  #
26
- # A `FileNotFound` exception is raised if the file does not exist.
14
+ # resolve("application", accept: "application/javascript")
15
+ # # => "/path/to/app/javascripts/application.js"
16
+ #
17
+ # The String path is returned or nil if no results are found.
27
18
  def resolve(path, options = {})
28
- resolve_all(path, options) do |filename|
29
- return filename
30
- end
31
-
32
- accept = options[:accept]
33
- message = "couldn't find file '#{path}'"
34
- message << " with type '#{accept}'" if accept
35
- raise FileNotFound, message
36
- end
19
+ logical_name, mime_type, _ = parse_path_extnames(path)
37
20
 
38
- def resolve_in_load_path(load_path, logical_path, options = {})
39
- if !self.paths.include?(load_path.to_s)
40
- raise FileOutsidePaths, "#{load_path} isn't in paths: #{self.paths.join(', ')}"
41
- end
21
+ paths = options[:load_paths] || self.paths
42
22
 
43
- resolve_all_under_load_path(load_path, logical_path, options) do |filename|
44
- return filename
23
+ if absolute_path?(path)
24
+ path = File.expand_path(path)
25
+ if paths_split(paths, path) && file?(path)
26
+ if accept = options[:accept]
27
+ find_best_q_match(accept, [path]) do |candidate, matcher|
28
+ match_mime_type?(mime_type || "application/octet-stream", matcher)
29
+ end
30
+ else
31
+ path
32
+ end
33
+ end
34
+ else
35
+ accepts = parse_accept_options(mime_type, options[:accept])
36
+ filename, _ = resolve_under_paths(paths, logical_name, mime_type, accepts)
37
+ filename
45
38
  end
46
-
47
- accept = options[:accept]
48
- message = "couldn't find file '#{logical_path}' under '#{load_path}'"
49
- message << " with type '#{accept}'" if accept
50
- raise FileNotFound, message
51
39
  end
52
40
 
53
- def resolve_all_under_load_path(load_path, logical_path, options = {}, &block)
54
- return to_enum(__method__, load_path, logical_path, options) unless block_given?
55
-
56
- logical_name, mime_type, _ = parse_path_extnames(logical_path)
57
- logical_basename = File.basename(logical_name)
58
-
59
- accepts = parse_accept_options(mime_type, options[:accept])
60
-
61
- _resolve_all_under_load_path(load_path, logical_name, logical_basename, accepts, &block)
62
-
63
- nil
64
- end
65
-
66
- # Public: Finds the expanded real path for a given logical path by searching
67
- # the environment's paths. Includes all matching paths including fallbacks
68
- # and shadowed matches.
41
+ # Public: Find Asset URI for given a logical path by searching the
42
+ # environment's load paths.
69
43
  #
70
- # resolve_all("application.js").first
71
- # # => "/path/to/app/javascripts/application.js.coffee"
44
+ # locate("application.js")
45
+ # # => "file:///path/to/app/javascripts/application.js?content_type=application/javascript"
72
46
  #
73
- # `resolve_all` returns an `Enumerator`. This allows you to filter your
74
- # matches by any condition.
47
+ # An accept content type can be given if the logical path doesn't have a
48
+ # format extension.
75
49
  #
76
- # resolve_all("application").find do |path|
77
- # mime_type_for(path) == "text/css"
78
- # end
50
+ # locate("application", accept: "application/javascript")
51
+ # # => "file:///path/to/app/javascripts/application.coffee?content_type=application/javascript"
79
52
  #
80
- def resolve_all(path, options = {}, &block)
81
- return to_enum(__method__, path, options) unless block_given?
82
- path = path.to_s
83
-
84
- logical_name, mime_type, _ = parse_path_extnames(path)
85
- logical_basename = File.basename(logical_name)
86
-
87
- accepts = parse_accept_options(mime_type, options[:accept])
88
-
89
- self.paths.each do |load_path|
90
- _resolve_all_under_load_path(load_path, logical_name, logical_basename, accepts, &block)
91
- end
92
-
93
- nil
94
- end
95
-
96
- # Experimental: Get transform type for filename
97
- def resolve_path_transform_type(filename, accept)
98
- mime_type = parse_path_extnames(filename)[1]
99
- resolve_transform_type(mime_type, accept)
100
- end
101
-
102
- # Experimental
103
- def resolve_asset_uri(path, options = {})
53
+ # The String Asset URI is returned or nil if no results are found.
54
+ def locate(path, options = {})
104
55
  path = path.to_s
105
56
  accept = options[:accept]
106
57
  skip_bundle = options.key?(:bundle) ? !options[:bundle] : false
@@ -108,19 +59,29 @@ module Sprockets
108
59
  available_encodings = self.encodings.keys + ['identity']
109
60
  encoding = find_best_q_match(options[:accept_encoding], available_encodings)
110
61
 
62
+ paths = options[:load_paths] || self.paths
63
+
111
64
  if absolute_path?(path)
112
65
  path = File.expand_path(path)
113
- if paths_split(self.paths, path) && file?(path) &&
114
- (accept.nil? || resolve_path_transform_type(path, accept))
115
- filename = path
116
- type = resolve_path_transform_type(path, accept)
66
+ if paths_split(paths, path) && file?(path)
67
+ mime_type = parse_path_extnames(path)[1]
68
+ _type = resolve_transform_type(mime_type, accept)
69
+ if !accept || _type
70
+ filename = path
71
+ type = _type
72
+ end
117
73
  end
118
74
  else
119
- if filename = resolve_all(path, accept: accept).first
120
- mime_type = parse_path_extnames(path)[1]
121
- accept = parse_accept_options(mime_type, accept).map { |t, v| "#{t}; q=#{v}" }.join(", ")
122
- type = resolve_path_transform_type(filename, accept)
75
+ logical_name, mime_type, _ = parse_path_extnames(path)
76
+ parsed_accept = parse_accept_options(mime_type, accept)
77
+
78
+ if parsed_accept.empty?
79
+ return
123
80
  end
81
+
82
+ transformed_accepts = expand_transform_accepts(parsed_accept)
83
+ filename, mime_type = resolve_under_paths(paths, logical_name, mime_type, transformed_accepts)
84
+ type = resolve_transform_type(mime_type, parsed_accept) if filename
124
85
  end
125
86
 
126
87
  if filename
@@ -129,58 +90,21 @@ module Sprockets
129
90
  end
130
91
  end
131
92
 
132
- # Public: Enumerate over all logical paths in the environment.
133
- #
134
- # Returns an Enumerator of [logical_path, filename].
135
- def logical_paths
136
- return to_enum(__method__) unless block_given?
137
-
138
- seen = Set.new
139
-
140
- self.paths.each do |load_path|
141
- stat_tree(load_path).each do |filename, stat|
142
- next unless stat.file?
143
-
144
- path = split_subpath(load_path, filename)
145
- path, mime_type, _ = parse_path_extnames(path)
146
- path = normalize_logical_path(path)
147
- path += mime_types[mime_type][:extensions].first if mime_type
93
+ protected
94
+ def resolve_under_paths(paths, logical_name, mime_type, accepts)
95
+ logical_basename = File.basename(logical_name)
148
96
 
149
- if !seen.include?(path)
150
- yield path, filename
151
- seen << path
97
+ paths.each do |load_path|
98
+ candidates = path_matches(load_path, logical_name, logical_basename)
99
+ candidate = find_best_q_match(accepts, candidates) do |c, matcher|
100
+ match_mime_type?(c[1] || "application/octet-stream", matcher)
152
101
  end
102
+ return candidate if candidate
153
103
  end
154
- end
155
-
156
- nil
157
- end
158
-
159
- # Deprecated: Iterate over all logical paths with a matcher.
160
- #
161
- # Remove from 4.x.
162
- #
163
- # args - List of matcher objects.
164
- #
165
- # Returns Enumerator if no block is given.
166
- def each_logical_path(*args, &block)
167
- return to_enum(__method__, *args) unless block_given?
168
104
 
169
- filters = args.flatten.map { |arg| Manifest.compile_match_filter(arg) }
170
- logical_paths.each do |a, b|
171
- if filters.any? { |f| f.call(a, b) }
172
- if block.arity == 2
173
- yield a, b
174
- else
175
- yield a
176
- end
177
- end
105
+ nil
178
106
  end
179
107
 
180
- nil
181
- end
182
-
183
- protected
184
108
  def parse_accept_options(mime_type, types)
185
109
  accepts = []
186
110
  accepts += parse_q_values(types) if types
@@ -206,52 +130,20 @@ module Sprockets
206
130
  path
207
131
  end
208
132
 
209
- def _resolve_all_under_load_path(load_path, logical_name, logical_basename, accepts, &block)
210
- filenames = path_matches(load_path, logical_name, logical_basename)
211
-
212
- matches = []
213
-
214
- # TODO: Cleanup double iteration of accept and filenames
215
-
216
- # Exact mime type match first
217
- matches += find_q_matches(accepts, filenames) do |filename, accepted|
218
- if !file?(filename)
219
- nil
220
- elsif accepted == '*/*'
221
- filename
222
- elsif parse_path_extnames(filename)[1] == accepted
223
- filename
224
- end
225
- end
226
-
227
- # Then transformable match
228
- matches += find_q_matches(accepts, filenames) do |filename, accepted|
229
- if !file?(filename)
230
- nil
231
- elsif accepted == '*/*'
232
- filename
233
- elsif resolve_path_transform_type(filename, accepted)
234
- filename
235
- end
236
- end
237
-
238
- matches.uniq.each(&block)
239
- end
240
-
241
133
  def path_matches(load_path, logical_name, logical_basename)
242
- filenames = []
134
+ candidates = []
243
135
  dirname = File.dirname(File.join(load_path, logical_name))
244
- dirname_matches(dirname, logical_basename) { |fn| filenames << fn }
245
- resolve_alternates(load_path, logical_name) { |fn| filenames << fn }
246
- dirname_matches(File.join(load_path, logical_name), "index") { |fn| filenames << fn }
247
- filenames
136
+ dirname_matches(dirname, logical_basename) { |candidate| candidates << candidate }
137
+ resolve_alternates(load_path, logical_name) { |fn| candidates << [fn, parse_path_extnames(fn)[1]] }
138
+ dirname_matches(File.join(load_path, logical_name), "index") { |candidate| candidates << candidate }
139
+ candidates.select { |fn, _| file?(fn) }
248
140
  end
249
141
 
250
142
  def dirname_matches(dirname, basename)
251
143
  self.entries(dirname).each do |entry|
252
- name = parse_path_extnames(entry)[0]
144
+ name, type, _ = parse_path_extnames(entry)
253
145
  if basename == name
254
- yield File.join(dirname, entry)
146
+ yield [File.join(dirname, entry), type]
255
147
  end
256
148
  end
257
149
  end