nanoc 4.4.5 → 4.4.6

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 (233) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +6 -2
  3. data/Gemfile.lock +37 -18
  4. data/NEWS.md +6 -0
  5. data/Rakefile +8 -20
  6. data/lib/nanoc/base/entities/code_snippet.rb +0 -2
  7. data/lib/nanoc/base/entities/item_rep.rb +4 -22
  8. data/lib/nanoc/base/entities/processing_actions/snapshot.rb +9 -7
  9. data/lib/nanoc/base/entities/rule_memory.rb +15 -7
  10. data/lib/nanoc/base/entities/snapshot_def.rb +2 -8
  11. data/lib/nanoc/base/repos/store.rb +2 -28
  12. data/lib/nanoc/base/services/checksummer.rb +9 -10
  13. data/lib/nanoc/base/services/compiler.rb +14 -16
  14. data/lib/nanoc/base/services/executor.rb +2 -13
  15. data/lib/nanoc/base/views/mixins/with_reps_view_mixin.rb +1 -1
  16. data/lib/nanoc/checking/checks/external_links.rb +2 -1
  17. data/lib/nanoc/cli/commands/compile.rb +8 -14
  18. data/lib/nanoc/extra/parallel_collection.rb +57 -0
  19. data/lib/nanoc/extra.rb +1 -0
  20. data/lib/nanoc/filters/mustache.rb +1 -1
  21. data/lib/nanoc/rule_dsl/recording_executor.rb +6 -34
  22. data/lib/nanoc/rule_dsl/rule_memory_calculator.rb +42 -12
  23. data/lib/nanoc/spec.rb +1 -1
  24. data/lib/nanoc/version.rb +1 -1
  25. data/nanoc.gemspec +1 -2
  26. data/spec/contributors_spec.rb +18 -0
  27. data/spec/nanoc/base/checksummer_spec.rb +381 -0
  28. data/spec/nanoc/base/compiler_spec.rb +181 -0
  29. data/spec/nanoc/base/entities/configuration_spec.rb +49 -0
  30. data/spec/nanoc/base/entities/content_spec.rb +193 -0
  31. data/spec/nanoc/base/entities/document_spec.rb +206 -0
  32. data/spec/nanoc/base/entities/identifier_spec.rb +460 -0
  33. data/spec/nanoc/base/entities/item_rep_spec.rb +226 -0
  34. data/spec/nanoc/base/entities/item_spec.rb +3 -0
  35. data/spec/nanoc/base/entities/layout_spec.rb +3 -0
  36. data/spec/nanoc/base/entities/lazy_value_spec.rb +106 -0
  37. data/spec/nanoc/base/entities/outdatedness_status_spec.rb +113 -0
  38. data/spec/nanoc/base/entities/pattern_spec.rb +125 -0
  39. data/spec/nanoc/base/entities/processing_action_spec.rb +9 -0
  40. data/spec/nanoc/base/entities/processing_actions/filter_spec.rb +18 -0
  41. data/spec/nanoc/base/entities/processing_actions/layout_spec.rb +18 -0
  42. data/spec/nanoc/base/entities/processing_actions/snapshot_spec.rb +32 -0
  43. data/spec/nanoc/base/entities/props_spec.rb +195 -0
  44. data/spec/nanoc/base/entities/rule_memory_spec.rb +131 -0
  45. data/spec/nanoc/base/entities/site_spec.rb +73 -0
  46. data/spec/nanoc/base/feature_spec.rb +107 -0
  47. data/spec/nanoc/base/filter_spec.rb +99 -0
  48. data/spec/nanoc/base/item_rep_writer_spec.rb +131 -0
  49. data/spec/nanoc/base/plugin_registry_spec.rb +29 -0
  50. data/spec/nanoc/base/repos/checksum_store_spec.rb +133 -0
  51. data/spec/nanoc/base/repos/compiled_content_cache_spec.rb +55 -0
  52. data/spec/nanoc/base/repos/config_loader_spec.rb +243 -0
  53. data/spec/nanoc/base/repos/dependency_store_spec.rb +195 -0
  54. data/spec/nanoc/base/repos/site_loader_spec.rb +214 -0
  55. data/spec/nanoc/base/services/dependency_tracker_spec.rb +238 -0
  56. data/spec/nanoc/base/services/executor_spec.rb +495 -0
  57. data/spec/nanoc/base/services/item_rep_router_spec.rb +134 -0
  58. data/spec/nanoc/base/services/item_rep_selector_spec.rb +169 -0
  59. data/spec/nanoc/base/services/outdatedness_checker_spec.rb +370 -0
  60. data/spec/nanoc/base/services/outdatedness_rules_spec.rb +432 -0
  61. data/spec/nanoc/base/services/pruner_spec.rb +105 -0
  62. data/spec/nanoc/base/services/temp_filename_factory_spec.rb +87 -0
  63. data/spec/nanoc/base/views/config_view_spec.rb +96 -0
  64. data/spec/nanoc/base/views/document_view_spec.rb +332 -0
  65. data/spec/nanoc/base/views/identifiable_collection_view_spec.rb +190 -0
  66. data/spec/nanoc/base/views/item_collection_with_reps_view_spec.rb +18 -0
  67. data/spec/nanoc/base/views/item_collection_without_reps_view_spec.rb +18 -0
  68. data/spec/nanoc/base/views/item_rep_collection_view_spec.rb +143 -0
  69. data/spec/nanoc/base/views/item_rep_view_spec.rb +265 -0
  70. data/spec/nanoc/base/views/item_view_spec.rb +341 -0
  71. data/spec/nanoc/base/views/layout_collection_view_spec.rb +18 -0
  72. data/spec/nanoc/base/views/layout_view_spec.rb +14 -0
  73. data/spec/nanoc/base/views/mutable_config_view_spec.rb +16 -0
  74. data/spec/nanoc/base/views/mutable_document_view_spec.rb +92 -0
  75. data/spec/nanoc/base/views/mutable_identifiable_collection_view_spec.rb +36 -0
  76. data/spec/nanoc/base/views/mutable_item_collection_view_spec.rb +49 -0
  77. data/spec/nanoc/base/views/mutable_item_view_spec.rb +22 -0
  78. data/spec/nanoc/base/views/mutable_layout_collection_view_spec.rb +49 -0
  79. data/spec/nanoc/base/views/mutable_layout_view_spec.rb +13 -0
  80. data/spec/nanoc/base/views/post_compile_item_rep_collection_view_spec.rb +4 -0
  81. data/spec/nanoc/base/views/post_compile_item_rep_view_spec.rb +137 -0
  82. data/spec/nanoc/base/views/post_compile_item_view_spec.rb +56 -0
  83. data/spec/nanoc/cli/commands/compile/file_action_printer_spec.rb +76 -0
  84. data/spec/nanoc/cli/commands/compile/timing_recorder_spec.rb +66 -0
  85. data/spec/nanoc/cli/commands/compile_spec.rb +64 -0
  86. data/spec/nanoc/cli/commands/deploy_spec.rb +327 -0
  87. data/spec/nanoc/cli/commands/shell_spec.rb +54 -0
  88. data/spec/nanoc/cli/commands/show_data_spec.rb +126 -0
  89. data/spec/nanoc/cli/commands/show_rules_spec.rb +112 -0
  90. data/spec/nanoc/cli/commands/view_spec.rb +58 -0
  91. data/spec/nanoc/data_sources/filesystem_spec.rb +56 -0
  92. data/spec/nanoc/deploying/fog_spec.rb +193 -0
  93. data/spec/nanoc/extra/parallel_collection_spec.rb +108 -0
  94. data/spec/nanoc/filters/colorize_syntax/rouge_spec.rb +195 -0
  95. data/spec/nanoc/filters/less_spec.rb +120 -0
  96. data/spec/nanoc/helpers/blogging_spec.rb +216 -0
  97. data/spec/nanoc/helpers/breadcrumbs_spec.rb +133 -0
  98. data/spec/nanoc/helpers/capturing_spec.rb +181 -0
  99. data/spec/nanoc/helpers/child_parent_spec.rb +105 -0
  100. data/spec/nanoc/helpers/filtering_spec.rb +72 -0
  101. data/spec/nanoc/helpers/html_escape_spec.rb +35 -0
  102. data/spec/nanoc/helpers/link_to_spec.rb +275 -0
  103. data/spec/nanoc/helpers/rendering_spec.rb +141 -0
  104. data/spec/nanoc/helpers/tagging_spec.rb +104 -0
  105. data/spec/nanoc/helpers/text_spec.rb +58 -0
  106. data/spec/nanoc/integration/outdatedness_integration_spec.rb +208 -0
  107. data/spec/nanoc/regressions/gh_1015_spec.rb +17 -0
  108. data/spec/nanoc/regressions/gh_1031_spec.rb +54 -0
  109. data/spec/nanoc/regressions/gh_1035_spec.rb +33 -0
  110. data/spec/nanoc/regressions/gh_1040_spec.rb +22 -0
  111. data/spec/nanoc/regressions/gh_761_spec.rb +23 -0
  112. data/spec/nanoc/regressions/gh_767_spec.rb +19 -0
  113. data/spec/nanoc/regressions/gh_769_spec.rb +30 -0
  114. data/spec/nanoc/regressions/gh_776_spec.rb +43 -0
  115. data/spec/nanoc/regressions/gh_787_spec.rb +19 -0
  116. data/spec/nanoc/regressions/gh_795_spec.rb +19 -0
  117. data/spec/nanoc/regressions/gh_804_spec.rb +26 -0
  118. data/spec/nanoc/regressions/gh_807_spec.rb +17 -0
  119. data/spec/nanoc/regressions/gh_809_spec.rb +17 -0
  120. data/spec/nanoc/regressions/gh_813_spec.rb +22 -0
  121. data/spec/nanoc/regressions/gh_815_spec.rb +18 -0
  122. data/spec/nanoc/regressions/gh_828_spec.rb +23 -0
  123. data/spec/nanoc/regressions/gh_833_spec.rb +14 -0
  124. data/spec/nanoc/regressions/gh_841_spec.rb +15 -0
  125. data/spec/nanoc/regressions/gh_867_spec.rb +15 -0
  126. data/spec/nanoc/regressions/gh_882_spec.rb +29 -0
  127. data/spec/nanoc/regressions/gh_885_spec.rb +30 -0
  128. data/spec/nanoc/regressions/gh_891_spec.rb +26 -0
  129. data/spec/nanoc/regressions/gh_913_spec.rb +24 -0
  130. data/spec/nanoc/regressions/gh_928_spec.rb +5 -0
  131. data/spec/nanoc/regressions/gh_937_spec.rb +25 -0
  132. data/spec/nanoc/regressions/gh_942_spec.rb +21 -0
  133. data/spec/nanoc/regressions/gh_947_spec.rb +21 -0
  134. data/spec/nanoc/regressions/gh_948_spec.rb +16 -0
  135. data/spec/nanoc/regressions/gh_951_spec.rb +19 -0
  136. data/spec/nanoc/regressions/gh_954_spec.rb +33 -0
  137. data/spec/nanoc/regressions/gh_970a_spec.rb +17 -0
  138. data/spec/nanoc/regressions/gh_970b_spec.rb +50 -0
  139. data/spec/nanoc/regressions/gh_974_spec.rb +17 -0
  140. data/spec/nanoc/regressions/gh_981_spec.rb +21 -0
  141. data/spec/nanoc/rule_dsl/recording_executor_spec.rb +142 -0
  142. data/spec/nanoc/rule_dsl/rule_context_spec.rb +177 -0
  143. data/spec/nanoc/rule_dsl/rule_memory_calculator_spec.rb +233 -0
  144. data/spec/nanoc/rule_dsl/rules_collection_spec.rb +299 -0
  145. data/spec/regression_filenames_spec.rb +16 -0
  146. data/spec/spec_helper.rb +173 -0
  147. data/test/base/core_ext/array_spec.rb +2 -0
  148. data/test/base/core_ext/hash_spec.rb +2 -0
  149. data/test/base/core_ext/string_spec.rb +2 -0
  150. data/test/base/test_code_snippet.rb +2 -0
  151. data/test/base/test_compiler.rb +2 -0
  152. data/test/base/test_context.rb +4 -2
  153. data/test/base/test_data_source.rb +2 -0
  154. data/test/base/test_dependency_tracker.rb +2 -0
  155. data/test/base/test_directed_graph.rb +2 -0
  156. data/test/base/test_filter.rb +7 -5
  157. data/test/base/test_item.rb +2 -0
  158. data/test/base/test_item_array.rb +3 -1
  159. data/test/base/test_layout.rb +2 -0
  160. data/test/base/test_memoization.rb +2 -0
  161. data/test/base/test_notification_center.rb +2 -0
  162. data/test/base/test_outdatedness_checker.rb +2 -0
  163. data/test/base/test_plugin.rb +2 -0
  164. data/test/base/test_site.rb +2 -0
  165. data/test/base/test_store.rb +2 -0
  166. data/test/checking/checks/test_css.rb +2 -0
  167. data/test/checking/checks/test_external_links.rb +4 -2
  168. data/test/checking/checks/test_html.rb +2 -0
  169. data/test/checking/checks/test_internal_links.rb +5 -3
  170. data/test/checking/checks/test_mixed_content.rb +2 -0
  171. data/test/checking/checks/test_stale.rb +2 -0
  172. data/test/checking/test_check.rb +2 -0
  173. data/test/checking/test_dsl.rb +2 -0
  174. data/test/checking/test_runner.rb +2 -0
  175. data/test/cli/commands/test_check.rb +2 -0
  176. data/test/cli/commands/test_compile.rb +2 -0
  177. data/test/cli/commands/test_create_site.rb +2 -0
  178. data/test/cli/commands/test_help.rb +2 -0
  179. data/test/cli/commands/test_info.rb +2 -0
  180. data/test/cli/commands/test_prune.rb +2 -0
  181. data/test/cli/test_cleaning_stream.rb +2 -0
  182. data/test/cli/test_cli.rb +11 -9
  183. data/test/cli/test_error_handler.rb +2 -0
  184. data/test/cli/test_logger.rb +2 -0
  185. data/test/data_sources/test_filesystem.rb +8 -6
  186. data/test/data_sources/test_filesystem_tools.rb +2 -0
  187. data/test/deploying/test_fog.rb +2 -0
  188. data/test/deploying/test_rsync.rb +4 -2
  189. data/test/extra/core_ext/test_pathname.rb +2 -0
  190. data/test/extra/core_ext/test_time.rb +2 -0
  191. data/test/extra/test_link_collector.rb +2 -0
  192. data/test/extra/test_piper.rb +2 -0
  193. data/test/filters/colorize_syntax/test_coderay.rb +2 -0
  194. data/test/filters/colorize_syntax/test_common.rb +2 -0
  195. data/test/filters/colorize_syntax/test_pygmentize.rb +2 -0
  196. data/test/filters/colorize_syntax/test_pygments.rb +2 -0
  197. data/test/filters/colorize_syntax/test_simon.rb +2 -0
  198. data/test/filters/test_asciidoc.rb +2 -0
  199. data/test/filters/test_bluecloth.rb +2 -0
  200. data/test/filters/test_coffeescript.rb +2 -0
  201. data/test/filters/test_erb.rb +7 -5
  202. data/test/filters/test_erubis.rb +6 -4
  203. data/test/filters/test_haml.rb +7 -5
  204. data/test/filters/test_handlebars.rb +2 -0
  205. data/test/filters/test_kramdown.rb +2 -0
  206. data/test/filters/test_markaby.rb +2 -0
  207. data/test/filters/test_maruku.rb +2 -0
  208. data/test/filters/test_mustache.rb +4 -2
  209. data/test/filters/test_pandoc.rb +2 -0
  210. data/test/filters/test_rainpress.rb +2 -0
  211. data/test/filters/test_rdiscount.rb +2 -0
  212. data/test/filters/test_rdoc.rb +2 -0
  213. data/test/filters/test_redcarpet.rb +2 -0
  214. data/test/filters/test_redcloth.rb +2 -0
  215. data/test/filters/test_relativize_paths.rb +2 -0
  216. data/test/filters/test_rubypants.rb +2 -0
  217. data/test/filters/test_sass.rb +4 -2
  218. data/test/filters/test_slim.rb +4 -2
  219. data/test/filters/test_typogruby.rb +2 -0
  220. data/test/filters/test_uglify_js.rb +2 -0
  221. data/test/filters/test_xsl.rb +2 -0
  222. data/test/filters/test_yui_compressor.rb +5 -3
  223. data/test/helpers/test_blogging.rb +2 -0
  224. data/test/helpers/test_capturing.rb +2 -0
  225. data/test/helpers/test_link_to.rb +2 -0
  226. data/test/helpers/test_xml_sitemap.rb +2 -0
  227. data/test/rule_dsl/test_action_provider.rb +2 -0
  228. data/test/rule_dsl/test_compiler_dsl.rb +6 -4
  229. data/test/rule_dsl/test_rule.rb +2 -0
  230. data/test/rule_dsl/test_rules_collection.rb +2 -0
  231. data/test/test_gem.rb +2 -0
  232. metadata +124 -17
  233. data/test/base/test_item_rep.rb +0 -156
@@ -0,0 +1,495 @@
1
+ describe Nanoc::Int::Executor do
2
+ let(:executor) { described_class.new(rep, compilation_context, dependency_tracker) }
3
+
4
+ let(:compilation_context) do
5
+ Nanoc::Int::Compiler::CompilationContext.new(
6
+ action_provider: action_provider,
7
+ reps: reps,
8
+ site: site,
9
+ compiled_content_cache: compiled_content_cache,
10
+ )
11
+ end
12
+
13
+ let(:item) { Nanoc::Int::Item.new(content, {}, '/index.md') }
14
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :donkey) }
15
+ let(:content) { Nanoc::Int::TextualContent.new('Donkey Power').tap(&:freeze) }
16
+
17
+ let(:action_provider) { double(:action_provider) }
18
+ let(:reps) { double(:reps) }
19
+ let(:site) { double(:site) }
20
+ let(:compiled_content_cache) { double(:compiled_content_cache) }
21
+
22
+ let(:dependency_tracker) { Nanoc::Int::DependencyTracker.new(double(:dependency_store)) }
23
+
24
+ describe '#filter' do
25
+ let(:assigns) { {} }
26
+
27
+ let(:content) { Nanoc::Int::TextualContent.new('<%= "Donkey" %> Power') }
28
+
29
+ before do
30
+ allow(compilation_context).to receive(:assigns_for) { assigns }
31
+ end
32
+
33
+ context 'normal flow with textual rep' do
34
+ before do
35
+ expect(Nanoc::Int::NotificationCenter)
36
+ .to receive(:post).with(:filtering_started, rep, :erb)
37
+ expect(Nanoc::Int::NotificationCenter)
38
+ .to receive(:post).with(:filtering_ended, rep, :erb)
39
+ end
40
+
41
+ example do
42
+ executor.filter(:erb)
43
+
44
+ expect(rep.snapshot_contents[:last].string).to eq('Donkey Power')
45
+ expect(rep.snapshot_contents[:pre]).to be_nil
46
+ expect(rep.snapshot_contents[:post]).to be_nil
47
+ end
48
+
49
+ it 'returns frozen data' do
50
+ executor.filter(:erb)
51
+
52
+ expect(rep.snapshot_contents[:last]).to be_frozen
53
+ end
54
+ end
55
+
56
+ context 'normal flow with binary rep' do
57
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('foo.dat')) }
58
+
59
+ before do
60
+ expect(Nanoc::Int::NotificationCenter)
61
+ .to receive(:post).with(:filtering_started, rep, :whatever)
62
+ expect(Nanoc::Int::NotificationCenter)
63
+ .to receive(:post).with(:filtering_ended, rep, :whatever)
64
+
65
+ File.write(content.filename, 'Foo Data')
66
+
67
+ filter_class = Class.new(::Nanoc::Filter) do
68
+ type :binary
69
+
70
+ def run(filename, _params = {})
71
+ File.write(output_filename, "Compiled data for #{filename}")
72
+ end
73
+ end
74
+
75
+ expect(Nanoc::Filter).to receive(:named).with(:whatever) { filter_class }
76
+ end
77
+
78
+ example do
79
+ executor.filter(:whatever)
80
+
81
+ expect(File.read(rep.snapshot_contents[:last].filename))
82
+ .to match(/\ACompiled data for \/.*\/foo.dat\z/)
83
+ expect(rep.snapshot_contents[:pre]).to be_nil
84
+ expect(rep.snapshot_contents[:post]).to be_nil
85
+ end
86
+
87
+ it 'returns frozen data' do
88
+ executor.filter(:whatever)
89
+
90
+ expect(rep.snapshot_contents[:last]).to be_frozen
91
+ end
92
+ end
93
+
94
+ context 'normal flow with binary rep and binary-to-text filter' do
95
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('foo.dat')) }
96
+
97
+ before do
98
+ expect(Nanoc::Int::NotificationCenter)
99
+ .to receive(:post).with(:filtering_started, rep, :whatever)
100
+ expect(Nanoc::Int::NotificationCenter)
101
+ .to receive(:post).with(:filtering_ended, rep, :whatever)
102
+
103
+ File.write(content.filename, 'Foo Data')
104
+
105
+ filter_class = Class.new(::Nanoc::Filter) do
106
+ type binary: :text
107
+
108
+ def run(filename, _params = {})
109
+ "Compiled data for #{filename}"
110
+ end
111
+ end
112
+
113
+ expect(Nanoc::Filter).to receive(:named).with(:whatever) { filter_class }
114
+ end
115
+
116
+ example do
117
+ executor.filter(:whatever)
118
+
119
+ expect(rep.snapshot_contents[:last].string).to match(/\ACompiled data for \/.*\/foo.dat\z/)
120
+ expect(rep.snapshot_contents[:pre]).to be_nil
121
+ expect(rep.snapshot_contents[:post]).to be_nil
122
+ end
123
+ end
124
+
125
+ context 'normal flow with textual rep and text-to-binary filter' do
126
+ before do
127
+ expect(Nanoc::Int::NotificationCenter)
128
+ .to receive(:post).with(:filtering_started, rep, :whatever)
129
+ expect(Nanoc::Int::NotificationCenter)
130
+ .to receive(:post).with(:filtering_ended, rep, :whatever)
131
+
132
+ filter_class = Class.new(::Nanoc::Filter) do
133
+ type text: :binary
134
+
135
+ def run(content, _params = {})
136
+ File.write(output_filename, "Binary #{content}")
137
+ end
138
+ end
139
+
140
+ expect(Nanoc::Filter).to receive(:named).with(:whatever) { filter_class }
141
+ end
142
+
143
+ example do
144
+ executor.filter(:whatever)
145
+
146
+ expect(File.read(rep.snapshot_contents[:last].filename))
147
+ .to eq('Binary <%= "Donkey" %> Power')
148
+ expect(rep.snapshot_contents[:pre]).to be_nil
149
+ expect(rep.snapshot_contents[:post]).to be_nil
150
+ end
151
+ end
152
+
153
+ context 'non-existant filter' do
154
+ it 'raises' do
155
+ expect { executor.filter(:ajlsdfjklaskldfj) }
156
+ .to raise_error(Nanoc::Int::Errors::UnknownFilter)
157
+ end
158
+ end
159
+
160
+ context 'non-binary rep, binary-to-something filter' do
161
+ before do
162
+ filter_class = Class.new(::Nanoc::Filter) do
163
+ type :binary
164
+
165
+ def run(_content, _params = {}); end
166
+ end
167
+
168
+ expect(Nanoc::Filter).to receive(:named).with(:whatever) { filter_class }
169
+ end
170
+
171
+ it 'raises' do
172
+ expect { executor.filter(:whatever) }
173
+ .to raise_error(Nanoc::Int::Errors::CannotUseBinaryFilter)
174
+ end
175
+ end
176
+
177
+ context 'binary rep, text-to-something filter' do
178
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('foo.md')) }
179
+
180
+ it 'raises' do
181
+ expect { executor.filter(:erb) }
182
+ .to raise_error(Nanoc::Int::Errors::CannotUseTextualFilter)
183
+ end
184
+ end
185
+
186
+ context 'binary filter that does not write anything' do
187
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('foo.dat')) }
188
+
189
+ before do
190
+ expect(Nanoc::Int::NotificationCenter)
191
+ .to receive(:post).with(:filtering_started, rep, :whatever)
192
+ expect(Nanoc::Int::NotificationCenter)
193
+ .to receive(:post).with(:filtering_ended, rep, :whatever)
194
+
195
+ File.write(content.filename, 'Foo Data')
196
+
197
+ filter_class = Class.new(::Nanoc::Filter) do
198
+ type :binary
199
+
200
+ def run(_filename, _params = {}); end
201
+ end
202
+
203
+ expect(Nanoc::Filter).to receive(:named).with(:whatever) { filter_class }
204
+ end
205
+
206
+ example do
207
+ expect { executor.filter(:whatever) }
208
+ .to raise_error(Nanoc::Int::Executor::OutputNotWrittenError)
209
+ end
210
+ end
211
+
212
+ context 'content is frozen' do
213
+ let(:item) do
214
+ Nanoc::Int::Item.new('foo bar', {}, '/foo.md').tap(&:freeze)
215
+ end
216
+
217
+ let(:filter_that_modifies_content) do
218
+ Class.new(::Nanoc::Filter) do
219
+ def run(content, _params = {})
220
+ content.gsub!('foo', 'moo')
221
+ content
222
+ end
223
+ end
224
+ end
225
+
226
+ let(:filter_that_modifies_params) do
227
+ Class.new(::Nanoc::Filter) do
228
+ def run(_content, params = {})
229
+ params[:foo] = 'bar'
230
+ 'asdf'
231
+ end
232
+ end
233
+ end
234
+
235
+ it 'errors when attempting to modify content' do
236
+ expect(Nanoc::Filter).to receive(:named).with(:whatever).and_return(filter_that_modifies_content)
237
+ expect { executor.filter(:whatever) }.to raise_frozen_error
238
+ end
239
+
240
+ it 'receives frozen filter args' do
241
+ expect(Nanoc::Filter).to receive(:named).with(:whatever).and_return(filter_that_modifies_params)
242
+ expect { executor.filter(:whatever) }.to raise_frozen_error
243
+ end
244
+ end
245
+ end
246
+
247
+ describe '#layout' do
248
+ let(:site) { double(:site, config: config, layouts: layouts) }
249
+
250
+ let(:config) do
251
+ {
252
+ string_pattern_type: 'glob',
253
+ }
254
+ end
255
+
256
+ let(:layout) do
257
+ Nanoc::Int::Layout.new(layout_content, { bug: 'Gum Emperor' }, '/default.erb')
258
+ end
259
+
260
+ let(:layouts) { [layout] }
261
+
262
+ let(:layout_content) { 'head <%= @foo %> foot' }
263
+
264
+ let(:assigns) do
265
+ { foo: 'hallo' }
266
+ end
267
+
268
+ let(:view_context) do
269
+ Nanoc::ViewContext.new(
270
+ reps: double(:reps),
271
+ items: double(:items),
272
+ dependency_tracker: dependency_tracker,
273
+ compilation_context: double(:compilation_context),
274
+ )
275
+ end
276
+
277
+ let(:rule_memory) do
278
+ Nanoc::Int::RuleMemory.new(rep).tap do |mem|
279
+ mem.add_filter(:erb, {})
280
+ end
281
+ end
282
+
283
+ before do
284
+ rep.snapshot_defs = [Nanoc::Int::SnapshotDef.new(:pre)]
285
+
286
+ allow(compilation_context).to receive(:site) { site }
287
+ allow(compilation_context).to receive(:assigns_for).with(rep, dependency_tracker) { assigns }
288
+ allow(compilation_context).to receive(:create_view_context).with(dependency_tracker).and_return(view_context)
289
+
290
+ allow(action_provider).to receive(:memory_for).with(layout).and_return(rule_memory)
291
+ end
292
+
293
+ subject { executor.layout('/default.*') }
294
+
295
+ context 'accessing layout attributes' do
296
+ let(:layout_content) { 'head <%= @layout[:bug] %> foot' }
297
+
298
+ it 'exposes @layout as view' do
299
+ allow(dependency_tracker).to receive(:enter)
300
+ .with(layout, raw_content: true, attributes: false, compiled_content: false, path: false)
301
+ allow(dependency_tracker).to receive(:enter)
302
+ .with(layout, raw_content: false, attributes: true, compiled_content: false, path: false)
303
+ allow(dependency_tracker).to receive(:exit)
304
+ subject
305
+ expect(rep.snapshot_contents[:last].string).to eq('head Gum Emperor foot')
306
+ end
307
+ end
308
+
309
+ context 'normal flow' do
310
+ it 'updates last content' do
311
+ subject
312
+ expect(rep.snapshot_contents[:last].string).to eq('head hallo foot')
313
+ end
314
+
315
+ it 'sets frozen content' do
316
+ subject
317
+ expect(rep.snapshot_contents[:last]).to be_frozen
318
+ expect(rep.snapshot_contents[:pre]).to be_frozen
319
+ end
320
+
321
+ it 'does not create pre snapshot' do
322
+ # a #layout is followed by a #snapshot(:pre, …)
323
+ expect(rep.snapshot_contents[:pre]).to be_nil
324
+ subject
325
+ expect(rep.snapshot_contents[:pre]).to be_nil
326
+ end
327
+
328
+ it 'sends notifications' do
329
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:filtering_started, rep, :erb).ordered
330
+ expect(Nanoc::Int::NotificationCenter).to receive(:post).with(:filtering_ended, rep, :erb).ordered
331
+
332
+ subject
333
+ end
334
+
335
+ context 'compiled_content reference in layout' do
336
+ let(:layout_content) { 'head <%= @item_rep.compiled_content(snapshot: :pre) %> foot' }
337
+
338
+ let(:assigns) do
339
+ { item_rep: Nanoc::ItemRepView.new(rep, view_context) }
340
+ end
341
+
342
+ before do
343
+ executor.snapshot(:pre)
344
+ end
345
+
346
+ it 'can contain compiled_content reference' do
347
+ subject
348
+ expect(rep.snapshot_contents[:last].string).to eq('head Donkey Power foot')
349
+ end
350
+ end
351
+
352
+ context 'content with layout reference' do
353
+ let(:layout_content) { 'head <%= @layout.identifier %> foot' }
354
+
355
+ it 'includes layout in assigns' do
356
+ subject
357
+ expect(rep.snapshot_contents[:last].string).to eq('head /default.erb foot')
358
+ end
359
+ end
360
+ end
361
+
362
+ context 'no layout found' do
363
+ let(:layouts) do
364
+ [Nanoc::Int::Layout.new('head <%= @foo %> foot', {}, '/other.erb')]
365
+ end
366
+
367
+ it 'raises' do
368
+ expect { subject }.to raise_error(Nanoc::Int::Errors::UnknownLayout)
369
+ end
370
+ end
371
+
372
+ context 'no filter specified' do
373
+ let(:rule_memory) do
374
+ Nanoc::Int::RuleMemory.new(rep)
375
+ end
376
+
377
+ it 'raises' do
378
+ expect { subject }.to raise_error(Nanoc::Int::Errors::UndefinedFilterForLayout)
379
+ end
380
+ end
381
+
382
+ context 'binary item' do
383
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('donkey.md')) }
384
+
385
+ it 'raises' do
386
+ expect { subject }.to raise_error(Nanoc::Int::Errors::CannotLayoutBinaryItem)
387
+ end
388
+ end
389
+
390
+ it 'receives frozen filter args' do
391
+ filter_class = Class.new(::Nanoc::Filter) do
392
+ def run(_content, params = {})
393
+ params[:foo] = 'bar'
394
+ 'asdf'
395
+ end
396
+ end
397
+
398
+ expect(Nanoc::Filter).to receive(:named).with(:erb) { filter_class }
399
+
400
+ expect { subject }.to raise_frozen_error
401
+ end
402
+ end
403
+
404
+ describe '#snapshot' do
405
+ context 'binary content' do
406
+ let(:content) { Nanoc::Int::BinaryContent.new(File.expand_path('donkey.dat')) }
407
+
408
+ it 'creates snapshots' do
409
+ executor.snapshot(:something)
410
+
411
+ expect(rep.snapshot_contents[:something]).not_to be_nil
412
+ end
413
+ end
414
+
415
+ context 'textual content' do
416
+ let(:content) { Nanoc::Int::TextualContent.new('Donkey Power') }
417
+
418
+ it 'creates a snapshot' do
419
+ executor.snapshot(:something)
420
+
421
+ expect(rep.snapshot_contents[:something].string).to eq('Donkey Power')
422
+ end
423
+ end
424
+
425
+ context 'final snapshot' do
426
+ let(:content) { Nanoc::Int::TextualContent.new('Donkey Power') }
427
+
428
+ context 'raw path' do
429
+ before do
430
+ rep.raw_paths = { something: 'output/donkey.md' }
431
+ end
432
+
433
+ it 'does not write' do
434
+ executor.snapshot(:something)
435
+
436
+ expect(File.file?('output/donkey.md')).not_to be
437
+ end
438
+ end
439
+
440
+ context 'no raw path' do
441
+ it 'does not write' do
442
+ executor.snapshot(:something)
443
+
444
+ expect(File.file?('output/donkey.md')).to eq(false)
445
+ end
446
+ end
447
+ end
448
+ end
449
+
450
+ describe '#find_layout' do
451
+ let(:site) { double(:site, config: config, layouts: layouts) }
452
+
453
+ let(:config) { {} }
454
+
455
+ before do
456
+ allow(compilation_context).to receive(:site) { site }
457
+ end
458
+
459
+ subject { executor.find_layout(arg) }
460
+
461
+ context 'layout with cleaned identifier exists' do
462
+ let(:arg) { '/default' }
463
+
464
+ let(:layouts) do
465
+ [Nanoc::Int::Layout.new('head <%= @foo %> foot', {}, '/default/')]
466
+ end
467
+
468
+ it { is_expected.to eq(layouts[0]) }
469
+ end
470
+
471
+ context 'no layout with cleaned identifier exists' do
472
+ let(:layouts) do
473
+ [Nanoc::Int::Layout.new('head <%= @foo %> foot', {}, '/default.erb')]
474
+ end
475
+
476
+ context 'globs' do
477
+ let(:config) { { string_pattern_type: 'glob' } }
478
+
479
+ let(:arg) { '/default.*' }
480
+
481
+ it { is_expected.to eq(layouts[0]) }
482
+ end
483
+
484
+ context 'no globs' do
485
+ let(:config) { { string_pattern_type: 'legacy' } }
486
+
487
+ let(:arg) { '/default.*' }
488
+
489
+ it 'raises' do
490
+ expect { subject }.to raise_error(Nanoc::Int::Errors::UnknownLayout)
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end
@@ -0,0 +1,134 @@
1
+ describe(Nanoc::Int::ItemRepRouter) do
2
+ subject(:item_rep_router) { described_class.new(reps, action_provider, site) }
3
+
4
+ let(:reps) { double(:reps) }
5
+ let(:action_provider) { double(:action_provider) }
6
+ let(:site) { double(:site, config: config) }
7
+ let(:config) { Nanoc::Int::Configuration.new.with_defaults }
8
+
9
+ describe '#run' do
10
+ subject { item_rep_router.run }
11
+
12
+ let(:item) { Nanoc::Int::Item.new('content', {}, '/foo.md') }
13
+
14
+ let(:reps) do
15
+ [
16
+ Nanoc::Int::ItemRep.new(item, :default),
17
+ Nanoc::Int::ItemRep.new(item, :csv),
18
+ ]
19
+ end
20
+
21
+ let(:paths_0) do
22
+ { last: '/foo/index.html' }
23
+ end
24
+
25
+ let(:paths_1) do
26
+ { last: '/bar.html' }
27
+ end
28
+
29
+ example do
30
+ expect(action_provider).to receive(:paths_for).with(reps[0]).and_return(paths_0)
31
+ expect(action_provider).to receive(:paths_for).with(reps[1]).and_return(paths_1)
32
+
33
+ subject
34
+
35
+ expect(reps[0].raw_paths).to eql(last: 'output/foo/index.html')
36
+ expect(reps[0].paths).to eql(last: '/foo/')
37
+
38
+ expect(reps[1].raw_paths).to eql(last: 'output/bar.html')
39
+ expect(reps[1].paths).to eql(last: '/bar.html')
40
+ end
41
+ end
42
+
43
+ describe '#route_rep' do
44
+ subject { item_rep_router.route_rep(rep, path, snapshot_name, paths_to_reps) }
45
+
46
+ let(:path) { basic_path }
47
+ let(:snapshot_name) { :foo }
48
+ let(:rep) { Nanoc::Int::ItemRep.new(item, :default) }
49
+ let(:item) { Nanoc::Int::Item.new('content', {}, '/foo.md') }
50
+ let(:paths_to_reps) { {} }
51
+
52
+ context 'basic path is nil' do
53
+ let(:basic_path) { nil }
54
+ it { is_expected.to be_nil }
55
+ end
56
+
57
+ context 'basic path is not nil' do
58
+ let(:basic_path) { '/foo/index.html' }
59
+
60
+ context 'other snapshot with this path already exists' do
61
+ let(:paths_to_reps) { { '/foo/index.html' => double(:other_rep) } }
62
+
63
+ it 'errors' do
64
+ expect { subject }.to raise_error(Nanoc::Int::ItemRepRouter::IdenticalRoutesError)
65
+ end
66
+ end
67
+
68
+ context 'path is unique' do
69
+ it 'sets the raw path' do
70
+ subject
71
+ expect(rep.raw_paths).to eql(foo: 'output/foo/index.html')
72
+ end
73
+
74
+ it 'sets the path' do
75
+ subject
76
+ expect(rep.paths).to eql(foo: '/foo/')
77
+ end
78
+
79
+ it 'adds to paths_to_reps' do
80
+ subject
81
+ expect(paths_to_reps).to have_key('/foo/index.html')
82
+ end
83
+
84
+ context 'path does not start with a slash' do
85
+ let(:basic_path) { 'foo/index.html' }
86
+
87
+ it 'errors' do
88
+ expect { subject }.to raise_error(Nanoc::Int::ItemRepRouter::RouteWithoutSlashError)
89
+ end
90
+ end
91
+
92
+ context 'path is not UTF-8' do
93
+ let(:basic_path) { '/foo/index.html'.encode('ISO-8859-1') }
94
+
95
+ it 'sets the path as UTF-8' do
96
+ subject
97
+ expect(rep.paths).to eql(foo: '/foo/')
98
+ expect(rep.paths[:foo].encoding.to_s).to eql('UTF-8')
99
+ end
100
+
101
+ it 'sets the raw path as UTF-8' do
102
+ subject
103
+ expect(rep.raw_paths).to eql(foo: 'output/foo/index.html')
104
+ expect(rep.raw_paths[:foo].encoding.to_s).to eql('UTF-8')
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ describe '#strip_index_filename' do
112
+ subject { item_rep_router.strip_index_filename(basic_path) }
113
+
114
+ context 'basic path ends with /index.html' do
115
+ let(:basic_path) { '/bar/index.html' }
116
+ it { is_expected.to eql('/bar/') }
117
+ end
118
+
119
+ context 'basic path contains /index.html' do
120
+ let(:basic_path) { '/bar/index.html/foo' }
121
+ it { is_expected.to eql('/bar/index.html/foo') }
122
+ end
123
+
124
+ context 'basic path ends with xindex.html' do
125
+ let(:basic_path) { '/bar/xindex.html' }
126
+ it { is_expected.to eql('/bar/xindex.html') }
127
+ end
128
+
129
+ context 'basic path does not contain /index.html' do
130
+ let(:basic_path) { '/bar/foo.html' }
131
+ it { is_expected.to eql('/bar/foo.html') }
132
+ end
133
+ end
134
+ end