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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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