nanoc3 3.2.0a1 → 3.2.0a2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/ChangeLog +2 -2
  2. data/NEWS.md +14 -0
  3. data/README.md +20 -12
  4. data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
  5. data/lib/nanoc3/base/compilation/checksum_store.rb +130 -0
  6. data/lib/nanoc3/base/compilation/checksummer.rb +68 -0
  7. data/lib/nanoc3/base/compilation/compiled_content_cache.rb +57 -0
  8. data/lib/nanoc3/base/{compiler.rb → compilation/compiler.rb} +255 -55
  9. data/lib/nanoc3/base/{compiler_dsl.rb → compilation/compiler_dsl.rb} +11 -6
  10. data/lib/nanoc3/base/{dependency_tracker.rb → compilation/dependency_tracker.rb} +62 -92
  11. data/lib/nanoc3/base/{filter.rb → compilation/filter.rb} +0 -0
  12. data/lib/nanoc3/base/compilation/item_rep_proxy.rb +87 -0
  13. data/lib/nanoc3/base/compilation/outdatedness_checker.rb +86 -0
  14. data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +43 -0
  15. data/lib/nanoc3/base/{rule.rb → compilation/rule.rb} +8 -2
  16. data/lib/nanoc3/base/{rule_context.rb → compilation/rule_context.rb} +17 -14
  17. data/lib/nanoc3/base/directed_graph.rb +8 -0
  18. data/lib/nanoc3/base/errors.rb +9 -17
  19. data/lib/nanoc3/base/plugin_registry.rb +1 -1
  20. data/lib/nanoc3/base/result_data/item_rep.rb +447 -0
  21. data/lib/nanoc3/base/{code_snippet.rb → source_data/code_snippet.rb} +7 -22
  22. data/lib/nanoc3/base/{data_source.rb → source_data/data_source.rb} +0 -0
  23. data/lib/nanoc3/base/{item.rb → source_data/item.rb} +20 -30
  24. data/lib/nanoc3/base/{layout.rb → source_data/layout.rb} +7 -26
  25. data/lib/nanoc3/base/source_data/site.rb +314 -0
  26. data/lib/nanoc3/base/store.rb +126 -0
  27. data/lib/nanoc3/base.rb +26 -15
  28. data/lib/nanoc3/cli/base.rb +2 -1
  29. data/lib/nanoc3/cli/commands/compile.rb +116 -48
  30. data/lib/nanoc3/cli/commands/create_item.rb +0 -1
  31. data/lib/nanoc3/cli/commands/create_layout.rb +0 -1
  32. data/lib/nanoc3/cli/commands/create_site.rb +11 -1
  33. data/lib/nanoc3/cli/commands/debug.rb +11 -6
  34. data/lib/nanoc3/cli/commands/info.rb +1 -2
  35. data/lib/nanoc3/cli/commands/update.rb +0 -1
  36. data/lib/nanoc3/cli/commands/watch.rb +27 -32
  37. data/lib/nanoc3/cli/logger.rb +2 -2
  38. data/lib/nanoc3/data_sources/filesystem.rb +1 -6
  39. data/lib/nanoc3/extra/auto_compiler.rb +2 -3
  40. data/lib/nanoc3/extra/deployers/rsync.rb +1 -0
  41. data/lib/nanoc3/extra/validators/links.rb +45 -26
  42. data/lib/nanoc3/filters/asciidoc.rb +58 -0
  43. data/lib/nanoc3/filters/colorize_syntax.rb +47 -9
  44. data/lib/nanoc3/filters/less.rb +6 -0
  45. data/lib/nanoc3/filters/sass.rb +8 -5
  46. data/lib/nanoc3/filters.rb +2 -0
  47. data/lib/nanoc3/helpers/blogging.rb +8 -0
  48. data/lib/nanoc3/helpers/html_escape.rb +37 -7
  49. data/lib/nanoc3/helpers/link_to.rb +15 -4
  50. data/lib/nanoc3/helpers/rendering.rb +6 -2
  51. data/lib/nanoc3/tasks/clean.rb +0 -4
  52. data/lib/nanoc3.rb +1 -1
  53. data/nanoc3.gemspec +42 -0
  54. metadata +35 -19
  55. data/lib/nanoc3/base/checksummer.rb +0 -40
  56. data/lib/nanoc3/base/compiled_content_cache.rb +0 -86
  57. data/lib/nanoc3/base/item_rep.rb +0 -537
  58. 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
@@ -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