sprockets 2.3.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprockets might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/LICENSE +2 -2
- data/README.md +332 -115
- data/bin/sprockets +8 -0
- data/lib/rake/sprocketstask.rb +25 -13
- data/lib/sprockets/asset.rb +143 -205
- data/lib/sprockets/autoload/closure.rb +7 -0
- data/lib/sprockets/autoload/coffee_script.rb +7 -0
- data/lib/sprockets/autoload/eco.rb +7 -0
- data/lib/sprockets/autoload/ejs.rb +7 -0
- data/lib/sprockets/autoload/sass.rb +7 -0
- data/lib/sprockets/autoload/uglifier.rb +7 -0
- data/lib/sprockets/autoload/yui.rb +7 -0
- data/lib/sprockets/autoload.rb +11 -0
- data/lib/sprockets/base.rb +49 -257
- data/lib/sprockets/bower.rb +58 -0
- data/lib/sprockets/bundle.rb +65 -0
- data/lib/sprockets/cache/file_store.rb +165 -14
- data/lib/sprockets/cache/memory_store.rb +66 -0
- data/lib/sprockets/cache/null_store.rb +46 -0
- data/lib/sprockets/cache.rb +234 -0
- data/lib/sprockets/cached_environment.rb +69 -0
- data/lib/sprockets/closure_compressor.rb +53 -0
- data/lib/sprockets/coffee_script_processor.rb +25 -0
- data/lib/sprockets/coffee_script_template.rb +6 -0
- data/lib/sprockets/compressing.rb +74 -0
- data/lib/sprockets/configuration.rb +83 -0
- data/lib/sprockets/context.rb +125 -131
- data/lib/sprockets/dependencies.rb +73 -0
- data/lib/sprockets/digest_utils.rb +156 -0
- data/lib/sprockets/directive_processor.rb +209 -211
- data/lib/sprockets/eco_processor.rb +32 -0
- data/lib/sprockets/eco_template.rb +3 -35
- data/lib/sprockets/ejs_processor.rb +31 -0
- data/lib/sprockets/ejs_template.rb +3 -34
- data/lib/sprockets/encoding_utils.rb +258 -0
- data/lib/sprockets/engines.rb +45 -38
- data/lib/sprockets/environment.rb +17 -67
- data/lib/sprockets/erb_processor.rb +30 -0
- data/lib/sprockets/erb_template.rb +6 -0
- data/lib/sprockets/errors.rb +6 -13
- data/lib/sprockets/file_reader.rb +15 -0
- data/lib/sprockets/http_utils.rb +115 -0
- data/lib/sprockets/jst_processor.rb +35 -19
- data/lib/sprockets/legacy.rb +314 -0
- data/lib/sprockets/legacy_proc_processor.rb +35 -0
- data/lib/sprockets/legacy_tilt_processor.rb +29 -0
- data/lib/sprockets/loader.rb +176 -0
- data/lib/sprockets/manifest.rb +179 -98
- data/lib/sprockets/manifest_utils.rb +45 -0
- data/lib/sprockets/mime.rb +114 -32
- data/lib/sprockets/path_dependency_utils.rb +85 -0
- data/lib/sprockets/path_digest_utils.rb +47 -0
- data/lib/sprockets/path_utils.rb +282 -0
- data/lib/sprockets/paths.rb +81 -0
- data/lib/sprockets/processing.rb +157 -189
- data/lib/sprockets/processor_utils.rb +103 -0
- data/lib/sprockets/resolve.rb +208 -0
- data/lib/sprockets/sass_cache_store.rb +19 -15
- data/lib/sprockets/sass_compressor.rb +59 -0
- data/lib/sprockets/sass_functions.rb +2 -0
- data/lib/sprockets/sass_importer.rb +2 -29
- data/lib/sprockets/sass_processor.rb +285 -0
- data/lib/sprockets/sass_template.rb +4 -44
- data/lib/sprockets/server.rb +109 -84
- data/lib/sprockets/transformers.rb +145 -0
- data/lib/sprockets/uglifier_compressor.rb +63 -0
- data/lib/sprockets/uri_utils.rb +190 -0
- data/lib/sprockets/utils.rb +193 -44
- data/lib/sprockets/version.rb +1 -1
- data/lib/sprockets/yui_compressor.rb +65 -0
- data/lib/sprockets.rb +144 -53
- metadata +248 -238
- data/lib/sprockets/asset_attributes.rb +0 -126
- data/lib/sprockets/bundled_asset.rb +0 -79
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- data/lib/sprockets/index.rb +0 -99
- data/lib/sprockets/processed_asset.rb +0 -152
- data/lib/sprockets/processor.rb +0 -32
- data/lib/sprockets/safety_colons.rb +0 -28
- data/lib/sprockets/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -57
- data/lib/sprockets/trail.rb +0 -90
data/lib/sprockets/mime.rb
CHANGED
@@ -1,48 +1,130 @@
|
|
1
|
-
require '
|
1
|
+
require 'sprockets/encoding_utils'
|
2
|
+
require 'sprockets/http_utils'
|
3
|
+
require 'sprockets/utils'
|
2
4
|
|
3
5
|
module Sprockets
|
4
6
|
module Mime
|
5
|
-
|
6
|
-
|
7
|
+
include HTTPUtils, Utils
|
8
|
+
|
9
|
+
# Public: Mapping of MIME type Strings to properties Hash.
|
7
10
|
#
|
8
|
-
#
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
# key - MIME Type String
|
12
|
+
# value - Hash
|
13
|
+
# extensions - Array of extnames
|
14
|
+
# charset - Default Encoding or function to detect encoding
|
15
|
+
#
|
16
|
+
# Returns Hash.
|
17
|
+
def mime_types
|
18
|
+
config[:mime_types]
|
16
19
|
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
# Internal: Mapping of MIME extension Strings to MIME type Strings.
|
22
|
+
#
|
23
|
+
# Used for internal fast lookup purposes.
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
#
|
27
|
+
# mime_exts['.js'] #=> 'application/javascript'
|
28
|
+
#
|
29
|
+
# key - MIME extension String
|
30
|
+
# value - MIME Type String
|
31
|
+
#
|
32
|
+
# Returns Hash.
|
33
|
+
def mime_exts
|
34
|
+
config[:mime_exts]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Register a new mime type.
|
38
|
+
#
|
39
|
+
# mime_type - String MIME Type
|
40
|
+
# options - Hash
|
41
|
+
# extensions: Array of String extnames
|
42
|
+
# charset: Proc/Method that detects the charset of a file.
|
43
|
+
# See EncodingUtils.
|
44
|
+
#
|
45
|
+
# Returns nothing.
|
46
|
+
def register_mime_type(mime_type, options = {})
|
47
|
+
# Legacy extension argument, will be removed from 4.x
|
48
|
+
if options.is_a?(String)
|
49
|
+
options = { extensions: [options] }
|
21
50
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
51
|
+
|
52
|
+
extnames = Array(options[:extensions]).map { |extname|
|
53
|
+
Sprockets::Utils.normalize_extension(extname)
|
54
|
+
}
|
55
|
+
|
56
|
+
charset = options[:charset]
|
57
|
+
charset ||= :default if mime_type.start_with?('text/')
|
58
|
+
charset = EncodingUtils::CHARSET_DETECT[charset] if charset.is_a?(Symbol)
|
59
|
+
|
60
|
+
self.computed_config = {}
|
61
|
+
|
62
|
+
self.config = hash_reassoc(config, :mime_exts) do |mime_exts|
|
63
|
+
extnames.each do |extname|
|
64
|
+
mime_exts[extname] = mime_type
|
65
|
+
end
|
66
|
+
mime_exts
|
67
|
+
end
|
68
|
+
|
69
|
+
self.config = hash_reassoc(config, :mime_types) do |mime_types|
|
70
|
+
type = { extensions: extnames }
|
71
|
+
type[:charset] = charset if charset
|
72
|
+
mime_types.merge(mime_type => type)
|
25
73
|
end
|
26
74
|
end
|
27
75
|
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
76
|
+
# Internal: Get detecter function for MIME type.
|
77
|
+
#
|
78
|
+
# mime_type - String MIME type
|
79
|
+
#
|
80
|
+
# Returns Proc detector or nil if none is available.
|
81
|
+
def mime_type_charset_detecter(mime_type)
|
82
|
+
if type = config[:mime_types][mime_type]
|
83
|
+
if detect = type[:charset]
|
84
|
+
return detect
|
85
|
+
end
|
86
|
+
end
|
32
87
|
end
|
33
88
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
89
|
+
# Public: Read file on disk with MIME type specific encoding.
|
90
|
+
#
|
91
|
+
# filename - String path
|
92
|
+
# content_type - String MIME type
|
93
|
+
#
|
94
|
+
# Returns String file contents transcoded to UTF-8 or in its external
|
95
|
+
# encoding.
|
96
|
+
def read_file(filename, content_type = nil)
|
97
|
+
data = File.binread(filename)
|
98
|
+
|
99
|
+
if detect = mime_type_charset_detecter(content_type)
|
100
|
+
detect.call(data).encode(Encoding::UTF_8, :universal_newline => true)
|
101
|
+
else
|
102
|
+
data
|
41
103
|
end
|
42
104
|
end
|
43
|
-
end
|
44
105
|
|
45
|
-
|
46
|
-
|
47
|
-
|
106
|
+
private
|
107
|
+
def extname_map
|
108
|
+
self.computed_config[:_extnames] ||= compute_extname_map
|
109
|
+
end
|
110
|
+
|
111
|
+
def compute_extname_map
|
112
|
+
graph = {}
|
113
|
+
|
114
|
+
([nil] + pipelines.keys.map(&:to_s)).each do |pipeline|
|
115
|
+
pipeline_extname = ".#{pipeline}" if pipeline
|
116
|
+
([[nil, nil]] + config[:mime_exts].to_a).each do |format_extname, format_type|
|
117
|
+
4.times do |n|
|
118
|
+
config[:engines].keys.permutation(n).each do |engine_extnames|
|
119
|
+
key = "#{pipeline_extname}#{format_extname}#{engine_extnames.join}"
|
120
|
+
type = format_type || config[:engine_mime_types][engine_extnames.first]
|
121
|
+
graph[key] = {type: type, engines: engine_extnames, pipeline: pipeline}
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
graph
|
128
|
+
end
|
129
|
+
end
|
48
130
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'sprockets/path_utils'
|
3
|
+
require 'sprockets/uri_utils'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
# Internal: Related PathUtils helpers that also track all the file system
|
7
|
+
# calls they make for caching purposes. All functions return a standard
|
8
|
+
# return value and a Set of cache dependency URIs that can be used in the
|
9
|
+
# future to see if the returned value should be invalidated from cache.
|
10
|
+
#
|
11
|
+
# entries_with_dependencies("app/assets/javascripts")
|
12
|
+
# # => [
|
13
|
+
# # ["application.js", "projects.js", "users.js", ...]
|
14
|
+
# # #<Set: {"file-digest:/path/to/app/assets/javascripts"}>
|
15
|
+
# # ]
|
16
|
+
#
|
17
|
+
# The returned dependency set can be passed to resolve_dependencies(deps)
|
18
|
+
# to check if the returned result is still fresh. In this case, entry always
|
19
|
+
# returns a single path, but multiple calls should accumulate dependencies
|
20
|
+
# into a single set thats saved off and checked later.
|
21
|
+
#
|
22
|
+
# resolve_dependencies(deps)
|
23
|
+
# # => "\x01\x02\x03"
|
24
|
+
#
|
25
|
+
# Later, resolving the same set again will produce a different hash if
|
26
|
+
# something on the file system has changed.
|
27
|
+
#
|
28
|
+
# resolve_dependencies(deps)
|
29
|
+
# # => "\x03\x04\x05"
|
30
|
+
#
|
31
|
+
module PathDependencyUtils
|
32
|
+
include PathUtils
|
33
|
+
include URIUtils
|
34
|
+
|
35
|
+
# Internal: List directory entries and return a set of dependencies that
|
36
|
+
# would invalid the cached return result.
|
37
|
+
#
|
38
|
+
# See PathUtils#entries
|
39
|
+
#
|
40
|
+
# path - String directory path
|
41
|
+
#
|
42
|
+
# Returns an Array of entry names and a Set of dependency URIs.
|
43
|
+
def entries_with_dependencies(path)
|
44
|
+
return entries(path), file_digest_dependency_set(path)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Internal: List directory filenames and associated Stats under a
|
48
|
+
# directory.
|
49
|
+
#
|
50
|
+
# See PathUtils#stat_directory
|
51
|
+
#
|
52
|
+
# dir - A String directory
|
53
|
+
#
|
54
|
+
# Returns an Array of filenames and a Set of dependency URIs.
|
55
|
+
def stat_directory_with_dependencies(dir)
|
56
|
+
return stat_directory(dir).to_a, file_digest_dependency_set(dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Internal: Returns a set of dependencies for a particular path.
|
60
|
+
#
|
61
|
+
# path - String directory path
|
62
|
+
#
|
63
|
+
# Returns a Set of dependency URIs.
|
64
|
+
def file_digest_dependency_set(path)
|
65
|
+
Set.new([build_file_digest_uri(path)])
|
66
|
+
end
|
67
|
+
|
68
|
+
# Internal: List directory filenames and associated Stats under an entire
|
69
|
+
# directory tree.
|
70
|
+
#
|
71
|
+
# See PathUtils#stat_sorted_tree
|
72
|
+
#
|
73
|
+
# dir - A String directory
|
74
|
+
#
|
75
|
+
# Returns an Array of filenames and a Set of dependency URIs.
|
76
|
+
def stat_sorted_tree_with_dependencies(dir)
|
77
|
+
deps = Set.new([build_file_digest_uri(dir)])
|
78
|
+
results = stat_sorted_tree(dir).map do |path, stat|
|
79
|
+
deps << build_file_digest_uri(path) if stat.directory?
|
80
|
+
[path, stat]
|
81
|
+
end
|
82
|
+
return results, deps
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -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
|
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# Internal: File and path related utilities. Mixed into Environment.
|
5
|
+
#
|
6
|
+
# Probably would be called FileUtils, but that causes namespace annoyances
|
7
|
+
# when code actually wants to reference ::FileUtils.
|
8
|
+
module PathUtils
|
9
|
+
extend self
|
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
|
+
Dir.entries(path, :encoding => Encoding.default_internal).reject! { |entry|
|
59
|
+
entry =~ /^\.|~$|^\#.*\#$/
|
60
|
+
}.sort!
|
61
|
+
else
|
62
|
+
[]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Public: Check if path is absolute or relative.
|
67
|
+
#
|
68
|
+
# path - String path.
|
69
|
+
#
|
70
|
+
# Returns true if path is absolute, otherwise false.
|
71
|
+
if File::ALT_SEPARATOR
|
72
|
+
require 'pathname'
|
73
|
+
|
74
|
+
# On Windows, ALT_SEPARATOR is \
|
75
|
+
# Delegate to Pathname since the logic gets complex.
|
76
|
+
def absolute_path?(path)
|
77
|
+
Pathname.new(path).absolute?
|
78
|
+
end
|
79
|
+
else
|
80
|
+
def absolute_path?(path)
|
81
|
+
path[0] == File::SEPARATOR
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if File::ALT_SEPARATOR
|
86
|
+
SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}|#{Regexp.quote(File::ALT_SEPARATOR)}"
|
87
|
+
else
|
88
|
+
SEPARATOR_PATTERN = "#{Regexp.quote(File::SEPARATOR)}"
|
89
|
+
end
|
90
|
+
|
91
|
+
# Public: Check if path is explicitly relative.
|
92
|
+
# Starts with "./" or "../".
|
93
|
+
#
|
94
|
+
# path - String path.
|
95
|
+
#
|
96
|
+
# Returns true if path is relative, otherwise false.
|
97
|
+
def relative_path?(path)
|
98
|
+
path =~ /^\.\.?($|#{SEPARATOR_PATTERN})/ ? true : false
|
99
|
+
end
|
100
|
+
|
101
|
+
# Internal: Get relative path for root path and subpath.
|
102
|
+
#
|
103
|
+
# path - String path
|
104
|
+
# subpath - String subpath of path
|
105
|
+
#
|
106
|
+
# Returns relative String path if subpath is a subpath of path, or nil if
|
107
|
+
# subpath is outside of path.
|
108
|
+
def split_subpath(path, subpath)
|
109
|
+
return "" if path == subpath
|
110
|
+
path = File.join(path, '')
|
111
|
+
if subpath.start_with?(path)
|
112
|
+
subpath[path.length..-1]
|
113
|
+
else
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Internal: Detect root path and base for file in a set of paths.
|
119
|
+
#
|
120
|
+
# paths - Array of String paths
|
121
|
+
# filename - String path of file expected to be in one of the paths.
|
122
|
+
#
|
123
|
+
# Returns [String root, String path]
|
124
|
+
def paths_split(paths, filename)
|
125
|
+
paths.each do |path|
|
126
|
+
if subpath = split_subpath(path, filename)
|
127
|
+
return path, subpath
|
128
|
+
end
|
129
|
+
end
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# Internal: Get path's extensions.
|
134
|
+
#
|
135
|
+
# path - String
|
136
|
+
#
|
137
|
+
# Returns an Array of String extnames.
|
138
|
+
def path_extnames(path)
|
139
|
+
File.basename(path).scan(/\.[^.]+/)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal: Match path extnames against available extensions.
|
143
|
+
#
|
144
|
+
# path - String
|
145
|
+
# extensions - Hash of String extnames to values
|
146
|
+
#
|
147
|
+
# Returns [String extname, Object value] or nil nothing matched.
|
148
|
+
def match_path_extname(path, extensions)
|
149
|
+
match, key = nil, ""
|
150
|
+
path_extnames(path).reverse_each do |extname|
|
151
|
+
key.prepend(extname)
|
152
|
+
if value = extensions[key]
|
153
|
+
match = [key.dup, value]
|
154
|
+
elsif match
|
155
|
+
break
|
156
|
+
end
|
157
|
+
end
|
158
|
+
match
|
159
|
+
end
|
160
|
+
|
161
|
+
# Internal: Returns all parents for path
|
162
|
+
#
|
163
|
+
# path - String absolute filename or directory
|
164
|
+
# root - String path to stop at (default: system root)
|
165
|
+
#
|
166
|
+
# Returns an Array of String paths.
|
167
|
+
def path_parents(path, root = nil)
|
168
|
+
root = "#{root}#{File::SEPARATOR}" if root
|
169
|
+
parents = []
|
170
|
+
|
171
|
+
loop do
|
172
|
+
parent = File.dirname(path)
|
173
|
+
break if parent == path
|
174
|
+
break if root && !path.start_with?(root)
|
175
|
+
parents << path = parent
|
176
|
+
end
|
177
|
+
|
178
|
+
parents
|
179
|
+
end
|
180
|
+
|
181
|
+
# Internal: Find target basename checking upwards from path.
|
182
|
+
#
|
183
|
+
# basename - String filename: ".sprocketsrc"
|
184
|
+
# path - String path to start search: "app/assets/javascripts/app.js"
|
185
|
+
# root - String path to stop at (default: system root)
|
186
|
+
#
|
187
|
+
# Returns String filename or nil.
|
188
|
+
def find_upwards(basename, path, root = nil)
|
189
|
+
path_parents(path, root).each do |dir|
|
190
|
+
filename = File.join(dir, basename)
|
191
|
+
return filename if file?(filename)
|
192
|
+
end
|
193
|
+
nil
|
194
|
+
end
|
195
|
+
|
196
|
+
# Public: Stat all the files under a directory.
|
197
|
+
#
|
198
|
+
# dir - A String directory
|
199
|
+
#
|
200
|
+
# Returns an Enumerator of [path, stat].
|
201
|
+
def stat_directory(dir)
|
202
|
+
return to_enum(__method__, dir) unless block_given?
|
203
|
+
|
204
|
+
self.entries(dir).each do |entry|
|
205
|
+
path = File.join(dir, entry)
|
206
|
+
if stat = self.stat(path)
|
207
|
+
yield path, stat
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
|
214
|
+
# Public: Recursive stat all the files under a directory.
|
215
|
+
#
|
216
|
+
# dir - A String directory
|
217
|
+
#
|
218
|
+
# Returns an Enumerator of [path, stat].
|
219
|
+
def stat_tree(dir, &block)
|
220
|
+
return to_enum(__method__, dir) unless block_given?
|
221
|
+
|
222
|
+
self.stat_directory(dir) do |path, stat|
|
223
|
+
yield path, stat
|
224
|
+
|
225
|
+
if stat.directory?
|
226
|
+
stat_tree(path, &block)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
|
233
|
+
# Public: Recursive stat all the files under a directory in alphabetical
|
234
|
+
# order.
|
235
|
+
#
|
236
|
+
# dir - A String directory
|
237
|
+
#
|
238
|
+
# Returns an Enumerator of [path, stat].
|
239
|
+
def stat_sorted_tree(dir, &block)
|
240
|
+
return to_enum(__method__, dir) unless block_given?
|
241
|
+
|
242
|
+
self.stat_directory(dir).sort_by { |path, stat|
|
243
|
+
stat.directory? ? "#{path}/" : path
|
244
|
+
}.each do |path, stat|
|
245
|
+
yield path, stat
|
246
|
+
|
247
|
+
if stat.directory?
|
248
|
+
stat_sorted_tree(path, &block)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
nil
|
253
|
+
end
|
254
|
+
|
255
|
+
# Public: Write to a file atomically. Useful for situations where you
|
256
|
+
# don't want other processes or threads to see half-written files.
|
257
|
+
#
|
258
|
+
# Utils.atomic_write('important.file') do |file|
|
259
|
+
# file.write('hello')
|
260
|
+
# end
|
261
|
+
#
|
262
|
+
# Returns nothing.
|
263
|
+
def atomic_write(filename)
|
264
|
+
dirname, basename = File.split(filename)
|
265
|
+
basename = [
|
266
|
+
basename,
|
267
|
+
Thread.current.object_id,
|
268
|
+
Process.pid,
|
269
|
+
rand(1000000)
|
270
|
+
].join('.')
|
271
|
+
tmpname = File.join(dirname, basename)
|
272
|
+
|
273
|
+
File.open(tmpname, 'wb+') do |f|
|
274
|
+
yield f
|
275
|
+
end
|
276
|
+
|
277
|
+
FileUtils.mv(tmpname, filename)
|
278
|
+
ensure
|
279
|
+
FileUtils.rm(tmpname) if File.exist?(tmpname)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'sprockets/path_utils'
|
2
|
+
require 'sprockets/utils'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
module Paths
|
6
|
+
include PathUtils, Utils
|
7
|
+
|
8
|
+
# Returns `Environment` root.
|
9
|
+
#
|
10
|
+
# All relative paths are expanded with root as its base. To be
|
11
|
+
# useful set this to your applications root directory. (`Rails.root`)
|
12
|
+
def root
|
13
|
+
config[:root]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Internal: Change Environment root.
|
17
|
+
#
|
18
|
+
# Only the initializer should change the root.
|
19
|
+
def root=(path)
|
20
|
+
self.config = hash_reassoc(config, :root) do
|
21
|
+
File.expand_path(path)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
private :root=
|
25
|
+
|
26
|
+
# Returns an `Array` of path `String`s.
|
27
|
+
#
|
28
|
+
# These paths will be used for asset logical path lookups.
|
29
|
+
def paths
|
30
|
+
config[:paths]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Prepend a `path` to the `paths` list.
|
34
|
+
#
|
35
|
+
# Paths at the end of the `Array` have the least priority.
|
36
|
+
def prepend_path(path)
|
37
|
+
self.config = hash_reassoc(config, :paths) do |paths|
|
38
|
+
path = File.expand_path(path, config[:root]).freeze
|
39
|
+
paths.unshift(path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Append a `path` to the `paths` list.
|
44
|
+
#
|
45
|
+
# Paths at the beginning of the `Array` have a higher priority.
|
46
|
+
def append_path(path)
|
47
|
+
self.config = hash_reassoc(config, :paths) do |paths|
|
48
|
+
path = File.expand_path(path, config[:root]).freeze
|
49
|
+
paths.push(path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Clear all paths and start fresh.
|
54
|
+
#
|
55
|
+
# There is no mechanism for reordering paths, so its best to
|
56
|
+
# completely wipe the paths list and reappend them in the order
|
57
|
+
# you want.
|
58
|
+
def clear_paths
|
59
|
+
self.config = hash_reassoc(config, :paths) do |paths|
|
60
|
+
paths.clear
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Public: Iterate over every file under all load paths.
|
65
|
+
#
|
66
|
+
# Returns Enumerator if no block is given.
|
67
|
+
def each_file
|
68
|
+
return to_enum(__method__) unless block_given?
|
69
|
+
|
70
|
+
paths.each do |root|
|
71
|
+
stat_tree(root).each do |filename, stat|
|
72
|
+
if stat.file?
|
73
|
+
yield filename
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|