nanoc 4.7.12 → 4.7.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS.md +6 -0
  3. data/lib/nanoc/base/core_ext.rb +0 -1
  4. data/lib/nanoc/base/entities.rb +4 -1
  5. data/lib/nanoc/base/entities/dependency.rb +5 -4
  6. data/lib/nanoc/base/entities/directed_graph.rb +12 -0
  7. data/lib/nanoc/base/entities/identifiable_collection.rb +11 -6
  8. data/lib/nanoc/base/entities/item_collection.rb +14 -0
  9. data/lib/nanoc/base/entities/layout_collection.rb +14 -0
  10. data/lib/nanoc/base/entities/outdatedness_reasons.rb +19 -0
  11. data/lib/nanoc/base/entities/props.rb +33 -10
  12. data/lib/nanoc/base/repos/aggregate_data_source.rb +2 -2
  13. data/lib/nanoc/base/repos/checksum_store.rb +1 -1
  14. data/lib/nanoc/base/repos/dependency_store.rb +25 -12
  15. data/lib/nanoc/base/services/dependency_tracker.rb +3 -2
  16. data/lib/nanoc/base/services/outdatedness_checker.rb +33 -10
  17. data/lib/nanoc/base/services/outdatedness_rules.rb +2 -0
  18. data/lib/nanoc/base/services/outdatedness_rules/item_collection_extended.rb +16 -0
  19. data/lib/nanoc/base/services/outdatedness_rules/layout_collection_extended.rb +16 -0
  20. data/lib/nanoc/base/services/pruner.rb +13 -1
  21. data/lib/nanoc/base/views/identifiable_collection_view.rb +24 -0
  22. data/lib/nanoc/cli/commands/show-data.rb +4 -0
  23. data/lib/nanoc/spec.rb +2 -2
  24. data/lib/nanoc/version.rb +1 -1
  25. data/spec/nanoc/base/checksummer_spec.rb +4 -4
  26. data/spec/nanoc/base/compiler_spec.rb +2 -2
  27. data/spec/nanoc/base/directed_graph_spec.rb +42 -0
  28. data/spec/nanoc/base/entities/identifiable_collection_spec.rb +110 -93
  29. data/spec/nanoc/base/entities/props_spec.rb +121 -1
  30. data/spec/nanoc/base/entities/site_spec.rb +2 -2
  31. data/spec/nanoc/base/repos/dependency_store_spec.rb +34 -40
  32. data/spec/nanoc/base/services/compiler/stages/calculate_checksums_spec.rb +2 -2
  33. data/spec/nanoc/base/services/compiler/stages/compile_reps_spec.rb +2 -2
  34. data/spec/nanoc/base/services/dependency_tracker_spec.rb +3 -4
  35. data/spec/nanoc/base/services/outdatedness_checker_spec.rb +290 -4
  36. data/spec/nanoc/base/services/outdatedness_rules_spec.rb +3 -3
  37. data/spec/nanoc/base/services/pruner_spec.rb +9 -0
  38. data/spec/nanoc/base/views/document_view_spec.rb +3 -4
  39. data/spec/nanoc/base/views/identifiable_collection_view_spec.rb +74 -7
  40. data/spec/nanoc/base/views/item_collection_with_reps_view_spec.rb +2 -1
  41. data/spec/nanoc/base/views/item_collection_without_reps_view_spec.rb +2 -1
  42. data/spec/nanoc/base/views/item_rep_view_spec.rb +3 -4
  43. data/spec/nanoc/base/views/item_view_spec.rb +5 -6
  44. data/spec/nanoc/base/views/layout_collection_view_spec.rb +2 -1
  45. data/spec/nanoc/base/views/mutable_identifiable_collection_view_spec.rb +1 -1
  46. data/spec/nanoc/base/views/mutable_item_collection_view_spec.rb +3 -2
  47. data/spec/nanoc/base/views/mutable_layout_collection_view_spec.rb +3 -2
  48. data/spec/nanoc/base/views/post_compile_item_rep_view_spec.rb +1 -1
  49. data/spec/nanoc/cli/commands/show_data_spec.rb +4 -4
  50. data/spec/nanoc/cli/commands/show_rules_spec.rb +2 -2
  51. data/spec/nanoc/helpers/rendering_spec.rb +5 -0
  52. data/spec/nanoc/rule_dsl/action_sequence_calculator_spec.rb +2 -2
  53. data/spec/nanoc/rule_dsl/rule_context_spec.rb +6 -4
  54. data/test/base/test_dependency_tracker.rb +22 -22
  55. data/test/base/test_item_array.rb +2 -2
  56. data/test/filters/test_xsl.rb +2 -2
  57. data/test/fixtures/vcr_cassettes/html_run_error.yml +17 -12
  58. data/test/fixtures/vcr_cassettes/html_run_ok.yml +17 -12
  59. data/test/helpers/test_blogging.rb +2 -2
  60. data/test/helpers/test_xml_sitemap.rb +7 -7
  61. metadata +6 -4
  62. data/lib/nanoc/base/core_ext/pathname.rb +0 -10
  63. data/test/extra/core_ext/test_pathname.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc70bc281ec6b39ca3de14107a1c2374d13e2807
4
- data.tar.gz: 0f16fd6cd14eb18bafd194ee2d0e0421fae93765
3
+ metadata.gz: d6113ac8b3ca8085d1247d4e880144d62af62037
4
+ data.tar.gz: 557d2ae493c26da9ebe71735fcb19da59f9aeba4
5
5
  SHA512:
6
- metadata.gz: ea4bfbf259d607544769201bf1cb950c5f8f9d4cbfe80aa48f53be0aaaa68db8b861949374a557700afb91193922c66dc62c16c510a2b2dddc99e4ea2d2b5858
7
- data.tar.gz: 3be571aecf8dbf7c742894aa1415726c915ee7eee916506f31f79c0d0ae986589176f76bfd90c8564111e11194fcd7ded82fe5aa1694340f4cddd9abf0facf10
6
+ metadata.gz: 7a0f046fd0f6b324dfea8451b0ab89142ff32a572e3e948cbf23e13337de70726097069101ed5484805d105f72ccd245ab8de0db2568f8310878e09eb083db4e
7
+ data.tar.gz: 06e63734588725c3260fa543ad6844fb94dd7369092bea7249415f1096f363eed66267b752b0cb309e52377d730737688ef8b445b5b19b4029ed13889e7c6894
data/NEWS.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.7.13 (2017-07-02)
4
+
5
+ Enhancements:
6
+
7
+ * Made adding an item or a layout only recompile items that likely depend on it (#1191, #1193)
8
+
3
9
  ## 4.7.12 (2017-06-23)
4
10
 
5
11
  Fixes:
@@ -2,5 +2,4 @@
2
2
 
3
3
  require 'nanoc/base/core_ext/array'
4
4
  require 'nanoc/base/core_ext/hash'
5
- require 'nanoc/base/core_ext/pathname'
6
5
  require 'nanoc/base/core_ext/string'
@@ -8,11 +8,14 @@ require_relative 'entities/content'
8
8
  require_relative 'entities/processing_action'
9
9
  require_relative 'entities/processing_actions'
10
10
 
11
+ require_relative 'entities/identifiable_collection'
12
+ require_relative 'entities/item_collection'
13
+ require_relative 'entities/layout_collection'
14
+
11
15
  require_relative 'entities/code_snippet'
12
16
  require_relative 'entities/configuration'
13
17
  require_relative 'entities/lazy_value'
14
18
  require_relative 'entities/document'
15
- require_relative 'entities/identifiable_collection'
16
19
  require_relative 'entities/item'
17
20
  require_relative 'entities/item_rep'
18
21
  require_relative 'entities/layout'
@@ -6,18 +6,19 @@ module Nanoc::Int
6
6
  class Dependency
7
7
  include Nanoc::Int::ContractsSupport
8
8
 
9
- C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration]
9
+ C_OBJ_FROM = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration, Nanoc::Int::IdentifiableCollection]
10
+ C_OBJ_TO = Nanoc::Int::Item
10
11
 
11
- contract C::None => C::Maybe[C_OBJ]
12
+ contract C::None => C::Maybe[C_OBJ_FROM]
12
13
  attr_reader :from
13
14
 
14
- contract C::None => C::Maybe[C_OBJ]
15
+ contract C::None => C::Maybe[C_OBJ_TO]
15
16
  attr_reader :to
16
17
 
17
18
  contract C::None => Nanoc::Int::Props
18
19
  attr_reader :props
19
20
 
20
- contract C::Maybe[C_OBJ], C::Maybe[C_OBJ], Nanoc::Int::Props => C::Any
21
+ contract C::Maybe[C_OBJ_FROM], C::Maybe[C_OBJ_TO], Nanoc::Int::Props => C::Any
21
22
  def initialize(from, to, props)
22
23
  @from = from
23
24
  @to = to
@@ -50,6 +50,18 @@ module Nanoc::Int
50
50
  invalidate_caches
51
51
  end
52
52
 
53
+ def inspect
54
+ s = []
55
+
56
+ @vertices.each_pair do |v1, _|
57
+ direct_successors_of(v1).each do |v2|
58
+ s << [v1.inspect + ' -> ' + v2.inspect + ' props=' + @edge_props[[v1, v2]].inspect]
59
+ end
60
+ end
61
+
62
+ self.class.to_s + '(' + s.join(', ') + ')'
63
+ end
64
+
53
65
  # @group Modifying the graph
54
66
 
55
67
  # Adds an edge from the first vertex to the second vertex.
@@ -12,14 +12,15 @@ module Nanoc::Int
12
12
  def_delegator :@objects, :each
13
13
  def_delegator :@objects, :size
14
14
 
15
- contract C::Or[Hash, C::Named['Nanoc::Int::Configuration']], C::IterOf[C::RespondTo[:identifier]] => C::Any
16
- def initialize(config, objects = [])
17
- @config = config
18
- @objects = Hamster::Vector.new(objects)
15
+ def initialize(*)
16
+ raise 'IdentifiableCollection is abstract and cannot be instantiated'
19
17
  end
20
18
 
21
- def self.from(enum, config)
22
- new(config, enum)
19
+ contract C::Or[Hash, C::Named['Nanoc::Int::Configuration']], C::IterOf[C::RespondTo[:identifier]], C::Maybe[String] => C::Any
20
+ def initialize_basic(config, objects = [], name = nil)
21
+ @config = config
22
+ @objects = Hamster::Vector.new(objects)
23
+ @name = name
23
24
  end
24
25
 
25
26
  contract C::None => self
@@ -58,14 +59,17 @@ module Nanoc::Int
58
59
  @objects.empty?
59
60
  end
60
61
 
62
+ contract C::RespondTo[:identifier] => self
61
63
  def add(obj)
62
64
  self.class.new(@config, @objects.add(obj))
63
65
  end
64
66
 
67
+ contract C::Func[C::RespondTo[:identifier] => C::Any] => self
65
68
  def reject(&block)
66
69
  self.class.new(@config, @objects.reject(&block))
67
70
  end
68
71
 
72
+ contract C::Any => C::Maybe[C::RespondTo[:identifier]]
69
73
  def object_with_identifier(identifier)
70
74
  if frozen?
71
75
  @mapping[identifier.to_s]
@@ -123,6 +127,7 @@ module Nanoc::Int
123
127
  @mapping.freeze
124
128
  end
125
129
 
130
+ contract C::None => C::Bool
126
131
  def use_globs?
127
132
  @config[:string_pattern_type] == 'glob'
128
133
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::Int
4
+ # @api private
5
+ class ItemCollection < IdentifiableCollection
6
+ def initialize(config, objects = [])
7
+ initialize_basic(config, objects, 'items')
8
+ end
9
+
10
+ def reference
11
+ :items
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc::Int
4
+ # @api private
5
+ class LayoutCollection < IdentifiableCollection
6
+ def initialize(config, objects = [])
7
+ initialize_basic(config, objects, 'layouts')
8
+ end
9
+
10
+ def reference
11
+ :layouts
12
+ end
13
+ end
14
+ end
@@ -46,6 +46,25 @@ module Nanoc::Int
46
46
  Props.new(raw_content: true, compiled_content: true),
47
47
  )
48
48
 
49
+ class DocumentCollectionExtended < Generic
50
+ attr_reader :objects
51
+
52
+ def initialize(objects)
53
+ super(
54
+ 'New items/layouts have been added to the site.',
55
+ Props.new(raw_content: true),
56
+ )
57
+
58
+ @objects = objects
59
+ end
60
+ end
61
+
62
+ class ItemCollectionExtended < DocumentCollectionExtended
63
+ end
64
+
65
+ class LayoutCollectionExtended < DocumentCollectionExtended
66
+ end
67
+
49
68
  class AttributesModified < Generic
50
69
  attr_reader :attributes
51
70
 
@@ -6,11 +6,13 @@ module Nanoc::Int
6
6
  include Nanoc::Int::ContractsSupport
7
7
 
8
8
  attr_reader :attributes
9
+ attr_reader :raw_content
9
10
 
11
+ # TODO: Split raw_content for documents and collections
12
+ C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
10
13
  C_ATTRS = C::Or[C::IterOf[Symbol], C::Bool]
11
- 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
14
+ 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
12
15
  def initialize(raw_content: false, attributes: false, compiled_content: false, path: false)
13
- @raw_content = raw_content
14
16
  @compiled_content = compiled_content
15
17
  @path = path
16
18
 
@@ -21,6 +23,14 @@ module Nanoc::Int
21
23
  else
22
24
  attributes
23
25
  end
26
+
27
+ @raw_content =
28
+ case raw_content
29
+ when Enumerable
30
+ Set.new(raw_content)
31
+ else
32
+ raw_content
33
+ end
24
34
  end
25
35
 
26
36
  contract C::None => String
@@ -37,7 +47,12 @@ module Nanoc::Int
37
47
 
38
48
  contract C::None => C::Bool
39
49
  def raw_content?
40
- @raw_content
50
+ case @raw_content
51
+ when Enumerable
52
+ @raw_content.any?
53
+ else
54
+ @raw_content
55
+ end
41
56
  end
42
57
 
43
58
  contract C::None => C::Bool
@@ -63,27 +78,35 @@ module Nanoc::Int
63
78
  contract Nanoc::Int::Props => Nanoc::Int::Props
64
79
  def merge(other)
65
80
  Props.new(
66
- raw_content: raw_content? || other.raw_content?,
81
+ raw_content: merge_raw_content(other),
67
82
  attributes: merge_attributes(other),
68
83
  compiled_content: compiled_content? || other.compiled_content?,
69
84
  path: path? || other.path?,
70
85
  )
71
86
  end
72
87
 
88
+ def merge_raw_content(other)
89
+ merge_prop(raw_content, other.raw_content)
90
+ end
91
+
73
92
  def merge_attributes(other)
74
- case attributes
93
+ merge_prop(attributes, other.attributes)
94
+ end
95
+
96
+ def merge_prop(own, other)
97
+ case own
75
98
  when true
76
99
  true
77
100
  when false
78
- other.attributes
101
+ other
79
102
  else
80
- case other.attributes
103
+ case other
81
104
  when true
82
105
  true
83
106
  when false
84
- attributes
107
+ own
85
108
  else
86
- attributes + other.attributes
109
+ own + other
87
110
  end
88
111
  end
89
112
  end
@@ -101,7 +124,7 @@ module Nanoc::Int
101
124
  contract C::None => Hash
102
125
  def to_h
103
126
  {
104
- raw_content: raw_content?,
127
+ raw_content: raw_content,
105
128
  attributes: attributes,
106
129
  compiled_content: compiled_content?,
107
130
  path: path?,
@@ -12,14 +12,14 @@ module Nanoc::Int
12
12
  def items
13
13
  @_items ||= begin
14
14
  objs = @data_sources.flat_map(&:items)
15
- Nanoc::Int::IdentifiableCollection.from(objs, @config)
15
+ Nanoc::Int::ItemCollection.new(@config, objs)
16
16
  end
17
17
  end
18
18
 
19
19
  def layouts
20
20
  @_layouts ||= begin
21
21
  objs = @data_sources.flat_map(&:layouts)
22
- Nanoc::Int::IdentifiableCollection.from(objs, @config)
22
+ Nanoc::Int::LayoutCollection.new(@config, objs)
23
23
  end
24
24
  end
25
25
  end
@@ -15,7 +15,7 @@ module Nanoc::Int
15
15
 
16
16
  contract C::KeywordArgs[site: C::Maybe[Nanoc::Int::Site], objects: C::IterOf[c_obj]] => C::Any
17
17
  def initialize(site: nil, objects:)
18
- super(Nanoc::Int::Store.tmp_path_for(site: site, store_name: 'checksums'), 1)
18
+ super(Nanoc::Int::Store.tmp_path_for(site: site, store_name: 'checksums'), 2)
19
19
 
20
20
  @objects = objects
21
21
 
@@ -8,8 +8,9 @@ module Nanoc::Int
8
8
  attr_accessor :items
9
9
  attr_accessor :layouts
10
10
 
11
+ contract Nanoc::Int::ItemCollection, Nanoc::Int::LayoutCollection, Nanoc::Int::Configuration, C::KeywordArgs[site: C::Optional[C::Maybe[Nanoc::Int::Site]]] => C::Any
11
12
  def initialize(items, layouts, config, site: nil)
12
- super(Nanoc::Int::Store.tmp_path_for(site: site, store_name: 'dependencies'), 4)
13
+ super(Nanoc::Int::Store.tmp_path_for(site: site, store_name: 'dependencies'), 5)
13
14
 
14
15
  @items = items
15
16
  @layouts = layouts
@@ -18,14 +19,17 @@ module Nanoc::Int
18
19
  items.each { |o| add_vertex_for(o) }
19
20
  layouts.each { |o| add_vertex_for(o) }
20
21
  add_vertex_for(config)
22
+ add_vertex_for(items)
23
+ add_vertex_for(layouts)
21
24
 
22
25
  @new_objects = []
23
26
  @graph = Nanoc::Int::DirectedGraph.new([nil] + objs2refs(@items) + objs2refs(@layouts))
24
27
  end
25
28
 
26
- # FIXME: ItemRep?
27
- C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout, Nanoc::Int::Configuration]
28
- contract C_OBJ => C::ArrayOf[Nanoc::Int::Dependency]
29
+ C_OBJ_SRC = Nanoc::Int::Item
30
+ C_OBJ_DST = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration, Nanoc::Int::IdentifiableCollection]
31
+
32
+ contract C_OBJ_SRC => C::ArrayOf[Nanoc::Int::Dependency]
29
33
  def dependencies_causing_outdatedness_of(object)
30
34
  objects_causing_outdatedness_of(object).map do |other_object|
31
35
  props = props_for(other_object, object)
@@ -46,11 +50,21 @@ module Nanoc::Int
46
50
  def items=(items)
47
51
  @items = items
48
52
  items.each { |o| @refs2objs[obj2ref(o)] = o }
53
+ add_vertex_for(items)
49
54
  end
50
55
 
51
56
  def layouts=(layouts)
52
57
  @layouts = layouts
53
58
  layouts.each { |o| @refs2objs[obj2ref(o)] = o }
59
+ add_vertex_for(layouts)
60
+ end
61
+
62
+ def new_items
63
+ @new_objects.select { |o| o.is_a?(Nanoc::Int::Item) }
64
+ end
65
+
66
+ def new_layouts
67
+ @new_objects.select { |o| o.is_a?(Nanoc::Int::Layout) }
54
68
  end
55
69
 
56
70
  # Returns the direct dependencies for the given object.
@@ -71,15 +85,14 @@ module Nanoc::Int
71
85
  # predecessors of
72
86
  # the given object
73
87
  def objects_causing_outdatedness_of(object)
74
- if @new_objects.any?
75
- [@new_objects.first]
76
- else
77
- refs2objs(@graph.direct_predecessors_of(obj2ref(object)))
78
- end
88
+ refs2objs(@graph.direct_predecessors_of(obj2ref(object)))
79
89
  end
80
90
 
91
+ C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
81
92
  C_ATTR = C::Or[C::IterOf[Symbol], C::Bool]
82
- contract C::Maybe[C_OBJ], C::Maybe[C_OBJ], 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::Any
93
+ 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]]
94
+
95
+ contract C::Maybe[C_OBJ_SRC], C::Maybe[C_OBJ_DST], C_KEYWORD_PROPS => C::Any
83
96
  # Records a dependency from `src` to `dst` in the dependency graph. When
84
97
  # `dst` is oudated, `src` will also become outdated.
85
98
  #
@@ -92,7 +105,7 @@ module Nanoc::Int
92
105
  #
93
106
  # @return [void]
94
107
  def record_dependency(src, dst, raw_content: false, attributes: false, compiled_content: false, path: false)
95
- # TODO: do src == dst check first (faster)
108
+ return if src == dst
96
109
 
97
110
  add_vertex_for(src)
98
111
  add_vertex_for(dst)
@@ -104,7 +117,7 @@ module Nanoc::Int
104
117
  new_props = Nanoc::Int::Props.new(raw_content: raw_content, attributes: attributes, compiled_content: compiled_content, path: path)
105
118
  props = existing_props.merge(new_props)
106
119
 
107
- @graph.add_edge(dst_ref, src_ref, props: props.to_h) unless src == dst
120
+ @graph.add_edge(dst_ref, src_ref, props: props.to_h)
108
121
  end
109
122
 
110
123
  def add_vertex_for(o)
@@ -5,9 +5,10 @@ module Nanoc::Int
5
5
  class DependencyTracker
6
6
  include Nanoc::Int::ContractsSupport
7
7
 
8
- C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration]
8
+ C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::Layout, Nanoc::Int::Configuration, Nanoc::Int::IdentifiableCollection]
9
+ C_RAW_CONTENT = C::Or[C::IterOf[C::Or[String, Regexp]], C::Bool]
9
10
  C_ATTR = C::Or[C::IterOf[Symbol], C::Bool]
10
- 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]]
11
+ 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]]
11
12
 
12
13
  class Null
13
14
  include Nanoc::Int::ContractsSupport
@@ -35,7 +35,17 @@ module Nanoc::Int
35
35
  Rules::AttributesModified,
36
36
  ].freeze
37
37
 
38
- C_OBJ_MAYBE_REP = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Configuration, Nanoc::Int::Layout]
38
+ RULES_FOR_ITEM_COLLECTION =
39
+ [
40
+ Rules::ItemCollectionExtended,
41
+ ].freeze
42
+
43
+ RULES_FOR_LAYOUT_COLLECTION =
44
+ [
45
+ Rules::LayoutCollectionExtended,
46
+ ].freeze
47
+
48
+ C_OBJ_MAYBE_REP = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Configuration, Nanoc::Int::Layout, Nanoc::Int::ItemCollection, Nanoc::Int::LayoutCollection]
39
49
 
40
50
  contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Int::ItemRepRepo] => C::Any
41
51
  def initialize(outdatedness_checker:, reps:)
@@ -54,6 +64,10 @@ module Nanoc::Int
54
64
  apply_rules(RULES_FOR_LAYOUT, obj)
55
65
  when Nanoc::Int::Configuration
56
66
  apply_rules(RULES_FOR_CONFIG, obj)
67
+ when Nanoc::Int::ItemCollection
68
+ apply_rules(RULES_FOR_ITEM_COLLECTION, obj)
69
+ when Nanoc::Int::LayoutCollection
70
+ apply_rules(RULES_FOR_LAYOUT_COLLECTION, obj)
57
71
  else
58
72
  raise Nanoc::Int::Errors::InternalInconsistency, "do not know how to check outdatedness of #{obj.inspect}"
59
73
  end
@@ -96,7 +110,8 @@ module Nanoc::Int
96
110
 
97
111
  Reasons = Nanoc::Int::OutdatednessReasons
98
112
 
99
- C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Configuration, Nanoc::Int::Layout]
113
+ C_OBJ = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Configuration, Nanoc::Int::Layout, Nanoc::Int::ItemCollection]
114
+ C_ITEM_OR_REP = C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep]
100
115
  C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Int::ActionSequence]
101
116
 
102
117
  contract C::KeywordArgs[site: Nanoc::Int::Site, checksum_store: Nanoc::Int::ChecksumStore, checksums: Nanoc::Int::ChecksumCollection, dependency_store: Nanoc::Int::DependencyStore, action_sequence_store: Nanoc::Int::ActionSequenceStore, action_sequences: C_ACTION_SEQUENCES, reps: Nanoc::Int::ItemRepRepo] => C::Any
@@ -140,7 +155,7 @@ module Nanoc::Int
140
155
  @_basic ||= Basic.new(outdatedness_checker: self, reps: @reps)
141
156
  end
142
157
 
143
- contract C_OBJ, Hamster::Set => C::Bool
158
+ contract C_ITEM_OR_REP, Hamster::Set => C::Bool
144
159
  def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
145
160
  # Convert from rep to item if necessary
146
161
  obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
@@ -176,19 +191,27 @@ module Nanoc::Int
176
191
  status = basic.outdatedness_status_for(dependency.from)
177
192
 
178
193
  active = status.props.active & dependency.props.active
179
- if attributes_unaffected?(status, dependency)
180
- active.delete(:attributes)
181
- end
194
+ active.delete(:attributes) if attributes_unaffected?(status, dependency)
195
+ active.delete(:raw_content) if raw_content_unaffected?(status, dependency)
182
196
 
183
197
  active.any?
184
198
  end
185
199
 
186
200
  def attributes_unaffected?(status, dependency)
187
- attr_reason = status.reasons.find do |r|
188
- r.is_a?(Nanoc::Int::OutdatednessReasons::AttributesModified)
189
- end
201
+ reason = status.reasons.find { |r| r.is_a?(Nanoc::Int::OutdatednessReasons::AttributesModified) }
202
+ reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & reason.attributes).empty?
203
+ end
190
204
 
191
- attr_reason && dependency.props.attributes.is_a?(Enumerable) && (dependency.props.attributes & attr_reason.attributes).empty?
205
+ def raw_content_unaffected?(status, dependency)
206
+ reason = status.reasons.find { |r| r.is_a?(Nanoc::Int::OutdatednessReasons::DocumentCollectionExtended) }
207
+ if reason.nil?
208
+ false
209
+ elsif !dependency.props.raw_content.is_a?(Enumerable)
210
+ false
211
+ else
212
+ patterns = dependency.props.raw_content.map { |r| Nanoc::Int::Pattern.from(r) }
213
+ patterns.none? { |pat| reason.objects.any? { |obj| pat.match?(obj.identifier) } }
214
+ end
192
215
  end
193
216
  end
194
217
  end