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