nanoc-core 4.11.12 → 4.11.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nanoc/core.rb +37 -0
  3. data/lib/nanoc/core/basic_item_rep_collection_view.rb +88 -0
  4. data/lib/nanoc/core/basic_item_rep_view.rb +83 -0
  5. data/lib/nanoc/core/basic_item_view.rb +54 -0
  6. data/lib/nanoc/core/changes_stream.rb +55 -0
  7. data/lib/nanoc/core/checksum_store.rb +1 -1
  8. data/lib/nanoc/core/checksummer.rb +4 -2
  9. data/lib/nanoc/core/compilation_item_rep_collection_view.rb +12 -0
  10. data/lib/nanoc/core/compilation_item_rep_view.rb +57 -0
  11. data/lib/nanoc/core/compilation_item_view.rb +47 -0
  12. data/lib/nanoc/core/compilation_phases/abstract.rb +48 -0
  13. data/lib/nanoc/core/compilation_phases/cache.rb +43 -0
  14. data/lib/nanoc/core/compilation_phases/mark_done.rb +23 -0
  15. data/lib/nanoc/core/compilation_phases/notify.rb +19 -0
  16. data/lib/nanoc/core/compilation_phases/recalculate.rb +49 -0
  17. data/lib/nanoc/core/compilation_phases/resume.rb +52 -0
  18. data/lib/nanoc/core/compilation_phases/write.rb +84 -0
  19. data/lib/nanoc/core/compilation_stages/build_reps.rb +36 -0
  20. data/lib/nanoc/core/compilation_stages/calculate_checksums.rb +42 -0
  21. data/lib/nanoc/core/compilation_stages/cleanup.rb +43 -0
  22. data/lib/nanoc/core/compilation_stages/compile_reps.rb +96 -0
  23. data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +49 -0
  24. data/lib/nanoc/core/compilation_stages/forget_outdated_dependencies.rb +20 -0
  25. data/lib/nanoc/core/compilation_stages/load_stores.rb +35 -0
  26. data/lib/nanoc/core/compilation_stages/postprocess.rb +21 -0
  27. data/lib/nanoc/core/compilation_stages/preprocess.rb +32 -0
  28. data/lib/nanoc/core/compilation_stages/prune.rb +30 -0
  29. data/lib/nanoc/core/compilation_stages/store_post_compilation_state.rb +20 -0
  30. data/lib/nanoc/core/compilation_stages/store_pre_compilation_state.rb +32 -0
  31. data/lib/nanoc/core/compiled_content_cache.rb +2 -2
  32. data/lib/nanoc/core/compiler.rb +214 -0
  33. data/lib/nanoc/core/compiler_loader.rb +48 -0
  34. data/lib/nanoc/core/config_loader.rb +95 -0
  35. data/lib/nanoc/core/config_view.rb +67 -0
  36. data/lib/nanoc/core/configuration.rb +2 -4
  37. data/lib/nanoc/core/contracts_support.rb +20 -0
  38. data/lib/nanoc/core/dependency_store.rb +3 -3
  39. data/lib/nanoc/core/document_view_mixin.rb +87 -0
  40. data/lib/nanoc/core/errors.rb +108 -0
  41. data/lib/nanoc/core/executor.rb +134 -0
  42. data/lib/nanoc/core/feature.rb +92 -0
  43. data/lib/nanoc/core/filter.rb +269 -0
  44. data/lib/nanoc/core/identifiable_collection_view.rb +111 -0
  45. data/lib/nanoc/core/item_collection_with_reps_view.rb +12 -0
  46. data/lib/nanoc/core/item_collection_without_reps_view.rb +12 -0
  47. data/lib/nanoc/core/item_rep_builder.rb +54 -0
  48. data/lib/nanoc/core/item_rep_selector.rb +67 -0
  49. data/lib/nanoc/core/item_rep_writer.rb +85 -0
  50. data/lib/nanoc/core/layout_collection_view.rb +12 -0
  51. data/lib/nanoc/core/layout_view.rb +9 -0
  52. data/lib/nanoc/core/mutable_config_view.rb +16 -0
  53. data/lib/nanoc/core/mutable_document_view_mixin.rb +60 -0
  54. data/lib/nanoc/core/mutable_identifiable_collection_view.rb +19 -0
  55. data/lib/nanoc/core/mutable_item_collection_view.rb +34 -0
  56. data/lib/nanoc/core/mutable_item_view.rb +9 -0
  57. data/lib/nanoc/core/mutable_layout_collection_view.rb +26 -0
  58. data/lib/nanoc/core/mutable_layout_view.rb +9 -0
  59. data/lib/nanoc/core/outdatedness_checker.rb +222 -0
  60. data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +41 -0
  61. data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +31 -0
  62. data/lib/nanoc/core/outdatedness_rules/content_modified.rb +21 -0
  63. data/lib/nanoc/core/outdatedness_rules/item_collection_extended.rb +20 -0
  64. data/lib/nanoc/core/outdatedness_rules/layout_collection_extended.rb +20 -0
  65. data/lib/nanoc/core/outdatedness_rules/not_written.rb +17 -0
  66. data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +45 -0
  67. data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +26 -0
  68. data/lib/nanoc/core/post_compile_item_collection_view.rb +12 -0
  69. data/lib/nanoc/core/post_compile_item_rep_collection_view.rb +12 -0
  70. data/lib/nanoc/core/post_compile_item_rep_view.rb +33 -0
  71. data/lib/nanoc/core/post_compile_item_view.rb +20 -0
  72. data/lib/nanoc/core/pruner.rb +123 -0
  73. data/lib/nanoc/core/site_loader.rb +102 -0
  74. data/lib/nanoc/core/trivial_error.rb +10 -0
  75. data/lib/nanoc/core/version.rb +1 -1
  76. data/lib/nanoc/core/view.rb +43 -0
  77. data/lib/nanoc/core/view_context_for_compilation.rb +6 -6
  78. metadata +97 -3
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class MutableIdentifiableCollectionView < Nanoc::Core::IdentifiableCollectionView
6
+ # Deletes every object for which the block evaluates to true.
7
+ #
8
+ # @yieldparam [#identifier] object
9
+ #
10
+ # @yieldreturn [Boolean]
11
+ #
12
+ # @return [self]
13
+ def delete_if(&_block)
14
+ @objects = @objects.reject { |o| yield(view_class.new(o, @context)) }
15
+ self
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class MutableItemCollectionView < Nanoc::Core::MutableIdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::MutableItemView
9
+ end
10
+
11
+ # Creates a new item and adds it to the site’s collection of items.
12
+ #
13
+ # @param [String] content The uncompiled item content (if it is a textual
14
+ # item) or the path to the filename containing the content (if it is a
15
+ # binary item).
16
+ #
17
+ # @param [Hash] attributes A hash containing this item's attributes.
18
+ #
19
+ # @param [Nanoc::Core::Identifier, String] identifier This item's identifier.
20
+ #
21
+ # @param [Boolean] binary Whether or not this item is binary
22
+ #
23
+ # @param [String] filename Absolute path to the file
24
+ # containing this content (if any)
25
+ #
26
+ # @return [self]
27
+ def create(content, attributes, identifier, binary: false, filename: nil)
28
+ content = Nanoc::Core::Content.create(content, binary: binary, filename: filename)
29
+ @objects = @objects.add(Nanoc::Core::Item.new(content, attributes, identifier))
30
+ self
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class MutableItemView < Nanoc::Core::BasicItemView
6
+ include Nanoc::Core::MutableDocumentViewMixin
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class MutableLayoutCollectionView < Nanoc::Core::MutableIdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::MutableLayoutView
9
+ end
10
+
11
+ # Creates a new layout and adds it to the site’s collection of layouts.
12
+ #
13
+ # @param [String] content The layout content.
14
+ #
15
+ # @param [Hash] attributes A hash containing this layout's attributes.
16
+ #
17
+ # @param [Nanoc::Core::Identifier, String] identifier This layout's identifier.
18
+ #
19
+ # @return [self]
20
+ def create(content, attributes, identifier)
21
+ @objects = @objects.add(Nanoc::Core::Layout.new(content, attributes, identifier))
22
+ self
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class MutableLayoutView < Nanoc::Core::LayoutView
6
+ include Nanoc::Core::MutableDocumentViewMixin
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Responsible for determining whether an item or a layout is outdated.
6
+ #
7
+ # @api private
8
+ class OutdatednessChecker
9
+ class Basic
10
+ DDMemoize.activate(self)
11
+
12
+ include Nanoc::Core::ContractsSupport
13
+
14
+ Rules = Nanoc::Core::OutdatednessRules
15
+
16
+ RULES_FOR_ITEM_REP =
17
+ [
18
+ Rules::RulesModified,
19
+ Rules::ContentModified,
20
+ Rules::AttributesModified,
21
+ Rules::NotWritten,
22
+ Rules::CodeSnippetsModified,
23
+ Rules::UsesAlwaysOutdatedFilter,
24
+ ].freeze
25
+
26
+ RULES_FOR_LAYOUT =
27
+ [
28
+ Rules::RulesModified,
29
+ Rules::ContentModified,
30
+ Rules::AttributesModified,
31
+ Rules::UsesAlwaysOutdatedFilter,
32
+ ].freeze
33
+
34
+ RULES_FOR_CONFIG =
35
+ [
36
+ Rules::AttributesModified,
37
+ ].freeze
38
+
39
+ RULES_FOR_ITEM_COLLECTION =
40
+ [
41
+ Rules::ItemCollectionExtended,
42
+ ].freeze
43
+
44
+ RULES_FOR_LAYOUT_COLLECTION =
45
+ [
46
+ Rules::LayoutCollectionExtended,
47
+ ].freeze
48
+
49
+ C_OBJ_MAYBE_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep, Nanoc::Core::Configuration, Nanoc::Core::Layout, Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection]
50
+
51
+ contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Core::ItemRepRepo] => C::Any
52
+ def initialize(outdatedness_checker:, reps:)
53
+ @outdatedness_checker = outdatedness_checker
54
+ @reps = reps
55
+ end
56
+
57
+ contract C_OBJ_MAYBE_REP => C::Maybe[Nanoc::Core::OutdatednessStatus]
58
+ memoized def outdatedness_status_for(obj)
59
+ case obj
60
+ when Nanoc::Core::ItemRep
61
+ apply_rules(RULES_FOR_ITEM_REP, obj)
62
+ when Nanoc::Core::Item
63
+ apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
64
+ when Nanoc::Core::Layout
65
+ apply_rules(RULES_FOR_LAYOUT, obj)
66
+ when Nanoc::Core::Configuration
67
+ apply_rules(RULES_FOR_CONFIG, obj)
68
+ when Nanoc::Core::ItemCollection
69
+ apply_rules(RULES_FOR_ITEM_COLLECTION, obj)
70
+ when Nanoc::Core::LayoutCollection
71
+ apply_rules(RULES_FOR_LAYOUT_COLLECTION, obj)
72
+ else
73
+ raise Nanoc::Core::Errors::InternalInconsistency, "do not know how to check outdatedness of #{obj.inspect}"
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ contract C::ArrayOf[Class], C_OBJ_MAYBE_REP, Nanoc::Core::OutdatednessStatus => C::Maybe[Nanoc::Core::OutdatednessStatus]
80
+ def apply_rules(rules, obj, status = Nanoc::Core::OutdatednessStatus.new)
81
+ rules.inject(status) do |acc, rule|
82
+ if !acc.useful_to_apply?(rule)
83
+ acc
84
+ else
85
+ reason = rule.instance.call(obj, @outdatedness_checker)
86
+ if reason
87
+ acc.update(reason)
88
+ else
89
+ acc
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ contract C::ArrayOf[Class], C::ArrayOf[C_OBJ_MAYBE_REP] => C::Maybe[Nanoc::Core::OutdatednessStatus]
96
+ def apply_rules_multi(rules, objs)
97
+ objs.inject(Nanoc::Core::OutdatednessStatus.new) { |acc, elem| apply_rules(rules, elem, acc) }
98
+ end
99
+ end
100
+
101
+ DDMemoize.activate(self)
102
+
103
+ include Nanoc::Core::ContractsSupport
104
+
105
+ attr_reader :checksum_store
106
+ attr_reader :checksums
107
+ attr_reader :dependency_store
108
+ attr_reader :action_sequence_store
109
+ attr_reader :action_sequences
110
+ attr_reader :site
111
+
112
+ Reasons = Nanoc::Core::OutdatednessReasons
113
+
114
+ C_OBJ = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep, Nanoc::Core::Configuration, Nanoc::Core::Layout, Nanoc::Core::ItemCollection]
115
+ C_ITEM_OR_REP = C::Or[Nanoc::Core::Item, Nanoc::Core::ItemRep]
116
+ C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Core::ActionSequence]
117
+
118
+ contract C::KeywordArgs[site: Nanoc::Core::Site, checksum_store: Nanoc::Core::ChecksumStore, checksums: Nanoc::Core::ChecksumCollection, dependency_store: Nanoc::Core::DependencyStore, action_sequence_store: Nanoc::Core::ActionSequenceStore, action_sequences: C_ACTION_SEQUENCES, reps: Nanoc::Core::ItemRepRepo] => C::Any
119
+ def initialize(site:, checksum_store:, checksums:, dependency_store:, action_sequence_store:, action_sequences:, reps:)
120
+ @site = site
121
+ @checksum_store = checksum_store
122
+ @checksums = checksums
123
+ @dependency_store = dependency_store
124
+ @action_sequence_store = action_sequence_store
125
+ @action_sequences = action_sequences
126
+ @reps = reps
127
+
128
+ @objects_outdated_due_to_dependencies = {}
129
+ end
130
+
131
+ def action_sequence_for(rep)
132
+ @action_sequences.fetch(rep)
133
+ end
134
+
135
+ contract C_OBJ => C::Bool
136
+ def outdated?(obj)
137
+ outdatedness_reasons_for(obj).any?
138
+ end
139
+
140
+ contract C_OBJ => C::IterOf[Reasons::Generic]
141
+ def outdatedness_reasons_for(obj)
142
+ reasons = basic.outdatedness_status_for(obj).reasons
143
+ if reasons.any?
144
+ reasons
145
+ elsif outdated_due_to_dependencies?(obj)
146
+ [Reasons::DependenciesOutdated]
147
+ else
148
+ []
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ contract C::None => Basic
155
+ def basic
156
+ @_basic ||= Basic.new(outdatedness_checker: self, reps: @reps)
157
+ end
158
+
159
+ contract C_OBJ, Hamster::Set => C::Bool
160
+ def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
161
+ # Convert from rep to item if necessary
162
+ obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
163
+
164
+ # Only items can have dependencies
165
+ return false unless obj.is_a?(Nanoc::Core::Item)
166
+
167
+ # Get from cache
168
+ if @objects_outdated_due_to_dependencies.key?(obj)
169
+ return @objects_outdated_due_to_dependencies[obj]
170
+ end
171
+
172
+ # Check processed
173
+ # Don’t return true; the false will be or’ed into a true if there
174
+ # really is a dependency that is causing outdatedness.
175
+ return false if processed.include?(obj)
176
+
177
+ # Calculate
178
+ is_outdated = dependency_store.dependencies_causing_outdatedness_of(obj).any? do |dep|
179
+ dependency_causes_outdatedness?(dep) ||
180
+ (dep.props.compiled_content? &&
181
+ outdated_due_to_dependencies?(dep.from, processed.merge([obj])))
182
+ end
183
+
184
+ # Cache
185
+ @objects_outdated_due_to_dependencies[obj] = is_outdated
186
+
187
+ # Done
188
+ is_outdated
189
+ end
190
+
191
+ contract Nanoc::Core::Dependency => C::Bool
192
+ def dependency_causes_outdatedness?(dependency)
193
+ return true if dependency.from.nil?
194
+
195
+ status = basic.outdatedness_status_for(dependency.from)
196
+
197
+ active = status.props.active & dependency.props.active
198
+ active.delete(:attributes) if attributes_unaffected?(status, dependency)
199
+ active.delete(:raw_content) if raw_content_unaffected?(status, dependency)
200
+
201
+ active.any?
202
+ end
203
+
204
+ def attributes_unaffected?(status, dependency)
205
+ reason = status.reasons.find { |r| r.is_a?(Nanoc::Core::OutdatednessReasons::AttributesModified) }
206
+ reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & reason.attributes).empty?
207
+ end
208
+
209
+ def raw_content_unaffected?(status, dependency)
210
+ reason = status.reasons.find { |r| r.is_a?(Nanoc::Core::OutdatednessReasons::DocumentCollectionExtended) }
211
+ if reason.nil?
212
+ false
213
+ elsif !dependency.props.raw_content.is_a?(Enumerable)
214
+ false
215
+ else
216
+ patterns = dependency.props.raw_content.map { |r| Nanoc::Core::Pattern.from(r) }
217
+ patterns.none? { |pat| reason.objects.any? { |obj| pat.match?(obj.identifier) } }
218
+ end
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class AttributesModified < Nanoc::Core::OutdatednessRule
7
+ include Nanoc::Core::ContractsSupport
8
+
9
+ affects_props :attributes, :compiled_content
10
+
11
+ contract C::Or[Nanoc::Core::ItemRep, Nanoc::Core::Item, Nanoc::Core::Configuration, Nanoc::Core::Layout], C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
12
+ def apply(obj, outdatedness_checker)
13
+ case obj
14
+ when Nanoc::Core::ItemRep
15
+ apply(obj.item, outdatedness_checker)
16
+ when Nanoc::Core::Item, Nanoc::Core::Layout, Nanoc::Core::Configuration
17
+ if outdatedness_checker.checksum_store[obj] == outdatedness_checker.checksums.checksum_for(obj)
18
+ return nil
19
+ end
20
+
21
+ old_checksums = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
22
+ unless old_checksums
23
+ return Nanoc::Core::OutdatednessReasons::AttributesModified.new(true)
24
+ end
25
+
26
+ new_checksums = outdatedness_checker.checksums.attributes_checksum_for(obj)
27
+
28
+ attributes = Set.new(old_checksums.keys) + Set.new(new_checksums.keys)
29
+ changed_attributes = attributes.reject { |a| old_checksums[a] == new_checksums[a] }
30
+
31
+ if changed_attributes.any?
32
+ Nanoc::Core::OutdatednessReasons::AttributesModified.new(changed_attributes)
33
+ end
34
+ else
35
+ raise ArgumentError
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class CodeSnippetsModified < Nanoc::Core::OutdatednessRule
7
+ DDMemoize.activate(self)
8
+
9
+ include Nanoc::Core::ContractsSupport
10
+
11
+ affects_props :raw_content, :attributes, :compiled_content, :path
12
+
13
+ def apply(_obj, outdatedness_checker)
14
+ if any_snippets_modified?(outdatedness_checker)
15
+ Nanoc::Core::OutdatednessReasons::CodeSnippetsModified
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ memoized def any_snippets_modified?(outdatedness_checker)
22
+ outdatedness_checker.site.code_snippets.any? do |cs|
23
+ ch_old = outdatedness_checker.checksum_store[cs]
24
+ ch_new = outdatedness_checker.checksums.checksum_for(cs)
25
+ ch_old != ch_new
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class ContentModified < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :compiled_content
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ obj = obj.item if obj.is_a?(Nanoc::Core::ItemRep)
11
+
12
+ ch_old = outdatedness_checker.checksum_store.content_checksum_for(obj)
13
+ ch_new = outdatedness_checker.checksums.content_checksum_for(obj)
14
+ if ch_old != ch_new
15
+ Nanoc::Core::OutdatednessReasons::ContentModified
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class ItemCollectionExtended < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content
8
+
9
+ contract Nanoc::Core::ItemCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
10
+ def apply(_obj, outdatedness_checker)
11
+ new_items = outdatedness_checker.dependency_store.new_items
12
+
13
+ if new_items.any?
14
+ Nanoc::Core::OutdatednessReasons::ItemCollectionExtended.new(new_items)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class LayoutCollectionExtended < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content
8
+
9
+ contract Nanoc::Core::LayoutCollection, C::Named['Nanoc::Core::OutdatednessChecker'] => C::Maybe[Nanoc::Core::OutdatednessReasons::Generic]
10
+ def apply(_obj, outdatedness_checker)
11
+ new_layouts = outdatedness_checker.dependency_store.new_layouts
12
+
13
+ if new_layouts.any?
14
+ Nanoc::Core::OutdatednessReasons::LayoutCollectionExtended.new(new_layouts)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end