nanoc 4.1.6 → 4.2.0b1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/Gemfile.lock +2 -1
  4. data/NEWS.md +11 -4
  5. data/lib/nanoc/base/checksummer.rb +135 -46
  6. data/lib/nanoc/base/compilation/compiler.rb +18 -28
  7. data/lib/nanoc/base/compilation/dependency_tracker.rb +22 -32
  8. data/lib/nanoc/base/compilation/filter.rb +2 -4
  9. data/lib/nanoc/base/entities.rb +1 -0
  10. data/lib/nanoc/base/entities/content.rb +14 -3
  11. data/lib/nanoc/base/entities/document.rb +14 -6
  12. data/lib/nanoc/base/entities/item.rb +0 -31
  13. data/lib/nanoc/base/entities/item_rep.rb +1 -1
  14. data/lib/nanoc/base/entities/lazy_value.rb +36 -0
  15. data/lib/nanoc/base/entities/pattern.rb +3 -2
  16. data/lib/nanoc/base/entities/site.rb +2 -0
  17. data/lib/nanoc/base/memoization.rb +17 -10
  18. data/lib/nanoc/base/repos/compiled_content_cache.rb +1 -1
  19. data/lib/nanoc/base/repos/data_source.rb +10 -6
  20. data/lib/nanoc/base/services/executor.rb +22 -22
  21. data/lib/nanoc/base/services/item_rep_router.rb +4 -5
  22. data/lib/nanoc/base/views.rb +0 -1
  23. data/lib/nanoc/base/views/item_rep_view.rb +3 -9
  24. data/lib/nanoc/base/views/mixins/document_view_mixin.rb +4 -11
  25. data/lib/nanoc/base/views/view.rb +1 -0
  26. data/lib/nanoc/base/views/view_context.rb +5 -1
  27. data/lib/nanoc/cli/commands/compile.rb +0 -6
  28. data/lib/nanoc/data_sources.rb +5 -5
  29. data/lib/nanoc/data_sources/filesystem.rb +219 -90
  30. data/lib/nanoc/extra/checking/check.rb +1 -2
  31. data/lib/nanoc/extra/checking/checks.rb +2 -0
  32. data/lib/nanoc/extra/checking/checks/css.rb +6 -14
  33. data/lib/nanoc/extra/checking/checks/html.rb +6 -14
  34. data/lib/nanoc/extra/checking/checks/internal_links.rb +14 -3
  35. data/lib/nanoc/extra/checking/checks/w3c_validator.rb +28 -0
  36. data/lib/nanoc/extra/deployers/fog.rb +134 -78
  37. data/lib/nanoc/extra/link_collector.rb +14 -18
  38. data/lib/nanoc/filters/sass.rb +3 -3
  39. data/lib/nanoc/helpers.rb +1 -0
  40. data/lib/nanoc/helpers/capturing.rb +16 -58
  41. data/lib/nanoc/helpers/child_parent.rb +51 -0
  42. data/lib/nanoc/helpers/filtering.rb +0 -1
  43. data/lib/nanoc/helpers/html_escape.rb +5 -0
  44. data/lib/nanoc/helpers/link_to.rb +2 -0
  45. data/lib/nanoc/helpers/rendering.rb +3 -4
  46. data/lib/nanoc/rule_dsl/action_provider.rb +20 -4
  47. data/lib/nanoc/rule_dsl/recording_executor.rb +3 -1
  48. data/lib/nanoc/rule_dsl/rule_context.rb +0 -1
  49. data/lib/nanoc/rule_dsl/rule_memory_calculator.rb +4 -1
  50. data/lib/nanoc/spec.rb +217 -0
  51. data/lib/nanoc/version.rb +1 -1
  52. data/test/base/test_data_source.rb +4 -2
  53. data/test/base/test_dependency_tracker.rb +5 -11
  54. data/test/data_sources/test_filesystem.rb +605 -69
  55. data/test/extra/checking/checks/test_internal_links.rb +25 -0
  56. data/test/extra/deployers/test_fog.rb +0 -177
  57. data/test/filters/test_less.rb +9 -4
  58. data/test/helpers/test_capturing.rb +38 -212
  59. data/test/helpers/test_link_to.rb +0 -205
  60. data/test/helpers/test_xml_sitemap.rb +2 -1
  61. metadata +7 -12
  62. data/lib/nanoc/base/views/site_view.rb +0 -14
  63. data/lib/nanoc/data_sources/filesystem_unified.rb +0 -101
  64. data/test/data_sources/test_filesystem_unified.rb +0 -559
  65. data/test/helpers/test_breadcrumbs.rb +0 -60
  66. data/test/helpers/test_filtering.rb +0 -112
  67. data/test/helpers/test_html_escape.rb +0 -26
  68. data/test/helpers/test_rendering.rb +0 -147
  69. data/test/helpers/test_tagging.rb +0 -92
  70. data/test/helpers/test_text.rb +0 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f6ba850197dc445a7c7ff2155f172cf559429e4
4
- data.tar.gz: 6ffcc91296268720adf1290c403b41d73c0fc762
3
+ metadata.gz: cc5bddbd9c1b7b365b227cd69def799b2a8da4a2
4
+ data.tar.gz: 1da63342f4909056377af782094d03c3293ca75f
5
5
  SHA512:
6
- metadata.gz: 6d87f9120c1c06ef221dc59d7b33a5c5d05deeedb008927232420d0a466a96b37048f25c706d389d393905b46b7bcd19f8487d8818db881d0d100d89b9443a5d
7
- data.tar.gz: f4da8a98b02735de58c7d7c57a09ea3f49af3323cb887e31112c5019dd5245ef686949a12540eb8ff469c0cb8544515544a881a53dd12505c0ad2da663c77e1a
6
+ metadata.gz: de802f2ea52c8b4ee7e9d72bfeffb4870f09b83b6d0edf64fd35508092eb74104b76201754f88382ffcbbab1458af520071e5e77d02b100cb5f76cf62f2ab13c
7
+ data.tar.gz: d6f3d7751f3c8d0d8f95d7913cb7df7ebf972a47dbe1ad35a91d4d4491cf30919311011846e420c49fb8b9b559bca7f90c18442aa80021d929efa717ed62a7b7
data/Gemfile CHANGED
@@ -11,6 +11,7 @@ group :devel do
11
11
  gem 'rake'
12
12
  gem 'rdoc'
13
13
  gem 'rspec'
14
+ gem 'rspec-mocks'
14
15
  gem 'rubocop'
15
16
  gem 'simplecov', require: false
16
17
  gem 'vcr'
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- nanoc (4.1.6)
4
+ nanoc (4.2.0b1)
5
5
  cri (~> 2.3)
6
6
 
7
7
  GEM
@@ -361,6 +361,7 @@ DEPENDENCIES
361
361
  redcarpet
362
362
  rouge
363
363
  rspec
364
+ rspec-mocks
364
365
  rubocop
365
366
  rubypants
366
367
  sass
data/NEWS.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Nanoc news
2
2
 
3
- ## 4.1.6 (2016-04-17)
3
+ ## 4.2.0b1 (2016-04-17)
4
4
 
5
- Fixes:
5
+ Features:
6
+
7
+ * Allow creating items and layouts with a pre-calculated checksum (#793) [Ruben Verborgh]
8
+ * Allow lazy-loading item/layout content and attributes (#794) [Ruben Verborgh]
9
+ * Added `exclude_origins` configuration option to internal links checker (#847)
10
+ * Added `ChildParent` helper, providing `#children_of` and `#parent_of` (#849)
11
+
12
+ Enhancements:
6
13
 
7
- * Strip index.html only if it is a full component (#850, #851)
8
- * Force UTF-8 for item rep paths (#837, #852)
14
+ * Made `#html_escape` raise an appropriate error when the given argument is not a String (#840) [Micha Rosenbaum]
15
+ * Improved memory usage of memoized values by using weak refs (#846)
9
16
 
10
17
  ## 4.1.5 (2016-03-24)
11
18
 
@@ -48,70 +48,159 @@ module Nanoc::Int
48
48
 
49
49
  def update(obj, digest, visited = Set.new)
50
50
  digest.update(obj.class.to_s)
51
- digest.update('<')
52
51
 
53
52
  if visited.include?(obj)
54
- digest.update('recur>')
55
- return
53
+ digest.update('<recur>')
54
+ else
55
+ digest.update('<')
56
+ behavior_for(obj).update(obj, digest) { |o| update(o, digest, visited + [obj]) }
57
+ digest.update('>')
56
58
  end
59
+ end
57
60
 
61
+ def behavior_for(obj)
58
62
  case obj
59
- when ::String, ::Symbol, ::Numeric
60
- digest.update(obj.to_s)
61
- when nil, true, false
62
- when ::Array, ::Nanoc::Int::IdentifiableCollection
63
- obj.each do |el|
64
- update(el, digest, visited + [obj])
65
- digest.update(',')
66
- end
67
- when ::Hash, ::Nanoc::Int::Configuration
68
- obj.each do |key, value|
69
- update(key, digest, visited + [obj])
70
- digest.update('=')
71
- update(value, digest, visited + [obj])
72
- digest.update(',')
73
- end
74
- when ::Pathname
75
- filename = obj.to_s
76
- if File.exist?(filename)
77
- stat = File.stat(filename)
78
- digest.update(stat.size.to_s + '-' + stat.mtime.to_i.to_s)
79
- else
80
- digest.update('???')
81
- end
63
+ when String, Symbol, Numeric
64
+ RawUpdateBehavior
65
+ when Pathname
66
+ PathnameUpdateBehavior
67
+ when Nanoc::Int::BinaryContent
68
+ BinaryContentUpdateBehavior
69
+ when Array, Nanoc::Int::IdentifiableCollection
70
+ ArrayUpdateBehavior
71
+ when Hash, Nanoc::Int::Configuration
72
+ HashUpdateBehavior
73
+ when Nanoc::Int::Item, Nanoc::Int::Layout
74
+ DocumentUpdateBehavior
75
+ when NilClass, TrueClass, FalseClass
76
+ NoUpdateBehavior
82
77
  when Time
83
- digest.update(obj.to_i.to_s)
78
+ ToIToSUpdateBehavior
84
79
  when Nanoc::Identifier
85
- update(obj.to_s, digest)
86
- # TODO: Use RuleMemory rather than RulesCollection
80
+ ToSUpdateBehavior
87
81
  when Nanoc::RuleDSL::RulesCollection, Nanoc::Int::CodeSnippet
88
- update(obj.data, digest)
82
+ DataUpdateBehavior
89
83
  when Nanoc::Int::TextualContent
90
- update(obj.string, digest)
91
- when Nanoc::Int::BinaryContent
92
- update(Pathname.new(obj.filename), digest)
93
- when Nanoc::Int::Item, Nanoc::Int::Layout
84
+ StringUpdateBehavior
85
+ when Nanoc::View
86
+ UnwrapUpdateBehavior
87
+ else
88
+ RescueUpdateBehavior
89
+ end
90
+ end
91
+ end
92
+
93
+ class UpdateBehavior
94
+ def self.update(_obj, _digest)
95
+ raise NotImpementedError
96
+ end
97
+ end
98
+
99
+ class RawUpdateBehavior < UpdateBehavior
100
+ def self.update(obj, digest)
101
+ digest.update(obj.to_s)
102
+ end
103
+ end
104
+
105
+ class ToSUpdateBehavior < UpdateBehavior
106
+ def self.update(obj, _digest)
107
+ yield(obj.to_s)
108
+ end
109
+ end
110
+
111
+ class ToIToSUpdateBehavior < UpdateBehavior
112
+ def self.update(obj, digest)
113
+ digest.update(obj.to_i.to_s)
114
+ end
115
+ end
116
+
117
+ class StringUpdateBehavior < UpdateBehavior
118
+ def self.update(obj, _digest)
119
+ yield(obj.string)
120
+ end
121
+ end
122
+
123
+ class DataUpdateBehavior < UpdateBehavior
124
+ def self.update(obj, _digest)
125
+ yield(obj.data)
126
+ end
127
+ end
128
+
129
+ class NoUpdateBehavior < UpdateBehavior
130
+ def self.update(_obj, _digest)
131
+ end
132
+ end
133
+
134
+ class UnwrapUpdateBehavior < UpdateBehavior
135
+ def self.update(obj, _digest)
136
+ yield(obj.unwrap)
137
+ end
138
+ end
139
+
140
+ class ArrayUpdateBehavior < UpdateBehavior
141
+ def self.update(obj, digest)
142
+ obj.each do |el|
143
+ yield(el)
144
+ digest.update(',')
145
+ end
146
+ end
147
+ end
148
+
149
+ class HashUpdateBehavior < UpdateBehavior
150
+ def self.update(obj, digest)
151
+ obj.each do |key, value|
152
+ yield(key)
153
+ digest.update('=')
154
+ yield(value)
155
+ digest.update(',')
156
+ end
157
+ end
158
+ end
159
+
160
+ class DocumentUpdateBehavior < UpdateBehavior
161
+ def self.update(obj, digest)
162
+ if obj.checksum_data
163
+ digest.update('checksum_data=' + obj.checksum_data)
164
+ else
94
165
  digest.update('content=')
95
- update(obj.content, digest)
166
+ yield(obj.content)
96
167
 
97
168
  digest.update(',attributes=')
98
- update(obj.attributes, digest, visited + [obj])
169
+ yield(obj.attributes)
99
170
 
100
171
  digest.update(',identifier=')
101
- update(obj.identifier, digest)
102
- when Nanoc::ItemWithRepsView, Nanoc::ItemWithoutRepsView, Nanoc::LayoutView, Nanoc::ConfigView, Nanoc::IdentifiableCollectionView
103
- update(obj.unwrap, digest)
172
+ yield(obj.identifier)
173
+ end
174
+ end
175
+ end
176
+
177
+ class PathnameUpdateBehavior < UpdateBehavior
178
+ def self.update(obj, digest)
179
+ filename = obj.to_s
180
+ if File.exist?(filename)
181
+ stat = File.stat(filename)
182
+ digest.update(stat.size.to_s + '-' + stat.mtime.to_i.to_s)
104
183
  else
105
- data = begin
106
- Marshal.dump(obj)
107
- rescue
108
- obj.inspect
109
- end
184
+ digest.update('???')
185
+ end
186
+ end
187
+ end
188
+
189
+ class BinaryContentUpdateBehavior < UpdateBehavior
190
+ def self.update(obj, _digest)
191
+ yield(Pathname.new(obj.filename))
192
+ end
193
+ end
110
194
 
111
- digest.update(data)
195
+ class RescueUpdateBehavior < UpdateBehavior
196
+ def self.update(obj, digest)
197
+ data = begin
198
+ Marshal.dump(obj)
199
+ rescue
200
+ obj.inspect
112
201
  end
113
202
 
114
- digest.update('>')
203
+ digest.update(data)
115
204
  end
116
205
  end
117
206
  end
@@ -16,18 +16,6 @@ module Nanoc::Int
16
16
  # this item representation (either successfully or with failure). Has one
17
17
  # argument: the item representation itself.
18
18
  #
19
- # * `visit_started` — indicates that the compiler requires content or
20
- # attributes from the item representation that will be visited. Has one
21
- # argument: the visited item identifier. This notification is used to
22
- # track dependencies of items on other items; a `visit_started` event
23
- # followed by another `visit_started` event indicates that the item
24
- # corresponding to the former event will depend on the item from the
25
- # latter event.
26
- #
27
- # * `visit_ended` — indicates that the compiler has finished visiting the
28
- # item representation and that the requested attributes or content have
29
- # been fetched (either successfully or with failure)
30
- #
31
19
  # * `processing_started` — indicates that the compiler has started
32
20
  # processing the specified object, which can be an item representation
33
21
  # (when it is compiled) or a layout (when it is used to lay out an item
@@ -98,10 +86,7 @@ module Nanoc::Int
98
86
  forget_dependencies_if_outdated
99
87
 
100
88
  @stack = []
101
- dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
102
- dependency_tracker.run do
103
- compile_reps
104
- end
89
+ compile_reps
105
90
  store
106
91
  ensure
107
92
  Nanoc::Int::TempFilenameFactory.instance.cleanup(
@@ -147,7 +132,7 @@ module Nanoc::Int
147
132
  # operation
148
133
  #
149
134
  # @api private
150
- def assigns_for(rep)
135
+ def assigns_for(rep, dependency_tracker)
151
136
  content_or_filename_assigns =
152
137
  if rep.binary?
153
138
  { filename: rep.snapshot_contents[:last].filename }
@@ -155,9 +140,8 @@ module Nanoc::Int
155
140
  { content: rep.snapshot_contents[:last].string }
156
141
  end
157
142
 
158
- view_context = create_view_context
143
+ view_context = create_view_context(dependency_tracker)
159
144
 
160
- # TODO: Do not expose @site (necessary for captures store though…)
161
145
  content_or_filename_assigns.merge(
162
146
  item: Nanoc::ItemWithRepsView.new(rep.item, view_context),
163
147
  rep: Nanoc::ItemRepView.new(rep, view_context),
@@ -165,12 +149,16 @@ module Nanoc::Int
165
149
  items: Nanoc::ItemCollectionWithRepsView.new(site.items, view_context),
166
150
  layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
167
151
  config: Nanoc::ConfigView.new(site.config, view_context),
168
- site: Nanoc::SiteView.new(site, view_context),
169
152
  )
170
153
  end
171
154
 
172
- def create_view_context
173
- Nanoc::ViewContext.new(reps: @reps, items: @site.items)
155
+ def create_view_context(dependency_tracker)
156
+ Nanoc::ViewContext.new(
157
+ reps: @reps,
158
+ items: @site.items,
159
+ dependency_tracker: dependency_tracker,
160
+ compiler: self,
161
+ )
174
162
  end
175
163
 
176
164
  # @api private
@@ -216,15 +204,17 @@ module Nanoc::Int
216
204
  #
217
205
  # @return [void]
218
206
  def compile_rep(rep)
207
+ dependency_tracker = Nanoc::Int::DependencyTracker.new(@dependency_store)
208
+
219
209
  Nanoc::Int::NotificationCenter.post(:compilation_started, rep)
220
210
  Nanoc::Int::NotificationCenter.post(:processing_started, rep)
221
- Nanoc::Int::NotificationCenter.post(:visit_started, rep.item)
211
+ dependency_tracker.enter(rep.item)
222
212
 
223
213
  if can_reuse_content_for_rep?(rep)
224
214
  Nanoc::Int::NotificationCenter.post(:cached_content_used, rep)
225
215
  rep.snapshot_contents = compiled_content_cache[rep]
226
216
  else
227
- recalculate_content_for_rep(rep)
217
+ recalculate_content_for_rep(rep, dependency_tracker)
228
218
  end
229
219
 
230
220
  rep.compiled = true
@@ -237,17 +227,17 @@ module Nanoc::Int
237
227
  Nanoc::Int::NotificationCenter.post(:compilation_failed, rep, e)
238
228
  raise e
239
229
  ensure
240
- Nanoc::Int::NotificationCenter.post(:visit_ended, rep.item)
230
+ dependency_tracker.exit(rep.item)
241
231
  end
242
232
 
243
233
  # @return [Boolean]
244
234
  def can_reuse_content_for_rep?(rep)
245
- !rep.item.forced_outdated? && !outdatedness_checker.outdated?(rep) && compiled_content_cache[rep]
235
+ !outdatedness_checker.outdated?(rep) && compiled_content_cache[rep]
246
236
  end
247
237
 
248
238
  # @return [void]
249
- def recalculate_content_for_rep(rep)
250
- executor = Nanoc::Int::Executor.new(self)
239
+ def recalculate_content_for_rep(rep, dependency_tracker)
240
+ executor = Nanoc::Int::Executor.new(self, dependency_tracker)
251
241
 
252
242
  action_provider.memory_for(rep).each do |action|
253
243
  case action
@@ -1,48 +1,38 @@
1
1
  module Nanoc::Int
2
2
  # @api private
3
3
  class DependencyTracker
4
- def initialize(dependency_store)
5
- @dependency_store = dependency_store
6
- end
4
+ class Null
5
+ def enter(_obj)
6
+ end
7
7
 
8
- # Record dependencies for the duration of the block.
9
- #
10
- # @return [void]
11
- def run
12
- unless block_given?
13
- raise ArgumentError, 'No block given'
8
+ def exit(_obj)
14
9
  end
15
10
 
16
- stack = []
17
- start_tracking(stack)
18
- yield
19
- ensure
20
- stop_tracking(stack)
11
+ def bounce(_obj)
12
+ end
21
13
  end
22
14
 
23
- # @api private
24
- def start_tracking(stack)
25
- Nanoc::Int::NotificationCenter.on(:visit_started, self) do |obj|
26
- unless stack.empty?
27
- Nanoc::Int::NotificationCenter.post(:dependency_created, stack.last, obj)
28
- @dependency_store.record_dependency(stack.last, obj)
29
- end
30
- stack.push(obj)
31
- end
15
+ def initialize(dependency_store)
16
+ @dependency_store = dependency_store
17
+ @stack = []
18
+ end
32
19
 
33
- Nanoc::Int::NotificationCenter.on(:visit_ended, self) do |_obj|
34
- stack.pop
20
+ def enter(obj)
21
+ unless @stack.empty?
22
+ Nanoc::Int::NotificationCenter.post(:dependency_created, @stack.last, obj)
23
+ @dependency_store.record_dependency(@stack.last, obj)
35
24
  end
25
+
26
+ @stack.push(obj)
36
27
  end
37
28
 
38
- # @api private
39
- def stop_tracking(stack)
40
- unless stack.empty?
41
- raise 'Internal inconsistency: dependency tracker stack not empty at end of compilation'
42
- end
29
+ def exit(_obj)
30
+ @stack.pop
31
+ end
43
32
 
44
- Nanoc::Int::NotificationCenter.remove(:visit_started, self)
45
- Nanoc::Int::NotificationCenter.remove(:visit_ended, self)
33
+ def bounce(obj)
34
+ enter(obj)
35
+ exit(obj)
46
36
  end
47
37
  end
48
38
  end
@@ -187,10 +187,8 @@ module Nanoc
187
187
  items = items.map { |i| i.is_a?(Nanoc::ItemWithRepsView) ? i.unwrap : i }
188
188
 
189
189
  # Notify
190
- items.each do |item|
191
- Nanoc::Int::NotificationCenter.post(:visit_started, item)
192
- Nanoc::Int::NotificationCenter.post(:visit_ended, item)
193
- end
190
+ dependency_tracker = @assigns[:item]._context.dependency_tracker
191
+ items.each { |item| dependency_tracker.bounce(item) }
194
192
 
195
193
  # Raise unmet dependency error if necessary
196
194
  items.each do |item|