-
-
Notifications
You must be signed in to change notification settings - Fork 10k
/
static_file.rb
203 lines (174 loc) · 5.68 KB
/
static_file.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# frozen_string_literal: true
module Jekyll
class StaticFile
extend Forwardable
attr_reader :relative_path, :extname,
:type, # Returns the type of the collection if present, nil otherwise.
:name
def_delegator :to_liquid, :to_json, :to_json
class << self
# The cache of last modification times [path] -> mtime.
def mtimes
@mtimes ||= {}
end
def reset_cache
@mtimes = nil
end
end
# Initialize a new StaticFile.
#
# site - The Site.
# base - The String path to the <source>.
# dir - The String path between <source> and the file.
# name - The String filename of the file.
# rubocop: disable Metrics/ParameterLists
def initialize(site, base, dir, name, collection = nil)
@site = site
@base = base
@dir = dir
@name = name
@collection = collection
@relative_path = File.join(*[@dir, @name].compact)
@extname = File.extname(@name)
@type = @collection&.label&.to_sym
end
# rubocop: enable Metrics/ParameterLists
# Returns source file path.
def path
@path ||= if !@collection.nil? && !@site.config["collections_dir"].empty?
File.join(*[@base, @site.config["collections_dir"], @dir, @name].compact)
else
File.join(*[@base, @dir, @name].compact)
end
end
# Obtain destination path.
#
# dest - The String path to the destination dir.
#
# Returns destination file path.
def destination(dest)
@destination ||= {}
@destination[dest] ||= @site.in_dest_dir(dest, Jekyll::URL.unescape_path(url))
end
def destination_rel_dir
if @collection
File.dirname(url)
else
@dir
end
end
def modified_time
@modified_time ||= File.stat(path).mtime
end
# Returns last modification time for this file.
def mtime
modified_time.to_i
end
# Is source path modified?
#
# Returns true if modified since last write.
def modified?
self.class.mtimes[path] != mtime
end
# Whether to write the file to the filesystem
#
# Returns true unless the defaults for the destination path from
# _config.yml contain `published: false`.
def write?
publishable = defaults.fetch("published", true)
return publishable unless @collection
publishable && @collection.write?
end
# Write the static file to the destination directory (if modified).
#
# dest - The String path to the destination dir.
#
# Returns false if the file was not modified since last time (no-op).
def write(dest)
dest_path = destination(dest)
return false if File.exist?(dest_path) && !modified?
self.class.mtimes[path] = mtime
FileUtils.mkdir_p(File.dirname(dest_path))
FileUtils.rm(dest_path) if File.exist?(dest_path)
copy_file(dest_path)
true
end
def data
@data ||= @site.frontmatter_defaults.all(relative_path, type)
end
def to_liquid
@to_liquid ||= Drops::StaticFileDrop.new(self)
end
# Generate "basename without extension" and strip away any trailing periods.
# NOTE: `String#gsub` removes all trailing periods (in comparison to `String#chomp`)
def basename
@basename ||= File.basename(name, extname).gsub(%r!\.*\z!, "")
end
def placeholders
{
:collection => @collection.label,
:path => cleaned_relative_path,
:output_ext => "",
:name => basename,
:title => "",
}
end
# Similar to Jekyll::Document#cleaned_relative_path.
# Generates a relative path with the collection's directory removed when applicable
# and additionally removes any multiple periods in the string.
#
# NOTE: `String#gsub!` removes all trailing periods (in comparison to `String#chomp!`)
#
# Examples:
# When `relative_path` is "_methods/site/my-cool-avatar...png":
# cleaned_relative_path
# # => "/site/my-cool-avatar"
#
# Returns the cleaned relative path of the static file.
def cleaned_relative_path
@cleaned_relative_path ||= begin
cleaned = relative_path[0..-extname.length - 1]
cleaned.gsub!(%r!\.*\z!, "")
cleaned.sub!(@collection.relative_directory, "") if @collection
cleaned
end
end
# Applies a similar URL-building technique as Jekyll::Document that takes
# the collection's URL template into account. The default URL template can
# be overridden in the collection's configuration in _config.yml.
def url
@url ||= begin
base = if @collection.nil?
cleaned_relative_path
else
Jekyll::URL.new(
:template => @collection.url_template,
:placeholders => placeholders
)
end.to_s.chomp("/")
base << extname
end
end
# Returns the front matter defaults defined for the file's URL and/or type
# as defined in _config.yml.
def defaults
@defaults ||= @site.frontmatter_defaults.all url, type
end
# Returns a debug string on inspecting the static file.
# Includes only the relative path of the object.
def inspect
"#<#{self.class} @relative_path=#{relative_path.inspect}>"
end
private
def copy_file(dest_path)
if @site.safe || Jekyll.env == "production"
FileUtils.cp(path, dest_path)
else
FileUtils.copy_entry(path, dest_path)
end
unless File.symlink?(dest_path)
File.utime(self.class.mtimes[path], self.class.mtimes[path], dest_path)
end
end
end
end