nanoc 4.4.4 → 4.4.5

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +9 -8
  4. data/NEWS.md +10 -0
  5. data/lib/nanoc/base.rb +0 -3
  6. data/lib/nanoc/base/contracts_support.rb +55 -2
  7. data/lib/nanoc/base/core_ext/array.rb +0 -2
  8. data/lib/nanoc/base/core_ext/hash.rb +0 -2
  9. data/lib/nanoc/base/entities.rb +1 -0
  10. data/lib/nanoc/base/entities/context.rb +1 -4
  11. data/lib/nanoc/base/entities/directed_graph.rb +0 -10
  12. data/lib/nanoc/base/entities/identifiable_collection.rb +1 -2
  13. data/lib/nanoc/base/entities/identifier.rb +6 -8
  14. data/lib/nanoc/base/entities/item_rep.rb +12 -18
  15. data/lib/nanoc/base/{compilation → entities}/outdatedness_reasons.rb +0 -0
  16. data/lib/nanoc/base/entities/site.rb +3 -19
  17. data/lib/nanoc/base/errors.rb +9 -0
  18. data/lib/nanoc/base/memoization.rb +0 -2
  19. data/lib/nanoc/base/repos/checksum_store.rb +21 -14
  20. data/lib/nanoc/base/repos/compiled_content_cache.rb +11 -15
  21. data/lib/nanoc/base/repos/dependency_store.rb +8 -27
  22. data/lib/nanoc/base/services.rb +3 -0
  23. data/lib/nanoc/base/services/compiler.rb +379 -0
  24. data/lib/nanoc/base/services/compiler_loader.rb +3 -1
  25. data/lib/nanoc/base/services/executor.rb +27 -41
  26. data/lib/nanoc/base/services/item_rep_builder.rb +4 -0
  27. data/lib/nanoc/base/services/item_rep_writer.rb +5 -2
  28. data/lib/nanoc/base/{compilation → services}/outdatedness_checker.rb +1 -1
  29. data/lib/nanoc/base/views/post_compile_item_rep_view.rb +1 -1
  30. data/lib/nanoc/base/views/view_context.rb +3 -3
  31. data/lib/nanoc/checking/check.rb +1 -1
  32. data/lib/nanoc/checking/checks/external_links.rb +1 -1
  33. data/lib/nanoc/cli.rb +0 -4
  34. data/lib/nanoc/cli/commands/compile.rb +2 -2
  35. data/lib/nanoc/cli/commands/shell.rb +1 -1
  36. data/lib/nanoc/data_sources/filesystem.rb +10 -20
  37. data/lib/nanoc/data_sources/filesystem/errors.rb +55 -0
  38. data/lib/nanoc/filters/asciidoc.rb +0 -2
  39. data/lib/nanoc/filters/coffeescript.rb +0 -2
  40. data/lib/nanoc/filters/colorize_syntax.rb +0 -2
  41. data/lib/nanoc/filters/handlebars.rb +0 -2
  42. data/lib/nanoc/filters/mustache.rb +0 -2
  43. data/lib/nanoc/filters/redcarpet.rb +0 -4
  44. data/lib/nanoc/filters/slim.rb +0 -2
  45. data/lib/nanoc/filters/typogruby.rb +0 -2
  46. data/lib/nanoc/filters/xsl.rb +0 -2
  47. data/lib/nanoc/filters/yui_compressor.rb +0 -2
  48. data/lib/nanoc/helpers/capturing.rb +22 -19
  49. data/lib/nanoc/helpers/link_to.rb +3 -7
  50. data/lib/nanoc/helpers/rendering.rb +1 -1
  51. data/lib/nanoc/rule_dsl/action_provider.rb +2 -2
  52. data/lib/nanoc/rule_dsl/compiler_dsl.rb +0 -2
  53. data/lib/nanoc/rule_dsl/recording_executor.rb +6 -6
  54. data/lib/nanoc/rule_dsl/rule.rb +0 -2
  55. data/lib/nanoc/rule_dsl/rule_context.rb +3 -3
  56. data/lib/nanoc/rule_dsl/rule_memory_calculator.rb +5 -5
  57. data/lib/nanoc/spec.rb +1 -1
  58. data/lib/nanoc/version.rb +1 -1
  59. data/test/base/test_compiler.rb +3 -1
  60. data/test/base/test_dependency_tracker.rb +0 -19
  61. data/test/base/test_item_rep.rb +3 -0
  62. data/test/cli/commands/test_create_site.rb +1 -1
  63. data/test/data_sources/test_filesystem.rb +5 -5
  64. data/test/filters/test_coffeescript.rb +2 -0
  65. data/test/filters/test_handlebars.rb +4 -0
  66. data/test/filters/test_uglify_js.rb +4 -0
  67. data/test/filters/test_xsl.rb +1 -1
  68. data/test/helper.rb +6 -0
  69. data/test/helpers/test_capturing.rb +6 -1
  70. data/test/helpers/test_xml_sitemap.rb +1 -1
  71. metadata +6 -6
  72. data/lib/nanoc/base/compilation/compiler.rb +0 -295
  73. data/test/base/test_checksum_store.rb +0 -28
@@ -207,5 +207,14 @@ module Nanoc::Int
207
207
  super("You cannot get the parent or children of an item that has a “full” identifier (#{identifier}). Getting the parent or children of an item is only possible for items that have a legacy identifier.")
208
208
  end
209
209
  end
210
+
211
+ class UndefinedFilterForLayout < Generic
212
+ def initialize(layout)
213
+ super("There is no filter defined for the layout #{layout.identifier}")
214
+ end
215
+ end
216
+
217
+ class InternalInconsistency < Generic
218
+ end
210
219
  end
211
220
  end
@@ -4,8 +4,6 @@ module Nanoc::Int
4
4
  # Adds support for memoizing functions.
5
5
  #
6
6
  # @api private
7
- #
8
- # @since 3.2.0
9
7
  module Memoization
10
8
  class Wrapper
11
9
  attr_reader :ref
@@ -6,27 +6,25 @@ module Nanoc::Int
6
6
  class ChecksumStore < ::Nanoc::Int::Store
7
7
  include Nanoc::Int::ContractsSupport
8
8
 
9
- # @param [Nanoc::Int::Site] site
10
- def initialize(site: nil)
9
+ attr_accessor :objects
10
+
11
+ c_obj = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration, Nanoc::Int::CodeSnippet]
12
+
13
+ contract C::KeywordArgs[site: C::Maybe[Nanoc::Int::Site], objects: C::IterOf[c_obj]] => C::Any
14
+ def initialize(site: nil, objects:)
11
15
  super(Nanoc::Int::Store.tmp_path_for(env_name: (site.config.env_name if site), store_name: 'checksums'), 1)
12
16
 
13
- @site = site
17
+ @objects = objects
14
18
 
15
19
  @checksums = {}
16
20
  end
17
21
 
18
- contract C::Any => C::Maybe[String]
19
- # Returns the old checksum for the given object. This makes sense for
20
- # items, layouts and code snippets.
21
- #
22
- # @param [#reference] obj The object for which to fetch the checksum
23
- #
24
- # @return [String] The checksum for the given object
22
+ contract c_obj => C::Maybe[String]
25
23
  def [](obj)
26
24
  @checksums[obj.reference]
27
25
  end
28
26
 
29
- # Calculates and stores the checksum for the given object.
27
+ contract c_obj => self
30
28
  def add(obj)
31
29
  if obj.is_a?(Nanoc::Int::Document)
32
30
  @checksums[[obj.reference, :content]] = Nanoc::Int::Checksummer.calc_for_content_of(obj)
@@ -34,14 +32,16 @@ module Nanoc::Int
34
32
  end
35
33
 
36
34
  @checksums[obj.reference] = Nanoc::Int::Checksummer.calc(obj)
35
+
36
+ self
37
37
  end
38
38
 
39
- contract C::Any => C::Maybe[String]
39
+ contract c_obj => C::Maybe[String]
40
40
  def content_checksum_for(obj)
41
41
  @checksums[[obj.reference, :content]]
42
42
  end
43
43
 
44
- contract C::Any => C::Maybe[String]
44
+ contract c_obj => C::Maybe[String]
45
45
  def attributes_checksum_for(obj)
46
46
  @checksums[[obj.reference, :attributes]]
47
47
  end
@@ -53,7 +53,14 @@ module Nanoc::Int
53
53
  end
54
54
 
55
55
  def data=(new_data)
56
- @checksums = new_data
56
+ references = Set.new(@objects.map(&:reference))
57
+
58
+ @checksums = {}
59
+ new_data.each_pair do |key, checksum|
60
+ if references.include?(key) || references.include?(key.first)
61
+ @checksums[key] = checksum
62
+ end
63
+ end
57
64
  end
58
65
  end
59
66
  end
@@ -4,6 +4,9 @@ module Nanoc::Int
4
4
  #
5
5
  # @api private
6
6
  class CompiledContentCache < ::Nanoc::Int::Store
7
+ include Nanoc::Int::ContractsSupport
8
+
9
+ contract C::KeywordArgs[env_name: C::Maybe[String], items: C::IterOf[Nanoc::Int::Item]] => C::Any
7
10
  def initialize(env_name: nil, items:)
8
11
  super(Nanoc::Int::Store.tmp_path_for(env_name: env_name, store_name: 'compiled_content'), 2)
9
12
 
@@ -11,32 +14,25 @@ module Nanoc::Int
11
14
  @cache = {}
12
15
  end
13
16
 
14
- # Returns the cached compiled content for the given item
15
- # representation. This cached compiled content is a hash where the keys
16
- # are the snapshot names and the values the compiled content at the
17
- # given snapshot.
18
- #
19
- # @param [Nanoc::Int::ItemRep] rep The item rep to fetch the content for
17
+ contract Nanoc::Int::ItemRep => C::Maybe[C::HashOf[Symbol => Nanoc::Int::Content]]
18
+ # Returns the cached compiled content for the given item representation.
20
19
  #
21
- # @return [Hash<Symbol,String>] A hash containing the cached compiled
22
- # content for the given item representation
20
+ # This cached compiled content is a hash where the keys are the snapshot
21
+ # names. and the values the compiled content at the given snapshot.
23
22
  def [](rep)
24
23
  item_cache = @cache[rep.item.identifier] || {}
25
24
  item_cache[rep.name]
26
25
  end
27
26
 
27
+ contract Nanoc::Int::ItemRep, C::HashOf[Symbol => Nanoc::Int::Content] => self
28
28
  # Sets the compiled content for the given representation.
29
29
  #
30
- # @param [Nanoc::Int::ItemRep] rep The item representation for which to set
31
- # the compiled content
32
- #
33
- # @param [Hash<Symbol,String>] content A hash containing the compiled
34
- # content of the given representation
35
- #
36
- # @return [void]
30
+ # This cached compiled content is a hash where the keys are the snapshot
31
+ # names. and the values the compiled content at the given snapshot.
37
32
  def []=(rep, content)
38
33
  @cache[rep.item.identifier] ||= {}
39
34
  @cache[rep.item.identifier][rep.name] = content
35
+ self
40
36
  end
41
37
 
42
38
  protected
@@ -11,7 +11,8 @@ module Nanoc::Int
11
11
  super(Nanoc::Int::Store.tmp_path_for(env_name: env_name, store_name: 'dependencies'), 4)
12
12
 
13
13
  @objects = objects
14
- @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
14
+ @new_objects = []
15
+ @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
15
16
  end
16
17
 
17
18
  contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::ArrayOf[Nanoc::Int::Dependency]
@@ -50,24 +51,11 @@ module Nanoc::Int
50
51
  # predecessors of
51
52
  # the given object
52
53
  def objects_causing_outdatedness_of(object)
53
- @graph.direct_predecessors_of(object)
54
- end
55
-
56
- # Returns the direct inverse dependencies for the given object.
57
- #
58
- # The direct inverse dependencies of the given object include the objects
59
- # that will be marked as outdated when the given object is outdated.
60
- # Indirect dependencies will not be returned (e.g. if A depends on B which
61
- # depends on C, then the direct inverse dependencies of C do not include
62
- # A).
63
- #
64
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for which to
65
- # fetch the direct successors
66
- #
67
- # @return [Array<Nanoc::Int::Item, Nanoc::Int::Layout>] The direct successors of
68
- # the given object
69
- def objects_outdated_due_to(object)
70
- @graph.direct_successors_of(object).compact
54
+ if @new_objects.any?
55
+ [@new_objects.first]
56
+ else
57
+ @graph.direct_predecessors_of(object)
58
+ end
71
59
  end
72
60
 
73
61
  contract C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C::Bool], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]] => C::Any
@@ -141,14 +129,7 @@ module Nanoc::Int
141
129
  end
142
130
 
143
131
  # Record dependency from all items on new items
144
- new_objects = (@objects - previous_objects)
145
- new_props = { raw_content: true, attributes: true, compiled_content: true, path: true }
146
- new_objects.each do |new_obj|
147
- @objects.each do |obj|
148
- next unless obj.is_a?(Nanoc::Int::Item)
149
- @graph.add_edge(new_obj, obj, props: new_props)
150
- end
151
- end
132
+ @new_objects = @objects - previous_objects
152
133
  end
153
134
  end
154
135
  end
@@ -1,5 +1,6 @@
1
1
  require_relative 'services/action_provider'
2
2
  require_relative 'services/checksummer'
3
+ require_relative 'services/compiler'
3
4
  require_relative 'services/compiler_loader'
4
5
  require_relative 'services/dependency_tracker'
5
6
  require_relative 'services/executor'
@@ -13,3 +14,5 @@ require_relative 'services/pruner'
13
14
  require_relative 'services/temp_filename_factory'
14
15
  require_relative 'services/outdatedness_rule'
15
16
  require_relative 'services/outdatedness_rules'
17
+
18
+ require_relative 'services/outdatedness_checker'
@@ -0,0 +1,379 @@
1
+ module Nanoc::Int
2
+ # Responsible for compiling a site’s item representations.
3
+ #
4
+ # The compilation process makes use of notifications (see
5
+ # {Nanoc::Int::NotificationCenter}) to track dependencies between items,
6
+ # layouts, etc. The following notifications are used:
7
+ #
8
+ # * `compilation_started` — indicates that the compiler has started
9
+ # compiling this item representation. Has one argument: the item
10
+ # representation itself. Only one item can be compiled at a given moment;
11
+ # therefore, it is not possible to get two consecutive
12
+ # `compilation_started` notifications without also getting a
13
+ # `compilation_ended` notification in between them.
14
+ #
15
+ # * `compilation_ended` — indicates that the compiler has finished compiling
16
+ # this item representation (either successfully or with failure). Has one
17
+ # argument: the item representation itself.
18
+ #
19
+ # @api private
20
+ class Compiler
21
+ # Provides common functionality for accesing “context” of an item that is being compiled.
22
+ class CompilationContext
23
+ def initialize(action_provider:, reps:, site:, compiled_content_cache:)
24
+ @action_provider = action_provider
25
+ @reps = reps
26
+ @site = site
27
+ @compiled_content_cache = compiled_content_cache
28
+ end
29
+
30
+ def filter_name_and_args_for_layout(layout)
31
+ mem = @action_provider.memory_for(layout)
32
+ if mem.nil? || mem.size != 1 || !mem[0].is_a?(Nanoc::Int::ProcessingActions::Filter)
33
+ raise Nanoc::Int::Errors::UndefinedFilterForLayout.new(layout)
34
+ end
35
+ [mem[0].filter_name, mem[0].params]
36
+ end
37
+
38
+ def create_view_context(dependency_tracker)
39
+ Nanoc::ViewContext.new(
40
+ reps: @reps,
41
+ items: @site.items,
42
+ dependency_tracker: dependency_tracker,
43
+ compilation_context: self,
44
+ )
45
+ end
46
+
47
+ def assigns_for(rep, dependency_tracker)
48
+ content_or_filename_assigns =
49
+ if rep.binary?
50
+ { filename: rep.snapshot_contents[:last].filename }
51
+ else
52
+ { content: rep.snapshot_contents[:last].string }
53
+ end
54
+
55
+ view_context = create_view_context(dependency_tracker)
56
+
57
+ content_or_filename_assigns.merge(
58
+ item: Nanoc::ItemWithRepsView.new(rep.item, view_context),
59
+ rep: Nanoc::ItemRepView.new(rep, view_context),
60
+ item_rep: Nanoc::ItemRepView.new(rep, view_context),
61
+ items: Nanoc::ItemCollectionWithRepsView.new(@site.items, view_context),
62
+ layouts: Nanoc::LayoutCollectionView.new(@site.layouts, view_context),
63
+ config: Nanoc::ConfigView.new(@site.config, view_context),
64
+ )
65
+ end
66
+
67
+ def site
68
+ @site
69
+ end
70
+
71
+ def compiled_content_cache
72
+ @compiled_content_cache
73
+ end
74
+ end
75
+
76
+ # Provides functionality for (re)calculating the content of an item rep, without caching or
77
+ # outdatedness checking.
78
+ class RecalculatingItemRepCompiler
79
+ include Nanoc::Int::ContractsSupport
80
+
81
+ def initialize(action_provider:, dependency_store:, compilation_context:)
82
+ @action_provider = action_provider
83
+ @dependency_store = dependency_store
84
+ @compilation_context = compilation_context
85
+ end
86
+
87
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
88
+ def run(rep, is_outdated:) # rubocop:disable Lint/UnusedMethodArgument
89
+ dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
90
+ dependency_tracker.enter(rep.item)
91
+
92
+ executor = Nanoc::Int::Executor.new(rep, @compilation_context, dependency_tracker)
93
+
94
+ @action_provider.memory_for(rep).each do |action|
95
+ case action
96
+ when Nanoc::Int::ProcessingActions::Filter
97
+ executor.filter(action.filter_name, action.params)
98
+ when Nanoc::Int::ProcessingActions::Layout
99
+ executor.layout(action.layout_identifier, action.params)
100
+ when Nanoc::Int::ProcessingActions::Snapshot
101
+ executor.snapshot(action.snapshot_name, final: action.final?, path: action.path)
102
+ else
103
+ raise Nanoc::Int::Errors::InternalInconsistency, "unknown action #{action.inspect}"
104
+ end
105
+ end
106
+ ensure
107
+ dependency_tracker.exit
108
+ end
109
+ end
110
+
111
+ # Provides functionality for (re)calculating the content of an item rep, with caching or
112
+ # outdatedness checking. Delegates to RecalculatingItemRepCompiler if outdated or no cache available.
113
+ class CachingItemRepCompiler
114
+ include Nanoc::Int::ContractsSupport
115
+
116
+ def initialize(compiled_content_cache:, wrapped:)
117
+ @compiled_content_cache = compiled_content_cache
118
+ @wrapped = wrapped
119
+ end
120
+
121
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
122
+ def run(rep, is_outdated:)
123
+ if can_reuse_content_for_rep?(rep, is_outdated: is_outdated)
124
+ Nanoc::Int::NotificationCenter.post(:cached_content_used, rep)
125
+ rep.snapshot_contents = @compiled_content_cache[rep]
126
+ else
127
+ @wrapped.run(rep, is_outdated: is_outdated)
128
+ end
129
+
130
+ rep.compiled = true
131
+ @compiled_content_cache[rep] = rep.snapshot_contents
132
+ end
133
+
134
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Bool
135
+ def can_reuse_content_for_rep?(rep, is_outdated:)
136
+ !is_outdated && !@compiled_content_cache[rep].nil?
137
+ end
138
+ end
139
+
140
+ # Provides functionality for suspending and resuming item rep compilation (using fibers).
141
+ class ResumableItemRepCompiler
142
+ include Nanoc::Int::ContractsSupport
143
+
144
+ def initialize(wrapped:)
145
+ @wrapped = wrapped
146
+ end
147
+
148
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
149
+ def run(rep, is_outdated:)
150
+ fiber = fiber_for(rep, is_outdated: is_outdated)
151
+ while fiber.alive?
152
+ Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
153
+ res = fiber.resume
154
+
155
+ case res
156
+ when Nanoc::Int::Errors::UnmetDependency
157
+ Nanoc::Int::NotificationCenter.post(:compilation_suspended, rep, res)
158
+ raise(res)
159
+ when Proc
160
+ fiber.resume(res.call)
161
+ else
162
+ # TODO: raise
163
+ end
164
+ end
165
+
166
+ Nanoc::Int::NotificationCenter.post(:compilation_ended, rep)
167
+ end
168
+
169
+ private
170
+
171
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => Fiber
172
+ def fiber_for(rep, is_outdated:)
173
+ @fibers ||= {}
174
+
175
+ @fibers[rep] ||=
176
+ Fiber.new do
177
+ @wrapped.run(rep, is_outdated: is_outdated)
178
+ @fibers.delete(rep)
179
+ end
180
+
181
+ @fibers[rep]
182
+ end
183
+ end
184
+
185
+ class WritingItemRepCompiler
186
+ include Nanoc::Int::ContractsSupport
187
+
188
+ def initialize(wrapped:)
189
+ @wrapped = wrapped
190
+ end
191
+
192
+ contract Nanoc::Int::ItemRep, C::KeywordArgs[is_outdated: C::Bool] => C::Any
193
+ def run(rep, is_outdated:)
194
+ @wrapped.run(rep, is_outdated: is_outdated)
195
+
196
+ rep.snapshot_defs.each do |sdef|
197
+ if sdef.final?
198
+ ItemRepWriter.new.write(rep, sdef.name)
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ include Nanoc::Int::ContractsSupport
205
+
206
+ # @api private
207
+ attr_reader :site
208
+
209
+ # @api private
210
+ attr_reader :compiled_content_cache
211
+
212
+ # @api private
213
+ attr_reader :checksum_store
214
+
215
+ # @api private
216
+ attr_reader :rule_memory_store
217
+
218
+ # @api private
219
+ attr_reader :action_provider
220
+
221
+ # @api private
222
+ attr_reader :dependency_store
223
+
224
+ # @api private
225
+ attr_reader :outdatedness_checker
226
+
227
+ # @api private
228
+ attr_reader :reps
229
+
230
+ def initialize(site, compiled_content_cache:, checksum_store:, rule_memory_store:, action_provider:, dependency_store:, outdatedness_checker:, reps:)
231
+ @site = site
232
+
233
+ @compiled_content_cache = compiled_content_cache
234
+ @checksum_store = checksum_store
235
+ @rule_memory_store = rule_memory_store
236
+ @dependency_store = dependency_store
237
+ @outdatedness_checker = outdatedness_checker
238
+ @reps = reps
239
+ @action_provider = action_provider
240
+ end
241
+
242
+ def run_all
243
+ @action_provider.preprocess(@site)
244
+ build_reps
245
+ prune
246
+ run
247
+ @action_provider.postprocess(@site, @reps)
248
+ end
249
+
250
+ def run
251
+ load_stores
252
+ @site.freeze
253
+
254
+ compile_reps
255
+ store
256
+ ensure
257
+ Nanoc::Int::TempFilenameFactory.instance.cleanup(
258
+ Nanoc::Filter::TMP_BINARY_ITEMS_DIR,
259
+ )
260
+ Nanoc::Int::TempFilenameFactory.instance.cleanup(
261
+ Nanoc::Int::ItemRepWriter::TMP_TEXT_ITEMS_DIR,
262
+ )
263
+ end
264
+
265
+ def load_stores
266
+ # FIXME: icky hack to update the dependency/checksum store’s list of objects
267
+ # (does not include preprocessed objects otherwise)
268
+ dependency_store.objects = site.items.to_a + site.layouts.to_a
269
+ checksum_store.objects = site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config]
270
+
271
+ stores.each(&:load)
272
+ end
273
+
274
+ # Store the modified helper data used for compiling the site.
275
+ #
276
+ # @return [void]
277
+ def store
278
+ # Calculate rule memory
279
+ (@reps.to_a + @site.layouts.to_a).each do |obj|
280
+ rule_memory_store[obj] = action_provider.memory_for(obj).serialize
281
+ end
282
+
283
+ # Calculate checksums
284
+ objects_to_checksum =
285
+ site.items.to_a + site.layouts.to_a + site.code_snippets + [site.config]
286
+ objects_to_checksum.each { |obj| checksum_store.add(obj) }
287
+
288
+ # Store
289
+ stores.each(&:store)
290
+ end
291
+
292
+ def build_reps
293
+ builder = Nanoc::Int::ItemRepBuilder.new(
294
+ site, action_provider, @reps
295
+ )
296
+ builder.run
297
+ end
298
+
299
+ def compilation_context
300
+ @_compilation_context ||= CompilationContext.new(
301
+ action_provider: action_provider,
302
+ reps: @reps,
303
+ site: @site,
304
+ compiled_content_cache: compiled_content_cache,
305
+ )
306
+ end
307
+
308
+ private
309
+
310
+ def prune
311
+ if site.config[:prune][:auto_prune]
312
+ Nanoc::Pruner.new(site.config, reps, exclude: prune_config_exclude).run
313
+ end
314
+ end
315
+
316
+ def prune_config
317
+ site.config[:prune] || {}
318
+ end
319
+
320
+ def prune_config_exclude
321
+ prune_config[:exclude] || {}
322
+ end
323
+
324
+ def compile_reps
325
+ outdated_items = @reps.select { |r| outdatedness_checker.outdated?(r) }.map(&:item).uniq
326
+ outdated_items.each { |i| @dependency_store.forget_dependencies_for(i) }
327
+
328
+ reps_to_recompile = Set.new(outdated_items.flat_map { |i| @reps[i] })
329
+ selector = Nanoc::Int::ItemRepSelector.new(reps_to_recompile)
330
+ selector.each do |rep|
331
+ handle_errors_while(rep) { compile_rep(rep, is_outdated: reps_to_recompile.include?(rep)) }
332
+ end
333
+ end
334
+
335
+ def handle_errors_while(item_rep)
336
+ yield
337
+ rescue => e
338
+ raise Nanoc::Int::Errors::CompilationError.new(e, item_rep)
339
+ end
340
+
341
+ def compile_rep(rep, is_outdated:)
342
+ item_rep_compiler.run(rep, is_outdated: is_outdated)
343
+ end
344
+
345
+ def item_rep_compiler
346
+ @_item_rep_compiler ||= begin
347
+ recalculating_item_rep_compiler = RecalculatingItemRepCompiler.new(
348
+ action_provider: action_provider,
349
+ dependency_store: @dependency_store,
350
+ compilation_context: compilation_context,
351
+ )
352
+
353
+ caching_item_rep_compiler = CachingItemRepCompiler.new(
354
+ compiled_content_cache: compiled_content_cache,
355
+ wrapped: recalculating_item_rep_compiler,
356
+ )
357
+
358
+ resumable_item_rep_compiler = ResumableItemRepCompiler.new(
359
+ wrapped: caching_item_rep_compiler,
360
+ )
361
+
362
+ WritingItemRepCompiler.new(
363
+ wrapped: resumable_item_rep_compiler,
364
+ )
365
+ end
366
+ end
367
+
368
+ # Returns all stores that can load/store data that can be used for
369
+ # compilation.
370
+ def stores
371
+ [
372
+ checksum_store,
373
+ compiled_content_cache,
374
+ @dependency_store,
375
+ rule_memory_store,
376
+ ]
377
+ end
378
+ end
379
+ end