nanoc 4.4.4 → 4.4.5

Sign up to get free protection for your applications and to get access to all the features.
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