nanoc 4.1.6 → 4.2.0b1

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