lib/nanoc/base/compilation/outdatedness_checker.rb in nanoc-4.4.3 vs lib/nanoc/base/compilation/outdatedness_checker.rb in nanoc-4.4.4
- old
+ new
@@ -1,17 +1,85 @@
module Nanoc::Int
# Responsible for determining whether an item or a layout is outdated.
#
# @api private
class OutdatednessChecker
+ class Basic
+ extend Nanoc::Int::Memoization
+
+ include Nanoc::Int::ContractsSupport
+
+ Rules = Nanoc::Int::OutdatednessRules
+
+ RULES_FOR_ITEM_REP =
+ [
+ Rules::RulesModified,
+ Rules::PathsModified,
+ Rules::ContentModified,
+ Rules::AttributesModified,
+ Rules::NotWritten,
+ Rules::CodeSnippetsModified,
+ Rules::ConfigurationModified,
+ ].freeze
+
+ RULES_FOR_LAYOUT =
+ [
+ Rules::RulesModified,
+ Rules::ContentModified,
+ Rules::AttributesModified,
+ ].freeze
+
+ contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Int::ItemRepRepo] => C::Any
+ def initialize(outdatedness_checker:, reps:)
+ @outdatedness_checker = outdatedness_checker
+ @reps = reps
+ end
+
+ contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Maybe[OutdatednessStatus]
+ def outdatedness_status_for(obj)
+ case obj
+ when Nanoc::Int::ItemRep
+ apply_rules(RULES_FOR_ITEM_REP, obj)
+ when Nanoc::Int::Item
+ apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
+ when Nanoc::Int::Layout
+ apply_rules(RULES_FOR_LAYOUT, obj)
+ else
+ raise "do not know how to check outdatedness of #{obj.inspect}"
+ end
+ end
+ memoize :outdatedness_status_for
+
+ private
+
+ contract C::ArrayOf[Class], C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout], OutdatednessStatus => C::Maybe[OutdatednessStatus]
+ def apply_rules(rules, obj, status = OutdatednessStatus.new)
+ rules.inject(status) do |acc, rule|
+ if !acc.useful_to_apply?(rule)
+ acc
+ elsif rule.instance.apply(obj, @outdatedness_checker)
+ acc.update(rule.instance.reason)
+ else
+ acc
+ end
+ end
+ end
+
+ contract C::ArrayOf[Class], C::ArrayOf[C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout]] => C::Maybe[OutdatednessStatus]
+ def apply_rules_multi(rules, objs)
+ objs.inject(OutdatednessStatus.new) { |acc, elem| apply_rules(rules, elem, acc) }
+ end
+ end
+
extend Nanoc::Int::Memoization
include Nanoc::Int::ContractsSupport
attr_reader :checksum_store
attr_reader :dependency_store
attr_reader :rule_memory_store
+ attr_reader :action_provider
attr_reader :site
Reasons = Nanoc::Int::OutdatednessReasons
# @param [Nanoc::Int::Site] site
@@ -26,12 +94,10 @@
@dependency_store = dependency_store
@rule_memory_store = rule_memory_store
@action_provider = action_provider
@reps = reps
- @basic_outdatedness_reasons = {}
- @outdatedness_reasons = {}
@objects_outdated_due_to_dependencies = {}
end
contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
# Checks whether the given object is outdated and therefore needs to be
@@ -62,91 +128,22 @@
end
memoize :outdatedness_reason_for
private
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
- # Checks whether the given object is outdated and therefore needs to be
- # recompiled. This method does not take dependencies into account; use
- # {#outdated?} if you want to include dependencies in the outdatedness
- # check.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
- # whose outdatedness should be checked.
- #
- # @return [Boolean] true if the object is outdated, false otherwise
- def basic_outdated?(obj)
- !basic_outdatedness_reason_for(obj).nil?
+ contract C::None => Basic
+ def basic
+ @_basic ||= Basic.new(outdatedness_checker: self, reps: @reps)
end
contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Maybe[Reasons::Generic]
- # Calculates the reason why the given object is outdated. This method does
- # not take dependencies into account; use {#outdatedness_reason_for?} if
- # you want to include dependencies in the outdatedness check.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
- # whose outdatedness reason should be calculated.
- #
- # @return [Reasons::Generic, nil] The reason why the
- # given object is outdated, or nil if the object is not outdated.
def basic_outdatedness_reason_for(obj)
- case obj
- when Nanoc::Int::ItemRep
- # Outdated if rules outdated
- return Reasons::RulesModified if
- rule_memory_differs_for(obj)
-
- # Outdated if checksums are missing or different
- return Reasons::NotEnoughData unless checksums_available?(obj.item)
- return Reasons::ContentModified unless content_checksums_identical?(obj.item)
- return Reasons::AttributesModified unless attributes_checksums_identical?(obj.item)
-
- # Outdated if compiled file doesn't exist (yet)
- return Reasons::NotWritten if obj.raw_path && !File.file?(obj.raw_path)
-
- # Outdated if code snippets outdated
- return Reasons::CodeSnippetsModified if site.code_snippets.any? do |cs|
- object_modified?(cs)
- end
-
- # Outdated if configuration outdated
- return Reasons::ConfigurationModified if object_modified?(site.config)
-
- # Not outdated
- nil
- when Nanoc::Int::Item
- @reps[obj].lazy.map { |rep| basic_outdatedness_reason_for(rep) }.find { |s| s }
- when Nanoc::Int::Layout
- # Outdated if rules outdated
- return Reasons::RulesModified if
- rule_memory_differs_for(obj)
-
- # Outdated if checksums are missing or different
- return Reasons::NotEnoughData unless checksums_available?(obj)
- return Reasons::ContentModified unless content_checksums_identical?(obj)
- return Reasons::AttributesModified unless attributes_checksums_identical?(obj)
-
- # Not outdated
- nil
- else
- raise "do not know how to check outdatedness of #{obj.inspect}"
- end
+ # FIXME: Stop using this; it is no longer accurate, as there can be >1 reasons
+ basic.outdatedness_status_for(obj).reasons.first
end
- memoize :basic_outdatedness_reason_for
contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout], Hamster::Set => C::Bool
- # Checks whether the given object is outdated due to dependencies.
- #
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
- # whose outdatedness should be checked.
- #
- # @param [Set] processed The collection of items that has been visited
- # during this outdatedness check. This is used to prevent checks for
- # items that (indirectly) depend on their own from looping
- # indefinitely. It should not be necessary to pass this a custom value.
- #
- # @return [Boolean] true if the object is outdated, false otherwise
def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
# Convert from rep to item if necessary
obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
# Get from cache
@@ -158,79 +155,27 @@
# Don’t return true; the false will be or’ed into a true if there
# really is a dependency that is causing outdatedness.
return false if processed.include?(obj)
# Calculate
- is_outdated = dependency_store.objects_causing_outdatedness_of(obj).any? do |other|
- other.nil? || basic_outdated?(other) || outdated_due_to_dependencies?(other, processed.merge([obj]))
+ is_outdated = dependency_store.dependencies_causing_outdatedness_of(obj).any? do |dep|
+ dependency_causes_outdatedness?(dep) ||
+ (dep.props.compiled_content? &&
+ outdated_due_to_dependencies?(dep.from, processed.merge([obj])))
end
# Cache
@objects_outdated_due_to_dependencies[obj] = is_outdated
# Done
is_outdated
end
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
- # @param [Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The layout or item
- # representation to check the rule memory for
- #
- # @return [Boolean] true if the rule memory for the given item
- # represenation has changed, false otherwise
- def rule_memory_differs_for(obj)
- !rule_memory_store[obj].eql?(@action_provider.memory_for(obj).serialize)
- end
- memoize :rule_memory_differs_for
+ contract Nanoc::Int::Dependency => C::Bool
+ def dependency_causes_outdatedness?(dependency)
+ return true if dependency.from.nil?
- contract C::Any => String
- # @param obj The object to create a checksum for
- #
- # @return [String] The digest
- def calc_checksum(obj)
- Nanoc::Int::Checksummer.calc(obj)
+ status = basic.outdatedness_status_for(dependency.from)
+ (status.props.active & dependency.props.active).any?
end
- memoize :calc_checksum
-
- contract C::Any => C::Bool
- # @param obj
- #
- # @return [Boolean] false if either the new or the old checksum for the
- # given object is not available, true if both checksums are available
- def checksums_available?(obj)
- checksum_store[obj] && calc_checksum(obj) ? true : false
- end
- memoize :checksums_available?
-
- contract C::Any => C::Bool
- # @param obj
- #
- # @return [Boolean] false if the old and new checksums for the given
- # object differ, true if they are identical
- def checksums_identical?(obj)
- checksum_store[obj] == calc_checksum(obj)
- end
- memoize :checksums_identical?
-
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::Layout] => C::Bool
- def content_checksums_identical?(obj)
- checksum_store.content_checksum_for(obj) == Nanoc::Int::Checksummer.calc_for_content_of(obj)
- end
- memoize :content_checksums_identical?
-
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::Layout] => C::Bool
- def attributes_checksums_identical?(obj)
- checksum_store.attributes_checksum_for(obj) == Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
- end
- memoize :attributes_checksums_identical?
-
- contract C::Any => C::Bool
- # @param obj
- #
- # @return [Boolean] true if the old and new checksums for the given object
- # are available and identical, false otherwise
- def object_modified?(obj)
- !checksums_available?(obj) || !checksums_identical?(obj)
- end
- memoize :object_modified?
end
end