nanoc-core 4.12.9 → 4.12.11

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nanoc/core/action_sequence.rb +1 -4
  3. data/lib/nanoc/core/action_sequence_builder.rb +8 -10
  4. data/lib/nanoc/core/basic_item_view.rb +1 -1
  5. data/lib/nanoc/core/basic_outdatedness_checker.rb +135 -0
  6. data/lib/nanoc/core/checksum_collection.rb +3 -1
  7. data/lib/nanoc/core/checksum_store.rb +11 -1
  8. data/lib/nanoc/core/compilation_stages/determine_outdatedness.rb +6 -1
  9. data/lib/nanoc/core/compilation_stages/load_stores.rb +5 -12
  10. data/lib/nanoc/core/configuration.rb +15 -0
  11. data/lib/nanoc/core/dependency_props.rb +2 -2
  12. data/lib/nanoc/core/dependency_store.rb +15 -13
  13. data/lib/nanoc/core/document.rb +5 -1
  14. data/lib/nanoc/core/executor.rb +2 -1
  15. data/lib/nanoc/core/identifiable_collection.rb +16 -33
  16. data/lib/nanoc/core/identifiable_collection_view.rb +54 -10
  17. data/lib/nanoc/core/item.rb +1 -1
  18. data/lib/nanoc/core/item_collection.rb +0 -6
  19. data/lib/nanoc/core/item_rep.rb +18 -0
  20. data/lib/nanoc/core/item_rep_builder.rb +4 -4
  21. data/lib/nanoc/core/item_rep_selector.rb +69 -17
  22. data/lib/nanoc/core/layout.rb +1 -1
  23. data/lib/nanoc/core/layout_collection.rb +0 -6
  24. data/lib/nanoc/core/mutable_document_view_mixin.rb +16 -7
  25. data/lib/nanoc/core/outdatedness_checker.rb +57 -123
  26. data/lib/nanoc/core/outdatedness_rule.rb +31 -3
  27. data/lib/nanoc/core/outdatedness_rules/attributes_modified.rb +6 -6
  28. data/lib/nanoc/core/outdatedness_rules/code_snippets_modified.rb +6 -6
  29. data/lib/nanoc/core/outdatedness_rules/content_modified.rb +3 -3
  30. data/lib/nanoc/core/outdatedness_rules/item_added.rb +3 -3
  31. data/lib/nanoc/core/outdatedness_rules/layout_added.rb +3 -3
  32. data/lib/nanoc/core/outdatedness_rules/not_written.rb +9 -9
  33. data/lib/nanoc/core/outdatedness_rules/rules_modified.rb +12 -10
  34. data/lib/nanoc/core/outdatedness_rules/uses_always_outdated_filter.rb +2 -2
  35. data/lib/nanoc/core/outdatedness_status.rb +6 -1
  36. data/lib/nanoc/core/store.rb +56 -23
  37. data/lib/nanoc/core/version.rb +1 -1
  38. data/lib/nanoc/core.rb +8 -0
  39. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 835f6e4a74f07183143caa0c2d27f304fa5a712c3231dddc2dd28974f714b117
4
- data.tar.gz: 71331c08284b2f0da1b6410c8a5c17230efa1606640c1ca7e8724fb97e037951
3
+ metadata.gz: d7fc026255728ddb8de8c0756afdfbdc7548e47938d9c4fb464023c0811ae4e2
4
+ data.tar.gz: a8531e9c3920f81c9caa88996b1f195eedec21ef392cda04cd2fcf76c0f83106
5
5
  SHA512:
6
- metadata.gz: 1c3b6430105aab73a5e9efff550b3519bd3e19cf99cb9b70f7a3b42916fd48becc091ea7325740673936373650e0d2950f986e507128aa1a7156f0e95bbb1520
7
- data.tar.gz: 24ef14012f2ed47f9dd77f885601c395b58fb4d50fb61afa2196fedeecac2571d3702ad87f0e7d80d8806a07b6712364774cf6012650595f4a0cae072e85353e
6
+ metadata.gz: f819fe95a0d25831e51ba4ce360ca76921c01517402bf67d3407d369b1fc8c7f05a7262fe0df1dbfd03e3c3f12d72281d818db19746f294f135e8b017a21a07d
7
+ data.tar.gz: c0de0aef8f83c0a7723d755bf804a25f3ca574e308b54b36cac6e0a91ac5b0b2c4658635cd899d18fdc830d51250a151cbb23d69dfdd4b4abd1cde5b12c96a2c
@@ -7,11 +7,9 @@ module Nanoc
7
7
  include Enumerable
8
8
  prepend MemoWise
9
9
 
10
- attr_reader :item_rep
11
10
  attr_reader :actions
12
11
 
13
- def initialize(item_rep, actions: [])
14
- @item_rep = item_rep
12
+ def initialize(actions: [])
15
13
  @actions = actions
16
14
  end
17
15
 
@@ -54,7 +52,6 @@ module Nanoc
54
52
  # contract C::Func[Nanoc::Core::ProcessingAction => C::Any] => self
55
53
  def map(&block)
56
54
  self.class.new(
57
- @item_rep,
58
55
  actions: @actions.map(&block),
59
56
  )
60
57
  end
@@ -15,14 +15,13 @@ module Nanoc
15
15
  end
16
16
  end
17
17
 
18
- def self.build(rep)
19
- builder = new(rep)
18
+ def self.build
19
+ builder = new
20
20
  yield(builder)
21
21
  builder.action_sequence
22
22
  end
23
23
 
24
- def initialize(item_rep)
25
- @item_rep = item_rep
24
+ def initialize
26
25
  @actions = []
27
26
  end
28
27
 
@@ -38,24 +37,23 @@ module Nanoc
38
37
  self
39
38
  end
40
39
 
41
- contract Symbol, C::Maybe[String] => self
42
- def add_snapshot(snapshot_name, path)
43
- will_add_snapshot(snapshot_name)
40
+ def add_snapshot(snapshot_name, path, rep)
41
+ will_add_snapshot(snapshot_name, rep)
44
42
  @actions << Nanoc::Core::ProcessingActions::Snapshot.new([snapshot_name], path ? [path] : [])
45
43
  self
46
44
  end
47
45
 
48
46
  contract C::None => Nanoc::Core::ActionSequence
49
47
  def action_sequence
50
- Nanoc::Core::ActionSequence.new(@item_rep, actions: @actions)
48
+ Nanoc::Core::ActionSequence.new(actions: @actions)
51
49
  end
52
50
 
53
51
  private
54
52
 
55
- def will_add_snapshot(name)
53
+ def will_add_snapshot(name, rep)
56
54
  @_snapshot_names ||= Set.new
57
55
  if @_snapshot_names.include?(name)
58
- raise CannotCreateMultipleSnapshotsWithSameNameError.new(@item_rep, name)
56
+ raise CannotCreateMultipleSnapshotsWithSameNameError.new(rep, name)
59
57
  else
60
58
  @_snapshot_names << name
61
59
  end
@@ -34,7 +34,7 @@ module Nanoc
34
34
  parent_identifier = '/' + _unwrap.identifier.components[0..-2].join('/') + '/'
35
35
  parent_identifier = '/' if parent_identifier == '//'
36
36
 
37
- parent = @context.items[parent_identifier]
37
+ parent = @context.items.object_with_identifier(parent_identifier)
38
38
 
39
39
  parent && self.class.new(parent, @context)
40
40
  end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Nanoc
4
+ module Core
5
+ class BasicOutdatednessChecker
6
+ include Nanoc::Core::ContractsSupport
7
+
8
+ attr_reader :site
9
+ attr_reader :checksum_store
10
+ attr_reader :checksums
11
+ attr_reader :dependency_store
12
+ attr_reader :action_sequence_store
13
+ attr_reader :action_sequences
14
+
15
+ Rules = Nanoc::Core::OutdatednessRules
16
+
17
+ RULES_FOR_ITEM_REP =
18
+ [
19
+ Rules::ItemAdded,
20
+ Rules::RulesModified,
21
+ Rules::ContentModified,
22
+ Rules::AttributesModified,
23
+ Rules::NotWritten,
24
+ Rules::CodeSnippetsModified,
25
+ Rules::UsesAlwaysOutdatedFilter,
26
+ ].freeze
27
+
28
+ RULES_FOR_LAYOUT =
29
+ [
30
+ Rules::LayoutAdded,
31
+ Rules::RulesModified,
32
+ Rules::ContentModified,
33
+ Rules::AttributesModified,
34
+ Rules::UsesAlwaysOutdatedFilter,
35
+ ].freeze
36
+
37
+ RULES_FOR_CONFIG =
38
+ [
39
+ Rules::AttributesModified,
40
+ ].freeze
41
+
42
+ C_OBJ = C::Or[
43
+ Nanoc::Core::Item,
44
+ Nanoc::Core::ItemRep,
45
+ Nanoc::Core::Configuration,
46
+ Nanoc::Core::Layout,
47
+ Nanoc::Core::ItemCollection,
48
+ ]
49
+
50
+ C_OBJ_MAYBE_REP = C::Or[
51
+ Nanoc::Core::Item,
52
+ Nanoc::Core::ItemRep,
53
+ Nanoc::Core::Configuration,
54
+ Nanoc::Core::Layout,
55
+ Nanoc::Core::ItemCollection,
56
+ Nanoc::Core::LayoutCollection,
57
+ ]
58
+
59
+ C_ACTION_SEQUENCES = C::HashOf[C_OBJ => Nanoc::Core::ActionSequence]
60
+
61
+ contract C::KeywordArgs[
62
+ site: Nanoc::Core::Site,
63
+ checksum_store: Nanoc::Core::ChecksumStore,
64
+ checksums: Nanoc::Core::ChecksumCollection,
65
+ dependency_store: Nanoc::Core::DependencyStore,
66
+ action_sequence_store: Nanoc::Core::ActionSequenceStore,
67
+ action_sequences: C_ACTION_SEQUENCES,
68
+ reps: Nanoc::Core::ItemRepRepo,
69
+ ] => C::Any
70
+ def initialize(site:, checksum_store:, checksums:, dependency_store:, action_sequence_store:, action_sequences:, reps:)
71
+ @reps = reps
72
+ @site = site
73
+ @checksum_store = checksum_store
74
+ @checksums = checksums
75
+ @dependency_store = dependency_store
76
+ @action_sequence_store = action_sequence_store
77
+ @action_sequences = action_sequences
78
+
79
+ # Memoize
80
+ @_outdatedness_status_for = {}
81
+ end
82
+
83
+ contract C_OBJ_MAYBE_REP => C::Maybe[Nanoc::Core::OutdatednessStatus]
84
+ def outdatedness_status_for(obj)
85
+ # TODO: remove memoization (no longer needed)
86
+ @_outdatedness_status_for[obj] ||=
87
+ case obj
88
+ when Nanoc::Core::ItemRep
89
+ apply_rules(RULES_FOR_ITEM_REP, obj)
90
+ when Nanoc::Core::Item
91
+ apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
92
+ when Nanoc::Core::Layout
93
+ apply_rules(RULES_FOR_LAYOUT, obj)
94
+ when Nanoc::Core::Configuration
95
+ apply_rules(RULES_FOR_CONFIG, obj)
96
+ when Nanoc::Core::ItemCollection, Nanoc::Core::LayoutCollection
97
+ # Collections are never outdated. Objects inside them might be,
98
+ # however.
99
+ apply_rules([], obj)
100
+ else
101
+ raise Nanoc::Core::Errors::InternalInconsistency, "do not know how to check outdatedness of #{obj.inspect}"
102
+ end
103
+ end
104
+
105
+ def action_sequence_for(rep)
106
+ @action_sequences.fetch(rep)
107
+ end
108
+
109
+ private
110
+
111
+ contract C::ArrayOf[Class], C_OBJ_MAYBE_REP, Nanoc::Core::OutdatednessStatus => C::Maybe[Nanoc::Core::OutdatednessStatus]
112
+ def apply_rules(rules, obj, status = Nanoc::Core::OutdatednessStatus.new)
113
+ rules.inject(status) do |acc, rule|
114
+ if acc.useful_to_apply?(rule)
115
+ reason = rule.instance.call(obj, self)
116
+ if reason
117
+ acc.update(reason)
118
+ else
119
+ acc
120
+ end
121
+ else
122
+ acc
123
+ end
124
+ end
125
+ end
126
+
127
+ contract C::ArrayOf[Class], C::ArrayOf[C_OBJ_MAYBE_REP] => C::Maybe[Nanoc::Core::OutdatednessStatus]
128
+ def apply_rules_multi(rules, objs)
129
+ objs.inject(Nanoc::Core::OutdatednessStatus.new) do |acc, elem|
130
+ apply_rules(rules, elem, acc)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -9,6 +9,8 @@ module Nanoc
9
9
 
10
10
  def initialize(checksums)
11
11
  @checksums = checksums
12
+
13
+ @_attribute_checksums = {}
12
14
  end
13
15
 
14
16
  contract c_obj => C::Maybe[String]
@@ -23,7 +25,7 @@ module Nanoc
23
25
 
24
26
  contract c_obj => C::Maybe[C::HashOf[Symbol, String]]
25
27
  def attributes_checksum_for(obj)
26
- @checksums[[obj.reference, :each_attribute]]
28
+ @_attribute_checksums[obj] ||= @checksums[[obj.reference, :each_attribute]]
27
29
  end
28
30
 
29
31
  def to_h
@@ -21,6 +21,8 @@ module Nanoc
21
21
  @objects = objects
22
22
 
23
23
  @checksums = {}
24
+
25
+ invalidate_memoization
24
26
  end
25
27
 
26
28
  contract c_obj => C::Maybe[String]
@@ -50,7 +52,7 @@ module Nanoc
50
52
 
51
53
  contract c_obj => C::Maybe[C::HashOf[Symbol, String]]
52
54
  def attributes_checksum_for(obj)
53
- @checksums[[obj.reference, :each_attribute]]
55
+ @_attribute_checksums[obj] ||= @checksums[[obj.reference, :each_attribute]]
54
56
  end
55
57
 
56
58
  protected
@@ -60,6 +62,8 @@ module Nanoc
60
62
  end
61
63
 
62
64
  def data=(new_data)
65
+ invalidate_memoization
66
+
63
67
  references = Set.new(@objects.map(&:reference))
64
68
 
65
69
  @checksums = {}
@@ -69,6 +73,12 @@ module Nanoc
69
73
  end
70
74
  end
71
75
  end
76
+
77
+ private
78
+
79
+ def invalidate_memoization
80
+ @_attribute_checksums = {}
81
+ end
72
82
  end
73
83
  end
74
84
  end
@@ -41,7 +41,12 @@ module Nanoc
41
41
  end
42
42
 
43
43
  def outdated?(rep)
44
- @outdatedness_store.include?(rep) || @outdatedness_checker.outdated?(rep)
44
+ if @outdatedness_store.include?(rep)
45
+ # We determined previously that this rep is outdated.
46
+ true
47
+ else
48
+ !@outdatedness_checker.outdatedness_reasons_for(rep).empty?
49
+ end
45
50
  end
46
51
  end
47
52
  end
@@ -16,18 +16,11 @@ module Nanoc
16
16
 
17
17
  contract C::None => C::Any
18
18
  def run
19
- load_store(@checksum_store)
20
- load_store(@compiled_content_cache)
21
- load_store(@dependency_store)
22
- load_store(@action_sequence_store)
23
- load_store(@outdatedness_store)
24
- end
25
-
26
- contract Nanoc::Core::Store => C::Any
27
- def load_store(store)
28
- Nanoc::Core::Instrumentor.call(:store_loaded, store.class) do
29
- store.load
30
- end
19
+ @checksum_store.load
20
+ @compiled_content_cache.load
21
+ @dependency_store.load
22
+ @action_sequence_store.load
23
+ @outdatedness_store.load
31
24
  end
32
25
  end
33
26
  end
@@ -176,6 +176,21 @@ module Nanoc
176
176
  "<#{self.class}>"
177
177
  end
178
178
 
179
+ contract C::None => C::Num
180
+ def hash
181
+ [@dir, @env_name].hash
182
+ end
183
+
184
+ contract C::Any => C::Bool
185
+ def ==(other)
186
+ eql?(other)
187
+ end
188
+
189
+ contract C::Any => C::Bool
190
+ def eql?(other)
191
+ other.is_a?(self.class) && @dir == other.dir && @env_name == other.env_name
192
+ end
193
+
179
194
  private
180
195
 
181
196
  def make_absolute(path)
@@ -111,7 +111,7 @@ module Nanoc
111
111
  def raw_content?
112
112
  case @raw_content
113
113
  when Set
114
- @raw_content.any?
114
+ !@raw_content.empty?
115
115
  else
116
116
  @raw_content
117
117
  end
@@ -121,7 +121,7 @@ module Nanoc
121
121
  def attributes?
122
122
  case @attributes
123
123
  when Set
124
- @attributes.any?
124
+ !@attributes.empty?
125
125
  else
126
126
  @attributes
127
127
  end
@@ -71,12 +71,7 @@ module Nanoc
71
71
  Nanoc::Core::Dependency.new(
72
72
  other_object,
73
73
  object,
74
- Nanoc::Core::DependencyProps.new(
75
- raw_content: props.fetch(:raw_content, false),
76
- attributes: props.fetch(:attributes, false),
77
- compiled_content: props.fetch(:compiled_content, false),
78
- path: props.fetch(:path, false),
79
- ),
74
+ props,
80
75
  )
81
76
  end
82
77
  end
@@ -192,15 +187,22 @@ module Nanoc
192
187
  refs.map { |r| ref2obj(r) }
193
188
  end
194
189
 
195
- # TODO: Return not a Hash, but a DependencyProps instead
196
190
  def props_for(from, to)
197
191
  props = @graph.props_for(obj2ref(from), obj2ref(to))
198
-
199
- if props&.active&.any?
200
- props.to_h
201
- else
202
- { raw_content: true, attributes: true, compiled_content: true, path: true }
203
- end
192
+ return props if props
193
+
194
+ # This is for backwards compatibility, in case there are no dependency
195
+ # props available yet. Pretend everything is set to `true`; it’ll be
196
+ # recompiled and correct props will be available in the next run.
197
+ #
198
+ # NOTE: This isn’t covered by tests, yet. (Not trivial to test because
199
+ # it’s not a condition that can arise in the current Nanoc version.)
200
+ Nanoc::Core::DependencyProps.new(
201
+ raw_content: true,
202
+ attributes: true,
203
+ compiled_content: true,
204
+ path: true,
205
+ )
204
206
  end
205
207
 
206
208
  def data
@@ -49,6 +49,10 @@ module Nanoc
49
49
  @checksum_data = checksum_data
50
50
  @content_checksum_data = content_checksum_data
51
51
  @attributes_checksum_data = attributes_checksum_data
52
+
53
+ # Precalculate for performance
54
+ @hash = [self.class, identifier].hash
55
+ reference
52
56
  end
53
57
 
54
58
  # @return [Hash]
@@ -107,7 +111,7 @@ module Nanoc
107
111
 
108
112
  contract C::None => C::Num
109
113
  def hash
110
- [self.class, identifier].hash
114
+ @hash
111
115
  end
112
116
 
113
117
  contract C::Any => C::Bool
@@ -90,10 +90,11 @@ module Nanoc
90
90
 
91
91
  def find_layout(arg)
92
92
  req_id = arg.__nanoc_cleaned_identifier
93
- layout = layouts[req_id]
93
+ layout = layouts.object_with_identifier(req_id)
94
94
  return layout if layout
95
95
 
96
96
  if use_globs?
97
+ # TODO: use object_matching_glob instead
97
98
  pat = Nanoc::Core::Pattern.from(arg)
98
99
  layout = layouts.find { |l| pat.match?(l.identifier) }
99
100
  return layout if layout
@@ -37,15 +37,6 @@ module Nanoc
37
37
  super
38
38
  end
39
39
 
40
- # contract C::Any => C::Maybe[C::RespondTo[:identifier]]
41
- def [](arg)
42
- if frozen?
43
- get_memoized(arg)
44
- else
45
- get_unmemoized(arg)
46
- end
47
- end
48
-
49
40
  # contract C::Any => C::IterOf[C::RespondTo[:identifier]]
50
41
  def find_all(arg)
51
42
  if frozen?
@@ -75,7 +66,6 @@ module Nanoc
75
66
  self.class.new(@config, @objects.reject(&block))
76
67
  end
77
68
 
78
- # contract C::Any => C::Maybe[C::RespondTo[:identifier]]
79
69
  def object_with_identifier(identifier)
80
70
  if frozen?
81
71
  @mapping[identifier.to_s]
@@ -84,26 +74,26 @@ module Nanoc
84
74
  end
85
75
  end
86
76
 
87
- protected
88
-
89
- # contract C::Any => C::Maybe[C::RespondTo[:identifier]]
90
- def get_unmemoized(arg)
91
- case arg
92
- when Nanoc::Core::Identifier
93
- object_with_identifier(arg)
94
- when String
95
- object_with_identifier(arg) || object_matching_glob(arg)
96
- when Regexp
97
- find { |i| i.identifier.to_s =~ arg }
77
+ def object_matching_glob(glob)
78
+ if frozen?
79
+ object_matching_glob_memoized(glob)
98
80
  else
99
- raise ArgumentError, "don’t know how to fetch objects by #{arg.inspect}"
81
+ object_matching_glob_unmemoized(glob)
100
82
  end
101
83
  end
102
84
 
103
- # contract C::Any => C::Maybe[C::RespondTo[:identifier]]
104
- def get_memoized(_arg)
105
- # TODO: Figure out how to get memo_wise to work with subclasses
106
- raise 'implement in subclasses'
85
+ protected
86
+
87
+ def object_matching_glob_memoized(glob)
88
+ object_matching_glob_unmemoized(glob)
89
+ end
90
+ memo_wise :object_matching_glob_memoized
91
+
92
+ def object_matching_glob_unmemoized(glob)
93
+ if use_globs?
94
+ pat = Pattern.from(glob)
95
+ find { |i| pat.match?(i.identifier) }
96
+ end
107
97
  end
108
98
 
109
99
  # contract C::Any => C::IterOf[C::RespondTo[:identifier]]
@@ -118,13 +108,6 @@ module Nanoc
118
108
  end
119
109
  memo_wise :find_all_memoized
120
110
 
121
- def object_matching_glob(glob)
122
- if use_globs?
123
- pat = Pattern.from(glob)
124
- find { |i| pat.match?(i.identifier) }
125
- end
126
- end
127
-
128
111
  def build_mapping
129
112
  @mapping = {}
130
113
  each do |object|
@@ -120,19 +120,63 @@ module Nanoc
120
120
  #
121
121
  # @return [#identifier] if an object was found
122
122
  def [](arg)
123
- prop_attribute =
124
- case arg
125
- when String, Nanoc::Core::Identifier
126
- [arg.to_s]
127
- when Regexp
128
- [arg]
123
+ # The argument to #[] fall in two categories: exact matches, and
124
+ # patterns. An example of an exact match is '/about.md', while an
125
+ # example of a pattern is '/about.*'.
126
+ #
127
+ # If a Nanoc::Core::Identifier is given, we know it will need to be an
128
+ # exact match. If a String is given, it could be either. If a Regexp is
129
+ # given, we know it’s a pattern match.
130
+ #
131
+ # If we have a pattern match, create a dependency on the item
132
+ # collection, with a `raw_content` property that contains the pattern.
133
+ # If we have an exact match, do nothing -- there is no reason to create
134
+ # a dependency on the item itself, because accessing that item
135
+ # (attributes, compiled content, …) will create the dependency later.
136
+
137
+ object_from_exact_match = nil
138
+ object_from_pattern_match = nil
139
+
140
+ case arg
141
+ when Nanoc::Core::Identifier
142
+ # Can only be an exact match
143
+ object_from_exact_match = @objects.object_with_identifier(arg)
144
+ when String
145
+ # Can be an exact match, or a pattern match
146
+ tmp = @objects.object_with_identifier(arg)
147
+ if tmp
148
+ object_from_exact_match = tmp
129
149
  else
130
- true
150
+ object_from_pattern_match = @objects.object_matching_glob(arg)
131
151
  end
152
+ when Regexp
153
+ # Can only be a pattern match
154
+ object_from_pattern_match = @objects.find { |i| i.identifier.to_s =~ arg }
155
+ else
156
+ raise ArgumentError, "Unexpected argument #{arg.class} to []: Can only pass strings, identfiers, and regular expressions"
157
+ end
132
158
 
133
- @context.dependency_tracker.bounce(_unwrap, raw_content: prop_attribute)
134
- res = @objects[arg]
135
- res && view_class.new(res, @context)
159
+ unless object_from_exact_match
160
+ # We got this object from a pattern match. Create a dependency with
161
+ # this pattern, because if the objects matching this pattern change,
162
+ # then the result of #[] will change too.
163
+ #
164
+ # NOTE: object_from_exact_match can also be nil, but in that case
165
+ # we still need to create a dependency.
166
+
167
+ prop_attribute =
168
+ case arg
169
+ when Identifier
170
+ [arg.to_s]
171
+ when String, Regexp
172
+ [arg]
173
+ end
174
+
175
+ @context.dependency_tracker.bounce(_unwrap, raw_content: prop_attribute)
176
+ end
177
+
178
+ object = object_from_exact_match || object_from_pattern_match
179
+ object && view_class.new(object, @context)
136
180
  end
137
181
  end
138
182
  end
@@ -4,7 +4,7 @@ module Nanoc
4
4
  module Core
5
5
  class Item < ::Nanoc::Core::Document
6
6
  def reference
7
- "item:#{identifier}"
7
+ @_reference ||= "item:#{identifier}"
8
8
  end
9
9
  end
10
10
  end
@@ -9,12 +9,6 @@ module Nanoc
9
9
  initialize_basic(config, objects, 'items')
10
10
  end
11
11
 
12
- # contract C::Any => C::Maybe[C::RespondTo[:identifier]]
13
- def get_memoized(arg)
14
- get_unmemoized(arg)
15
- end
16
- memo_wise :get_memoized
17
-
18
12
  def reference
19
13
  'items'
20
14
  end
@@ -42,6 +42,9 @@ module Nanoc
42
42
  # Reset flags
43
43
  @compiled = false
44
44
  @modified = false
45
+
46
+ # Precalculate for performance
47
+ @hash = [self.class, item, name].hash
45
48
  end
46
49
 
47
50
  contract C::HashOf[Symbol => C::IterOf[C::AbsolutePathString]] => C::HashOf[Symbol => C::IterOf[C::AbsolutePathString]]
@@ -75,6 +78,21 @@ module Nanoc
75
78
  @paths.fetch(snapshot, []).first
76
79
  end
77
80
 
81
+ contract C::None => C::Num
82
+ def hash
83
+ @hash
84
+ end
85
+
86
+ contract C::Any => C::Bool
87
+ def ==(other)
88
+ eql?(other)
89
+ end
90
+
91
+ contract C::Any => C::Bool
92
+ def eql?(other)
93
+ other.is_a?(self.class) && item == other.item && name == other.name
94
+ end
95
+
78
96
  # Returns an object that can be used for uniquely identifying objects.
79
97
  def reference
80
98
  "item_rep:#{item.identifier}:#{name}"
@@ -25,15 +25,15 @@ module Nanoc
25
25
  action_sequences = Nanoc::Core::ItemRepRouter.new(@reps, @action_provider, @site).run
26
26
 
27
27
  @reps.each do |rep|
28
- rep.snapshot_defs = self.class.snapshot_defs_for(action_sequences[rep])
28
+ rep.snapshot_defs = self.class.snapshot_defs_for(action_sequences[rep], rep)
29
29
  end
30
30
 
31
31
  action_sequences
32
32
  end
33
33
 
34
- contract Nanoc::Core::ActionSequence => C::ArrayOf[Nanoc::Core::SnapshotDef]
35
- def self.snapshot_defs_for(action_sequence)
36
- is_binary = action_sequence.item_rep.item.content.binary?
34
+ contract Nanoc::Core::ActionSequence, Nanoc::Core::ItemRep => C::ArrayOf[Nanoc::Core::SnapshotDef]
35
+ def self.snapshot_defs_for(action_sequence, rep)
36
+ is_binary = rep.item.content.binary?
37
37
  snapshot_defs = []
38
38
 
39
39
  action_sequence.each do |action|