nanoc-core 4.11.12 → 4.11.13
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.
- checksums.yaml +4 -4
- data/lib/nanoc/core.rb +37 -0
- data/lib/nanoc/core/basic_item_rep_collection_view.rb +88 -0
- data/lib/nanoc/core/basic_item_rep_view.rb +83 -0
- data/lib/nanoc/core/basic_item_view.rb +54 -0
- data/lib/nanoc/core/checksummer.rb +2 -0
- data/lib/nanoc/core/compilation_item_rep_collection_view.rb +12 -0
- data/lib/nanoc/core/compilation_item_rep_view.rb +51 -0
- data/lib/nanoc/core/compilation_item_view.rb +47 -0
- data/lib/nanoc/core/compilation_phases/abstract.rb +48 -0
- data/lib/nanoc/core/compilation_phases/cache.rb +43 -0
- data/lib/nanoc/core/compilation_phases/mark_done.rb +23 -0
- data/lib/nanoc/core/compilation_phases/notify.rb +19 -0
- data/lib/nanoc/core/compilation_phases/recalculate.rb +49 -0
- data/lib/nanoc/core/compilation_phases/resume.rb +52 -0
- data/lib/nanoc/core/compilation_phases/write.rb +84 -0
- data/lib/nanoc/core/compilation_stages/build_reps.rb +36 -0
- data/lib/nanoc/core/compilation_stages/calculate_checksums.rb +42 -0
- data/lib/nanoc/core/compilation_stages/cleanup.rb +43 -0
- data/lib/nanoc/core/compilation_stages/compile_reps.rb +96 -0
- data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +49 -0
- data/lib/nanoc/core/compilation_stages/forget_outdated_dependencies.rb +20 -0
- data/lib/nanoc/core/compilation_stages/load_stores.rb +35 -0
- data/lib/nanoc/core/compilation_stages/postprocess.rb +21 -0
- data/lib/nanoc/core/compilation_stages/preprocess.rb +32 -0
- data/lib/nanoc/core/compilation_stages/prune.rb +30 -0
- data/lib/nanoc/core/compilation_stages/store_post_compilation_state.rb +20 -0
- data/lib/nanoc/core/compilation_stages/store_pre_compilation_state.rb +32 -0
- data/lib/nanoc/core/compiler.rb +214 -0
- data/lib/nanoc/core/compiler_loader.rb +48 -0
- data/lib/nanoc/core/config_loader.rb +95 -0
- data/lib/nanoc/core/config_view.rb +67 -0
- data/lib/nanoc/core/configuration.rb +2 -4
- data/lib/nanoc/core/document_view_mixin.rb +87 -0
- data/lib/nanoc/core/errors.rb +97 -0
- data/lib/nanoc/core/executor.rb +134 -0
- data/lib/nanoc/core/feature.rb +92 -0
- data/lib/nanoc/core/filter.rb +269 -0
- data/lib/nanoc/core/identifiable_collection_view.rb +111 -0
- data/lib/nanoc/core/item_collection_with_reps_view.rb +12 -0
- data/lib/nanoc/core/item_collection_without_reps_view.rb +12 -0
- data/lib/nanoc/core/item_rep_builder.rb +54 -0
- data/lib/nanoc/core/item_rep_selector.rb +67 -0
- data/lib/nanoc/core/item_rep_writer.rb +85 -0
- data/lib/nanoc/core/layout_collection_view.rb +12 -0
- data/lib/nanoc/core/layout_view.rb +9 -0
- data/lib/nanoc/core/mutable_config_view.rb +16 -0
- data/lib/nanoc/core/mutable_document_view_mixin.rb +60 -0
- data/lib/nanoc/core/mutable_identifiable_collection_view.rb +19 -0
- data/lib/nanoc/core/mutable_item_collection_view.rb +34 -0
- data/lib/nanoc/core/mutable_item_view.rb +9 -0
- data/lib/nanoc/core/mutable_layout_collection_view.rb +26 -0
- data/lib/nanoc/core/mutable_layout_view.rb +9 -0
- data/lib/nanoc/core/outdatedness_checker.rb +222 -0
- data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +41 -0
- data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +31 -0
- data/lib/nanoc/core/outdatedness_rules/content_modified.rb +21 -0
- data/lib/nanoc/core/outdatedness_rules/item_collection_extended.rb +20 -0
- data/lib/nanoc/core/outdatedness_rules/layout_collection_extended.rb +20 -0
- data/lib/nanoc/core/outdatedness_rules/not_written.rb +17 -0
- data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +45 -0
- data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +26 -0
- data/lib/nanoc/core/post_compile_item_collection_view.rb +12 -0
- data/lib/nanoc/core/post_compile_item_rep_collection_view.rb +12 -0
- data/lib/nanoc/core/post_compile_item_rep_view.rb +33 -0
- data/lib/nanoc/core/post_compile_item_view.rb +20 -0
- data/lib/nanoc/core/pruner.rb +119 -0
- data/lib/nanoc/core/site_loader.rb +102 -0
- data/lib/nanoc/core/trivial_error.rb +10 -0
- data/lib/nanoc/core/version.rb +1 -1
- data/lib/nanoc/core/view.rb +43 -0
- data/lib/nanoc/core/view_context_for_compilation.rb +6 -6
- metadata +95 -2
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
# @api private
|
6
|
+
class ItemRepBuilder
|
7
|
+
include Nanoc::Core::ContractsSupport
|
8
|
+
|
9
|
+
attr_reader :reps
|
10
|
+
|
11
|
+
contract Nanoc::Core::Site, Nanoc::Core::ActionProvider, Nanoc::Core::ItemRepRepo => C::Any
|
12
|
+
def initialize(site, action_provider, reps)
|
13
|
+
@site = site
|
14
|
+
@action_provider = action_provider
|
15
|
+
@reps = reps
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
@site.items.each do |item|
|
20
|
+
@action_provider.rep_names_for(item).each do |rep_name|
|
21
|
+
@reps << Nanoc::Core::ItemRep.new(item, rep_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
action_sequences = Nanoc::Core::ItemRepRouter.new(@reps, @action_provider, @site).run
|
26
|
+
|
27
|
+
@reps.each do |rep|
|
28
|
+
rep.snapshot_defs = self.class.snapshot_defs_for(action_sequences[rep])
|
29
|
+
end
|
30
|
+
|
31
|
+
action_sequences
|
32
|
+
end
|
33
|
+
|
34
|
+
contract Nanoc::Core::ActionSequence => C::ArrayOf[Nanoc::Core::SnapshotDef]
|
35
|
+
def self.snapshot_defs_for(action_sequence)
|
36
|
+
is_binary = action_sequence.item_rep.item.content.binary?
|
37
|
+
snapshot_defs = []
|
38
|
+
|
39
|
+
action_sequence.each do |action|
|
40
|
+
case action
|
41
|
+
when Nanoc::Core::ProcessingActions::Snapshot
|
42
|
+
action.snapshot_names.each do |snapshot_name|
|
43
|
+
snapshot_defs << Nanoc::Core::SnapshotDef.new(snapshot_name, binary: is_binary)
|
44
|
+
end
|
45
|
+
when Nanoc::Core::ProcessingActions::Filter
|
46
|
+
is_binary = Nanoc::Core::Filter.named!(action.filter_name).to_binary?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
snapshot_defs
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
# Yields item reps to compile.
|
6
|
+
class ItemRepSelector
|
7
|
+
def initialize(reps)
|
8
|
+
@reps = reps
|
9
|
+
end
|
10
|
+
|
11
|
+
class MicroGraph
|
12
|
+
def initialize(reps)
|
13
|
+
@reps = Set.new(reps)
|
14
|
+
@stack = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def next
|
18
|
+
if @stack.any?
|
19
|
+
@stack.last
|
20
|
+
elsif @reps.any?
|
21
|
+
@reps.each { |rep| break rep }.tap do |rep|
|
22
|
+
@reps.delete(rep)
|
23
|
+
@stack.push(rep)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def mark_ok
|
31
|
+
@stack.pop
|
32
|
+
end
|
33
|
+
|
34
|
+
def mark_failed(dep)
|
35
|
+
if @stack.include?(dep)
|
36
|
+
raise Nanoc::Core::Errors::DependencyCycle.new(@stack + [dep])
|
37
|
+
end
|
38
|
+
|
39
|
+
@reps.delete(dep)
|
40
|
+
@stack.push(dep)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def each
|
45
|
+
mg = MicroGraph.new(@reps)
|
46
|
+
|
47
|
+
loop do
|
48
|
+
rep = mg.next
|
49
|
+
break if rep.nil?
|
50
|
+
|
51
|
+
begin
|
52
|
+
yield(rep)
|
53
|
+
mg.mark_ok
|
54
|
+
rescue => e
|
55
|
+
actual_error = e.is_a?(Nanoc::Core::Errors::CompilationError) ? e.unwrap : e
|
56
|
+
|
57
|
+
if actual_error.is_a?(Nanoc::Core::Errors::UnmetDependency)
|
58
|
+
mg.mark_failed(actual_error.rep)
|
59
|
+
else
|
60
|
+
raise(e)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
class ItemRepWriter
|
6
|
+
include Nanoc::Core::ContractsSupport
|
7
|
+
include Nanoc::Core::Assertions::Mixin
|
8
|
+
|
9
|
+
TMP_TEXT_ITEMS_DIR = 'text_items'
|
10
|
+
|
11
|
+
def write_all(item_rep, compiled_content_store)
|
12
|
+
written_paths = Set.new
|
13
|
+
|
14
|
+
item_rep.snapshot_defs.map(&:name).each do |snapshot_name|
|
15
|
+
write(item_rep, compiled_content_store, snapshot_name, written_paths)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def write(item_rep, compiled_content_store, snapshot_name, written_paths)
|
20
|
+
item_rep.raw_paths.fetch(snapshot_name, []).each do |raw_path|
|
21
|
+
write_single(item_rep, compiled_content_store, snapshot_name, raw_path, written_paths)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def write_single(item_rep, compiled_content_store, snapshot_name, raw_path, written_paths)
|
26
|
+
assert Nanoc::Core::Assertions::PathIsAbsolute.new(path: raw_path)
|
27
|
+
|
28
|
+
# Don’t write twice
|
29
|
+
# TODO: test written_paths behavior
|
30
|
+
return if written_paths.include?(raw_path)
|
31
|
+
|
32
|
+
written_paths << raw_path
|
33
|
+
|
34
|
+
# Create parent directory
|
35
|
+
FileUtils.mkdir_p(File.dirname(raw_path))
|
36
|
+
|
37
|
+
# Check if file will be created
|
38
|
+
is_created = !File.file?(raw_path)
|
39
|
+
|
40
|
+
# Notify
|
41
|
+
Nanoc::Core::NotificationCenter.post(
|
42
|
+
:rep_write_started, item_rep, raw_path
|
43
|
+
)
|
44
|
+
|
45
|
+
content = compiled_content_store.get(item_rep, snapshot_name)
|
46
|
+
if content.binary?
|
47
|
+
temp_path = content.filename
|
48
|
+
else
|
49
|
+
temp_path = temp_filename
|
50
|
+
File.write(temp_path, content.string)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Check whether content was modified
|
54
|
+
is_modified = is_created || !FileUtils.identical?(raw_path, temp_path)
|
55
|
+
|
56
|
+
# Notify ready for diff generation
|
57
|
+
if !is_created && is_modified && !content.binary?
|
58
|
+
Nanoc::Core::NotificationCenter.post(
|
59
|
+
:rep_ready_for_diff, raw_path, File.read(raw_path, encoding: 'UTF-8'), content.string
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Write
|
64
|
+
if is_modified
|
65
|
+
begin
|
66
|
+
FileUtils.ln(temp_path, raw_path, force: true)
|
67
|
+
rescue Errno::EXDEV, Errno::EACCES
|
68
|
+
FileUtils.cp(temp_path, raw_path)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
item_rep.modified = is_modified
|
73
|
+
|
74
|
+
# Notify
|
75
|
+
Nanoc::Core::NotificationCenter.post(
|
76
|
+
:rep_write_ended, item_rep, content.binary?, raw_path, is_created, is_modified
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def temp_filename
|
81
|
+
Nanoc::Core::TempFilenameFactory.instance.create(TMP_TEXT_ITEMS_DIR)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
class MutableConfigView < Nanoc::Core::ConfigView
|
6
|
+
# Sets the value for the given attribute.
|
7
|
+
#
|
8
|
+
# @param [Symbol] key
|
9
|
+
#
|
10
|
+
# @see Hash#[]=
|
11
|
+
def []=(key, value)
|
12
|
+
@config[key] = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module Core
|
5
|
+
module MutableDocumentViewMixin
|
6
|
+
# @api private
|
7
|
+
class DisallowedAttributeValueError < Nanoc::Core::Error
|
8
|
+
attr_reader :value
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def message
|
15
|
+
"The #{value.class} cannot be stored inside an attribute. Store its identifier instead."
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def raw_content=(arg)
|
20
|
+
_unwrap.content = Nanoc::Core::Content.create(arg)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets the value for the given attribute.
|
24
|
+
#
|
25
|
+
# @param [Symbol] key
|
26
|
+
#
|
27
|
+
# @see Hash#[]=
|
28
|
+
def []=(key, value)
|
29
|
+
disallowed_value_classes = Set.new([
|
30
|
+
Nanoc::Core::Item,
|
31
|
+
Nanoc::Core::Layout,
|
32
|
+
Nanoc::Core::CompilationItemView,
|
33
|
+
Nanoc::Core::LayoutView,
|
34
|
+
])
|
35
|
+
if disallowed_value_classes.include?(value.class)
|
36
|
+
raise DisallowedAttributeValueError.new(value)
|
37
|
+
end
|
38
|
+
|
39
|
+
_unwrap.set_attribute(key, value)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Sets the identifier to the given argument.
|
43
|
+
#
|
44
|
+
# @param [String, Nanoc::Core::Identifier] arg
|
45
|
+
def identifier=(arg)
|
46
|
+
_unwrap.identifier = Nanoc::Core::Identifier.from(arg)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Updates the attributes based on the given hash.
|
50
|
+
#
|
51
|
+
# @param [Hash] hash
|
52
|
+
#
|
53
|
+
# @return [self]
|
54
|
+
def update_attributes(hash)
|
55
|
+
hash.each { |k, v| _unwrap.set_attribute(k, v) }
|
56
|
+
self
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -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,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,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
|