nanoc-core 4.11.12 → 4.11.17

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