nanoc 4.4.3 → 4.4.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 949b7676e2070a525cf434e0157e0423e1f694af
4
- data.tar.gz: 2b1c582dd18c89efdec5ba5c8d89803dd938930f
3
+ metadata.gz: a8f94a3171ddf9ebc26963938e4399d7075b1e5a
4
+ data.tar.gz: 81f10a1af119995497a8a11ff564ecc0408c3703
5
5
  SHA512:
6
- metadata.gz: cb85fb348177d808a2bc17d47b33564a4f226d98adbfb08bbbf543ab7a2efc6dc65710f7040b877f47d547a08daa5bad8726b4ab99f5fb9dd2a12ad6d1425179
7
- data.tar.gz: 77d463b8d56a22823571ec42d870f044aaf33f8ded0fff69da80ddbaef09c19a9dc4ca163760a4b460a58858ce22d96c4a3a6eb1382f4640d3f2d20d5e7d4697
6
+ metadata.gz: 78b10318c7b1160ce58257201d03f82295db7cd027bf6cb1e5265c0bbcde0cacbf9907ee140166c51d572c1b8ca1a4c53ed93af8dfafdba3ff37f322b4110cf2
7
+ data.tar.gz: 67260b141c829d973f341bf469a0fae490232d628265a9e55bc6e89a73a672b8811505702b8c0327694ea6e098db5b8d16462580b920e319ddea141510df3521
@@ -9,7 +9,7 @@ GIT
9
9
  PATH
10
10
  remote: .
11
11
  specs:
12
- nanoc (4.4.3)
12
+ nanoc (4.4.4)
13
13
  cri (~> 2.3)
14
14
  hamster (~> 3.0)
15
15
  parallel (~> 1.9)
@@ -51,7 +51,7 @@ GEM
51
51
  sass (>= 3.3.0, < 3.5)
52
52
  compass-import-once (1.0.5)
53
53
  sass (>= 3.2, < 3.5)
54
- concurrent-ruby (1.0.2)
54
+ concurrent-ruby (1.0.3)
55
55
  contracts (0.14.0)
56
56
  coveralls (0.8.17)
57
57
  json (>= 1.8, < 3)
@@ -332,7 +332,7 @@ GEM
332
332
  execjs (>= 0.3.0, < 3)
333
333
  unicode-display_width (1.1.2)
334
334
  vcr (3.0.3)
335
- w3c_validators (1.3)
335
+ w3c_validators (1.3.1)
336
336
  json (~> 2.0)
337
337
  nokogiri (~> 1.6)
338
338
  webmock (2.3.1)
data/NEWS.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Nanoc news
2
2
 
3
+ ## 4.4.4 (2016-12-19)
4
+
5
+ Enhancements:
6
+
7
+ * Improved speed of incremental compilations (#1017, #1019, #1024)
8
+
3
9
  ## 4.4.3 (2016-12-17)
4
10
 
5
11
  Fixes:
@@ -33,6 +33,7 @@ require 'forwardable'
33
33
  require 'pathname'
34
34
  require 'pstore'
35
35
  require 'set'
36
+ require 'singleton'
36
37
  require 'tempfile'
37
38
  require 'thread'
38
39
  require 'time'
@@ -3,6 +3,73 @@ module Nanoc::Int
3
3
  #
4
4
  # @api private
5
5
  class OutdatednessChecker
6
+ class Basic
7
+ extend Nanoc::Int::Memoization
8
+
9
+ include Nanoc::Int::ContractsSupport
10
+
11
+ Rules = Nanoc::Int::OutdatednessRules
12
+
13
+ RULES_FOR_ITEM_REP =
14
+ [
15
+ Rules::RulesModified,
16
+ Rules::PathsModified,
17
+ Rules::ContentModified,
18
+ Rules::AttributesModified,
19
+ Rules::NotWritten,
20
+ Rules::CodeSnippetsModified,
21
+ Rules::ConfigurationModified,
22
+ ].freeze
23
+
24
+ RULES_FOR_LAYOUT =
25
+ [
26
+ Rules::RulesModified,
27
+ Rules::ContentModified,
28
+ Rules::AttributesModified,
29
+ ].freeze
30
+
31
+ contract C::KeywordArgs[outdatedness_checker: OutdatednessChecker, reps: Nanoc::Int::ItemRepRepo] => C::Any
32
+ def initialize(outdatedness_checker:, reps:)
33
+ @outdatedness_checker = outdatedness_checker
34
+ @reps = reps
35
+ end
36
+
37
+ contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Maybe[OutdatednessStatus]
38
+ def outdatedness_status_for(obj)
39
+ case obj
40
+ when Nanoc::Int::ItemRep
41
+ apply_rules(RULES_FOR_ITEM_REP, obj)
42
+ when Nanoc::Int::Item
43
+ apply_rules_multi(RULES_FOR_ITEM_REP, @reps[obj])
44
+ when Nanoc::Int::Layout
45
+ apply_rules(RULES_FOR_LAYOUT, obj)
46
+ else
47
+ raise "do not know how to check outdatedness of #{obj.inspect}"
48
+ end
49
+ end
50
+ memoize :outdatedness_status_for
51
+
52
+ private
53
+
54
+ contract C::ArrayOf[Class], C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout], OutdatednessStatus => C::Maybe[OutdatednessStatus]
55
+ def apply_rules(rules, obj, status = OutdatednessStatus.new)
56
+ rules.inject(status) do |acc, rule|
57
+ if !acc.useful_to_apply?(rule)
58
+ acc
59
+ elsif rule.instance.apply(obj, @outdatedness_checker)
60
+ acc.update(rule.instance.reason)
61
+ else
62
+ acc
63
+ end
64
+ end
65
+ end
66
+
67
+ contract C::ArrayOf[Class], C::ArrayOf[C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout]] => C::Maybe[OutdatednessStatus]
68
+ def apply_rules_multi(rules, objs)
69
+ objs.inject(OutdatednessStatus.new) { |acc, elem| apply_rules(rules, elem, acc) }
70
+ end
71
+ end
72
+
6
73
  extend Nanoc::Int::Memoization
7
74
 
8
75
  include Nanoc::Int::ContractsSupport
@@ -10,6 +77,7 @@ module Nanoc::Int
10
77
  attr_reader :checksum_store
11
78
  attr_reader :dependency_store
12
79
  attr_reader :rule_memory_store
80
+ attr_reader :action_provider
13
81
  attr_reader :site
14
82
 
15
83
  Reasons = Nanoc::Int::OutdatednessReasons
@@ -28,8 +96,6 @@ module Nanoc::Int
28
96
  @action_provider = action_provider
29
97
  @reps = reps
30
98
 
31
- @basic_outdatedness_reasons = {}
32
- @outdatedness_reasons = {}
33
99
  @objects_outdated_due_to_dependencies = {}
34
100
  end
35
101
 
@@ -64,87 +130,18 @@ module Nanoc::Int
64
130
 
65
131
  private
66
132
 
67
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
68
- # Checks whether the given object is outdated and therefore needs to be
69
- # recompiled. This method does not take dependencies into account; use
70
- # {#outdated?} if you want to include dependencies in the outdatedness
71
- # check.
72
- #
73
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
74
- # whose outdatedness should be checked.
75
- #
76
- # @return [Boolean] true if the object is outdated, false otherwise
77
- def basic_outdated?(obj)
78
- !basic_outdatedness_reason_for(obj).nil?
133
+ contract C::None => Basic
134
+ def basic
135
+ @_basic ||= Basic.new(outdatedness_checker: self, reps: @reps)
79
136
  end
80
137
 
81
138
  contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Maybe[Reasons::Generic]
82
- # Calculates the reason why the given object is outdated. This method does
83
- # not take dependencies into account; use {#outdatedness_reason_for?} if
84
- # you want to include dependencies in the outdatedness check.
85
- #
86
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
87
- # whose outdatedness reason should be calculated.
88
- #
89
- # @return [Reasons::Generic, nil] The reason why the
90
- # given object is outdated, or nil if the object is not outdated.
91
139
  def basic_outdatedness_reason_for(obj)
92
- case obj
93
- when Nanoc::Int::ItemRep
94
- # Outdated if rules outdated
95
- return Reasons::RulesModified if
96
- rule_memory_differs_for(obj)
97
-
98
- # Outdated if checksums are missing or different
99
- return Reasons::NotEnoughData unless checksums_available?(obj.item)
100
- return Reasons::ContentModified unless content_checksums_identical?(obj.item)
101
- return Reasons::AttributesModified unless attributes_checksums_identical?(obj.item)
102
-
103
- # Outdated if compiled file doesn't exist (yet)
104
- return Reasons::NotWritten if obj.raw_path && !File.file?(obj.raw_path)
105
-
106
- # Outdated if code snippets outdated
107
- return Reasons::CodeSnippetsModified if site.code_snippets.any? do |cs|
108
- object_modified?(cs)
109
- end
110
-
111
- # Outdated if configuration outdated
112
- return Reasons::ConfigurationModified if object_modified?(site.config)
113
-
114
- # Not outdated
115
- nil
116
- when Nanoc::Int::Item
117
- @reps[obj].lazy.map { |rep| basic_outdatedness_reason_for(rep) }.find { |s| s }
118
- when Nanoc::Int::Layout
119
- # Outdated if rules outdated
120
- return Reasons::RulesModified if
121
- rule_memory_differs_for(obj)
122
-
123
- # Outdated if checksums are missing or different
124
- return Reasons::NotEnoughData unless checksums_available?(obj)
125
- return Reasons::ContentModified unless content_checksums_identical?(obj)
126
- return Reasons::AttributesModified unless attributes_checksums_identical?(obj)
127
-
128
- # Not outdated
129
- nil
130
- else
131
- raise "do not know how to check outdatedness of #{obj.inspect}"
132
- end
140
+ # FIXME: Stop using this; it is no longer accurate, as there can be >1 reasons
141
+ basic.outdatedness_status_for(obj).reasons.first
133
142
  end
134
- memoize :basic_outdatedness_reason_for
135
143
 
136
144
  contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout], Hamster::Set => C::Bool
137
- # Checks whether the given object is outdated due to dependencies.
138
- #
139
- # @param [Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The object
140
- # whose outdatedness should be checked.
141
- #
142
- # @param [Set] processed The collection of items that has been visited
143
- # during this outdatedness check. This is used to prevent checks for
144
- # items that (indirectly) depend on their own from looping
145
- # indefinitely. It should not be necessary to pass this a custom value.
146
- #
147
- # @return [Boolean] true if the object is outdated, false otherwise
148
145
  def outdated_due_to_dependencies?(obj, processed = Hamster::Set.new)
149
146
  # Convert from rep to item if necessary
150
147
  obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
@@ -160,8 +157,10 @@ module Nanoc::Int
160
157
  return false if processed.include?(obj)
161
158
 
162
159
  # Calculate
163
- is_outdated = dependency_store.objects_causing_outdatedness_of(obj).any? do |other|
164
- other.nil? || basic_outdated?(other) || outdated_due_to_dependencies?(other, processed.merge([obj]))
160
+ is_outdated = dependency_store.dependencies_causing_outdatedness_of(obj).any? do |dep|
161
+ dependency_causes_outdatedness?(dep) ||
162
+ (dep.props.compiled_content? &&
163
+ outdated_due_to_dependencies?(dep.from, processed.merge([obj])))
165
164
  end
166
165
 
167
166
  # Cache
@@ -171,66 +170,12 @@ module Nanoc::Int
171
170
  is_outdated
172
171
  end
173
172
 
174
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::Bool
175
- # @param [Nanoc::Int::ItemRep, Nanoc::Int::Layout] obj The layout or item
176
- # representation to check the rule memory for
177
- #
178
- # @return [Boolean] true if the rule memory for the given item
179
- # represenation has changed, false otherwise
180
- def rule_memory_differs_for(obj)
181
- !rule_memory_store[obj].eql?(@action_provider.memory_for(obj).serialize)
182
- end
183
- memoize :rule_memory_differs_for
184
-
185
- contract C::Any => String
186
- # @param obj The object to create a checksum for
187
- #
188
- # @return [String] The digest
189
- def calc_checksum(obj)
190
- Nanoc::Int::Checksummer.calc(obj)
191
- end
192
- memoize :calc_checksum
193
-
194
- contract C::Any => C::Bool
195
- # @param obj
196
- #
197
- # @return [Boolean] false if either the new or the old checksum for the
198
- # given object is not available, true if both checksums are available
199
- def checksums_available?(obj)
200
- checksum_store[obj] && calc_checksum(obj) ? true : false
201
- end
202
- memoize :checksums_available?
203
-
204
- contract C::Any => C::Bool
205
- # @param obj
206
- #
207
- # @return [Boolean] false if the old and new checksums for the given
208
- # object differ, true if they are identical
209
- def checksums_identical?(obj)
210
- checksum_store[obj] == calc_checksum(obj)
211
- end
212
- memoize :checksums_identical?
213
-
214
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::Layout] => C::Bool
215
- def content_checksums_identical?(obj)
216
- checksum_store.content_checksum_for(obj) == Nanoc::Int::Checksummer.calc_for_content_of(obj)
217
- end
218
- memoize :content_checksums_identical?
219
-
220
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::Layout] => C::Bool
221
- def attributes_checksums_identical?(obj)
222
- checksum_store.attributes_checksum_for(obj) == Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
223
- end
224
- memoize :attributes_checksums_identical?
173
+ contract Nanoc::Int::Dependency => C::Bool
174
+ def dependency_causes_outdatedness?(dependency)
175
+ return true if dependency.from.nil?
225
176
 
226
- contract C::Any => C::Bool
227
- # @param obj
228
- #
229
- # @return [Boolean] true if the old and new checksums for the given object
230
- # are available and identical, false otherwise
231
- def object_modified?(obj)
232
- !checksums_available?(obj) || !checksums_identical?(obj)
177
+ status = basic.outdatedness_status_for(dependency.from)
178
+ (status.props.active & dependency.props.active).any?
233
179
  end
234
- memoize :object_modified?
235
180
  end
236
181
  end
@@ -9,43 +9,54 @@ module Nanoc::Int
9
9
  # @return [String] A descriptive message for this outdatedness reason
10
10
  attr_reader :message
11
11
 
12
+ # @return [Nanoc::Int::Props]
13
+ attr_reader :props
14
+
12
15
  # @param [String] message The descriptive message for this outdatedness
13
16
  # reason
14
- def initialize(message)
17
+ def initialize(message, props = Nanoc::Int::Props.new)
15
18
  @message = message
19
+ @props = props
16
20
  end
17
21
  end
18
22
 
19
23
  CodeSnippetsModified = Generic.new(
20
24
  'The code snippets have been modified since the last time the site was compiled.',
25
+ Props.new(raw_content: true, attributes: true, compiled_content: true, path: true),
21
26
  )
22
27
 
23
28
  ConfigurationModified = Generic.new(
24
29
  'The site configuration has been modified since the last time the site was compiled.',
30
+ Props.new(raw_content: true, attributes: true, compiled_content: true, path: true),
25
31
  )
26
32
 
27
33
  DependenciesOutdated = Generic.new(
28
34
  'This item uses content or attributes that have changed since the last time the site was compiled.',
29
35
  )
30
36
 
31
- NotEnoughData = Generic.new(
32
- 'Not enough data is present to correctly determine whether the item is outdated.',
33
- )
34
-
35
37
  NotWritten = Generic.new(
36
38
  'This item representation has not yet been written to the output directory (but it does have a path).',
39
+ Props.new(raw_content: true, attributes: true, compiled_content: true, path: true),
37
40
  )
38
41
 
39
42
  RulesModified = Generic.new(
40
43
  'The rules file has been modified since the last time the site was compiled.',
44
+ Props.new(compiled_content: true, path: true),
41
45
  )
42
46
 
43
47
  ContentModified = Generic.new(
44
48
  'The content of this item has been modified since the last time the site was compiled.',
49
+ Props.new(raw_content: true, compiled_content: true),
45
50
  )
46
51
 
47
52
  AttributesModified = Generic.new(
48
53
  'The attributes of this item have been modified since the last time the site was compiled.',
54
+ Props.new(attributes: true, compiled_content: true),
55
+ )
56
+
57
+ PathsModified = Generic.new(
58
+ 'One or more output paths of this item have been modified since the last time the site was compiled.',
59
+ Props.new(path: true),
49
60
  )
50
61
  end
51
62
  end
@@ -15,6 +15,10 @@ require_relative 'entities/item'
15
15
  require_relative 'entities/item_rep'
16
16
  require_relative 'entities/layout'
17
17
  require_relative 'entities/pattern'
18
+ require_relative 'entities/props'
18
19
  require_relative 'entities/rule_memory'
19
20
  require_relative 'entities/site'
20
21
  require_relative 'entities/snapshot_def'
22
+
23
+ require_relative 'entities/outdatedness_status'
24
+ require_relative 'entities/dependency'
@@ -0,0 +1,28 @@
1
+ module Nanoc::Int
2
+ # @api private
3
+ # A dependency between two items/layouts.
4
+ class Dependency
5
+ include Nanoc::Int::ContractsSupport
6
+
7
+ contract C::None => C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]]
8
+ attr_reader :from
9
+
10
+ contract C::None => C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]]
11
+ attr_reader :to
12
+
13
+ contract C::None => Nanoc::Int::Props
14
+ attr_reader :props
15
+
16
+ contract C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], Nanoc::Int::Props => C::Any
17
+ def initialize(from, to, props)
18
+ @from = from
19
+ @to = to
20
+ @props = props
21
+ end
22
+
23
+ contract C::None => String
24
+ def inspect
25
+ "Dependency(#{@from.inspect} -> #{@to.inspect}, #{@props.inspect})"
26
+ end
27
+ end
28
+ end
@@ -5,7 +5,7 @@ module Nanoc
5
5
  include Nanoc::Int::ContractsSupport
6
6
 
7
7
  # @return [Nanoc::Int::Content]
8
- attr_reader :content
8
+ attr_accessor :content
9
9
 
10
10
  # @return [Hash]
11
11
  def attributes
@@ -0,0 +1,23 @@
1
+ module Nanoc::Int
2
+ # @api private
3
+ class OutdatednessStatus
4
+ attr_reader :reasons
5
+ attr_reader :props
6
+
7
+ def initialize(reasons: [], props: Props.new)
8
+ @reasons = reasons
9
+ @props = props
10
+ end
11
+
12
+ def useful_to_apply?(rule)
13
+ (rule.instance.reason.props.active - @props.active).any?
14
+ end
15
+
16
+ def update(reason)
17
+ self.class.new(
18
+ reasons: @reasons + [reason],
19
+ props: @props.merge(reason.props),
20
+ )
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,76 @@
1
+ module Nanoc::Int
2
+ # @api private
3
+ class Props
4
+ include Nanoc::Int::ContractsSupport
5
+
6
+ contract C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C::Bool], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]] => C::Any
7
+ def initialize(raw_content: false, attributes: false, compiled_content: false, path: false)
8
+ @raw_content = raw_content
9
+ @attributes = attributes
10
+ @compiled_content = compiled_content
11
+ @path = path
12
+ end
13
+
14
+ contract C::None => String
15
+ def inspect
16
+ ''.tap do |s|
17
+ s << 'Props('
18
+ s << (raw_content? ? 'r' : '_')
19
+ s << (attributes? ? 'a' : '_')
20
+ s << (compiled_content? ? 'c' : '_')
21
+ s << (path? ? 'p' : '_')
22
+ s << ')'
23
+ end
24
+ end
25
+
26
+ contract C::None => C::Bool
27
+ def raw_content?
28
+ @raw_content
29
+ end
30
+
31
+ contract C::None => C::Bool
32
+ def attributes?
33
+ @attributes
34
+ end
35
+
36
+ contract C::None => C::Bool
37
+ def compiled_content?
38
+ @compiled_content
39
+ end
40
+
41
+ contract C::None => C::Bool
42
+ def path?
43
+ @path
44
+ end
45
+
46
+ contract Nanoc::Int::Props => Nanoc::Int::Props
47
+ def merge(other)
48
+ Props.new(
49
+ raw_content: raw_content? || other.raw_content?,
50
+ attributes: attributes? || other.attributes?,
51
+ compiled_content: compiled_content? || other.compiled_content?,
52
+ path: path? || other.path?,
53
+ )
54
+ end
55
+
56
+ contract C::None => Set
57
+ def active
58
+ Set.new.tap do |pr|
59
+ pr << :raw_content if raw_content?
60
+ pr << :attributes if attributes?
61
+ pr << :compiled_content if compiled_content?
62
+ pr << :path if path?
63
+ end
64
+ end
65
+
66
+ contract C::None => Hash
67
+ def to_h
68
+ {
69
+ raw_content: raw_content?,
70
+ attributes: attributes?,
71
+ compiled_content: compiled_content?,
72
+ path: path?,
73
+ }
74
+ end
75
+ end
76
+ end
@@ -47,6 +47,13 @@ module Nanoc::Int
47
47
  @actions.any? { |a| a.is_a?(Nanoc::Int::ProcessingActions::Layout) }
48
48
  end
49
49
 
50
+ contract C::None => Hash
51
+ def paths
52
+ snapshot_actions.each_with_object({}) do |action, paths|
53
+ paths[action.snapshot_name] = action.path
54
+ end
55
+ end
56
+
50
57
  # TODO: Add contract
51
58
  def serialize
52
59
  map(&:serialize)
@@ -157,7 +157,7 @@ module Nanoc::Int
157
157
  res
158
158
  end
159
159
 
160
- # @param [Class] klass
160
+ # @param [Class] subclass
161
161
  #
162
162
  # @return [Class]
163
163
  #
@@ -1,48 +1,6 @@
1
1
  module Nanoc::Int
2
2
  # @api private
3
3
  class DependencyStore < ::Nanoc::Int::Store
4
- # A dependency between two items/layouts.
5
- class Dependency
6
- include Nanoc::Int::ContractsSupport
7
-
8
- contract C::None => C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]
9
- attr_reader :from
10
-
11
- contract C::None => C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]
12
- attr_reader :to
13
-
14
- contract C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], C::Maybe[C::Or[Nanoc::Int::Item, Nanoc::Int::Layout]], C::KeywordArgs[raw_content: C::Optional[C::Bool], attributes: C::Optional[C::Bool], compiled_content: C::Optional[C::Bool], path: C::Optional[C::Bool]] => C::Any
15
- def initialize(from, to, raw_content:, attributes:, compiled_content:, path:)
16
- @from = from
17
- @to = to
18
-
19
- @raw_content = raw_content
20
- @attributes = attributes
21
- @compiled_content = compiled_content
22
- @path = path
23
- end
24
-
25
- contract C::None => C::Bool
26
- def raw_content?
27
- @raw_content
28
- end
29
-
30
- contract C::None => C::Bool
31
- def attributes?
32
- @attributes
33
- end
34
-
35
- contract C::None => C::Bool
36
- def compiled_content?
37
- @compiled_content
38
- end
39
-
40
- contract C::None => C::Bool
41
- def path?
42
- @path
43
- end
44
- end
45
-
46
4
  include Nanoc::Int::ContractsSupport
47
5
 
48
6
  # @return [Array<Nanoc::Int::Item, Nanoc::Int::Layout>]
@@ -56,18 +14,20 @@ module Nanoc::Int
56
14
  @graph = Nanoc::Int::DirectedGraph.new([nil] + @objects)
57
15
  end
58
16
 
59
- contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::ArrayOf[Dependency]
17
+ contract C::Or[Nanoc::Int::Item, Nanoc::Int::ItemRep, Nanoc::Int::Layout] => C::ArrayOf[Nanoc::Int::Dependency]
60
18
  def dependencies_causing_outdatedness_of(object)
61
19
  objects_causing_outdatedness_of(object).map do |other_object|
62
- props = @graph.props_for(other_object, object) || {}
20
+ props = props_for(other_object, object)
63
21
 
64
- Dependency.new(
22
+ Nanoc::Int::Dependency.new(
65
23
  other_object,
66
24
  object,
67
- raw_content: props.fetch(:raw_content, false),
68
- attributes: props.fetch(:attributes, false),
69
- compiled_content: props.fetch(:compiled_content, false),
70
- path: props.fetch(:path, false),
25
+ Nanoc::Int::Props.new(
26
+ raw_content: props.fetch(:raw_content, false),
27
+ attributes: props.fetch(:attributes, false),
28
+ compiled_content: props.fetch(:compiled_content, false),
29
+ path: props.fetch(:path, false),
30
+ ),
71
31
  )
72
32
  end
73
33
  end
@@ -123,12 +83,12 @@ module Nanoc::Int
123
83
  #
124
84
  # @return [void]
125
85
  def record_dependency(src, dst, raw_content: false, attributes: false, compiled_content: false, path: false)
126
- existing_props = @graph.props_for(dst, src) || {}
127
- new_props = { raw_content: raw_content, attributes: attributes, compiled_content: compiled_content, path: path }
128
- props = merge_props(existing_props, new_props)
86
+ existing_props = Nanoc::Int::Props.new(@graph.props_for(dst, src) || {})
87
+ new_props = Nanoc::Int::Props.new(raw_content: raw_content, attributes: attributes, compiled_content: compiled_content, path: path)
88
+ props = existing_props.merge(new_props)
129
89
 
130
90
  # Warning! dst and src are *reversed* here!
131
- @graph.add_edge(dst, src, props: props) unless src == dst
91
+ @graph.add_edge(dst, src, props: props.to_h) unless src == dst
132
92
  end
133
93
 
134
94
  # Empties the list of dependencies for the given object. This is necessary
@@ -146,10 +106,13 @@ module Nanoc::Int
146
106
 
147
107
  protected
148
108
 
149
- def merge_props(p1, p2)
150
- keys = (p1.keys + p2.keys).uniq
151
- keys.each_with_object({}) do |key, memo|
152
- memo[key] = p1.fetch(key, false) || p2.fetch(key, false)
109
+ def props_for(a, b)
110
+ props = @graph.props_for(a, b) || {}
111
+
112
+ if props.values.any? { |v| v }
113
+ props
114
+ else
115
+ { raw_content: true, attributes: true, compiled_content: true, path: true }
153
116
  end
154
117
  end
155
118
 
@@ -179,10 +142,11 @@ module Nanoc::Int
179
142
 
180
143
  # Record dependency from all items on new items
181
144
  new_objects = (@objects - previous_objects)
145
+ new_props = { raw_content: true, attributes: true, compiled_content: true, path: true }
182
146
  new_objects.each do |new_obj|
183
147
  @objects.each do |obj|
184
148
  next unless obj.is_a?(Nanoc::Int::Item)
185
- @graph.add_edge(new_obj, obj)
149
+ @graph.add_edge(new_obj, obj, props: new_props)
186
150
  end
187
151
  end
188
152
  end
@@ -11,3 +11,5 @@ require_relative 'services/item_rep_writer'
11
11
  require_relative 'services/notification_center'
12
12
  require_relative 'services/pruner'
13
13
  require_relative 'services/temp_filename_factory'
14
+ require_relative 'services/outdatedness_rule'
15
+ require_relative 'services/outdatedness_rules'
@@ -19,8 +19,8 @@ module Nanoc::Int
19
19
  raise NotImplementedError
20
20
  end
21
21
 
22
- def paths_for(_rep)
23
- raise NotImplementedError
22
+ def paths_for(rep)
23
+ memory_for(rep).paths
24
24
  end
25
25
  end
26
26
  end
@@ -129,7 +129,7 @@ module Nanoc
129
129
  # Sets up the filter and runs the filter. This method passes its arguments
130
130
  # to {#run} unchanged and returns the return value from {#run}.
131
131
  #
132
- # @see {#run}
132
+ # @see #run
133
133
  #
134
134
  # @api private
135
135
  def setup_and_run(*args)
@@ -0,0 +1,21 @@
1
+ module Nanoc::Int
2
+ # @api private
3
+ class OutdatednessRule
4
+ include Nanoc::Int::ContractsSupport
5
+ include Singleton
6
+
7
+ def apply(_obj, _outdatedness_checker)
8
+ raise NotImplementedError.new('Nanoc::Int::OutdatednessRule subclasses must implement ##reason, and #apply')
9
+ end
10
+
11
+ contract C::None => String
12
+ def inspect
13
+ "#{self.class.name}(#{reason})"
14
+ end
15
+
16
+ # TODO: remove
17
+ def reason
18
+ raise NotImplementedError.new('Nanoc::Int::OutdatednessRule subclasses must implement ##reason, and #apply')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,121 @@
1
+ module Nanoc::Int
2
+ # @api private
3
+ module OutdatednessRules
4
+ class CodeSnippetsModified < OutdatednessRule
5
+ extend Nanoc::Int::Memoization
6
+
7
+ include Nanoc::Int::ContractsSupport
8
+
9
+ def reason
10
+ Nanoc::Int::OutdatednessReasons::CodeSnippetsModified
11
+ end
12
+
13
+ def apply(_obj, outdatedness_checker)
14
+ any_snippets_modified?(outdatedness_checker)
15
+ end
16
+
17
+ private
18
+
19
+ def any_snippets_modified?(outdatedness_checker)
20
+ outdatedness_checker.site.code_snippets.any? do |cs|
21
+ ch_old = outdatedness_checker.checksum_store[cs]
22
+ ch_new = Nanoc::Int::Checksummer.calc(cs)
23
+ ch_old != ch_new
24
+ end
25
+ end
26
+ memoize :any_snippets_modified?
27
+ end
28
+
29
+ class ConfigurationModified < OutdatednessRule
30
+ extend Nanoc::Int::Memoization
31
+
32
+ def reason
33
+ Nanoc::Int::OutdatednessReasons::ConfigurationModified
34
+ end
35
+
36
+ def apply(_obj, outdatedness_checker)
37
+ config_modified?(outdatedness_checker)
38
+ end
39
+
40
+ private
41
+
42
+ def config_modified?(outdatedness_checker)
43
+ obj = outdatedness_checker.site.config
44
+ ch_old = outdatedness_checker.checksum_store[obj]
45
+ ch_new = Nanoc::Int::Checksummer.calc(obj)
46
+ ch_old != ch_new
47
+ end
48
+ memoize :config_modified?
49
+ end
50
+
51
+ class NotWritten < OutdatednessRule
52
+ def reason
53
+ Nanoc::Int::OutdatednessReasons::NotWritten
54
+ end
55
+
56
+ def apply(obj, _outdatedness_checker)
57
+ # FIXME: check all paths (for all snapshots)
58
+ obj.raw_path && !File.file?(obj.raw_path)
59
+ end
60
+ end
61
+
62
+ class ContentModified < OutdatednessRule
63
+ def reason
64
+ Nanoc::Int::OutdatednessReasons::ContentModified
65
+ end
66
+
67
+ def apply(obj, outdatedness_checker)
68
+ obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
69
+
70
+ ch_old = outdatedness_checker.checksum_store.content_checksum_for(obj)
71
+ ch_new = Nanoc::Int::Checksummer.calc_for_content_of(obj)
72
+ ch_old != ch_new
73
+ end
74
+ end
75
+
76
+ class AttributesModified < OutdatednessRule
77
+ def reason
78
+ Nanoc::Int::OutdatednessReasons::AttributesModified
79
+ end
80
+
81
+ def apply(obj, outdatedness_checker)
82
+ obj = obj.item if obj.is_a?(Nanoc::Int::ItemRep)
83
+
84
+ ch_old = outdatedness_checker.checksum_store.attributes_checksum_for(obj)
85
+ ch_new = Nanoc::Int::Checksummer.calc_for_attributes_of(obj)
86
+ ch_old != ch_new
87
+ end
88
+ end
89
+
90
+ class RulesModified < OutdatednessRule
91
+ def reason
92
+ Nanoc::Int::OutdatednessReasons::RulesModified
93
+ end
94
+
95
+ def apply(obj, outdatedness_checker)
96
+ mem_old = outdatedness_checker.rule_memory_store[obj]
97
+ mem_new = outdatedness_checker.action_provider.memory_for(obj).serialize
98
+ !mem_old.eql?(mem_new)
99
+ end
100
+ end
101
+
102
+ class PathsModified < OutdatednessRule
103
+ def reason
104
+ Nanoc::Int::OutdatednessReasons::PathsModified
105
+ end
106
+
107
+ def apply(obj, outdatedness_checker)
108
+ # FIXME: Prefer to not work on serialised version
109
+
110
+ mem_old = outdatedness_checker.rule_memory_store[obj]
111
+ mem_new = outdatedness_checker.action_provider.memory_for(obj).serialize
112
+ return true if mem_old.nil?
113
+
114
+ paths_old = mem_old.select { |pa| pa[0] == :snapshot }
115
+ paths_new = mem_new.select { |pa| pa[0] == :snapshot }
116
+
117
+ paths_old != paths_new
118
+ end
119
+ end
120
+ end
121
+ end
@@ -90,10 +90,10 @@ module Nanoc::CLI::Commands
90
90
  end
91
91
 
92
92
  props = ''
93
- props << (dep.raw_content? ? 'r' : '_')
94
- props << (dep.attributes? ? 'a' : '_')
95
- props << (dep.compiled_content? ? 'c' : '_')
96
- props << (dep.path? ? 'p' : '_')
93
+ props << (dep.props.raw_content? ? 'r' : '_')
94
+ props << (dep.props.attributes? ? 'a' : '_')
95
+ props << (dep.props.compiled_content? ? 'c' : '_')
96
+ props << (dep.props.path? ? 'p' : '_')
97
97
 
98
98
  if pred
99
99
  puts " [ #{format '%6s', type} ] (#{props}) #{pred.identifier}"
@@ -40,10 +40,6 @@ module Nanoc::RuleDSL
40
40
  @rule_memory_calculator.snapshots_defs_for(rep)
41
41
  end
42
42
 
43
- def paths_for(rep)
44
- @rule_memory_calculator.paths_for_rep(rep)
45
- end
46
-
47
43
  def preprocess(site)
48
44
  ctx = new_preprocessor_context(site)
49
45
 
@@ -47,7 +47,6 @@ module Nanoc::RuleDSL
47
47
  raise UnsupportedObjectTypeException.new(obj)
48
48
  end
49
49
  end
50
- memoize :[]
51
50
 
52
51
  # @param [Nanoc::Int::ItemRep] rep The item representation for which to fetch
53
52
  # the list of snapshots
@@ -89,17 +88,6 @@ module Nanoc::RuleDSL
89
88
  executor.rule_memory
90
89
  end
91
90
 
92
- # @param [Nanoc::Int::ItemRep] rep The item representation to get the rule
93
- # memory for
94
- #
95
- # @return [Hash<Symbol, String>] Pairs of snapshot name and path
96
- def paths_for_rep(rep)
97
- snapshot_actions = new_rule_memory_for_rep(rep).snapshot_actions
98
- snapshot_actions.each_with_object({}) do |action, paths|
99
- paths[action.snapshot_name] = action.path
100
- end
101
- end
102
-
103
91
  # @param [Nanoc::Int::Layout] layout
104
92
  #
105
93
  # @return [Nanoc::Int::RuleMemory]
@@ -1,4 +1,4 @@
1
1
  module Nanoc
2
2
  # The current Nanoc version.
3
- VERSION = '4.4.3'.freeze
3
+ VERSION = '4.4.4'.freeze
4
4
  end
@@ -42,7 +42,7 @@ class Nanoc::Int::OutdatednessCheckerTest < Nanoc::TestCase
42
42
  site.compiler.load_stores
43
43
  outdatedness_checker = site.compiler.send :outdatedness_checker
44
44
  rep = site.compiler.reps[site.items.find { |i| i.identifier == '/' }][0]
45
- assert_equal ::Nanoc::Int::OutdatednessReasons::NotEnoughData, outdatedness_checker.outdatedness_reason_for(rep)
45
+ assert_equal ::Nanoc::Int::OutdatednessReasons::ContentModified, outdatedness_checker.outdatedness_reason_for(rep)
46
46
  end
47
47
  end
48
48
 
@@ -2,7 +2,7 @@ class Nanoc::Checking::Checks::HTMLTest < Nanoc::TestCase
2
2
  def test_run_ok
3
3
  require 'w3c_validators'
4
4
 
5
- if ::W3CValidators::VERSION == '1.3'
5
+ if ::W3CValidators::VERSION =~ /\A1\.3|1\.3\.1\z/
6
6
  skip 'broken (see https://github.com/w3c-validators/w3c_validators/issues/25)'
7
7
  end
8
8
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.4.3
4
+ version: 4.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Denis Defreyne
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-12-17 00:00:00.000000000 Z
11
+ date: 2016-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cri
@@ -138,6 +138,7 @@ files:
138
138
  - lib/nanoc/base/entities/configuration.rb
139
139
  - lib/nanoc/base/entities/content.rb
140
140
  - lib/nanoc/base/entities/context.rb
141
+ - lib/nanoc/base/entities/dependency.rb
141
142
  - lib/nanoc/base/entities/directed_graph.rb
142
143
  - lib/nanoc/base/entities/document.rb
143
144
  - lib/nanoc/base/entities/identifiable_collection.rb
@@ -146,12 +147,14 @@ files:
146
147
  - lib/nanoc/base/entities/item_rep.rb
147
148
  - lib/nanoc/base/entities/layout.rb
148
149
  - lib/nanoc/base/entities/lazy_value.rb
150
+ - lib/nanoc/base/entities/outdatedness_status.rb
149
151
  - lib/nanoc/base/entities/pattern.rb
150
152
  - lib/nanoc/base/entities/processing_action.rb
151
153
  - lib/nanoc/base/entities/processing_actions.rb
152
154
  - lib/nanoc/base/entities/processing_actions/filter.rb
153
155
  - lib/nanoc/base/entities/processing_actions/layout.rb
154
156
  - lib/nanoc/base/entities/processing_actions/snapshot.rb
157
+ - lib/nanoc/base/entities/props.rb
155
158
  - lib/nanoc/base/entities/rule_memory.rb
156
159
  - lib/nanoc/base/entities/site.rb
157
160
  - lib/nanoc/base/entities/snapshot_def.rb
@@ -182,6 +185,8 @@ files:
182
185
  - lib/nanoc/base/services/item_rep_selector.rb
183
186
  - lib/nanoc/base/services/item_rep_writer.rb
184
187
  - lib/nanoc/base/services/notification_center.rb
188
+ - lib/nanoc/base/services/outdatedness_rule.rb
189
+ - lib/nanoc/base/services/outdatedness_rules.rb
185
190
  - lib/nanoc/base/services/pruner.rb
186
191
  - lib/nanoc/base/services/temp_filename_factory.rb
187
192
  - lib/nanoc/base/views.rb