lib/nanoc/base/compilation/dependency_tracker.rb in nanoc-4.0.2 vs lib/nanoc/base/compilation/dependency_tracker.rb in nanoc-4.1.0a1
- old
+ new
@@ -1,193 +1,48 @@
module Nanoc::Int
- # Responsible for remembering dependencies between items and layouts. It is
- # used to speed up compilation by only letting an item be recompiled when it
- # is outdated or any of its dependencies (or dependencies’ dependencies,
- # etc) is outdated.
- #
- # The dependencies tracked by the dependency tracker are not dependencies
- # based on an item’s or a layout’s content. When one object uses an
- # attribute of another object, then this is also treated as a dependency.
- # While dependencies based on an item’s or layout’s content (handled in
- # {Nanoc::Int::Compiler}) cannot be mutually recursive, the more general
- # dependencies in Nanoc::Int::DependencyTracker can (e.g. item A can use an
- # attribute of item B and vice versa without problems).
- #
- # The dependency tracker remembers the dependency information between runs.
- # Dependency information is stored in the `tmp/dependencies` file.
- #
# @api private
- class DependencyTracker < ::Nanoc::Int::Store
- # @return [Array<Nanoc::Int::Item, Nanoc::Int::Layout>] The list of items and
- # layouts that are being tracked by the dependency tracker
- attr_reader :objects
-
- # @return [Nanoc::Int::Compiler] The compiler that corresponds to this
- # dependency tracker
- attr_accessor :compiler
-
- # Creates a new dependency tracker for the given items and layouts.
- #
- # @param [Array<Nanoc::Int::Item, Nanoc::Int::Layout>] objects The list of items
- # and layouts whose dependencies should be managed
- def initialize(objects)
- super('tmp/dependencies', 4)
-
- @objects = objects
- @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
- @stack = []
+ class DependencyTracker
+ def initialize(dependency_store)
+ @dependency_store = dependency_store
end
- # Starts listening for dependency messages (`:visit_started` and
- # `:visit_ended`) and start recording dependencies.
+ # Record dependencies for the duration of the block.
#
# @return [void]
- def start
- # Initialize dependency stack. An object will be pushed onto this stack
- # when it is visited. Therefore, an object on the stack always depends
- # on all objects pushed above it.
- @stack = []
+ def run
+ unless block_given?
+ raise ArgumentError, 'No block given'
+ end
- # Register start of visits
+ stack = []
+ start_tracking(stack)
+ yield
+ ensure
+ stop_tracking(stack)
+ end
+
+ # @api private
+ def start_tracking(stack)
Nanoc::Int::NotificationCenter.on(:visit_started, self) do |obj|
- unless @stack.empty?
- Nanoc::Int::NotificationCenter.post(:dependency_created, @stack.last, obj)
- record_dependency(@stack.last, obj)
+ unless stack.empty?
+ Nanoc::Int::NotificationCenter.post(:dependency_created, stack.last, obj)
+ @dependency_store.record_dependency(stack.last, obj)
end
- @stack.push(obj)
+ stack.push(obj)
end
- # Register end of visits
Nanoc::Int::NotificationCenter.on(:visit_ended, self) do |_obj|
- @stack.pop
+ stack.pop
end
end
- # Stop listening for dependency messages and stop recording dependencies.
- #
- # @return [void]
- def stop
- # Sanity check
- unless @stack.empty?
+ # @api private
+ def stop_tracking(stack)
+ unless stack.empty?
raise 'Internal inconsistency: dependency tracker stack not empty at end of compilation'
end
- # Unregister
Nanoc::Int::NotificationCenter.remove(:visit_started, self)
Nanoc::Int::NotificationCenter.remove(:visit_ended, self)
- end
-
- # @return The topmost item on the stack, i.e. the one currently being
- # compiled
- def top
- @stack.last
- end
-
- # Returns the direct dependencies for the given object.
- #
- # The direct dependencies of the given object include the items and
- # layouts that, when outdated will cause the given object to be marked as
- # outdated. Indirect dependencies will not be returned (e.g. if A depends
- # on B which depends on C, then the direct dependencies of A do not
- # include C).
- #
- # The direct predecessors can include nil, which indicates an item that is
- # no longer present in the site.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for
- # which to fetch the direct predecessors
- #
- # @return [Array<Nanoc::Int::Item, Nanoc::Int::Layout, nil>] The direct
- # predecessors of
- # the given object
- def objects_causing_outdatedness_of(object)
- @graph.direct_predecessors_of(object)
- end
-
- # Returns the direct inverse dependencies for the given object.
- #
- # The direct inverse dependencies of the given object include the objects
- # that will be marked as outdated when the given object is outdated.
- # Indirect dependencies will not be returned (e.g. if A depends on B which
- # depends on C, then the direct inverse dependencies of C do not include
- # A).
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for which to
- # fetch the direct successors
- #
- # @return [Array<Nanoc::Int::Item, Nanoc::Int::Layout>] The direct successors of
- # the given object
- def objects_outdated_due_to(object)
- @graph.direct_successors_of(object).compact
- end
-
- # Records a dependency from `src` to `dst` in the dependency graph. When
- # `dst` is oudated, `src` will also become outdated.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] src The source of the dependency,
- # i.e. the object that will become outdated if dst is outdated
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] dst The destination of the
- # dependency, i.e. the object that will cause the source to become
- # outdated if the destination is outdated
- #
- # @return [void]
- def record_dependency(src, dst)
- # Warning! dst and src are *reversed* here!
- @graph.add_edge(dst, src) unless src == dst
- end
-
- # Empties the list of dependencies for the given object. This is necessary
- # before recompiling the given object, because otherwise old dependencies
- # will stick around and new dependencies will appear twice. This function
- # removes all incoming edges for the given vertex.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::Layout] object The object for which to
- # forget all dependencies
- #
- # @return [void]
- def forget_dependencies_for(object)
- @graph.delete_edges_to(object)
- end
-
- # @see Nanoc::Int::Store#unload
- def unload
- @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
- end
-
- protected
-
- def data
- {
- edges: @graph.edges,
- vertices: @graph.vertices.map { |obj| obj && obj.reference },
- }
- end
-
- def data=(new_data)
- # Create new graph
- @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
-
- # Load vertices
- previous_objects = new_data[:vertices].map do |reference|
- @objects.find { |obj| reference == obj.reference }
- end
-
- # Load edges
- new_data[:edges].each do |edge|
- from_index, to_index = *edge
- from = from_index && previous_objects[from_index]
- to = to_index && previous_objects[to_index]
- @graph.add_edge(from, to)
- end
-
- # Record dependency from all items on new items
- new_objects = (@objects - previous_objects)
- new_objects.each do |new_obj|
- @objects.each do |obj|
- next unless obj.is_a?(Nanoc::Int::Item)
- @graph.add_edge(new_obj, obj)
- end
- end
end
end
end