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,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class NotWritten < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :attributes, :compiled_content, :path
8
+
9
+ def apply(obj, _outdatedness_checker)
10
+ if obj.raw_paths.values.flatten.compact.any? { |fn| !File.file?(fn) }
11
+ Nanoc::Core::OutdatednessReasons::NotWritten
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class RulesModified < Nanoc::Core::OutdatednessRule
7
+ affects_props :compiled_content, :path
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ # Check rules of obj itself
11
+ if rules_modified?(obj, outdatedness_checker)
12
+ return Nanoc::Core::OutdatednessReasons::RulesModified
13
+ end
14
+
15
+ # Check rules of layouts used by obj
16
+ layouts = layouts_touched_by(obj, outdatedness_checker)
17
+ if layouts.any? { |layout| rules_modified?(layout, outdatedness_checker) }
18
+ return Nanoc::Core::OutdatednessReasons::RulesModified
19
+ end
20
+
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ def rules_modified?(obj, outdatedness_checker)
27
+ seq_old = outdatedness_checker.action_sequence_store[obj]
28
+ seq_new = outdatedness_checker.action_sequence_for(obj).serialize
29
+
30
+ !seq_old.eql?(seq_new)
31
+ end
32
+
33
+ def layouts_touched_by(obj, outdatedness_checker)
34
+ actions = outdatedness_checker.action_sequence_store[obj]
35
+ layout_actions = actions.select { |a| a.first == :layout }
36
+
37
+ layout_actions.map do |layout_action|
38
+ layout_pattern = layout_action[1]
39
+ outdatedness_checker.site.layouts[layout_pattern]
40
+ end.compact
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ module OutdatednessRules
6
+ class UsesAlwaysOutdatedFilter < Nanoc::Core::OutdatednessRule
7
+ affects_props :raw_content, :attributes, :path
8
+
9
+ def apply(obj, outdatedness_checker)
10
+ seq = outdatedness_checker.action_sequence_for(obj)
11
+ if any_always_outdated?(seq)
12
+ Nanoc::Core::OutdatednessReasons::UsesAlwaysOutdatedFilter
13
+ end
14
+ end
15
+
16
+ def any_always_outdated?(seq)
17
+ seq
18
+ .select { |a| a.is_a?(Nanoc::Core::ProcessingActions::Filter) }
19
+ .map { |a| Nanoc::Core::Filter.named(a.filter_name) }
20
+ .compact
21
+ .any?(&:always_outdated?)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemCollectionView < Nanoc::Core::IdentifiableCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::PostCompileItemView
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 PostCompileItemRepCollectionView < Nanoc::Core::BasicItemRepCollectionView
6
+ # @api private
7
+ def view_class
8
+ Nanoc::Core::PostCompileItemRepView
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemRepView < ::Nanoc::Core::BasicItemRepView
6
+ def item_view_class
7
+ Nanoc::Core::PostCompileItemView
8
+ end
9
+
10
+ def compiled_content(snapshot: nil)
11
+ compilation_context = @context.compilation_context
12
+ snapshot_contents = compilation_context.compiled_content_cache[_unwrap] || {}
13
+
14
+ snapshot_name = snapshot || (snapshot_contents[:pre] ? :pre : :last)
15
+
16
+ unless snapshot_contents[snapshot_name]
17
+ raise Nanoc::Core::Errors::NoSuchSnapshot.new(_unwrap, snapshot_name)
18
+ end
19
+
20
+ content = snapshot_contents[snapshot_name]
21
+ if content.binary?
22
+ raise Nanoc::Core::Errors::CannotGetCompiledContentOfBinaryItem.new(_unwrap)
23
+ end
24
+
25
+ content.string
26
+ end
27
+
28
+ def raw_path(snapshot: :last)
29
+ @item_rep.raw_path(snapshot: snapshot)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class PostCompileItemView < Nanoc::Core::CompilationItemView
6
+ def reps
7
+ Nanoc::Core::PostCompileItemRepCollectionView.new(@context.reps[_unwrap], @context)
8
+ end
9
+
10
+ # @deprecated Use {#modified_reps} instead
11
+ def modified
12
+ modified_reps
13
+ end
14
+
15
+ def modified_reps
16
+ reps.select { |rep| rep._unwrap.modified? }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Responsible for finding and deleting files in the site’s output directory
6
+ # that are not managed by Nanoc.
7
+ class Pruner
8
+ include Nanoc::Core::ContractsSupport
9
+
10
+ contract Nanoc::Core::Configuration, Nanoc::Core::ItemRepRepo, C::KeywordArgs[dry_run: C::Optional[C::Bool], exclude: C::Optional[C::IterOf[String]]] => C::Any
11
+ def initialize(config, reps, dry_run: false, exclude: [])
12
+ @config = config
13
+ @reps = reps
14
+ @dry_run = dry_run
15
+ @exclude = Set.new(exclude)
16
+ end
17
+
18
+ def run
19
+ return unless File.directory?(@config.output_dir)
20
+
21
+ compiled_files = @reps.flat_map { |r| r.raw_paths.values.flatten }.compact
22
+ present_files, present_dirs = files_and_dirs_in(@config.output_dir + '/')
23
+
24
+ remove_stray_files(present_files, compiled_files)
25
+ remove_empty_directories(present_dirs)
26
+ end
27
+
28
+ contract String => C::Bool
29
+ def filename_excluded?(filename)
30
+ pathname = Pathname.new(strip_output_dir(filename))
31
+ @exclude.any? { |e| pathname_components(pathname).include?(e) }
32
+ end
33
+
34
+ contract String => String
35
+ def strip_output_dir(filename)
36
+ if filename.start_with?(@config.output_dir)
37
+ filename[@config.output_dir.size..-1]
38
+ else
39
+ filename
40
+ end
41
+ end
42
+
43
+ contract Pathname => C::ArrayOf[String]
44
+ def pathname_components(pathname)
45
+ components = []
46
+ tmp = pathname
47
+ loop do
48
+ old = tmp
49
+ components << File.basename(tmp)
50
+ tmp = File.dirname(tmp)
51
+ break if old == tmp
52
+ end
53
+ components.reverse
54
+ end
55
+
56
+ contract C::ArrayOf[String], C::ArrayOf[String] => self
57
+ # @api private
58
+ def remove_stray_files(present_files, compiled_files)
59
+ (present_files - compiled_files).each do |f|
60
+ delete_file(f) unless filename_excluded?(f)
61
+ end
62
+ self
63
+ end
64
+
65
+ contract C::ArrayOf[String] => self
66
+ # @api private
67
+ def remove_empty_directories(present_dirs)
68
+ present_dirs.reverse_each do |dir|
69
+ next if Dir.foreach(dir) { |n| break true if n !~ /\A\.\.?\z/ }
70
+ next if filename_excluded?(dir)
71
+
72
+ delete_dir(dir)
73
+ end
74
+ self
75
+ end
76
+
77
+ contract String => C::ArrayOf[C::ArrayOf[String]]
78
+ # @api private
79
+ def files_and_dirs_in(dir)
80
+ present_files = []
81
+ present_dirs = []
82
+
83
+ expanded_dir = File.expand_path(dir)
84
+
85
+ Find.find(dir) do |f|
86
+ case File.ftype(f)
87
+ when 'file'
88
+ unless filename_excluded?(f)
89
+ present_files << f
90
+ end
91
+ when 'directory'
92
+ if filename_excluded?(f)
93
+ Find.prune
94
+ elsif expanded_dir != File.expand_path(f)
95
+ present_dirs << f
96
+ end
97
+ end
98
+ end
99
+
100
+ [present_files, present_dirs]
101
+ end
102
+
103
+ protected
104
+
105
+ def delete_file(file)
106
+ log_delete_and_run(file) { FileUtils.rm(file) }
107
+ end
108
+
109
+ def delete_dir(dir)
110
+ log_delete_and_run(dir) { Dir.rmdir(dir) }
111
+ end
112
+
113
+ def log_delete_and_run(thing)
114
+ if @dry_run
115
+ Nanoc::Core::NotificationCenter.post(:file_listed_for_pruning, thing)
116
+ else
117
+ Nanoc::Core::NotificationCenter.post(:file_pruned, thing)
118
+ yield
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class SiteLoader
6
+ ENCODING_REGEX = /\A#\s+(-\*-\s+)?(en)?coding: (?<encoding>[^\s]+)(\s+-\*-\s*)?\n{0,2}/.freeze
7
+
8
+ def new_from_cwd
9
+ site_from_config(Nanoc::Core::ConfigLoader.new.new_from_cwd)
10
+ end
11
+
12
+ # @return [Boolean]
13
+ def self.cwd_is_nanoc_site?
14
+ Nanoc::Core::ConfigLoader.cwd_is_nanoc_site?
15
+ end
16
+
17
+ def gen_data_source_for_config(config)
18
+ data_sources_to_aggregate =
19
+ with_data_sources(config) do |data_sources|
20
+ data_sources.map do |ds|
21
+ Nanoc::Core::PrefixedDataSource.new(ds, ds.items_root, ds.layouts_root)
22
+ end
23
+ end
24
+
25
+ Nanoc::Core::AggregateDataSource.new(data_sources_to_aggregate, config)
26
+ end
27
+
28
+ private
29
+
30
+ def site_from_config(config)
31
+ code_snippets = code_snippets_from_config(config)
32
+ code_snippets.each(&:load)
33
+
34
+ data_source = gen_data_source_for_config(config)
35
+
36
+ Nanoc::Core::Site.new(
37
+ config: config,
38
+ code_snippets: code_snippets,
39
+ data_source: data_source,
40
+ )
41
+ end
42
+
43
+ def with_data_sources(config, &_block)
44
+ data_sources = create_data_sources(config)
45
+
46
+ begin
47
+ data_sources.each(&:use)
48
+ yield(data_sources)
49
+ ensure
50
+ data_sources.each(&:unuse)
51
+ end
52
+ end
53
+
54
+ def create_data_sources(config)
55
+ config[:data_sources].map do |data_source_hash|
56
+ # Get data source class
57
+ data_source_class = Nanoc::Core::DataSource.named(data_source_hash[:type].to_sym)
58
+ if data_source_class.nil?
59
+ raise Nanoc::Core::Errors::UnknownDataSource.new(data_source_hash[:type])
60
+ end
61
+
62
+ # Create data source
63
+ data_source_class.new(
64
+ config,
65
+ data_source_hash[:items_root],
66
+ data_source_hash[:layouts_root],
67
+ data_source_hash.merge(data_source_hash[:config] || {}),
68
+ )
69
+ end
70
+ end
71
+
72
+ def code_snippets_from_config(config)
73
+ config[:lib_dirs].flat_map do |lib|
74
+ Dir["#{lib}/**/*.rb"].sort.map do |filename|
75
+ Nanoc::Core::CodeSnippet.new(
76
+ read_code_snippet_contents(filename),
77
+ filename,
78
+ )
79
+ end
80
+ end
81
+ end
82
+
83
+ def encoding_from_magic_comment(raw)
84
+ match = ENCODING_REGEX.match(raw)
85
+ match ? match['encoding'] : nil
86
+ end
87
+
88
+ def read_code_snippet_contents(filename)
89
+ raw = File.read(filename, encoding: 'ASCII-8BIT')
90
+
91
+ enc = encoding_from_magic_comment(raw)
92
+ if enc
93
+ raw = raw.force_encoding(enc).encode('UTF-8').sub(ENCODING_REGEX, '')
94
+ else
95
+ raw.force_encoding('UTF-8')
96
+ end
97
+
98
+ raw
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ # Generic trivial error. Superclass for all Nanoc-specific errors that are
6
+ # considered "trivial", i.e. errors that do not require a full crash report.
7
+ class TrivialError < ::Nanoc::Core::Error
8
+ end
9
+ end
10
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Nanoc
4
4
  module Core
5
- VERSION = '4.11.12'
5
+ VERSION = '4.11.17'
6
6
  end
7
7
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class View
6
+ include Nanoc::Core::ContractsSupport
7
+
8
+ # @api private
9
+ # TODO: disallow nil
10
+ contract C::Maybe[C::Or[
11
+ Nanoc::Core::ViewContextForCompilation,
12
+ Nanoc::Core::ViewContextForPreCompilation,
13
+ Nanoc::Core::ViewContextForShell
14
+ ]] => C::Any
15
+ def initialize(context)
16
+ @context = context
17
+ end
18
+
19
+ # @api private
20
+ def _context
21
+ @context
22
+ end
23
+
24
+ # @api private
25
+ def _unwrap
26
+ raise NotImplementedError
27
+ end
28
+
29
+ # True if the wrapped object is frozen; false otherwise.
30
+ #
31
+ # @return [Boolean]
32
+ #
33
+ # @see Object#frozen?
34
+ def frozen?
35
+ _unwrap.frozen?
36
+ end
37
+
38
+ def inspect
39
+ "<#{self.class}>"
40
+ end
41
+ end
42
+ end
43
+ end