nanoc3 3.2.0a1 → 3.2.0a2
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.
- data/ChangeLog +2 -2
- data/NEWS.md +14 -0
- data/README.md +20 -12
- data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
- data/lib/nanoc3/base/compilation/checksum_store.rb +130 -0
- data/lib/nanoc3/base/compilation/checksummer.rb +68 -0
- data/lib/nanoc3/base/compilation/compiled_content_cache.rb +57 -0
- data/lib/nanoc3/base/{compiler.rb → compilation/compiler.rb} +255 -55
- data/lib/nanoc3/base/{compiler_dsl.rb → compilation/compiler_dsl.rb} +11 -6
- data/lib/nanoc3/base/{dependency_tracker.rb → compilation/dependency_tracker.rb} +62 -92
- data/lib/nanoc3/base/{filter.rb → compilation/filter.rb} +0 -0
- data/lib/nanoc3/base/compilation/item_rep_proxy.rb +87 -0
- data/lib/nanoc3/base/compilation/outdatedness_checker.rb +86 -0
- data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +43 -0
- data/lib/nanoc3/base/{rule.rb → compilation/rule.rb} +8 -2
- data/lib/nanoc3/base/{rule_context.rb → compilation/rule_context.rb} +17 -14
- data/lib/nanoc3/base/directed_graph.rb +8 -0
- data/lib/nanoc3/base/errors.rb +9 -17
- data/lib/nanoc3/base/plugin_registry.rb +1 -1
- data/lib/nanoc3/base/result_data/item_rep.rb +447 -0
- data/lib/nanoc3/base/{code_snippet.rb → source_data/code_snippet.rb} +7 -22
- data/lib/nanoc3/base/{data_source.rb → source_data/data_source.rb} +0 -0
- data/lib/nanoc3/base/{item.rb → source_data/item.rb} +20 -30
- data/lib/nanoc3/base/{layout.rb → source_data/layout.rb} +7 -26
- data/lib/nanoc3/base/source_data/site.rb +314 -0
- data/lib/nanoc3/base/store.rb +126 -0
- data/lib/nanoc3/base.rb +26 -15
- data/lib/nanoc3/cli/base.rb +2 -1
- data/lib/nanoc3/cli/commands/compile.rb +116 -48
- data/lib/nanoc3/cli/commands/create_item.rb +0 -1
- data/lib/nanoc3/cli/commands/create_layout.rb +0 -1
- data/lib/nanoc3/cli/commands/create_site.rb +11 -1
- data/lib/nanoc3/cli/commands/debug.rb +11 -6
- data/lib/nanoc3/cli/commands/info.rb +1 -2
- data/lib/nanoc3/cli/commands/update.rb +0 -1
- data/lib/nanoc3/cli/commands/watch.rb +27 -32
- data/lib/nanoc3/cli/logger.rb +2 -2
- data/lib/nanoc3/data_sources/filesystem.rb +1 -6
- data/lib/nanoc3/extra/auto_compiler.rb +2 -3
- data/lib/nanoc3/extra/deployers/rsync.rb +1 -0
- data/lib/nanoc3/extra/validators/links.rb +45 -26
- data/lib/nanoc3/filters/asciidoc.rb +58 -0
- data/lib/nanoc3/filters/colorize_syntax.rb +47 -9
- data/lib/nanoc3/filters/less.rb +6 -0
- data/lib/nanoc3/filters/sass.rb +8 -5
- data/lib/nanoc3/filters.rb +2 -0
- data/lib/nanoc3/helpers/blogging.rb +8 -0
- data/lib/nanoc3/helpers/html_escape.rb +37 -7
- data/lib/nanoc3/helpers/link_to.rb +15 -4
- data/lib/nanoc3/helpers/rendering.rb +6 -2
- data/lib/nanoc3/tasks/clean.rb +0 -4
- data/lib/nanoc3.rb +1 -1
- data/nanoc3.gemspec +42 -0
- metadata +35 -19
- data/lib/nanoc3/base/checksummer.rb +0 -40
- data/lib/nanoc3/base/compiled_content_cache.rb +0 -86
- data/lib/nanoc3/base/item_rep.rb +0 -537
- data/lib/nanoc3/base/site.rb +0 -490
@@ -1,86 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Nanoc3
|
4
|
-
|
5
|
-
# Represents a cache than can be used to store already compiled content,
|
6
|
-
# to prevent it from being needlessly recompiled.
|
7
|
-
#
|
8
|
-
# This class is intended for internal use only. Do not rely on its
|
9
|
-
# presence; future versions of nanoc, even in the 3.x branch, may no
|
10
|
-
# longer contain this class.
|
11
|
-
class CompiledContentCache
|
12
|
-
|
13
|
-
# @return [String] The filename where the cache will be loaded from
|
14
|
-
# and stored to
|
15
|
-
attr_reader :filename
|
16
|
-
|
17
|
-
# Creates a new cache for the given filename.
|
18
|
-
#
|
19
|
-
# @param [String] filename The filename where the cache will be loaded
|
20
|
-
# from and stored to
|
21
|
-
def initialize(filename)
|
22
|
-
require 'pstore'
|
23
|
-
|
24
|
-
@filename = filename
|
25
|
-
end
|
26
|
-
|
27
|
-
# Loads the cache from the filesystem into memory.
|
28
|
-
#
|
29
|
-
# @return [void]
|
30
|
-
def load
|
31
|
-
cache = nil
|
32
|
-
return if !File.file?(filename)
|
33
|
-
pstore.transaction { cache = pstore[:compiled_content] }
|
34
|
-
end
|
35
|
-
|
36
|
-
# Stores the content of the (probably modified) in-memory cache to the
|
37
|
-
# filesystem.
|
38
|
-
#
|
39
|
-
# @return [void]
|
40
|
-
def store
|
41
|
-
FileUtils.mkdir_p(File.dirname(filename))
|
42
|
-
pstore.transaction { pstore[:compiled_content] = cache }
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns the cached compiled content for the given item
|
46
|
-
# representation. This cached compiled content is a hash where the keys
|
47
|
-
# are the snapshot names and the values the compiled content at the
|
48
|
-
# given snapshot.
|
49
|
-
#
|
50
|
-
# @param [Nanoc3::ItemRep] rep The item rep to fetch the content for
|
51
|
-
#
|
52
|
-
# @return [Hash<Symbol,String>] A hash containing the cached compiled
|
53
|
-
# content for the given item representation
|
54
|
-
def [](rep)
|
55
|
-
item_cache = cache[rep.item.identifier] || {}
|
56
|
-
item_cache[rep.name]
|
57
|
-
end
|
58
|
-
|
59
|
-
# Sets the compiled content for the given representation.
|
60
|
-
#
|
61
|
-
# @param [Nanoc3::ItemRep] rep The item representation for which to set
|
62
|
-
# the compiled content
|
63
|
-
#
|
64
|
-
# @param [Hash<Symbol,String>] content A hash containing the compiled
|
65
|
-
# content of the given representation
|
66
|
-
#
|
67
|
-
# @return [void]
|
68
|
-
def []=(rep, content)
|
69
|
-
cache[rep.item.identifier] ||= {}
|
70
|
-
cache[rep.item.identifier][rep.name] = content
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def cache
|
76
|
-
@cache ||= {}
|
77
|
-
end
|
78
|
-
|
79
|
-
def pstore
|
80
|
-
require 'pstore'
|
81
|
-
@store ||= PStore.new(@filename)
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|
data/lib/nanoc3/base/item_rep.rb
DELETED
@@ -1,537 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
module Nanoc3
|
4
|
-
|
5
|
-
# A single representation (rep) of an item ({Nanoc3::Item}). An item can
|
6
|
-
# have multiple representations. A representation has its own output file.
|
7
|
-
# A single item can therefore have multiple output files, each run through
|
8
|
-
# a different set of filters with a different layout.
|
9
|
-
class ItemRep
|
10
|
-
|
11
|
-
# The descriptive strings for each outdatedness reason. This hash is used
|
12
|
-
# by the {#outdatedness_reason} method.
|
13
|
-
OUTDATEDNESS_REASON_DESCRIPTIONS = {
|
14
|
-
:not_enough_data => 'Not enough data is present to correctly determine whether the item is outdated.',
|
15
|
-
:forced => 'All items are recompiled because of a `--force` flag given to the compilation command.',
|
16
|
-
:not_written => 'This item representation has not yet been written to the output directory (but it does have a path).',
|
17
|
-
:source_modified => 'The source file of this item has been modified since the last time this item representation was compiled.',
|
18
|
-
:layouts_outdated => 'The source of one or more layouts has been modified since the last time this item representation was compiled.',
|
19
|
-
:code_outdated => 'The code snippets in the `lib/` directory have been modified since the last time this item representation was compiled.',
|
20
|
-
:config_outdated => 'The site configuration has been modified since the last time this item representation was compiled.',
|
21
|
-
:rules_outdated => 'The rules file has been modified since the last time this item representation was compiled.',
|
22
|
-
}
|
23
|
-
|
24
|
-
# @return [Nanoc3::Item] The item to which this rep belongs
|
25
|
-
attr_reader :item
|
26
|
-
|
27
|
-
# @return [Symbol] The representation's unique name
|
28
|
-
attr_reader :name
|
29
|
-
|
30
|
-
# @return [Boolean] true if this rep is forced to be dirty (e.g. because
|
31
|
-
# of the `--force` commandline option); false otherwise
|
32
|
-
attr_accessor :force_outdated
|
33
|
-
|
34
|
-
# @return [Boolean] true if this rep is currently binary; false otherwise
|
35
|
-
attr_reader :binary
|
36
|
-
alias_method :binary?, :binary
|
37
|
-
|
38
|
-
# @return [Boolean] true if this representation has already been compiled
|
39
|
-
# during the current or last compilation session; false otherwise
|
40
|
-
attr_accessor :compiled
|
41
|
-
alias_method :compiled?, :compiled
|
42
|
-
|
43
|
-
# @return [Hash<Symbol,String>] A hash containing the raw paths (paths
|
44
|
-
# including the path to the output directory and the filename) for all
|
45
|
-
# snapshots. The keys correspond with the snapshot names, and the values
|
46
|
-
# with the path.
|
47
|
-
attr_accessor :raw_paths
|
48
|
-
|
49
|
-
# @return [Hash<Symbol,String>] A hash containing the paths for all
|
50
|
-
# snapshots. The keys correspond with the snapshot names, and the values
|
51
|
-
# with the path.
|
52
|
-
attr_accessor :paths
|
53
|
-
|
54
|
-
# @return [Hash<Symbol,String>] A hash containing the content at all
|
55
|
-
# snapshots. The keys correspond with the snapshot names, and the
|
56
|
-
# values with the content.
|
57
|
-
attr_accessor :content
|
58
|
-
|
59
|
-
# Creates a new item representation for the given item.
|
60
|
-
#
|
61
|
-
# @param [Nanoc3::Item] item The item to which the new representation will
|
62
|
-
# belong.
|
63
|
-
#
|
64
|
-
# @param [Symbol] name The unique name for the new item representation.
|
65
|
-
def initialize(item, name)
|
66
|
-
# Set primary attributes
|
67
|
-
@item = item
|
68
|
-
@name = name
|
69
|
-
|
70
|
-
# Set binary
|
71
|
-
@binary = @item.binary?
|
72
|
-
|
73
|
-
# Initialize content and filenames and paths
|
74
|
-
@raw_paths = {}
|
75
|
-
@paths = {}
|
76
|
-
@old_content = nil
|
77
|
-
initialize_content
|
78
|
-
|
79
|
-
# Reset flags
|
80
|
-
@compiled = false
|
81
|
-
@force_outdated = false
|
82
|
-
end
|
83
|
-
|
84
|
-
# Calculates the reason why this item representation is outdated. The
|
85
|
-
# output will be a hash with a `:type` key, containing the reason why the
|
86
|
-
# item is outdated in the form of a symbol, and a `:description` key,
|
87
|
-
# containing a descriptive string that can be printed if necessary.
|
88
|
-
#
|
89
|
-
# For documentation on the types that this method can return, check the
|
90
|
-
# {OUTDATEDNESS_REASON_DESCRIPTIONS} hash in this class.
|
91
|
-
#
|
92
|
-
# @return [Hash, nil] A hash containing the reason why this item rep is
|
93
|
-
# outdated, both in the form of a symbol and as a descriptive string, or
|
94
|
-
# nil if the item representation is not outdated.
|
95
|
-
def outdatedness_reason
|
96
|
-
# Get reason symbol
|
97
|
-
reason = lambda do
|
98
|
-
# Outdated if we’re compiling with --force
|
99
|
-
return :forced if @force_outdated
|
100
|
-
|
101
|
-
# Outdated if checksums are missing
|
102
|
-
if !@item.old_checksum || !@item.new_checksum
|
103
|
-
return :not_enough_data
|
104
|
-
end
|
105
|
-
|
106
|
-
# Outdated if compiled file doesn't exist (yet)
|
107
|
-
return :not_written if self.raw_path && !File.file?(self.raw_path)
|
108
|
-
|
109
|
-
# Outdated if file too old
|
110
|
-
if @item.old_checksum != @item.new_checksum
|
111
|
-
return :source_modified
|
112
|
-
end
|
113
|
-
|
114
|
-
# Outdated if other site parts outdated
|
115
|
-
return :code_outdated if @item.site.code_snippets.any? { |cs| cs.outdated? }
|
116
|
-
return :config_outdated if @item.site.config_outdated?
|
117
|
-
return :rules_outdated if @item.site.rules_outdated?
|
118
|
-
|
119
|
-
return nil
|
120
|
-
end[]
|
121
|
-
|
122
|
-
# Build reason symbol and description
|
123
|
-
if reason.nil?
|
124
|
-
nil
|
125
|
-
else
|
126
|
-
{
|
127
|
-
:type => reason,
|
128
|
-
:description => OUTDATEDNESS_REASON_DESCRIPTIONS[reason]
|
129
|
-
}
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# @return [Boolean] true if this item rep's output file is outdated and
|
134
|
-
# must be regenerated, false otherwise
|
135
|
-
def outdated?
|
136
|
-
!outdatedness_reason.nil?
|
137
|
-
end
|
138
|
-
|
139
|
-
# @return [Hash] The assignments that should be available when compiling
|
140
|
-
# the content.
|
141
|
-
def assigns
|
142
|
-
if self.binary?
|
143
|
-
content_or_filename_assigns = { :filename => @filenames[:last] }
|
144
|
-
else
|
145
|
-
content_or_filename_assigns = { :content => @content[:last] }
|
146
|
-
end
|
147
|
-
|
148
|
-
content_or_filename_assigns.merge({
|
149
|
-
:item => self.item,
|
150
|
-
:item_rep => self,
|
151
|
-
:items => self.item.site.items,
|
152
|
-
:layouts => self.item.site.layouts,
|
153
|
-
:config => self.item.site.config,
|
154
|
-
:site => self.item.site
|
155
|
-
})
|
156
|
-
end
|
157
|
-
|
158
|
-
# Returns the compiled content from a given snapshot.
|
159
|
-
#
|
160
|
-
# @option params [String] :snapshot The name of the snapshot from which to
|
161
|
-
# fetch the compiled content. By default, the returned compiled content
|
162
|
-
# will be the content compiled right before the first layout call (if
|
163
|
-
# any).
|
164
|
-
#
|
165
|
-
# @return [String] The compiled content at the given snapshot (or the
|
166
|
-
# default snapshot if no snapshot is specified)
|
167
|
-
def compiled_content(params={})
|
168
|
-
# Notify
|
169
|
-
Nanoc3::NotificationCenter.post(:visit_started, self.item)
|
170
|
-
Nanoc3::NotificationCenter.post(:visit_ended, self.item)
|
171
|
-
|
172
|
-
# Require compilation
|
173
|
-
raise Nanoc3::Errors::UnmetDependency.new(self) if !compiled? && !params[:force]
|
174
|
-
|
175
|
-
# Get name of last pre-layout snapshot
|
176
|
-
snapshot_name = params[:snapshot]
|
177
|
-
if @content[:pre]
|
178
|
-
snapshot_name ||= :pre
|
179
|
-
else
|
180
|
-
snapshot_name ||= :last
|
181
|
-
end
|
182
|
-
|
183
|
-
# Check presence of snapshot
|
184
|
-
if @content[snapshot_name].nil?
|
185
|
-
warn(('-' * 78 + "\nWARNING: The “#{self.item.identifier}” item (rep “#{self.name}”) does not have the requested snapshot named #{snapshot_name.inspect}.\n\n* Make sure that you are requesting the correct snapshot.\n* It is not possible to request the compiled content of a binary item representation; if this item is marked as binary even though you believe it should be textual, you may need to add the extension of this item to the site configuration’s `text_extensions` array.\n" + '-' * 78).make_compatible_with_env)
|
186
|
-
end
|
187
|
-
|
188
|
-
# Get content
|
189
|
-
@content[snapshot_name]
|
190
|
-
end
|
191
|
-
|
192
|
-
# Checks whether content exists at a given snapshot.
|
193
|
-
#
|
194
|
-
# @return [Boolean] True if content exists for the snapshot with the
|
195
|
-
# given name, false otherwise
|
196
|
-
def has_snapshot?(snapshot_name)
|
197
|
-
!@content[snapshot_name].nil?
|
198
|
-
end
|
199
|
-
|
200
|
-
# Returns the item rep’s raw path. It includes the path to the output
|
201
|
-
# directory and the full filename.
|
202
|
-
#
|
203
|
-
# @option params [Symbol] :snapshot (:last) The snapshot for which the
|
204
|
-
# path should be returned
|
205
|
-
#
|
206
|
-
# @return [String] The item rep’s path
|
207
|
-
def raw_path(params={})
|
208
|
-
snapshot_name = params[:snapshot] || :last
|
209
|
-
@raw_paths[snapshot_name]
|
210
|
-
end
|
211
|
-
|
212
|
-
# Returns the item rep’s path, as used when being linked to. It starts
|
213
|
-
# with a slash and it is relative to the output directory. It does not
|
214
|
-
# include the path to the output directory. It will not include the
|
215
|
-
# filename if the filename is an index filename.
|
216
|
-
#
|
217
|
-
# @option params [Symbol] :snapshot (:last) The snapshot for which the
|
218
|
-
# path should be returned
|
219
|
-
#
|
220
|
-
# @return [String] The item rep’s path
|
221
|
-
def path(params={})
|
222
|
-
snapshot_name = params[:snapshot] || :last
|
223
|
-
@paths[snapshot_name]
|
224
|
-
end
|
225
|
-
|
226
|
-
# @deprecated Modify the {#raw_paths} attribute instead
|
227
|
-
def raw_path=(raw_path)
|
228
|
-
raw_paths[:last] = raw_path
|
229
|
-
end
|
230
|
-
|
231
|
-
# @deprecated Modify the {#paths} attribute instead
|
232
|
-
def path=(path)
|
233
|
-
paths[:last] = path
|
234
|
-
end
|
235
|
-
|
236
|
-
# @deprecated Use {Nanoc3::ItemRep#compiled_content} instead.
|
237
|
-
def content_at_snapshot(snapshot=:pre)
|
238
|
-
compiled_content(:snapshot => snapshot)
|
239
|
-
end
|
240
|
-
|
241
|
-
# Resets the compilation progress for this item representation. This is
|
242
|
-
# necessary when an unmet dependency is detected during compilation.
|
243
|
-
# This method should probably not be called directly.
|
244
|
-
#
|
245
|
-
# @return [void]
|
246
|
-
def forget_progress
|
247
|
-
initialize_content
|
248
|
-
end
|
249
|
-
|
250
|
-
# Runs the item content through the given filter with the given arguments.
|
251
|
-
# This method will replace the content of the `:last` snapshot with the
|
252
|
-
# filtered content of the last snapshot.
|
253
|
-
#
|
254
|
-
# This method is supposed to be called only in a compilation rule block
|
255
|
-
# (see {Nanoc3::CompilerDSL#compile}).
|
256
|
-
#
|
257
|
-
# @param [Symbol] filter_name The name of the filter to run the item
|
258
|
-
# representations' content through
|
259
|
-
#
|
260
|
-
# @param [Hash] filter_args The filter arguments that should be passed to
|
261
|
-
# the filter's #run method
|
262
|
-
#
|
263
|
-
# @return [void]
|
264
|
-
def filter(filter_name, filter_args={})
|
265
|
-
# Get filter class
|
266
|
-
klass = filter_named(filter_name)
|
267
|
-
raise Nanoc3::Errors::UnknownFilter.new(filter_name) if klass.nil?
|
268
|
-
|
269
|
-
# Check whether filter can be applied
|
270
|
-
if klass.from_binary? && !self.binary?
|
271
|
-
raise Nanoc3::Errors::CannotUseBinaryFilter.new(self, klass)
|
272
|
-
elsif !klass.from_binary? && self.binary?
|
273
|
-
raise Nanoc3::Errors::CannotUseTextualFilter.new(self, klass)
|
274
|
-
end
|
275
|
-
|
276
|
-
# Create filter
|
277
|
-
filter = klass.new(assigns)
|
278
|
-
|
279
|
-
# Run filter
|
280
|
-
Nanoc3::NotificationCenter.post(:filtering_started, self, filter_name)
|
281
|
-
source = self.binary? ? @filenames[:last] : @content[:last]
|
282
|
-
result = filter.run(source, filter_args)
|
283
|
-
if klass.to_binary?
|
284
|
-
@filenames[:last] = filter.output_filename
|
285
|
-
else
|
286
|
-
@content[:last] = result
|
287
|
-
end
|
288
|
-
@binary = klass.to_binary?
|
289
|
-
Nanoc3::NotificationCenter.post(:filtering_ended, self, filter_name)
|
290
|
-
|
291
|
-
# Check whether file was written
|
292
|
-
if self.binary? && !File.file?(filter.output_filename)
|
293
|
-
raise RuntimeError,
|
294
|
-
"The #{filter_name.inspect} filter did not write anything to the required output file, #{filter.output_filename}."
|
295
|
-
end
|
296
|
-
|
297
|
-
# Create snapshot
|
298
|
-
snapshot(@content[:post] ? :post : :pre, :final => false) unless self.binary?
|
299
|
-
end
|
300
|
-
|
301
|
-
# Lays out the item using the given layout. This method will replace the
|
302
|
-
# content of the `:last` snapshot with the laid out content of the last
|
303
|
-
# snapshot.
|
304
|
-
#
|
305
|
-
# This method is supposed to be called only in a compilation rule block
|
306
|
-
# (see {Nanoc3::CompilerDSL#compile}).
|
307
|
-
#
|
308
|
-
# @param [String] layout_identifier The identifier of the layout the item
|
309
|
-
# should be laid out with
|
310
|
-
#
|
311
|
-
# @return [void]
|
312
|
-
def layout(layout_identifier)
|
313
|
-
# Check whether item can be laid out
|
314
|
-
raise Nanoc3::Errors::CannotLayoutBinaryItem.new(self) if self.binary?
|
315
|
-
|
316
|
-
# Create "pre" snapshot
|
317
|
-
if @content[:post].nil?
|
318
|
-
snapshot(:pre, :final => true)
|
319
|
-
end
|
320
|
-
|
321
|
-
# Create filter
|
322
|
-
layout = layout_with_identifier(layout_identifier)
|
323
|
-
filter, filter_name, filter_args = filter_for_layout(layout)
|
324
|
-
|
325
|
-
# Visit
|
326
|
-
Nanoc3::NotificationCenter.post(:visit_started, layout)
|
327
|
-
Nanoc3::NotificationCenter.post(:visit_ended, layout)
|
328
|
-
|
329
|
-
# Layout
|
330
|
-
@item.site.compiler.stack.push(layout)
|
331
|
-
Nanoc3::NotificationCenter.post(:filtering_started, self, filter_name)
|
332
|
-
@content[:last] = filter.run(layout.raw_content, filter_args)
|
333
|
-
Nanoc3::NotificationCenter.post(:filtering_ended, self, filter_name)
|
334
|
-
@item.site.compiler.stack.pop
|
335
|
-
|
336
|
-
# Create "post" snapshot
|
337
|
-
snapshot(:post, :final => false)
|
338
|
-
end
|
339
|
-
|
340
|
-
# Creates a snapshot of the current compiled item content.
|
341
|
-
#
|
342
|
-
# @param [Symbol] snapshot_name The name of the snapshot to create
|
343
|
-
#
|
344
|
-
# @option params [Boolean] :final (true) True if this is the final time
|
345
|
-
# the snapshot will be updated; false if it is a non-final moving
|
346
|
-
# snapshot (such as `:pre`, `:post` or `:last`)
|
347
|
-
#
|
348
|
-
# @return [void]
|
349
|
-
def snapshot(snapshot_name, params={})
|
350
|
-
# Parse params
|
351
|
-
params[:final] = true if !params.has_key?(:final)
|
352
|
-
|
353
|
-
# Create snapshot
|
354
|
-
@content[snapshot_name] = @content[:last] unless self.binary?
|
355
|
-
|
356
|
-
# Write
|
357
|
-
write(snapshot_name) if params[:final]
|
358
|
-
end
|
359
|
-
|
360
|
-
# Writes the item rep's compiled content to the rep's output file.
|
361
|
-
#
|
362
|
-
# This method should not be called directly, even in a compilation block;
|
363
|
-
# the compiler is responsible for calling this method.
|
364
|
-
#
|
365
|
-
# @param [String, nil] raw_path The raw path to write the compiled rep to.
|
366
|
-
# If nil, the default raw path will be used.
|
367
|
-
#
|
368
|
-
# @return [void]
|
369
|
-
def write(snapshot=:last)
|
370
|
-
# Get raw path
|
371
|
-
raw_path = self.raw_path(:snapshot => snapshot)
|
372
|
-
return if raw_path.nil?
|
373
|
-
|
374
|
-
# Create parent directory
|
375
|
-
FileUtils.mkdir_p(File.dirname(raw_path))
|
376
|
-
|
377
|
-
# Check if file will be created
|
378
|
-
is_created = !File.file?(raw_path)
|
379
|
-
|
380
|
-
# Calculate characteristics of old content
|
381
|
-
if File.file?(raw_path)
|
382
|
-
hash_old = Nanoc3::Checksummer.checksum_for(raw_path)
|
383
|
-
size_old = File.size(raw_path)
|
384
|
-
end
|
385
|
-
|
386
|
-
if self.binary?
|
387
|
-
# Copy
|
388
|
-
FileUtils.cp(@filenames[:last], raw_path)
|
389
|
-
else
|
390
|
-
# Write
|
391
|
-
File.open(raw_path, 'w') { |io| io.write(@content[:last]) }
|
392
|
-
|
393
|
-
# Generate diff
|
394
|
-
generate_diff
|
395
|
-
end
|
396
|
-
|
397
|
-
# Check if file was modified
|
398
|
-
size_new = File.size(raw_path)
|
399
|
-
hash_new = Nanoc3::Checksummer.checksum_for(raw_path) if size_old == size_new
|
400
|
-
is_modified = (size_old != size_new || hash_old != hash_new)
|
401
|
-
|
402
|
-
# Notify
|
403
|
-
Nanoc3::NotificationCenter.post(
|
404
|
-
:rep_written,
|
405
|
-
self, raw_path, is_created, is_modified
|
406
|
-
)
|
407
|
-
end
|
408
|
-
|
409
|
-
# Creates and returns a diff between the compiled content before the
|
410
|
-
# current compilation session and the content compiled in the current
|
411
|
-
# compilation session.
|
412
|
-
#
|
413
|
-
# @return [String, nil] The difference between the old and new compiled
|
414
|
-
# content in `diff(1)` format, or nil if there is no previous compiled
|
415
|
-
# content
|
416
|
-
def diff
|
417
|
-
if self.binary?
|
418
|
-
nil
|
419
|
-
else
|
420
|
-
@diff_thread.join if @diff_thread
|
421
|
-
@diff
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
# @deprecated
|
426
|
-
def created
|
427
|
-
raise NotImplementedError, "Nanoc3::ItemRep#created is no longer implemented"
|
428
|
-
end
|
429
|
-
|
430
|
-
# @deprecated
|
431
|
-
def created?
|
432
|
-
raise NotImplementedError, "Nanoc3::ItemRep#created? is no longer implemented"
|
433
|
-
end
|
434
|
-
|
435
|
-
# @deprecated
|
436
|
-
def modified
|
437
|
-
raise NotImplementedError, "Nanoc3::ItemRep#modified is no longer implemented"
|
438
|
-
end
|
439
|
-
|
440
|
-
# @deprecated
|
441
|
-
def modified?
|
442
|
-
raise NotImplementedError, "Nanoc3::ItemRep#modified? is no longer implemented"
|
443
|
-
end
|
444
|
-
|
445
|
-
# @deprecated
|
446
|
-
def written
|
447
|
-
raise NotImplementedError, "Nanoc3::ItemRep#written is no longer implemented"
|
448
|
-
end
|
449
|
-
|
450
|
-
# @deprecated
|
451
|
-
def written?
|
452
|
-
raise NotImplementedError, "Nanoc3::ItemRep#written? is no longer implemented"
|
453
|
-
end
|
454
|
-
|
455
|
-
def inspect
|
456
|
-
"<#{self.class}:0x#{self.object_id.to_s(16)} name=#{self.name} binary=#{self.binary?} raw_path=#{self.raw_path} item.identifier=#{self.item.identifier}>"
|
457
|
-
end
|
458
|
-
|
459
|
-
private
|
460
|
-
|
461
|
-
def initialize_content
|
462
|
-
# Initialize content and filenames
|
463
|
-
if self.binary?
|
464
|
-
@filenames = { :last => @item.raw_filename }
|
465
|
-
@content = {}
|
466
|
-
else
|
467
|
-
@content = { :last => @item.raw_content }
|
468
|
-
@filenames = {}
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def filter_named(name)
|
473
|
-
Nanoc3::Filter.named(name)
|
474
|
-
end
|
475
|
-
|
476
|
-
def layout_with_identifier(layout_identifier)
|
477
|
-
layout ||= @item.site.layouts.find { |l| l.identifier == layout_identifier.cleaned_identifier }
|
478
|
-
raise Nanoc3::Errors::UnknownLayout.new(layout_identifier) if layout.nil?
|
479
|
-
layout
|
480
|
-
end
|
481
|
-
|
482
|
-
def filter_for_layout(layout)
|
483
|
-
# Get filter name and args
|
484
|
-
filter_name, filter_args = @item.site.compiler.filter_for_layout(layout)
|
485
|
-
raise Nanoc3::Errors::CannotDetermineFilter.new(layout_identifier) if filter_name.nil?
|
486
|
-
|
487
|
-
# Get filter class
|
488
|
-
filter_class = Nanoc3::Filter.named(filter_name)
|
489
|
-
raise Nanoc3::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
|
490
|
-
|
491
|
-
# Create filter
|
492
|
-
filter = filter_class.new(assigns.merge({ :layout => layout }))
|
493
|
-
|
494
|
-
# Done
|
495
|
-
[ filter, filter_name, filter_args ]
|
496
|
-
end
|
497
|
-
|
498
|
-
def generate_diff
|
499
|
-
if @old_content.nil? || self.raw_path.nil? || !@item.site.config[:enable_output_diff]
|
500
|
-
@diff = nil
|
501
|
-
else
|
502
|
-
@diff_thread = Thread.new do
|
503
|
-
@diff = diff_strings(@old_content, @content[:last])
|
504
|
-
sleep 2
|
505
|
-
@diff_thread = nil
|
506
|
-
end
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
def diff_strings(a, b)
|
511
|
-
require 'tempfile'
|
512
|
-
require 'open3'
|
513
|
-
|
514
|
-
# Create files
|
515
|
-
Tempfile.open('old') do |old_file|
|
516
|
-
Tempfile.open('new') do |new_file|
|
517
|
-
# Write files
|
518
|
-
old_file.write(a)
|
519
|
-
new_file.write(b)
|
520
|
-
|
521
|
-
# Diff
|
522
|
-
cmd = [ 'diff', '-u', old_file.path, new_file.path ]
|
523
|
-
Open3.popen3(*cmd) do |stdin, stdout, stderr|
|
524
|
-
result = stdout.read
|
525
|
-
return (result == '' ? nil : result)
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
rescue Errno::ENOENT
|
530
|
-
warn 'Failed to run `diff`, so no diff with the previously compiled ' \
|
531
|
-
'content will be available.'
|
532
|
-
nil
|
533
|
-
end
|
534
|
-
|
535
|
-
end
|
536
|
-
|
537
|
-
end
|