nanoc3 3.2.0a3 → 3.2.0a4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/.gemtest +0 -0
  2. data/LICENSE +1 -1
  3. data/NEWS.md +23 -4
  4. data/README.md +7 -0
  5. data/lib/nanoc3/base/compilation/checksum_store.rb +17 -90
  6. data/lib/nanoc3/base/compilation/compiled_content_cache.rb +5 -0
  7. data/lib/nanoc3/base/compilation/compiler.rb +112 -175
  8. data/lib/nanoc3/base/compilation/compiler_dsl.rb +54 -11
  9. data/lib/nanoc3/base/compilation/dependency_tracker.rb +32 -65
  10. data/lib/nanoc3/base/compilation/filter.rb +4 -3
  11. data/lib/nanoc3/base/compilation/item_rep_proxy.rb +19 -4
  12. data/lib/nanoc3/base/compilation/item_rep_recorder_proxy.rb +90 -0
  13. data/lib/nanoc3/base/compilation/outdatedness_checker.rb +152 -15
  14. data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +12 -9
  15. data/lib/nanoc3/base/compilation/rule.rb +3 -1
  16. data/lib/nanoc3/base/compilation/rule_memory_calculator.rb +42 -0
  17. data/lib/nanoc3/base/compilation/rule_memory_store.rb +53 -0
  18. data/lib/nanoc3/base/compilation/rules_collection.rb +205 -0
  19. data/lib/nanoc3/base/core_ext/array.rb +20 -0
  20. data/lib/nanoc3/base/core_ext/hash.rb +30 -0
  21. data/lib/nanoc3/base/core_ext/pathname.rb +26 -0
  22. data/lib/nanoc3/base/core_ext/string.rb +12 -0
  23. data/lib/nanoc3/base/core_ext.rb +1 -0
  24. data/lib/nanoc3/base/directed_graph.rb +11 -3
  25. data/lib/nanoc3/base/errors.rb +0 -4
  26. data/lib/nanoc3/base/memoization.rb +72 -0
  27. data/lib/nanoc3/base/result_data/item_rep.rb +64 -25
  28. data/lib/nanoc3/base/source_data/code_snippet.rb +9 -0
  29. data/lib/nanoc3/base/source_data/configuration.rb +20 -0
  30. data/lib/nanoc3/base/source_data/item.rb +29 -4
  31. data/lib/nanoc3/base/source_data/layout.rb +20 -1
  32. data/lib/nanoc3/base/source_data/site.rb +49 -26
  33. data/lib/nanoc3/base/store.rb +10 -1
  34. data/lib/nanoc3/base.rb +6 -1
  35. data/lib/nanoc3/cli/base.rb +20 -7
  36. data/lib/nanoc3/cli/commands/compile.rb +0 -2
  37. data/lib/nanoc3/cli/commands/create_site.rb +16 -7
  38. data/lib/nanoc3/cli/commands/debug.rb +3 -3
  39. data/lib/nanoc3/cli/commands/view.rb +1 -0
  40. data/lib/nanoc3/cli/commands/watch.rb +2 -1
  41. data/lib/nanoc3/data_sources/deprecated/delicious.rb +0 -2
  42. data/lib/nanoc3/data_sources/deprecated/last_fm.rb +0 -2
  43. data/lib/nanoc3/data_sources/deprecated/twitter.rb +0 -2
  44. data/lib/nanoc3/data_sources/filesystem.rb +17 -3
  45. data/lib/nanoc3/data_sources/filesystem_unified.rb +17 -17
  46. data/lib/nanoc3/extra/auto_compiler.rb +5 -1
  47. data/lib/nanoc3/extra/core_ext/time.rb +1 -1
  48. data/lib/nanoc3/extra/file_proxy.rb +11 -1
  49. data/lib/nanoc3/extra/validators/links.rb +1 -1
  50. data/lib/nanoc3/filters/asciidoc.rb +3 -3
  51. data/lib/nanoc3/filters/colorize_syntax.rb +106 -27
  52. data/lib/nanoc3/filters/erb.rb +16 -6
  53. data/lib/nanoc3/filters/erubis.rb +5 -1
  54. data/lib/nanoc3/filters/haml.rb +2 -1
  55. data/lib/nanoc3/filters/less.rb +3 -6
  56. data/lib/nanoc3/filters/mustache.rb +3 -0
  57. data/lib/nanoc3/filters/redcarpet.rb +27 -0
  58. data/lib/nanoc3/filters/sass.rb +1 -5
  59. data/lib/nanoc3/filters/slim.rb +25 -0
  60. data/lib/nanoc3/filters/typogruby.rb +23 -0
  61. data/lib/nanoc3/filters.rb +6 -0
  62. data/lib/nanoc3/helpers/blogging.rb +22 -26
  63. data/lib/nanoc3/helpers/rendering.rb +1 -1
  64. data/lib/nanoc3/helpers/xml_sitemap.rb +11 -2
  65. data/lib/nanoc3.rb +24 -3
  66. data/nanoc3.gemspec +4 -3
  67. data/tasks/clean.rake +11 -0
  68. data/tasks/doc.rake +14 -0
  69. data/tasks/test.rake +38 -0
  70. data/test/base/core_ext/array_spec.rb +55 -0
  71. data/test/base/core_ext/hash_spec.rb +82 -0
  72. data/test/base/core_ext/pathname_spec.rb +29 -0
  73. data/test/base/core_ext/string_spec.rb +39 -0
  74. data/test/base/test_checksum_store.rb +37 -0
  75. data/test/base/test_code_snippet.rb +33 -0
  76. data/test/base/test_compiler.rb +303 -0
  77. data/test/base/test_compiler_dsl.rb +156 -0
  78. data/test/base/test_context.rb +33 -0
  79. data/test/base/test_data_source.rb +48 -0
  80. data/test/base/test_dependency_tracker.rb +264 -0
  81. data/test/base/test_directed_graph.rb +285 -0
  82. data/test/base/test_filter.rb +85 -0
  83. data/test/base/test_item.rb +164 -0
  84. data/test/base/test_item_rep.rb +555 -0
  85. data/test/base/test_layout.rb +44 -0
  86. data/test/base/test_memoization.rb +53 -0
  87. data/test/base/test_notification_center.rb +36 -0
  88. data/test/base/test_outdatedness_checker.rb +365 -0
  89. data/test/base/test_plugin.rb +32 -0
  90. data/test/base/test_rule.rb +21 -0
  91. data/test/base/test_rule_context.rb +67 -0
  92. data/test/base/test_site.rb +144 -0
  93. data/test/cli/commands/test_compile.rb +12 -0
  94. data/test/cli/commands/test_create_item.rb +12 -0
  95. data/test/cli/commands/test_create_layout.rb +28 -0
  96. data/test/cli/commands/test_create_site.rb +24 -0
  97. data/test/cli/commands/test_help.rb +12 -0
  98. data/test/cli/commands/test_info.rb +12 -0
  99. data/test/cli/commands/test_update.rb +12 -0
  100. data/test/cli/test_logger.rb +12 -0
  101. data/test/data_sources/test_filesystem.rb +420 -0
  102. data/test/data_sources/test_filesystem_unified.rb +562 -0
  103. data/test/data_sources/test_filesystem_verbose.rb +359 -0
  104. data/test/extra/core_ext/test_enumerable.rb +32 -0
  105. data/test/extra/core_ext/test_time.rb +17 -0
  106. data/test/extra/deployers/test_rsync.rb +234 -0
  107. data/test/extra/test_auto_compiler.rb +417 -0
  108. data/test/extra/test_file_proxy.rb +21 -0
  109. data/test/extra/test_vcs.rb +24 -0
  110. data/test/extra/validators/test_links.rb +53 -0
  111. data/test/extra/validators/test_w3c.rb +49 -0
  112. data/test/filters/test_asciidoc.rb +22 -0
  113. data/test/filters/test_bluecloth.rb +20 -0
  114. data/test/filters/test_coderay.rb +46 -0
  115. data/test/filters/test_colorize_syntax.rb +149 -0
  116. data/test/filters/test_erb.rb +101 -0
  117. data/test/filters/test_erubis.rb +72 -0
  118. data/test/filters/test_haml.rb +98 -0
  119. data/test/filters/test_kramdown.rb +20 -0
  120. data/test/filters/test_less.rb +59 -0
  121. data/test/filters/test_markaby.rb +26 -0
  122. data/test/filters/test_maruku.rb +20 -0
  123. data/test/filters/test_mustache.rb +27 -0
  124. data/test/filters/test_rainpress.rb +31 -0
  125. data/test/filters/test_rdiscount.rb +33 -0
  126. data/test/filters/test_rdoc.rb +18 -0
  127. data/test/filters/test_redcarpet.rb +63 -0
  128. data/test/filters/test_redcloth.rb +35 -0
  129. data/test/filters/test_relativize_paths.rb +231 -0
  130. data/test/filters/test_rubypants.rb +20 -0
  131. data/test/filters/test_sass.rb +103 -0
  132. data/test/filters/test_slim.rb +37 -0
  133. data/test/filters/test_typogruby.rb +23 -0
  134. data/test/helper.rb +161 -0
  135. data/test/helpers/test_blogging.rb +756 -0
  136. data/test/helpers/test_breadcrumbs.rb +83 -0
  137. data/test/helpers/test_capturing.rb +43 -0
  138. data/test/helpers/test_filtering.rb +108 -0
  139. data/test/helpers/test_html_escape.rb +34 -0
  140. data/test/helpers/test_link_to.rb +251 -0
  141. data/test/helpers/test_rendering.rb +90 -0
  142. data/test/helpers/test_tagging.rb +89 -0
  143. data/test/helpers/test_text.rb +26 -0
  144. data/test/helpers/test_xml_sitemap.rb +105 -0
  145. data/test/tasks/test_clean.rb +69 -0
  146. metadata +96 -27
  147. data/lib/nanoc3/base/compilation/checksummer.rb +0 -68
@@ -0,0 +1,562 @@
1
+ # encoding: utf-8
2
+
3
+ require 'test/helper'
4
+
5
+ class Nanoc3::DataSources::FilesystemUnifiedTest < MiniTest::Unit::TestCase
6
+
7
+ include Nanoc3::TestHelpers
8
+
9
+ def new_data_source(params=nil)
10
+ # Mock site
11
+ site = Nanoc3::Site.new({})
12
+
13
+ # Create data source
14
+ data_source = Nanoc3::DataSources::FilesystemUnified.new(site, nil, nil, params)
15
+
16
+ # Done
17
+ data_source
18
+ end
19
+
20
+ def test_create_object_not_at_root
21
+ # Create item
22
+ data_source = new_data_source
23
+ data_source.send(:create_object, 'foobar', 'content here', { :foo => 'bar' }, '/asdf/')
24
+
25
+ # Check file existance
26
+ assert File.directory?('foobar')
27
+ assert !File.directory?('foobar/content')
28
+ assert !File.directory?('foobar/asdf')
29
+ assert File.file?('foobar/asdf.html')
30
+
31
+ # Check file content
32
+ expected = "--- \nfoo: bar\n---\n\ncontent here"
33
+ assert_equal expected, File.read('foobar/asdf.html')
34
+ end
35
+
36
+ def test_create_object_at_root
37
+ # Create item
38
+ data_source = new_data_source
39
+ data_source.send(:create_object, 'foobar', 'content here', { :foo => 'bar' }, '/')
40
+
41
+ # Check file existance
42
+ assert File.directory?('foobar')
43
+ assert !File.directory?('foobar/index')
44
+ assert !File.directory?('foobar/foobar')
45
+ assert File.file?('foobar/index.html')
46
+
47
+ # Check file content
48
+ expected = "--- \nfoo: bar\n---\n\ncontent here"
49
+ assert_equal expected, File.read('foobar/index.html')
50
+ end
51
+
52
+ def test_load_objects
53
+ # Create data source
54
+ data_source = new_data_source
55
+
56
+ # Create a fake class
57
+ klass = Class.new do
58
+ attr_reader :stuff
59
+ def initialize(*stuff)
60
+ @stuff = stuff
61
+ end
62
+ def ==(other)
63
+ @stuff == other.stuff
64
+ end
65
+ end
66
+
67
+ # Create sample files
68
+ FileUtils.mkdir_p('foo')
69
+ FileUtils.mkdir_p('foo/a/b')
70
+ File.open('foo/bar.html', 'w') { |io| io.write("---\nnum: 1\n---\ntest 1") }
71
+ File.open('foo/b.c.html', 'w') { |io| io.write("---\nnum: 2\n---\ntest 2") }
72
+ File.open('foo/a/b/c.html', 'w') { |io| io.write("---\nnum: 3\n---\ntest 3") }
73
+ File.open('foo/ugly.html~', 'w') { |io| io.write("---\nnum: 4\n---\ntest 4") }
74
+ File.open('foo/ugly.html.orig', 'w') { |io| io.write("---\nnum: 5\n---\ntest 5") }
75
+ File.open('foo/ugly.html.rej', 'w') { |io| io.write("---\nnum: 6\n---\ntest 6") }
76
+ File.open('foo/ugly.html.bak', 'w') { |io| io.write("---\nnum: 7\n---\ntest 7") }
77
+
78
+ # Get expected and actual output
79
+ expected_out = [
80
+ klass.new(
81
+ 'test 1',
82
+ { 'num' => 1, :filename => 'foo/bar.html', :extension => 'html', :file => File.open('foo/bar.html') },
83
+ '/bar/',
84
+ :binary => false, :mtime => File.mtime('foo/bar.html')
85
+ ),
86
+ klass.new(
87
+ 'test 2',
88
+ { 'num' => 2, :filename => 'foo/b.c.html', :extension => 'c.html', :file => File.open('foo/b.c.html') },
89
+ '/b/',
90
+ :binary => false, :mtime => File.mtime('foo/b.c.html')
91
+ ),
92
+ klass.new(
93
+ 'test 3',
94
+ { 'num' => 3, :filename => 'foo/a/b/c.html', :extension => 'html', :file => File.open('foo/a/b/c.html') },
95
+ '/a/b/c/',
96
+ :binary => false, :mtime => File.mtime('foo/a/b/c.html')
97
+ )
98
+ ]
99
+ actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[0] }
100
+
101
+ # Check
102
+ (0..expected_out.size-1).each do |i|
103
+ assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0], 'content must match'
104
+ assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match'
105
+ assert_equal expected_out[i].stuff[3][:mtime], actual_out[i].stuff[3][:mtime], 'mtime must match'
106
+ assert_equal expected_out[i].stuff[1][:file].path, actual_out[i].stuff[1][:file].path, 'file paths must match'
107
+ expected_out[i].stuff[1][:file].close;
108
+ actual_out[i].stuff[1][:file].close
109
+ [ 'num', :filename, :extension ].each do |key|
110
+ assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match"
111
+ end
112
+ end
113
+ end
114
+
115
+ def test_load_binary_objects
116
+ # Create data source
117
+ data_source = new_data_source
118
+
119
+ # Create sample files
120
+ FileUtils.mkdir_p('foo')
121
+ File.open('foo/stuff.dat', 'w') { |io| io.write("random binary data") }
122
+
123
+ # Load
124
+ items = data_source.send(:load_objects, 'foo', 'item', Nanoc3::Item)
125
+
126
+ # Check
127
+ assert_equal 1, items.size
128
+ assert items[0].binary?
129
+ assert_equal 'foo/stuff.dat', items[0].raw_filename
130
+ assert_nil items[0].raw_content
131
+ end
132
+
133
+ def test_load_binary_layouts
134
+ # Create data source
135
+ data_source = new_data_source
136
+
137
+ # Create sample files
138
+ FileUtils.mkdir_p('foo')
139
+ File.open('foo/stuff.dat', 'w') { |io| io.write("random binary data") }
140
+
141
+ # Load
142
+ items = data_source.send(:load_objects, 'foo', 'item', Nanoc3::Layout)
143
+
144
+ # Check
145
+ assert_equal 1, items.size
146
+ assert_equal 'random binary data', items[0].raw_content
147
+ end
148
+
149
+ def test_identifier_for_filename_allowing_periods_in_identifiers
150
+ # Create data source
151
+ data_source = new_data_source(:allow_periods_in_identifiers => true)
152
+
153
+ # Get input and expected output
154
+ expected = {
155
+ '/foo' => '/foo/',
156
+ '/foo.html' => '/foo/',
157
+ '/foo/index.html' => '/foo/',
158
+ '/foo.entry.html' => '/foo.entry/'
159
+ }
160
+
161
+ # Check
162
+ expected.each_pair do |input, expected_output|
163
+ actual_output = data_source.send(:identifier_for_filename, input)
164
+ assert_equal(
165
+ expected_output, actual_output,
166
+ "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}"
167
+ )
168
+ end
169
+ end
170
+
171
+ def test_identifier_for_filename_disallowing_periods_in_identifiers
172
+ # Create data source
173
+ data_source = new_data_source
174
+
175
+ # Get input and expected output
176
+ expected = {
177
+ '/foo' => '/foo/',
178
+ '/foo.html' => '/foo/',
179
+ '/foo/index.html' => '/foo/',
180
+ '/foo.html.erb' => '/foo/'
181
+ }
182
+
183
+ # Check
184
+ expected.each_pair do |input, expected_output|
185
+ actual_output = data_source.send(:identifier_for_filename, input)
186
+ assert_equal(
187
+ expected_output, actual_output,
188
+ "identifier_for_filename(#{input.inspect}) should equal #{expected_output.inspect}, not #{actual_output.inspect}"
189
+ )
190
+ end
191
+ end
192
+
193
+ def test_identifier_for_filename_with_subfilename_allowing_periods_in_identifiers
194
+ # Create data source
195
+ data_source = new_data_source(:allow_periods_in_identifiers => true)
196
+
197
+ # Build directory
198
+ FileUtils.mkdir_p('foo')
199
+ File.open('foo/bar.yaml', 'w') { |io| io.write('test') }
200
+ File.open('foo/bar.html', 'w') { |io| io.write('test') }
201
+ File.open('foo/quxbar.yaml', 'w') { |io| io.write('test') }
202
+ File.open('foo/quxbar.html', 'w') { |io| io.write('test') }
203
+ File.open('foo/barqux.yaml', 'w') { |io| io.write('test') }
204
+ File.open('foo/barqux.html', 'w') { |io| io.write('test') }
205
+ File.open('foo/quxbarqux.yaml', 'w') { |io| io.write('test') }
206
+ File.open('foo/quxbarqux.html', 'w') { |io| io.write('test') }
207
+ File.open('foo/qux.bar.yaml', 'w') { |io| io.write('test') }
208
+ File.open('foo/qux.bar.html', 'w') { |io| io.write('test') }
209
+ File.open('foo/bar.qux.yaml', 'w') { |io| io.write('test') }
210
+ File.open('foo/bar.qux.html', 'w') { |io| io.write('test') }
211
+ File.open('foo/qux.bar.qux.yaml', 'w') { |io| io.write('test') }
212
+ File.open('foo/qux.bar.qux.html', 'w') { |io| io.write('test') }
213
+
214
+ # Check content filename
215
+ {
216
+ 'foo/bar.yaml' => '/foo/bar/',
217
+ 'foo/quxbar.yaml' => '/foo/quxbar/',
218
+ 'foo/barqux.yaml' => '/foo/barqux/',
219
+ 'foo/quxbarqux.yaml' => '/foo/quxbarqux/',
220
+ 'foo/qux.bar.yaml' => '/foo/qux.bar/',
221
+ 'foo/bar.qux.yaml' => '/foo/bar.qux/',
222
+ 'foo/qux.bar.qux.yaml' => '/foo/qux.bar.qux/'
223
+ }.each_pair do |meta_filename, expected_identifier|
224
+ assert_equal(
225
+ expected_identifier,
226
+ data_source.instance_eval { identifier_for_filename(meta_filename) }
227
+ )
228
+ end
229
+ end
230
+
231
+ def test_identifier_for_filename_with_subfilename_disallowing_periods_in_identifiers
232
+ # Create data source
233
+ data_source = new_data_source
234
+
235
+ # Build directory
236
+ FileUtils.mkdir_p('foo')
237
+ File.open('foo/bar.yaml', 'w') { |io| io.write('test') }
238
+ File.open('foo/bar.html', 'w') { |io| io.write('test') }
239
+ File.open('foo/quxbar.yaml', 'w') { |io| io.write('test') }
240
+ File.open('foo/quxbar.html', 'w') { |io| io.write('test') }
241
+ File.open('foo/barqux.yaml', 'w') { |io| io.write('test') }
242
+ File.open('foo/barqux.html', 'w') { |io| io.write('test') }
243
+ File.open('foo/quxbarqux.yaml', 'w') { |io| io.write('test') }
244
+ File.open('foo/quxbarqux.html', 'w') { |io| io.write('test') }
245
+ File.open('foo/qux.bar.yaml', 'w') { |io| io.write('test') }
246
+ File.open('foo/qux.bar.html', 'w') { |io| io.write('test') }
247
+ File.open('foo/bar.qux.yaml', 'w') { |io| io.write('test') }
248
+ File.open('foo/bar.qux.html', 'w') { |io| io.write('test') }
249
+ File.open('foo/qux.bar.qux.yaml', 'w') { |io| io.write('test') }
250
+ File.open('foo/qux.bar.qux.html', 'w') { |io| io.write('test') }
251
+
252
+ # Check content filename
253
+ {
254
+ 'foo/bar.yaml' => '/foo/bar/',
255
+ 'foo/quxbar.yaml' => '/foo/quxbar/',
256
+ 'foo/barqux.yaml' => '/foo/barqux/',
257
+ 'foo/quxbarqux.yaml' => '/foo/quxbarqux/',
258
+ 'foo/qux.bar.yaml' => '/foo/qux/',
259
+ 'foo/bar.qux.yaml' => '/foo/bar/',
260
+ 'foo/qux.bar.qux.yaml' => '/foo/qux/'
261
+ }.each_pair do |meta_filename, expected_identifier|
262
+ assert_equal(
263
+ expected_identifier,
264
+ data_source.instance_eval { identifier_for_filename(meta_filename) }
265
+ )
266
+ end
267
+ end
268
+
269
+ def test_load_objects_allowing_periods_in_identifiers
270
+ # Create data source
271
+ data_source = new_data_source(:allow_periods_in_identifiers => true)
272
+
273
+ # Create a fake class
274
+ klass = Class.new do
275
+ attr_reader :stuff
276
+ def initialize(*stuff)
277
+ @stuff = stuff
278
+ end
279
+ def ==(other)
280
+ @stuff == other.stuff
281
+ end
282
+ end
283
+
284
+ # Create sample files
285
+ FileUtils.mkdir_p('foo')
286
+ FileUtils.mkdir_p('foo/a/b')
287
+ File.open('foo/a/b/c.yaml', 'w') { |io| io.write("---\nnum: 1\n") }
288
+ File.open('foo/b.c.yaml', 'w') { |io| io.write("---\nnum: 2\n") }
289
+ File.open('foo/b.c.html', 'w') { |io| io.write("test 2") }
290
+ File.open('foo/car.html', 'w') { |io| io.write("test 3") }
291
+ File.open('foo/ugly.yaml~', 'w') { |io| io.write("blah") }
292
+ File.open('foo/ugly.html~', 'w') { |io| io.write("blah") }
293
+ File.open('foo/ugly.html.orig', 'w') { |io| io.write("blah") }
294
+ File.open('foo/ugly.html.rej', 'w') { |io| io.write("blah") }
295
+ File.open('foo/ugly.html.bak', 'w') { |io| io.write("blah") }
296
+
297
+ # Get expected output
298
+ expected_out = [
299
+ klass.new(
300
+ '',
301
+ {
302
+ 'num' => 1,
303
+ :content_filename => nil,
304
+ :meta_filename => 'foo/a/b/c.yaml',
305
+ :extension => nil,
306
+ :file => nil
307
+ },
308
+ '/a/b/c/',
309
+ :binary => false, :mtime => File.mtime('foo/a/b/c.yaml')
310
+ ),
311
+ klass.new(
312
+ 'test 2',
313
+ {
314
+ 'num' => 2,
315
+ :content_filename => 'foo/b.c.html',
316
+ :meta_filename => 'foo/b.c.yaml',
317
+ :extension => 'html',
318
+ :file => File.open('foo/b.c.html')
319
+ },
320
+ '/b.c/',
321
+ :binary => false, :mtime => File.mtime('foo/b.c.html') > File.mtime('foo/b.c.yaml') ? File.mtime('foo/b.c.html') : File.mtime('foo/b.c.yaml')
322
+ ),
323
+ klass.new(
324
+ 'test 3',
325
+ {
326
+ :content_filename => 'foo/car.html',
327
+ :meta_filename => nil,
328
+ :extension => 'html',
329
+ :file => File.open('foo/car.html')
330
+ },
331
+ '/car/',
332
+ :binary => false, :mtime => File.mtime('foo/car.html')
333
+ )
334
+ ]
335
+
336
+ # Get actual output ordered by identifier
337
+ actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[2] }
338
+
339
+ # Check
340
+ (0..expected_out.size-1).each do |i|
341
+ assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0], 'content must match'
342
+ assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match'
343
+ assert_equal expected_out[i].stuff[3][:mtime], actual_out[i].stuff[3][:mtime], 'mtime must match'
344
+
345
+ actual_file = actual_out[i].stuff[1][:file]
346
+ expected_file = expected_out[i].stuff[1][:file]
347
+ assert(actual_file == expected_file || actual_file.path == expected_file.path, 'file paths must match')
348
+ actual_file.close unless actual_file.nil?
349
+ expected_file.close unless expected_file.nil?
350
+
351
+ [ 'num', :content_filename, :meta_filename, :extension ].each do |key|
352
+ assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match"
353
+ end
354
+ end
355
+ end
356
+
357
+ def test_load_objects_disallowing_periods_in_identifiers
358
+ # Create data source
359
+ data_source = new_data_source
360
+
361
+ # Create a fake class
362
+ klass = Class.new do
363
+ attr_reader :stuff
364
+ def initialize(*stuff)
365
+ @stuff = stuff
366
+ end
367
+ def ==(other)
368
+ @stuff == other.stuff
369
+ end
370
+ end
371
+
372
+ # Create sample files
373
+ FileUtils.mkdir_p('foo')
374
+ FileUtils.mkdir_p('foo/a/b')
375
+ File.open('foo/a/b/c.yaml', 'w') { |io| io.write("---\nnum: 1\n") }
376
+ File.open('foo/b.yaml', 'w') { |io| io.write("---\nnum: 2\n") }
377
+ File.open('foo/b.html.erb', 'w') { |io| io.write("test 2") }
378
+ File.open('foo/car.html', 'w') { |io| io.write("test 3") }
379
+ File.open('foo/ugly.yaml~', 'w') { |io| io.write("blah") }
380
+ File.open('foo/ugly.html~', 'w') { |io| io.write("blah") }
381
+ File.open('foo/ugly.html.orig', 'w') { |io| io.write("blah") }
382
+ File.open('foo/ugly.html.rej', 'w') { |io| io.write("blah") }
383
+ File.open('foo/ugly.html.bak', 'w') { |io| io.write("blah") }
384
+
385
+ # Get expected output
386
+ expected_out = [
387
+ klass.new(
388
+ '',
389
+ {
390
+ 'num' => 1,
391
+ :content_filename => nil,
392
+ :meta_filename => 'foo/a/b/c.yaml',
393
+ :extension => nil,
394
+ :file => nil
395
+ },
396
+ '/a/b/c/',
397
+ :binary => false, :mtime => File.mtime('foo/a/b/c.yaml')
398
+ ),
399
+ klass.new(
400
+ 'test 2',
401
+ {
402
+ 'num' => 2,
403
+ :content_filename => 'foo/b.html.erb',
404
+ :meta_filename => 'foo/b.yaml',
405
+ :extension => 'html.erb',
406
+ :file => File.open('foo/b.html.erb')
407
+ },
408
+ '/b/',
409
+ :binary => false, :mtime => File.mtime('foo/b.html.erb') > File.mtime('foo/b.yaml') ? File.mtime('foo/b.html.erb') : File.mtime('foo/b.yaml')
410
+ ),
411
+ klass.new(
412
+ 'test 3',
413
+ {
414
+ :content_filename => 'foo/car.html',
415
+ :meta_filename => nil,
416
+ :extension => 'html',
417
+ :file => File.open('foo/car.html')
418
+ },
419
+ '/car/',
420
+ :binary => false, :mtime => File.mtime('foo/car.html')
421
+ )
422
+ ]
423
+
424
+ # Get actual output ordered by identifier
425
+ actual_out = data_source.send(:load_objects, 'foo', 'The Foo', klass).sort_by { |i| i.stuff[2] }
426
+
427
+ # Check
428
+ (0..expected_out.size-1).each do |i|
429
+ assert_equal expected_out[i].stuff[0], actual_out[i].stuff[0], 'content must match'
430
+ assert_equal expected_out[i].stuff[2], actual_out[i].stuff[2], 'identifier must match'
431
+ assert_equal expected_out[i].stuff[3][:mtime], actual_out[i].stuff[3][:mtime], 'mtime must match'
432
+
433
+ actual_file = actual_out[i].stuff[1][:file]
434
+ expected_file = expected_out[i].stuff[1][:file]
435
+ assert(actual_file == expected_file || actual_file.path == expected_file.path, 'file paths must match')
436
+ actual_file.close unless actual_file.nil?
437
+ expected_file.close unless expected_file.nil?
438
+
439
+ [ 'num', :content_filename, :meta_filename, :extension ].each do |key|
440
+ assert_equal expected_out[i].stuff[1][key], actual_out[i].stuff[1][key], "attribute key #{key} must match"
441
+ end
442
+ end
443
+ end
444
+
445
+ def test_create_object_allowing_periods_in_identifiers
446
+ # Create data source
447
+ data_source = new_data_source(:allow_periods_in_identifiers => true)
448
+
449
+ # Create object without period
450
+ data_source.send(:create_object, 'foo', 'some content', { :some => 'attributes' }, '/asdf/')
451
+ assert File.file?('foo/asdf.html')
452
+ data = data_source.send(:parse, 'foo/asdf.html', nil, 'moo')
453
+ assert_equal({ 'some' => 'attributes' }, data[0])
454
+ assert_equal('some content', data[1])
455
+
456
+ # Create object with period
457
+ data_source.send(:create_object, 'foo', 'some content', { :some => 'attributes' }, '/as.df/')
458
+ assert File.file?('foo/as.df.html')
459
+ data = data_source.send(:parse, 'foo/as.df.html', nil, 'moo')
460
+ assert_equal({ 'some' => 'attributes' }, data[0])
461
+ assert_equal('some content', data[1])
462
+ end
463
+
464
+ def test_create_object_disallowing_periods_in_identifiers
465
+ # Create data source
466
+ data_source = new_data_source
467
+
468
+ # Create object without period
469
+ data_source.send(:create_object, 'foo', 'some content', { :some => 'attributes' }, '/asdf/')
470
+ assert File.file?('foo/asdf.html')
471
+ data = data_source.send(:parse, 'foo/asdf.html', nil, 'moo')
472
+ assert_equal({ 'some' => 'attributes' }, data[0])
473
+ assert_equal('some content', data[1])
474
+
475
+ # Create object with period
476
+ assert_raises(RuntimeError) do
477
+ data_source.send(:create_object, 'foo', 'some content', { :some => 'attributes' }, '/as.df/')
478
+ end
479
+ end
480
+
481
+ def test_filename_for
482
+ data_source = new_data_source
483
+
484
+ assert_equal '/foo.bar', data_source.send(:filename_for, '/foo', 'bar')
485
+ assert_equal '/foo.bar.baz', data_source.send(:filename_for, '/foo', 'bar.baz')
486
+ assert_equal '/foo', data_source.send(:filename_for, '/foo', '')
487
+ assert_equal nil, data_source.send(:filename_for, '/foo', nil)
488
+ end
489
+
490
+ def test_compile_huge_site
491
+ if_implemented do
492
+ # Create data source
493
+ data_source = new_data_source
494
+
495
+ # Create a lot of items
496
+ count = Process.getrlimit(Process::RLIMIT_NOFILE)[0] + 5
497
+ count.times do |i|
498
+ FileUtils.mkdir_p("content/#{i}")
499
+ File.open("content/#{i}/#{i}.html", 'w') { |io| io << "This is item #{i}." }
500
+ File.open("content/#{i}/#{i}.yaml", 'w') { |io| io << "title: Item #{i}" }
501
+ end
502
+
503
+ # Read all items
504
+ data_source.items
505
+ end
506
+ end
507
+
508
+ def test_compile_iso_8859_1_site
509
+ # Check encoding
510
+ if !''.respond_to?(:encode)
511
+ skip "Test only works on 1.9.x"
512
+ return
513
+ end
514
+
515
+ # Create data source
516
+ data_source = new_data_source
517
+
518
+ # Create item
519
+ data_source.create_item("Hëllö", {}, '/foo/')
520
+
521
+ # Parse
522
+ begin
523
+ original_default_external_encoding = Encoding.default_external
524
+ Encoding.default_external = 'ISO-8859-1'
525
+
526
+ items = data_source.items
527
+
528
+ assert_equal 1, items.size
529
+ assert_equal Encoding.find("UTF-8"), items[0].raw_content.encoding
530
+ ensure
531
+ Encoding.default_external = original_default_external_encoding
532
+ end
533
+ end
534
+
535
+ def test_compile_iso_8859_1_site_with_explicit_encoding
536
+ # Check encoding
537
+ if !''.respond_to?(:encode)
538
+ skip "Test only works on 1.9.x"
539
+ return
540
+ end
541
+
542
+ # Create data source
543
+ data_source = new_data_source({})
544
+ data_source.config[:encoding] = 'ISO-8859-1'
545
+
546
+ # Create item
547
+ begin
548
+ original_default_external_encoding = Encoding.default_external
549
+ Encoding.default_external = 'ISO-8859-1'
550
+
551
+ data_source.create_item("Hëllö", {}, '/foo/')
552
+ ensure
553
+ Encoding.default_external = original_default_external_encoding
554
+ end
555
+
556
+ # Parse
557
+ items = data_source.items
558
+ assert_equal 1, items.size
559
+ assert_equal Encoding.find("UTF-8"), items[0].raw_content.encoding
560
+ end
561
+
562
+ end