nanoc-core 4.11.12 → 4.11.17

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nanoc/core.rb +37 -0
  3. data/lib/nanoc/core/basic_item_rep_collection_view.rb +88 -0
  4. data/lib/nanoc/core/basic_item_rep_view.rb +83 -0
  5. data/lib/nanoc/core/basic_item_view.rb +54 -0
  6. data/lib/nanoc/core/changes_stream.rb +55 -0
  7. data/lib/nanoc/core/checksum_store.rb +1 -1
  8. data/lib/nanoc/core/checksummer.rb +4 -2
  9. data/lib/nanoc/core/compilation_item_rep_collection_view.rb +12 -0
  10. data/lib/nanoc/core/compilation_item_rep_view.rb +57 -0
  11. data/lib/nanoc/core/compilation_item_view.rb +47 -0
  12. data/lib/nanoc/core/compilation_phases/abstract.rb +48 -0
  13. data/lib/nanoc/core/compilation_phases/cache.rb +43 -0
  14. data/lib/nanoc/core/compilation_phases/mark_done.rb +23 -0
  15. data/lib/nanoc/core/compilation_phases/notify.rb +19 -0
  16. data/lib/nanoc/core/compilation_phases/recalculate.rb +49 -0
  17. data/lib/nanoc/core/compilation_phases/resume.rb +52 -0
  18. data/lib/nanoc/core/compilation_phases/write.rb +84 -0
  19. data/lib/nanoc/core/compilation_stages/build_reps.rb +36 -0
  20. data/lib/nanoc/core/compilation_stages/calculate_checksums.rb +42 -0
  21. data/lib/nanoc/core/compilation_stages/cleanup.rb +43 -0
  22. data/lib/nanoc/core/compilation_stages/compile_reps.rb +96 -0
  23. data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +49 -0
  24. data/lib/nanoc/core/compilation_stages/forget_outdated_dependencies.rb +20 -0
  25. data/lib/nanoc/core/compilation_stages/load_stores.rb +35 -0
  26. data/lib/nanoc/core/compilation_stages/postprocess.rb +21 -0
  27. data/lib/nanoc/core/compilation_stages/preprocess.rb +32 -0
  28. data/lib/nanoc/core/compilation_stages/prune.rb +30 -0
  29. data/lib/nanoc/core/compilation_stages/store_post_compilation_state.rb +20 -0
  30. data/lib/nanoc/core/compilation_stages/store_pre_compilation_state.rb +32 -0
  31. data/lib/nanoc/core/compiled_content_cache.rb +2 -2
  32. data/lib/nanoc/core/compiler.rb +214 -0
  33. data/lib/nanoc/core/compiler_loader.rb +48 -0
  34. data/lib/nanoc/core/config_loader.rb +95 -0
  35. data/lib/nanoc/core/config_view.rb +67 -0
  36. data/lib/nanoc/core/configuration.rb +2 -4
  37. data/lib/nanoc/core/contracts_support.rb +20 -0
  38. data/lib/nanoc/core/dependency_store.rb +3 -3
  39. data/lib/nanoc/core/document_view_mixin.rb +87 -0
  40. data/lib/nanoc/core/errors.rb +108 -0
  41. data/lib/nanoc/core/executor.rb +134 -0
  42. data/lib/nanoc/core/feature.rb +92 -0
  43. data/lib/nanoc/core/filter.rb +269 -0
  44. data/lib/nanoc/core/identifiable_collection_view.rb +111 -0
  45. data/lib/nanoc/core/item_collection_with_reps_view.rb +12 -0
  46. data/lib/nanoc/core/item_collection_without_reps_view.rb +12 -0
  47. data/lib/nanoc/core/item_rep_builder.rb +54 -0
  48. data/lib/nanoc/core/item_rep_selector.rb +67 -0
  49. data/lib/nanoc/core/item_rep_writer.rb +85 -0
  50. data/lib/nanoc/core/layout_collection_view.rb +12 -0
  51. data/lib/nanoc/core/layout_view.rb +9 -0
  52. data/lib/nanoc/core/mutable_config_view.rb +16 -0
  53. data/lib/nanoc/core/mutable_document_view_mixin.rb +60 -0
  54. data/lib/nanoc/core/mutable_identifiable_collection_view.rb +19 -0
  55. data/lib/nanoc/core/mutable_item_collection_view.rb +34 -0
  56. data/lib/nanoc/core/mutable_item_view.rb +9 -0
  57. data/lib/nanoc/core/mutable_layout_collection_view.rb +26 -0
  58. data/lib/nanoc/core/mutable_layout_view.rb +9 -0
  59. data/lib/nanoc/core/outdatedness_checker.rb +222 -0
  60. data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +41 -0
  61. data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +31 -0
  62. data/lib/nanoc/core/outdatedness_rules/content_modified.rb +21 -0
  63. data/lib/nanoc/core/outdatedness_rules/item_collection_extended.rb +20 -0
  64. data/lib/nanoc/core/outdatedness_rules/layout_collection_extended.rb +20 -0
  65. data/lib/nanoc/core/outdatedness_rules/not_written.rb +17 -0
  66. data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +45 -0
  67. data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +26 -0
  68. data/lib/nanoc/core/post_compile_item_collection_view.rb +12 -0
  69. data/lib/nanoc/core/post_compile_item_rep_collection_view.rb +12 -0
  70. data/lib/nanoc/core/post_compile_item_rep_view.rb +33 -0
  71. data/lib/nanoc/core/post_compile_item_view.rb +20 -0
  72. data/lib/nanoc/core/pruner.rb +123 -0
  73. data/lib/nanoc/core/site_loader.rb +102 -0
  74. data/lib/nanoc/core/trivial_error.rb +10 -0
  75. data/lib/nanoc/core/version.rb +1 -1
  76. data/lib/nanoc/core/view.rb +43 -0
  77. data/lib/nanoc/core/view_context_for_compilation.rb +6 -6
  78. metadata +97 -3
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class IdentifiableCollectionView < ::Nanoc::Core::View
6
+ include Enumerable
7
+
8
+ NOTHING = Object.new
9
+
10
+ # @api private
11
+ def initialize(objects, context)
12
+ super(context)
13
+ @objects = objects
14
+ end
15
+
16
+ # @api private
17
+ def _unwrap
18
+ @objects
19
+ end
20
+
21
+ # @abstract
22
+ #
23
+ # @api private
24
+ def view_class
25
+ raise NotImplementedError
26
+ end
27
+
28
+ # Calls the given block once for each object, passing that object as a parameter.
29
+ #
30
+ # @yieldparam [#identifier] object
31
+ #
32
+ # @yieldreturn [void]
33
+ #
34
+ # @return [self]
35
+ def each
36
+ @context.dependency_tracker.bounce(_unwrap, raw_content: true)
37
+ @objects.each { |i| yield view_class.new(i, @context) }
38
+ self
39
+ end
40
+
41
+ # @return [Integer]
42
+ def size
43
+ @context.dependency_tracker.bounce(_unwrap, raw_content: true)
44
+ @objects.size
45
+ end
46
+
47
+ # Finds all objects whose identifier matches the given argument.
48
+ #
49
+ # @param [String, Regex] arg
50
+ #
51
+ # @return [Enumerable]
52
+ def find_all(arg = NOTHING, &block)
53
+ if NOTHING.equal?(arg)
54
+ @context.dependency_tracker.bounce(_unwrap, raw_content: true)
55
+ return @objects.map { |i| view_class.new(i, @context) }.select(&block)
56
+ end
57
+
58
+ prop_attribute =
59
+ case arg
60
+ when String, Nanoc::Core::Identifier
61
+ [arg.to_s]
62
+ when Regexp
63
+ [arg]
64
+ else
65
+ true
66
+ end
67
+
68
+ @context.dependency_tracker.bounce(_unwrap, raw_content: prop_attribute)
69
+ @objects.find_all(arg).map { |i| view_class.new(i, @context) }
70
+ end
71
+
72
+ # @overload [](string)
73
+ #
74
+ # Finds the object whose identifier matches the given string.
75
+ #
76
+ # If the glob syntax is enabled, the string can be a glob, in which case
77
+ # this method finds the first object that matches the given glob.
78
+ #
79
+ # @param [String] string
80
+ #
81
+ # @return [nil] if no object matches the string
82
+ #
83
+ # @return [#identifier] if an object was found
84
+ #
85
+ # @overload [](regex)
86
+ #
87
+ # Finds the object whose identifier matches the given regular expression.
88
+ #
89
+ # @param [Regex] regex
90
+ #
91
+ # @return [nil] if no object matches the regex
92
+ #
93
+ # @return [#identifier] if an object was found
94
+ def [](arg)
95
+ prop_attribute =
96
+ case arg
97
+ when String, Nanoc::Core::Identifier
98
+ [arg.to_s]
99
+ when Regexp
100
+ [arg]
101
+ else
102
+ true
103
+ end
104
+
105
+ @context.dependency_tracker.bounce(_unwrap, raw_content: prop_attribute)
106
+ res = @objects[arg]
107
+ res && view_class.new(res, @context)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class ItemCollectionWithRepsView < ::Nanoc::Core::IdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::CompilationItemView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class ItemCollectionWithoutRepsView < ::Nanoc::Core::IdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::BasicItemView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class LayoutCollectionView < ::Nanoc::Core::IdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::LayoutView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class LayoutView < ::Nanoc::Core::View
6
+ include Nanoc::Core::DocumentViewMixin
7
+ end
8
+ end
9
+ 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