nanoc 4.11.8 → 4.11.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +8 -0
  3. data/lib/nanoc.rb +4 -1
  4. data/lib/nanoc/base.rb +0 -1
  5. data/lib/nanoc/base/errors.rb +4 -49
  6. data/lib/nanoc/base/repos.rb +0 -15
  7. data/lib/nanoc/base/repos/site_loader.rb +1 -1
  8. data/lib/nanoc/base/services.rb +0 -2
  9. data/lib/nanoc/base/services/compiler.rb +1 -1
  10. data/lib/nanoc/base/services/compiler/phases/recalculate.rb +1 -1
  11. data/lib/nanoc/base/services/compiler/stages/build_reps.rb +1 -1
  12. data/lib/nanoc/base/services/compiler/stages/cleanup.rb +1 -1
  13. data/lib/nanoc/base/services/compiler/stages/load_stores.rb +1 -1
  14. data/lib/nanoc/base/services/compiler_loader.rb +6 -6
  15. data/lib/nanoc/base/services/outdatedness_checker.rb +10 -10
  16. data/lib/nanoc/base/services/outdatedness_rules/attributes_modified.rb +4 -4
  17. data/lib/nanoc/base/services/outdatedness_rules/code_snippets_modified.rb +2 -2
  18. data/lib/nanoc/base/services/outdatedness_rules/content_modified.rb +2 -2
  19. data/lib/nanoc/base/services/outdatedness_rules/item_collection_extended.rb +3 -3
  20. data/lib/nanoc/base/services/outdatedness_rules/layout_collection_extended.rb +3 -3
  21. data/lib/nanoc/base/services/outdatedness_rules/not_written.rb +2 -2
  22. data/lib/nanoc/base/services/outdatedness_rules/rules_modified.rb +3 -3
  23. data/lib/nanoc/base/services/outdatedness_rules/uses_always_outdated_filter.rb +2 -2
  24. data/lib/nanoc/base/services/pruner.rb +1 -1
  25. data/lib/nanoc/base/views/view_context_for_compilation.rb +1 -1
  26. data/lib/nanoc/base/views/view_context_for_pre_compilation.rb +1 -1
  27. data/lib/nanoc/base/views/view_context_for_shell.rb +2 -2
  28. data/lib/nanoc/checking/check.rb +17 -1
  29. data/lib/nanoc/cli/commands/shell.rb +1 -1
  30. data/lib/nanoc/cli/error_handler.rb +1 -1
  31. data/lib/nanoc/filters/handlebars.rb +2 -2
  32. data/lib/nanoc/filters/sass/importer.rb +3 -2
  33. data/lib/nanoc/rule_dsl/action_provider.rb +1 -1
  34. data/lib/nanoc/spec.rb +4 -4
  35. data/lib/nanoc/version.rb +1 -1
  36. metadata +5 -21
  37. data/lib/nanoc/base/entities.rb +0 -7
  38. data/lib/nanoc/base/entities/outdatedness_reasons.rb +0 -88
  39. data/lib/nanoc/base/entities/outdatedness_status.rb +0 -27
  40. data/lib/nanoc/base/repos/action_sequence_store.rb +0 -50
  41. data/lib/nanoc/base/repos/binary_compiled_content_cache.rb +0 -128
  42. data/lib/nanoc/base/repos/checksum_store.rb +0 -74
  43. data/lib/nanoc/base/repos/compiled_content_cache.rb +0 -68
  44. data/lib/nanoc/base/repos/compiled_content_store.rb +0 -77
  45. data/lib/nanoc/base/repos/dependency_store.rb +0 -204
  46. data/lib/nanoc/base/repos/item_rep_repo.rb +0 -37
  47. data/lib/nanoc/base/repos/outdatedness_store.rb +0 -55
  48. data/lib/nanoc/base/repos/prefixed_data_source.rb +0 -31
  49. data/lib/nanoc/base/repos/store.rb +0 -114
  50. data/lib/nanoc/base/repos/textual_compiled_content_cache.rb +0 -82
  51. data/lib/nanoc/base/services/dependency_tracker.rb +0 -63
  52. data/lib/nanoc/base/services/outdatedness_rule.rb +0 -34
@@ -1,128 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # Represents a cache than can be used to store already compiled content,
6
- # to prevent it from being needlessly recompiled.
7
- #
8
- # @api private
9
- class BinaryCompiledContentCache < ::Nanoc::Int::Store
10
- include Nanoc::Core::ContractsSupport
11
-
12
- contract C::KeywordArgs[config: Nanoc::Core::Configuration] => C::Any
13
- def initialize(config:)
14
- super(Nanoc::Int::Store.tmp_path_for(config: config, store_name: 'binary_content'), 1)
15
-
16
- @cache = {}
17
- end
18
-
19
- contract Nanoc::Core::ItemRep => C::Maybe[C::HashOf[Symbol => Nanoc::Core::Content]]
20
- # Returns the cached compiled content for the given item representation.
21
- #
22
- # This cached compiled content is a hash where the keys are the snapshot
23
- # names, and the values the compiled content at the given snapshot.
24
- def [](rep)
25
- item_cache = @cache[rep.item.identifier] || {}
26
-
27
- rep_cache = item_cache[rep.name]
28
- return nil if rep_cache.nil?
29
-
30
- rep_cache.transform_values do |filename|
31
- Nanoc::Core::Content.create(filename, binary: true)
32
- end
33
- end
34
-
35
- contract Nanoc::Core::ItemRep => C::Bool
36
- def include?(rep)
37
- item_cache = @cache[rep.item.identifier] || {}
38
- item_cache.key?(rep.name)
39
- end
40
-
41
- contract Nanoc::Core::ItemRep, C::HashOf[Symbol => Nanoc::Core::BinaryContent] => C::HashOf[Symbol => Nanoc::Core::Content]
42
- # Sets the compiled content for the given representation.
43
- #
44
- # This cached compiled content is a hash where the keys are the snapshot
45
- # names, and the values the compiled content at the given snapshot.
46
- def []=(rep, content)
47
- @cache[rep.item.identifier] ||= {}
48
- @cache[rep.item.identifier][rep.name] ||= {}
49
- rep_cache = @cache[rep.item.identifier][rep.name]
50
-
51
- content.each do |snapshot, binary_content|
52
- filename = build_filename(rep, snapshot)
53
- rep_cache[snapshot] = filename
54
-
55
- # Avoid reassigning the same content if this binary cached content was
56
- # already used, because it was available and the item wasn’t oudated.
57
- next if binary_content.filename == filename
58
-
59
- # Copy
60
- #
61
- # NOTE: hardlinking is not an option in this case, because hardlinking
62
- # would make it possible for the content to be (inadvertently)
63
- # changed outside of Nanoc.
64
- FileUtils.mkdir_p(File.dirname(filename))
65
- FileUtils.cp(binary_content.filename, filename)
66
- end
67
- end
68
-
69
- def prune(items:)
70
- item_identifiers = Set.new(items.map(&:identifier))
71
-
72
- @cache.each_key do |key|
73
- # TODO: remove unused item reps
74
- next if item_identifiers.include?(key)
75
-
76
- @cache.delete(key)
77
- path = dirname_for_item_identifier(key)
78
- FileUtils.rm_rf(path)
79
- end
80
- end
81
-
82
- def data
83
- @cache
84
- end
85
-
86
- def data=(new_data)
87
- @cache = {}
88
-
89
- new_data.each_pair do |item_identifier, content_per_rep|
90
- @cache[item_identifier] ||= content_per_rep
91
- end
92
- end
93
-
94
- private
95
-
96
- def dirname
97
- filename + '_data'
98
- end
99
-
100
- def string_to_path_component(string)
101
- string.gsub(/[^a-zA-Z0-9]+/, '_') +
102
- '-' +
103
- Digest::SHA1.hexdigest(string)[0..9]
104
- end
105
-
106
- def dirname_for_item_identifier(item_identifier)
107
- File.join(
108
- dirname,
109
- string_to_path_component(item_identifier.to_s),
110
- )
111
- end
112
-
113
- def dirname_for_item_rep(rep)
114
- File.join(
115
- dirname_for_item_identifier(rep.item.identifier),
116
- string_to_path_component(rep.name.to_s),
117
- )
118
- end
119
-
120
- def build_filename(rep, snapshot_name)
121
- File.join(
122
- dirname_for_item_rep(rep),
123
- string_to_path_component(snapshot_name.to_s),
124
- )
125
- end
126
- end
127
- end
128
- end
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # Stores checksums for objects in order to be able to detect whether a file
6
- # has changed since the last site compilation.
7
- #
8
- # @api private
9
- class ChecksumStore < ::Nanoc::Int::Store
10
- include Nanoc::Core::ContractsSupport
11
-
12
- attr_writer :checksums
13
- attr_accessor :objects
14
-
15
- c_obj = C::Or[Nanoc::Core::Item, Nanoc::Core::Layout, Nanoc::Core::Configuration, Nanoc::Core::CodeSnippet]
16
-
17
- contract C::KeywordArgs[config: Nanoc::Core::Configuration, objects: C::IterOf[c_obj]] => C::Any
18
- def initialize(config:, objects:)
19
- super(Nanoc::Int::Store.tmp_path_for(config: config, store_name: 'checksums'), 2)
20
-
21
- @objects = objects
22
-
23
- @checksums = {}
24
- end
25
-
26
- contract c_obj => C::Maybe[String]
27
- def [](obj)
28
- @checksums[obj.reference]
29
- end
30
-
31
- contract c_obj => self
32
- def add(obj)
33
- if obj.is_a?(Nanoc::Core::Document)
34
- @checksums[[obj.reference, :content]] = Nanoc::Core::Checksummer.calc_for_content_of(obj)
35
- end
36
-
37
- if obj.is_a?(Nanoc::Core::Document) || obj.is_a?(Nanoc::Core::Configuration)
38
- @checksums[[obj.reference, :each_attribute]] = Nanoc::Core::Checksummer.calc_for_each_attribute_of(obj)
39
- end
40
-
41
- @checksums[obj.reference] = Nanoc::Core::Checksummer.calc(obj)
42
-
43
- self
44
- end
45
-
46
- contract c_obj => C::Maybe[String]
47
- def content_checksum_for(obj)
48
- @checksums[[obj.reference, :content]]
49
- end
50
-
51
- contract c_obj => C::Maybe[C::HashOf[Symbol, String]]
52
- def attributes_checksum_for(obj)
53
- @checksums[[obj.reference, :each_attribute]]
54
- end
55
-
56
- protected
57
-
58
- def data
59
- @checksums
60
- end
61
-
62
- def data=(new_data)
63
- references = Set.new(@objects.map(&:reference))
64
-
65
- @checksums = {}
66
- new_data.each_pair do |key, checksum|
67
- if references.include?(key) || references.include?(key.first)
68
- @checksums[key] = checksum
69
- end
70
- end
71
- end
72
- end
73
- end
74
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # Represents a cache than can be used to store already compiled content,
6
- # to prevent it from being needlessly recompiled.
7
- #
8
- # @api private
9
- class CompiledContentCache < ::Nanoc::Int::Store
10
- include Nanoc::Core::ContractsSupport
11
-
12
- contract C::KeywordArgs[config: Nanoc::Core::Configuration] => C::Any
13
- def initialize(config:)
14
- @textual_cache = Nanoc::Int::TextualCompiledContentCache.new(config: config)
15
- @binary_cache = Nanoc::Int::BinaryCompiledContentCache.new(config: config)
16
-
17
- @wrapped_caches = [@textual_cache, @binary_cache]
18
- end
19
-
20
- contract Nanoc::Core::ItemRep => C::Maybe[C::HashOf[Symbol => Nanoc::Core::Content]]
21
- # Returns the cached compiled content for the given item representation.
22
- #
23
- # This cached compiled content is a hash where the keys are the snapshot
24
- # names. and the values the compiled content at the given snapshot.
25
- def [](rep)
26
- textual_content_map = @textual_cache[rep]
27
- binary_content_map = @binary_cache[rep]
28
-
29
- # If either the textual or the binary content cache is nil, assume the
30
- # cache is entirely absent.
31
- #
32
- # This is necessary to support the case where only textual content is
33
- # cached (which was the case in older versions of Nanoc).
34
- return nil if [textual_content_map, binary_content_map].any?(&:nil?)
35
-
36
- textual_content_map.merge(binary_content_map)
37
- end
38
-
39
- contract Nanoc::Core::ItemRep, C::HashOf[Symbol => Nanoc::Core::Content] => C::Any
40
- # Sets the compiled content for the given representation.
41
- #
42
- # This cached compiled content is a hash where the keys are the snapshot
43
- # names and the values the compiled content at the given snapshot.
44
- def []=(rep, content)
45
- @textual_cache[rep] = content.select { |_key, c| c.textual? }
46
- @binary_cache[rep] = content.select { |_key, c| c.binary? }
47
- end
48
-
49
- def prune(*args)
50
- @wrapped_caches.each { |w| w.prune(*args) }
51
- end
52
-
53
- # True if there is cached compiled content available for this item, and
54
- # all entries are present (either textual or binary).
55
- def full_cache_available?(rep)
56
- @textual_cache.include?(rep) && @binary_cache.include?(rep)
57
- end
58
-
59
- def load(*args)
60
- @wrapped_caches.each { |w| w.load(*args) }
61
- end
62
-
63
- def store(*args)
64
- @wrapped_caches.each { |w| w.store(*args) }
65
- end
66
- end
67
- end
68
- end
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # @api private
6
- class CompiledContentStore
7
- include Nanoc::Core::ContractsSupport
8
-
9
- def initialize
10
- @contents = Hash.new { |hash, rep| hash[rep] = {} }
11
- @current_content = {}
12
- end
13
-
14
- contract Nanoc::Core::ItemRep, Symbol => C::Maybe[Nanoc::Core::Content]
15
- def get(rep, snapshot_name)
16
- @contents[rep][snapshot_name]
17
- end
18
-
19
- contract Nanoc::Core::ItemRep => C::Maybe[Nanoc::Core::Content]
20
- def get_current(rep)
21
- @current_content[rep]
22
- end
23
-
24
- contract Nanoc::Core::ItemRep, Symbol, Nanoc::Core::Content => C::Any
25
- def set(rep, snapshot_name, contents)
26
- @contents[rep][snapshot_name] = contents
27
- end
28
-
29
- contract Nanoc::Core::ItemRep, Nanoc::Core::Content => C::Any
30
- def set_current(rep, content)
31
- @current_content[rep] = content
32
- end
33
-
34
- contract Nanoc::Core::ItemRep => C::HashOf[Symbol => Nanoc::Core::Content]
35
- def get_all(rep)
36
- @contents[rep]
37
- end
38
-
39
- contract Nanoc::Core::ItemRep, C::HashOf[Symbol => Nanoc::Core::Content] => C::Any
40
- def set_all(rep, contents_per_snapshot)
41
- @contents[rep] = contents_per_snapshot
42
- end
43
-
44
- contract C::KeywordArgs[rep: Nanoc::Core::ItemRep, snapshot: C::Optional[C::Maybe[Symbol]]] => Nanoc::Core::Content
45
- def raw_compiled_content(rep:, snapshot: nil)
46
- # Get name of last pre-layout snapshot
47
- has_pre = rep.snapshot_defs.any? { |sd| sd.name == :pre }
48
- snapshot_name = snapshot || (has_pre ? :pre : :last)
49
-
50
- # Check existance of snapshot
51
- snapshot_def = rep.snapshot_defs.reverse.find { |sd| sd.name == snapshot_name }
52
- unless snapshot_def
53
- raise Nanoc::Int::Errors::NoSuchSnapshot.new(rep, snapshot_name)
54
- end
55
-
56
- # Return content if it is available
57
- content = get(rep, snapshot_name)
58
- return content if content
59
-
60
- # Content is unavailable; notify and try again
61
- Fiber.yield(Nanoc::Int::Errors::UnmetDependency.new(rep, snapshot_name))
62
- get(rep, snapshot_name)
63
- end
64
-
65
- contract C::KeywordArgs[rep: Nanoc::Core::ItemRep, snapshot: C::Optional[C::Maybe[Symbol]]] => String
66
- def compiled_content(rep:, snapshot: nil)
67
- snapshot_content = raw_compiled_content(rep: rep, snapshot: snapshot)
68
-
69
- if snapshot_content.binary?
70
- raise Nanoc::Int::Errors::CannotGetCompiledContentOfBinaryItem.new(rep)
71
- end
72
-
73
- snapshot_content.string
74
- end
75
- end
76
- end
77
- end
@@ -1,204 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Nanoc
4
- module Int
5
- # @api private
6
- class DependencyStore < ::Nanoc::Int::Store
7
- include Nanoc::Core::ContractsSupport
8
-
9
- attr_reader :items
10
- attr_reader :layouts
11
-
12
- contract Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection, Nanoc::Core::Configuration => C::Any
13
- def initialize(items, layouts, config)
14
- super(Nanoc::Int::Store.tmp_path_for(config: config, store_name: 'dependencies'), 5)
15
-
16
- @items = items
17
- @layouts = layouts
18
-
19
- @refs2objs = {}
20
- items.each { |o| add_vertex_for(o) }
21
- layouts.each { |o| add_vertex_for(o) }
22
- add_vertex_for(config)
23
- add_vertex_for(items)
24
- add_vertex_for(layouts)
25
-
26
- @new_objects = []
27
- @graph = Nanoc::Core::DirectedGraph.new([nil] + objs2refs(@items) + objs2refs(@layouts))
28
- end
29
-
30
- C_OBJ_SRC = Nanoc::Core::Item
31
- C_OBJ_DST = C::Or[Nanoc::Core::Item, Nanoc::Core::Layout, Nanoc::Core::Configuration, Nanoc::Core::IdentifiableCollection]
32
-
33
- contract C_OBJ_SRC => C::ArrayOf[Nanoc::Core::Dependency]
34
- def dependencies_causing_outdatedness_of(object)
35
- objects_causing_outdatedness_of(object).map do |other_object|
36
- props = props_for(other_object, object)
37
-
38
- Nanoc::Core::Dependency.new(
39
- other_object,
40
- object,
41
- Nanoc::Core::DependencyProps.new(
42
- raw_content: props.fetch(:raw_content, false),
43
- attributes: props.fetch(:attributes, false),
44
- compiled_content: props.fetch(:compiled_content, false),
45
- path: props.fetch(:path, false),
46
- ),
47
- )
48
- end
49
- end
50
-
51
- def items=(items)
52
- @items = items
53
- items.each { |o| @refs2objs[obj2ref(o)] = o }
54
- add_vertex_for(items)
55
- end
56
-
57
- def layouts=(layouts)
58
- @layouts = layouts
59
- layouts.each { |o| @refs2objs[obj2ref(o)] = o }
60
- add_vertex_for(layouts)
61
- end
62
-
63
- def new_items
64
- @new_objects.select { |o| o.is_a?(Nanoc::Core::Item) }
65
- end
66
-
67
- def new_layouts
68
- @new_objects.select { |o| o.is_a?(Nanoc::Core::Layout) }
69
- end
70
-
71
- # Returns the direct dependencies for the given object.
72
- #
73
- # The direct dependencies of the given object include the items and
74
- # layouts that, when outdated will cause the given object to be marked as
75
- # outdated. Indirect dependencies will not be returned (e.g. if A depends
76
- # on B which depends on C, then the direct dependencies of A do not
77
- # include C).
78
- #
79
- # The direct predecessors can include nil, which indicates an item that is
80
- # no longer present in the site.
81
- #
82
- # @param [Nanoc::Core::Item, Nanoc::Core::Layout] object The object for
83
- # which to fetch the direct predecessors
84
- #
85
- # @return [Array<Nanoc::Core::Item, Nanoc::Core::Layout, nil>] The direct
86
- # predecessors of
87
- # the given object
88
- def objects_causing_outdatedness_of(object)
89
- refs2objs(@graph.direct_predecessors_of(obj2ref(object)))
90
- end
91
-
92
- C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
93
- C_ATTR = C::Or[C::IterOf[Symbol], C::Bool]
94
- C_KEYWORD_PROPS = C::KeywordArgs[raw_content: C::Optional[C_RAW_CONTENT], attributes: C::Optional[C_ATTR], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]]
95
-
96
- contract C::Maybe[C_OBJ_SRC], C::Maybe[C_OBJ_DST], C_KEYWORD_PROPS => C::Any
97
- # Records a dependency from `src` to `dst` in the dependency graph. When
98
- # `dst` is oudated, `src` will also become outdated.
99
- #
100
- # @param [Nanoc::Core::Item, Nanoc::Core::Layout] src The source of the dependency,
101
- # i.e. the object that will become outdated if dst is outdated
102
- #
103
- # @param [Nanoc::Core::Item, Nanoc::Core::Layout] dst The destination of the
104
- # dependency, i.e. the object that will cause the source to become
105
- # outdated if the destination is outdated
106
- #
107
- # @return [void]
108
- def record_dependency(src, dst, raw_content: false, attributes: false, compiled_content: false, path: false)
109
- return if src == dst
110
-
111
- add_vertex_for(src)
112
- add_vertex_for(dst)
113
-
114
- src_ref = obj2ref(src)
115
- dst_ref = obj2ref(dst)
116
-
117
- existing_props = Nanoc::Core::DependencyProps.new(@graph.props_for(dst_ref, src_ref) || {})
118
- new_props = Nanoc::Core::DependencyProps.new(raw_content: raw_content, attributes: attributes, compiled_content: compiled_content, path: path)
119
- props = existing_props.merge(new_props)
120
-
121
- @graph.add_edge(dst_ref, src_ref, props: props.to_h)
122
- end
123
-
124
- def add_vertex_for(obj)
125
- @refs2objs[obj2ref(obj)] = obj
126
- end
127
-
128
- # Empties the list of dependencies for the given object. This is necessary
129
- # before recompiling the given object, because otherwise old dependencies
130
- # will stick around and new dependencies will appear twice. This function
131
- # removes all incoming edges for the given vertex.
132
- #
133
- # @param [Nanoc::Core::Item, Nanoc::Core::Layout] object The object for which to
134
- # forget all dependencies
135
- #
136
- # @return [void]
137
- def forget_dependencies_for(object)
138
- @graph.delete_edges_to(obj2ref(object))
139
- end
140
-
141
- protected
142
-
143
- def obj2ref(obj)
144
- obj&.reference
145
- end
146
-
147
- def ref2obj(reference)
148
- if reference
149
- @refs2objs[reference]
150
- else
151
- nil
152
- end
153
- end
154
-
155
- def objs2refs(objs)
156
- objs.map { |o| obj2ref(o) }
157
- end
158
-
159
- def refs2objs(refs)
160
- refs.map { |r| ref2obj(r) }
161
- end
162
-
163
- def props_for(from, to)
164
- props = @graph.props_for(obj2ref(from), obj2ref(to)) || {}
165
-
166
- if props.values.any? { |v| v }
167
- props
168
- else
169
- { raw_content: true, attributes: true, compiled_content: true, path: true }
170
- end
171
- end
172
-
173
- def data
174
- {
175
- edges: @graph.edges,
176
- vertices: @graph.vertices,
177
- }
178
- end
179
-
180
- def data=(new_data)
181
- objects = Set.new(@items.to_a + @layouts.to_a)
182
- refs = objs2refs(objects)
183
-
184
- # Create new graph
185
- @graph = Nanoc::Core::DirectedGraph.new([nil] + refs)
186
-
187
- # Load vertices
188
- previous_refs = new_data[:vertices]
189
- previous_objects = Set.new(refs2objs(previous_refs))
190
-
191
- # Load edges
192
- new_data[:edges].each do |edge|
193
- from_index, to_index, props = *edge
194
- from = from_index && previous_refs[from_index]
195
- to = to_index && previous_refs[to_index]
196
- @graph.add_edge(from, to, props: props)
197
- end
198
-
199
- # Record dependency from all items on new items
200
- @new_objects = objects - previous_objects
201
- end
202
- end
203
- end
204
- end