sprockets 4.0.1
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +72 -0
- data/README.md +665 -0
- data/bin/sprockets +93 -0
- data/lib/rake/sprocketstask.rb +153 -0
- data/lib/sprockets.rb +229 -0
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +202 -0
- data/lib/sprockets/autoload.rb +16 -0
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +8 -0
- data/lib/sprockets/autoload/coffee_script.rb +8 -0
- data/lib/sprockets/autoload/eco.rb +8 -0
- data/lib/sprockets/autoload/ejs.rb +8 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +8 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +8 -0
- data/lib/sprockets/autoload/yui.rb +8 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +147 -0
- data/lib/sprockets/bower.rb +61 -0
- data/lib/sprockets/bundle.rb +105 -0
- data/lib/sprockets/cache.rb +271 -0
- data/lib/sprockets/cache/file_store.rb +208 -0
- data/lib/sprockets/cache/memory_store.rb +75 -0
- data/lib/sprockets/cache/null_store.rb +54 -0
- data/lib/sprockets/cached_environment.rb +64 -0
- data/lib/sprockets/closure_compressor.rb +48 -0
- data/lib/sprockets/coffee_script_processor.rb +39 -0
- data/lib/sprockets/compressing.rb +134 -0
- data/lib/sprockets/configuration.rb +79 -0
- data/lib/sprockets/context.rb +304 -0
- data/lib/sprockets/dependencies.rb +74 -0
- data/lib/sprockets/digest_utils.rb +200 -0
- data/lib/sprockets/directive_processor.rb +414 -0
- data/lib/sprockets/eco_processor.rb +33 -0
- data/lib/sprockets/ejs_processor.rb +32 -0
- data/lib/sprockets/encoding_utils.rb +262 -0
- data/lib/sprockets/environment.rb +46 -0
- data/lib/sprockets/erb_processor.rb +37 -0
- data/lib/sprockets/errors.rb +12 -0
- data/lib/sprockets/exporters/base.rb +71 -0
- data/lib/sprockets/exporters/file_exporter.rb +24 -0
- data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
- data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
- data/lib/sprockets/exporting.rb +73 -0
- data/lib/sprockets/file_reader.rb +16 -0
- data/lib/sprockets/http_utils.rb +135 -0
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +50 -0
- data/lib/sprockets/loader.rb +345 -0
- data/lib/sprockets/manifest.rb +338 -0
- data/lib/sprockets/manifest_utils.rb +48 -0
- data/lib/sprockets/mime.rb +96 -0
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +77 -0
- data/lib/sprockets/path_digest_utils.rb +48 -0
- data/lib/sprockets/path_utils.rb +367 -0
- data/lib/sprockets/paths.rb +82 -0
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +228 -0
- data/lib/sprockets/processor_utils.rb +169 -0
- data/lib/sprockets/resolve.rb +295 -0
- data/lib/sprockets/sass_cache_store.rb +30 -0
- data/lib/sprockets/sass_compressor.rb +63 -0
- data/lib/sprockets/sass_functions.rb +3 -0
- data/lib/sprockets/sass_importer.rb +3 -0
- data/lib/sprockets/sass_processor.rb +313 -0
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +295 -0
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +173 -0
- data/lib/sprockets/uglifier_compressor.rb +66 -0
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +191 -0
- data/lib/sprockets/utils.rb +202 -0
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/version.rb +4 -0
- data/lib/sprockets/yui_compressor.rb +56 -0
- metadata +444 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
module Npm
|
6
|
+
# Internal: Override resolve_alternates to install package.json behavior.
|
7
|
+
#
|
8
|
+
# load_path - String environment path
|
9
|
+
# logical_path - String path relative to base
|
10
|
+
#
|
11
|
+
# Returns candiate filenames.
|
12
|
+
def resolve_alternates(load_path, logical_path)
|
13
|
+
candidates, deps = super
|
14
|
+
|
15
|
+
dirname = File.join(load_path, logical_path)
|
16
|
+
|
17
|
+
if directory?(dirname)
|
18
|
+
filename = File.join(dirname, 'package.json')
|
19
|
+
|
20
|
+
if self.file?(filename)
|
21
|
+
deps << build_file_digest_uri(filename)
|
22
|
+
read_package_directives(dirname, filename) do |path|
|
23
|
+
if file?(path)
|
24
|
+
candidates << path
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
return candidates, deps
|
31
|
+
end
|
32
|
+
|
33
|
+
# Internal: Read package.json's main and style directives.
|
34
|
+
#
|
35
|
+
# dirname - String path to component directory.
|
36
|
+
# filename - String path to package.json.
|
37
|
+
#
|
38
|
+
# Returns nothing.
|
39
|
+
def read_package_directives(dirname, filename)
|
40
|
+
package = JSON.parse(File.read(filename), create_additions: false)
|
41
|
+
|
42
|
+
case package['main']
|
43
|
+
when String
|
44
|
+
yield File.expand_path(package['main'], dirname)
|
45
|
+
when nil
|
46
|
+
yield File.expand_path('index.js', dirname)
|
47
|
+
end
|
48
|
+
|
49
|
+
yield File.expand_path(package['style'], dirname) if package['style']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'set'
|
3
|
+
require 'sprockets/path_utils'
|
4
|
+
require 'sprockets/uri_utils'
|
5
|
+
|
6
|
+
module Sprockets
|
7
|
+
# Internal: Related PathUtils helpers that also track all the file system
|
8
|
+
# calls they make for caching purposes. All functions return a standard
|
9
|
+
# return value and a Set of cache dependency URIs that can be used in the
|
10
|
+
# future to see if the returned value should be invalidated from cache.
|
11
|
+
#
|
12
|
+
# entries_with_dependencies("app/assets/javascripts")
|
13
|
+
# # => [
|
14
|
+
# # ["application.js", "projects.js", "users.js", ...]
|
15
|
+
# # #<Set: {"file-digest:/path/to/app/assets/javascripts"}>
|
16
|
+
# # ]
|
17
|
+
#
|
18
|
+
# The returned dependency set can be passed to resolve_dependencies(deps)
|
19
|
+
# to check if the returned result is still fresh. In this case, entry always
|
20
|
+
# returns a single path, but multiple calls should accumulate dependencies
|
21
|
+
# into a single set thats saved off and checked later.
|
22
|
+
#
|
23
|
+
# resolve_dependencies(deps)
|
24
|
+
# # => "\x01\x02\x03"
|
25
|
+
#
|
26
|
+
# Later, resolving the same set again will produce a different hash if
|
27
|
+
# something on the file system has changed.
|
28
|
+
#
|
29
|
+
# resolve_dependencies(deps)
|
30
|
+
# # => "\x03\x04\x05"
|
31
|
+
#
|
32
|
+
module PathDependencyUtils
|
33
|
+
include PathUtils
|
34
|
+
include URIUtils
|
35
|
+
|
36
|
+
# Internal: List directory entries and return a set of dependencies that
|
37
|
+
# would invalid the cached return result.
|
38
|
+
#
|
39
|
+
# See PathUtils#entries
|
40
|
+
#
|
41
|
+
# path - String directory path
|
42
|
+
#
|
43
|
+
# Returns an Array of entry names and a Set of dependency URIs.
|
44
|
+
def entries_with_dependencies(path)
|
45
|
+
return entries(path), Set.new([build_file_digest_uri(path)])
|
46
|
+
end
|
47
|
+
|
48
|
+
# Internal: List directory filenames and associated Stats under a
|
49
|
+
# directory.
|
50
|
+
#
|
51
|
+
# See PathUtils#stat_directory
|
52
|
+
#
|
53
|
+
# dir - A String directory
|
54
|
+
#
|
55
|
+
# Returns an Array of filenames and a Set of dependency URIs.
|
56
|
+
def stat_directory_with_dependencies(dir)
|
57
|
+
return stat_directory(dir).to_a, Set.new([build_file_digest_uri(dir)])
|
58
|
+
end
|
59
|
+
|
60
|
+
# Internal: List directory filenames and associated Stats under an entire
|
61
|
+
# directory tree.
|
62
|
+
#
|
63
|
+
# See PathUtils#stat_sorted_tree
|
64
|
+
#
|
65
|
+
# dir - A String directory
|
66
|
+
#
|
67
|
+
# Returns an Array of filenames and a Set of dependency URIs.
|
68
|
+
def stat_sorted_tree_with_dependencies(dir)
|
69
|
+
deps = Set.new([build_file_digest_uri(dir)])
|
70
|
+
results = stat_sorted_tree(dir).map do |path, stat|
|
71
|
+
deps << build_file_digest_uri(path) if stat.directory?
|
72
|
+
[path, stat]
|
73
|
+
end
|
74
|
+
return results, deps
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'sprockets/digest_utils'
|
3
|
+
require 'sprockets/path_utils'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
# Internal: Crossover of path and digest utilities functions.
|
7
|
+
module PathDigestUtils
|
8
|
+
include DigestUtils, PathUtils
|
9
|
+
|
10
|
+
# Internal: Compute digest for file stat.
|
11
|
+
#
|
12
|
+
# path - String filename
|
13
|
+
# stat - File::Stat
|
14
|
+
#
|
15
|
+
# Returns String digest bytes.
|
16
|
+
def stat_digest(path, stat)
|
17
|
+
if stat.directory?
|
18
|
+
# If its a directive, digest the list of filenames
|
19
|
+
digest_class.digest(self.entries(path).join(','.freeze))
|
20
|
+
elsif stat.file?
|
21
|
+
# If its a file, digest the contents
|
22
|
+
digest_class.file(path.to_s).digest
|
23
|
+
else
|
24
|
+
raise TypeError, "stat was not a directory or file: #{stat.ftype}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Internal: Compute digest for path.
|
29
|
+
#
|
30
|
+
# path - String filename or directory path.
|
31
|
+
#
|
32
|
+
# Returns String digest bytes or nil.
|
33
|
+
def file_digest(path)
|
34
|
+
if stat = self.stat(path)
|
35
|
+
self.stat_digest(path, stat)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Internal: Compute digest for a set of paths.
|
40
|
+
#
|
41
|
+
# paths - Array of filename or directory paths.
|
42
|
+
#
|
43
|
+
# Returns String digest bytes.
|
44
|
+
def files_digest(paths)
|
45
|
+
self.digest(paths.map { |path| self.file_digest(path) })
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,367 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Sprockets
|
3
|
+
# Internal: File and path related utilities. Mixed into Environment.
|
4
|
+
#
|
5
|
+
# Probably would be called FileUtils, but that causes namespace annoyances
|
6
|
+
# when code actually wants to reference ::FileUtils.
|
7
|
+
module PathUtils
|
8
|
+
extend self
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
# Public: Like `File.stat`.
|
12
|
+
#
|
13
|
+
# path - String file or directory path
|
14
|
+
#
|
15
|
+
# Returns nil if the file does not exist.
|
16
|
+
def stat(path)
|
17
|
+
if File.exist?(path)
|
18
|
+
File.stat(path.to_s)
|
19
|
+
else
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Public: Like `File.file?`.
|
25
|
+
#
|
26
|
+
# path - String file path.
|
27
|
+
#
|
28
|
+
# Returns true path exists and is a file.
|
29
|
+
def file?(path)
|
30
|
+
if stat = self.stat(path)
|
31
|
+
stat.file?
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Like `File.directory?`.
|
38
|
+
#
|
39
|
+
# path - String file path.
|
40
|
+
#
|
41
|
+
# Returns true path exists and is a directory.
|
42
|
+
def directory?(path)
|
43
|
+
if stat = self.stat(path)
|
44
|
+
stat.directory?
|
45
|
+
else
|
46
|
+
false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public: A version of `Dir.entries` that filters out `.` files and `~`
|
51
|
+
# swap files.
|
52
|
+
#
|
53
|
+
# path - String directory path
|
54
|
+
#
|
55
|
+
# Returns an empty `Array` if the directory does not exist.
|
56
|
+
def entries(path)
|
57
|
+
if File.directory?(path)
|
58
|
+
entries = Dir.entries(path, encoding: Encoding.default_internal)
|
59
|
+
entries.reject! { |entry|
|
60
|
+
entry.start_with?(".".freeze) ||
|
61
|
+
(entry.start_with?("#".freeze) && entry.end_with?("#".freeze)) ||
|
62
|
+
entry.end_with?("~".freeze)
|
63
|
+
}
|
64
|
+
entries.sort!
|
65
|
+
entries
|
66
|
+
else
|
67
|
+
[]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: Check if path is absolute or relative.
|
72
|
+
#
|
73
|
+
# path - String path.
|
74
|
+
#
|
75
|
+
# Returns true if path is absolute, otherwise false.
|
76
|
+
if File::ALT_SEPARATOR
|
77
|
+
# On Windows, ALT_SEPARATOR is \
|
78
|
+
# Delegate to Pathname since the logic gets complex.
|
79
|
+
def absolute_path?(path)
|
80
|
+
Pathname.new(path).absolute?
|
81
|
+
end
|
82
|
+
else
|
83
|
+
def absolute_path?(path)
|
84
|
+
path.start_with?(File::SEPARATOR)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if File::ALT_SEPARATOR
|
89
|
+
SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}|#{Regexp.quote(File::ALT_SEPARATOR)}"
|
90
|
+
else
|
91
|
+
SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Public: Check if path is explicitly relative.
|
95
|
+
# Starts with "./" or "../".
|
96
|
+
#
|
97
|
+
# path - String path.
|
98
|
+
#
|
99
|
+
# Returns true if path is relative, otherwise false.
|
100
|
+
def relative_path?(path)
|
101
|
+
path.match?(/^\.\.?($|#{SEPARATOR_PATTERN})/) ? true : false
|
102
|
+
end
|
103
|
+
|
104
|
+
# Public: Get relative path from `start` to `dest`.
|
105
|
+
#
|
106
|
+
# start - String start path (file or dir)
|
107
|
+
# dest - String destination path
|
108
|
+
#
|
109
|
+
# Returns relative String path from `start` to `dest`
|
110
|
+
def relative_path_from(start, dest)
|
111
|
+
start, dest = Pathname.new(start), Pathname.new(dest)
|
112
|
+
start = start.dirname unless start.directory?
|
113
|
+
dest.relative_path_from(start).to_s
|
114
|
+
end
|
115
|
+
|
116
|
+
# Public: Joins path to base path.
|
117
|
+
#
|
118
|
+
# base - Root path
|
119
|
+
# path - Extending path
|
120
|
+
#
|
121
|
+
# Example
|
122
|
+
#
|
123
|
+
# join('base/path/', '../file.js')
|
124
|
+
# # => 'base/file.js'
|
125
|
+
#
|
126
|
+
# Returns string path starting from base and ending at path
|
127
|
+
def join(base, path)
|
128
|
+
(Pathname.new(base) + path).to_s
|
129
|
+
end
|
130
|
+
|
131
|
+
# Public: Sets pipeline for path
|
132
|
+
#
|
133
|
+
# path - String path
|
134
|
+
# extensions - List of file extensions
|
135
|
+
# pipeline - Pipeline
|
136
|
+
#
|
137
|
+
# Examples
|
138
|
+
#
|
139
|
+
# set_pipeline('path/file.js.erb', config[:mime_exts], config[:pipeline_exts], :source)
|
140
|
+
# # => 'path/file.source.js.erb'
|
141
|
+
#
|
142
|
+
# set_pipeline('path/some.file.source.js.erb', config[:mime_exts], config[:pipeline_exts], :debug)
|
143
|
+
# # => 'path/some.file.debug.js.erb'
|
144
|
+
#
|
145
|
+
# Returns string path with pipeline parsed in
|
146
|
+
def set_pipeline(path, mime_exts, pipeline_exts, pipeline)
|
147
|
+
extension, _ = match_path_extname(path, mime_exts)
|
148
|
+
path.chomp!(extension)
|
149
|
+
pipeline_old, _ = match_path_extname(path, pipeline_exts)
|
150
|
+
path.chomp!(pipeline_old)
|
151
|
+
|
152
|
+
"#{path}.#{pipeline}#{extension}"
|
153
|
+
end
|
154
|
+
|
155
|
+
# Internal: Get relative path for root path and subpath.
|
156
|
+
#
|
157
|
+
# path - String path
|
158
|
+
# subpath - String subpath of path
|
159
|
+
#
|
160
|
+
# Returns relative String path if subpath is a subpath of path, or nil if
|
161
|
+
# subpath is outside of path.
|
162
|
+
def split_subpath(path, subpath)
|
163
|
+
return "" if path == subpath
|
164
|
+
path = File.join(path, ''.freeze)
|
165
|
+
if subpath.start_with?(path)
|
166
|
+
subpath[path.length..-1]
|
167
|
+
else
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Internal: Detect root path and base for file in a set of paths.
|
173
|
+
#
|
174
|
+
# paths - Array of String paths
|
175
|
+
# filename - String path of file expected to be in one of the paths.
|
176
|
+
#
|
177
|
+
# Returns [String root, String path]
|
178
|
+
def paths_split(paths, filename)
|
179
|
+
paths.each do |path|
|
180
|
+
if subpath = split_subpath(path, filename)
|
181
|
+
return path, subpath
|
182
|
+
end
|
183
|
+
end
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
|
187
|
+
# Internal: Get path's extensions.
|
188
|
+
#
|
189
|
+
# path - String
|
190
|
+
#
|
191
|
+
# Returns an Array of String extnames.
|
192
|
+
def path_extnames(path)
|
193
|
+
File.basename(path).scan(/\.[^.]+/)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Internal: Match path extnames against available extensions.
|
197
|
+
#
|
198
|
+
# path - String
|
199
|
+
# extensions - Hash of String extnames to values
|
200
|
+
#
|
201
|
+
# Returns [String extname, Object value] or nil nothing matched.
|
202
|
+
def match_path_extname(path, extensions)
|
203
|
+
basename = File.basename(path)
|
204
|
+
|
205
|
+
i = basename.index('.'.freeze)
|
206
|
+
while i && i < basename.length - 1
|
207
|
+
extname = basename[i..-1]
|
208
|
+
if value = extensions[extname]
|
209
|
+
return extname, value
|
210
|
+
end
|
211
|
+
|
212
|
+
i = basename.index('.'.freeze, i+1)
|
213
|
+
end
|
214
|
+
|
215
|
+
nil
|
216
|
+
end
|
217
|
+
|
218
|
+
# Internal: Match paths in a directory against available extensions.
|
219
|
+
#
|
220
|
+
# path - String directory
|
221
|
+
# basename - String basename of target file
|
222
|
+
# extensions - Hash of String extnames to values
|
223
|
+
#
|
224
|
+
# Examples
|
225
|
+
#
|
226
|
+
# exts = { ".js" => "application/javascript" }
|
227
|
+
# find_matching_path_for_extensions("app/assets", "application", exts)
|
228
|
+
# # => ["app/assets/application.js", "application/javascript"]
|
229
|
+
#
|
230
|
+
# Returns an Array of [String path, Object value] matches.
|
231
|
+
def find_matching_path_for_extensions(path, basename, extensions)
|
232
|
+
matches = []
|
233
|
+
entries(path).each do |entry|
|
234
|
+
next unless File.basename(entry).start_with?(basename)
|
235
|
+
extname, value = match_path_extname(entry, extensions)
|
236
|
+
if basename == entry.chomp(extname)
|
237
|
+
filename = File.join(path, entry)
|
238
|
+
if file?(filename)
|
239
|
+
matches << [filename, value]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
matches
|
244
|
+
end
|
245
|
+
|
246
|
+
# Internal: Returns all parents for path
|
247
|
+
#
|
248
|
+
# path - String absolute filename or directory
|
249
|
+
# root - String path to stop at (default: system root)
|
250
|
+
#
|
251
|
+
# Returns an Array of String paths.
|
252
|
+
def path_parents(path, root = nil)
|
253
|
+
root = "#{root}#{File::SEPARATOR}" if root
|
254
|
+
parents = []
|
255
|
+
|
256
|
+
loop do
|
257
|
+
parent = File.dirname(path)
|
258
|
+
break if parent == path
|
259
|
+
break if root && !path.start_with?(root)
|
260
|
+
parents << path = parent
|
261
|
+
end
|
262
|
+
|
263
|
+
parents
|
264
|
+
end
|
265
|
+
|
266
|
+
# Internal: Find target basename checking upwards from path.
|
267
|
+
#
|
268
|
+
# basename - String filename: ".sprocketsrc"
|
269
|
+
# path - String path to start search: "app/assets/javascripts/app.js"
|
270
|
+
# root - String path to stop at (default: system root)
|
271
|
+
#
|
272
|
+
# Returns String filename or nil.
|
273
|
+
def find_upwards(basename, path, root = nil)
|
274
|
+
path_parents(path, root).each do |dir|
|
275
|
+
filename = File.join(dir, basename)
|
276
|
+
return filename if file?(filename)
|
277
|
+
end
|
278
|
+
nil
|
279
|
+
end
|
280
|
+
|
281
|
+
# Public: Stat all the files under a directory.
|
282
|
+
#
|
283
|
+
# dir - A String directory
|
284
|
+
#
|
285
|
+
# Returns an Enumerator of [path, stat].
|
286
|
+
def stat_directory(dir)
|
287
|
+
return to_enum(__method__, dir) unless block_given?
|
288
|
+
|
289
|
+
self.entries(dir).each do |entry|
|
290
|
+
path = File.join(dir, entry)
|
291
|
+
if stat = self.stat(path)
|
292
|
+
yield path, stat
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
nil
|
297
|
+
end
|
298
|
+
|
299
|
+
# Public: Recursive stat all the files under a directory.
|
300
|
+
#
|
301
|
+
# dir - A String directory
|
302
|
+
#
|
303
|
+
# Returns an Enumerator of [path, stat].
|
304
|
+
def stat_tree(dir, &block)
|
305
|
+
return to_enum(__method__, dir) unless block_given?
|
306
|
+
|
307
|
+
self.stat_directory(dir) do |path, stat|
|
308
|
+
yield path, stat
|
309
|
+
|
310
|
+
if stat.directory?
|
311
|
+
stat_tree(path, &block)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
nil
|
316
|
+
end
|
317
|
+
|
318
|
+
# Public: Recursive stat all the files under a directory in alphabetical
|
319
|
+
# order.
|
320
|
+
#
|
321
|
+
# dir - A String directory
|
322
|
+
#
|
323
|
+
# Returns an Enumerator of [path, stat].
|
324
|
+
def stat_sorted_tree(dir, &block)
|
325
|
+
return to_enum(__method__, dir) unless block_given?
|
326
|
+
|
327
|
+
self.stat_directory(dir).sort_by { |path, stat|
|
328
|
+
stat.directory? ? "#{path}/" : path
|
329
|
+
}.each do |path, stat|
|
330
|
+
yield path, stat
|
331
|
+
|
332
|
+
if stat.directory?
|
333
|
+
stat_sorted_tree(path, &block)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
nil
|
338
|
+
end
|
339
|
+
|
340
|
+
# Public: Write to a file atomically. Useful for situations where you
|
341
|
+
# don't want other processes or threads to see half-written files.
|
342
|
+
#
|
343
|
+
# Utils.atomic_write('important.file') do |file|
|
344
|
+
# file.write('hello')
|
345
|
+
# end
|
346
|
+
#
|
347
|
+
# Returns nothing.
|
348
|
+
def atomic_write(filename)
|
349
|
+
dirname, basename = File.split(filename)
|
350
|
+
basename = [
|
351
|
+
basename,
|
352
|
+
Thread.current.object_id,
|
353
|
+
Process.pid,
|
354
|
+
rand(1000000)
|
355
|
+
].join('.'.freeze)
|
356
|
+
tmpname = File.join(dirname, basename)
|
357
|
+
|
358
|
+
File.open(tmpname, 'wb+') do |f|
|
359
|
+
yield f
|
360
|
+
end
|
361
|
+
|
362
|
+
File.rename(tmpname, filename)
|
363
|
+
ensure
|
364
|
+
File.delete(tmpname) if File.exist?(tmpname)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|