Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make item/layout collection dependencies finer-grained #1193

Merged
merged 4 commits into from Jul 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 17 additions & 8 deletions lib/nanoc/base/entities/outdatedness_reasons.rb
Expand Up @@ -46,15 +46,24 @@ def initialize(message, props = Nanoc::Int::Props.new)
Props.new(raw_content: true, compiled_content: true),
)

ItemCollectionExtended = Generic.new(
'A new item has been added to the site.',
Props.new(raw_content: true),
)
class DocumentCollectionExtended < Generic
attr_reader :objects

LayoutCollectionExtended = Generic.new(
'A new layout has been added to the site.',
Props.new(raw_content: true),
)
def initialize(objects)
super(
'New items/layouts have been added to the site.',
Props.new(raw_content: true),
)

@objects = objects
end
end

class ItemCollectionExtended < DocumentCollectionExtended
end

class LayoutCollectionExtended < DocumentCollectionExtended
end

class AttributesModified < Generic
attr_reader :attributes
Expand Down
43 changes: 33 additions & 10 deletions lib/nanoc/base/entities/props.rb
Expand Up @@ -6,11 +6,13 @@ class Props
include Nanoc::Int::ContractsSupport

attr_reader :attributes
attr_reader :raw_content

# TODO: Split raw_content for documents and collections
C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
C_ATTRS = C::Or[C::IterOf[Symbol], C::Bool]
contract C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C_ATTRS], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]] => C::Any
contract C::KeywordArgs[raw_content: C::Optional[C_RAW_CONTENT], attributes: C::Optional[C_ATTRS], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]] => C::Any
def initialize(raw_content: false, attributes: false, compiled_content: false, path: false)
@raw_content = raw_content
@compiled_content = compiled_content
@path = path

Expand All @@ -21,6 +23,14 @@ def initialize(raw_content: false, attributes: false, compiled_content: false, p
else
attributes
end

@raw_content =
case raw_content
when Enumerable
Set.new(raw_content)
else
raw_content
end
end

contract C::None => String
Expand All @@ -37,7 +47,12 @@ def inspect

contract C::None => C::Bool
def raw_content?
@raw_content
case @raw_content
when Enumerable
@raw_content.any?
else
@raw_content
end
end

contract C::None => C::Bool
Expand All @@ -63,27 +78,35 @@ def path?
contract Nanoc::Int::Props => Nanoc::Int::Props
def merge(other)
Props.new(
raw_content: raw_content? || other.raw_content?,
raw_content: merge_raw_content(other),
attributes: merge_attributes(other),
compiled_content: compiled_content? || other.compiled_content?,
path: path? || other.path?,
)
end

def merge_raw_content(other)
merge_prop(raw_content, other.raw_content)
end

def merge_attributes(other)
case attributes
merge_prop(attributes, other.attributes)
end

def merge_prop(own, other)
case own
when true
true
when false
other.attributes
other
else
case other.attributes
case other
when true
true
when false
attributes
own
else
attributes + other.attributes
own + other
end
end
end
Expand All @@ -101,7 +124,7 @@ def active
contract C::None => Hash
def to_h
{
raw_content: raw_content?,
raw_content: raw_content,
attributes: attributes,
compiled_content: compiled_content?,
path: path?,
Expand Down
13 changes: 9 additions & 4 deletions lib/nanoc/base/repos/dependency_store.rb
Expand Up @@ -59,9 +59,12 @@ def layouts=(layouts)
add_vertex_for(layouts)
end

contract C::None => C::Bool
def any_new_objects?
@new_objects.any?
def new_items
@new_objects.select { |o| o.is_a?(Nanoc::Int::Item) }
end

def new_layouts
@new_objects.select { |o| o.is_a?(Nanoc::Int::Layout) }
end

# Returns the direct dependencies for the given object.
Expand All @@ -85,8 +88,10 @@ def objects_causing_outdatedness_of(object)
refs2objs(@graph.direct_predecessors_of(obj2ref(object)))
end

C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
C_ATTR = C::Or[C::IterOf[Symbol], C::Bool]
C_KEYWORD_PROPS = C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C_ATTR], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]]
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]]

contract C::Maybe[C_OBJ_SRC], C::Maybe[C_OBJ_DST], C_KEYWORD_PROPS => C::Any
# Records a dependency from `src` to `dst` in the dependency graph. When
# `dst` is oudated, `src` will also become outdated.
Expand Down
3 changes: 2 additions & 1 deletion lib/nanoc/base/services/dependency_tracker.rb
Expand Up @@ -6,8 +6,9 @@ class DependencyTracker
include Nanoc::Int::ContractsSupport

C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration, Nanoc::Int::IdentifiableCollection]
C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
C_ATTR = C::Or[C::IterOf[Symbol], C::Bool]
C_ARGS = C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C_ATTR], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]]
C_ARGS = 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]]

class Null
include Nanoc::Int::ContractsSupport
Expand Down
22 changes: 15 additions & 7 deletions lib/nanoc/base/services/outdatedness_checker.rb
Expand Up @@ -191,19 +191,27 @@ def dependency_causes_outdatedness?(dependency)
status = basic.outdatedness_status_for(dependency.from)

active = status.props.active & dependency.props.active
if attributes_unaffected?(status, dependency)
active.delete(:attributes)
end
active.delete(:attributes) if attributes_unaffected?(status, dependency)
active.delete(:raw_content) if raw_content_unaffected?(status, dependency)

active.any?
end

def attributes_unaffected?(status, dependency)
attr_reason = status.reasons.find do |r|
r.is_a?(Nanoc::Int::OutdatednessReasons::AttributesModified)
end
reason = status.reasons.find { |r| r.is_a?(Nanoc::Int::OutdatednessReasons::AttributesModified) }
reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & reason.attributes).empty?
end

attr_reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & attr_reason.attributes).empty?
def raw_content_unaffected?(status, dependency)
reason = status.reasons.find { |r| r.is_a?(Nanoc::Int::OutdatednessReasons::DocumentCollectionExtended) }
if reason.nil?
false
elsif !dependency.props.raw_content.is_a?(Enumerable)
false
else
patterns = dependency.props.raw_content.map { |r| Nanoc::Int::Pattern.from(r) }
patterns.none? { |pat| reason.objects.any? { |obj| pat.match?(obj.identifier) } }
end
end
end
end
Expand Up @@ -6,8 +6,10 @@ class ItemCollectionExtended < Nanoc::Int::OutdatednessRule

contract Nanoc::Int::ItemCollection, C::Named['Nanoc::Int::OutdatednessChecker'] => C::Maybe[Nanoc::Int::OutdatednessReasons::Generic]
def apply(_obj, outdatedness_checker)
if outdatedness_checker.dependency_store.any_new_objects?
Nanoc::Int::OutdatednessReasons::ItemCollectionExtended
new_items = outdatedness_checker.dependency_store.new_items

if new_items.any?
Nanoc::Int::OutdatednessReasons::ItemCollectionExtended.new(new_items)
end
end
end
Expand Down
Expand Up @@ -6,8 +6,10 @@ class LayoutCollectionExtended < Nanoc::Int::OutdatednessRule

contract Nanoc::Int::LayoutCollection, C::Named['Nanoc::Int::OutdatednessChecker'] => C::Maybe[Nanoc::Int::OutdatednessReasons::Generic]
def apply(_obj, outdatedness_checker)
if outdatedness_checker.dependency_store.any_new_objects?
Nanoc::Int::OutdatednessReasons::LayoutCollectionExtended
new_layouts = outdatedness_checker.dependency_store.new_layouts

if new_layouts.any?
Nanoc::Int::OutdatednessReasons::LayoutCollectionExtended.new(new_layouts)
end
end
end
Expand Down
24 changes: 22 additions & 2 deletions lib/nanoc/base/views/identifiable_collection_view.rb
Expand Up @@ -47,7 +47,17 @@ def size
#
# @return [Enumerable]
def find_all(arg)
@context.dependency_tracker.bounce(unwrap, raw_content: true)
prop_attribute =
case arg
when String, Nanoc::Identifier
[arg.to_s]
when Regexp
[arg]
else
true
end

@context.dependency_tracker.bounce(unwrap, raw_content: prop_attribute)
@objects.find_all(arg).map { |i| view_class.new(i, @context) }
end

Expand All @@ -74,7 +84,17 @@ def find_all(arg)
#
# @return [#identifier] if an object was found
def [](arg)
@context.dependency_tracker.bounce(unwrap, raw_content: true)
prop_attribute =
case arg
when String, Nanoc::Identifier
[arg.to_s]
when Regexp
[arg]
else
true
end

@context.dependency_tracker.bounce(unwrap, raw_content: prop_attribute)
res = @objects[arg]
res && view_class.new(res, @context)
end
Expand Down