nanoc 4.4.3 → 4.4.4

Sign up to get free protection for your applications and to get access to all the features.
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