nanoc3 3.2.0a1 → 3.2.0a2

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 (58) hide show
  1. data/ChangeLog +2 -2
  2. data/NEWS.md +14 -0
  3. data/README.md +20 -12
  4. data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
  5. data/lib/nanoc3/base/compilation/checksum_store.rb +130 -0
  6. data/lib/nanoc3/base/compilation/checksummer.rb +68 -0
  7. data/lib/nanoc3/base/compilation/compiled_content_cache.rb +57 -0
  8. data/lib/nanoc3/base/{compiler.rb → compilation/compiler.rb} +255 -55
  9. data/lib/nanoc3/base/{compiler_dsl.rb → compilation/compiler_dsl.rb} +11 -6
  10. data/lib/nanoc3/base/{dependency_tracker.rb → compilation/dependency_tracker.rb} +62 -92
  11. data/lib/nanoc3/base/{filter.rb → compilation/filter.rb} +0 -0
  12. data/lib/nanoc3/base/compilation/item_rep_proxy.rb +87 -0
  13. data/lib/nanoc3/base/compilation/outdatedness_checker.rb +86 -0
  14. data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +43 -0
  15. data/lib/nanoc3/base/{rule.rb → compilation/rule.rb} +8 -2
  16. data/lib/nanoc3/base/{rule_context.rb → compilation/rule_context.rb} +17 -14
  17. data/lib/nanoc3/base/directed_graph.rb +8 -0
  18. data/lib/nanoc3/base/errors.rb +9 -17
  19. data/lib/nanoc3/base/plugin_registry.rb +1 -1
  20. data/lib/nanoc3/base/result_data/item_rep.rb +447 -0
  21. data/lib/nanoc3/base/{code_snippet.rb → source_data/code_snippet.rb} +7 -22
  22. data/lib/nanoc3/base/{data_source.rb → source_data/data_source.rb} +0 -0
  23. data/lib/nanoc3/base/{item.rb → source_data/item.rb} +20 -30
  24. data/lib/nanoc3/base/{layout.rb → source_data/layout.rb} +7 -26
  25. data/lib/nanoc3/base/source_data/site.rb +314 -0
  26. data/lib/nanoc3/base/store.rb +126 -0
  27. data/lib/nanoc3/base.rb +26 -15
  28. data/lib/nanoc3/cli/base.rb +2 -1
  29. data/lib/nanoc3/cli/commands/compile.rb +116 -48
  30. data/lib/nanoc3/cli/commands/create_item.rb +0 -1
  31. data/lib/nanoc3/cli/commands/create_layout.rb +0 -1
  32. data/lib/nanoc3/cli/commands/create_site.rb +11 -1
  33. data/lib/nanoc3/cli/commands/debug.rb +11 -6
  34. data/lib/nanoc3/cli/commands/info.rb +1 -2
  35. data/lib/nanoc3/cli/commands/update.rb +0 -1
  36. data/lib/nanoc3/cli/commands/watch.rb +27 -32
  37. data/lib/nanoc3/cli/logger.rb +2 -2
  38. data/lib/nanoc3/data_sources/filesystem.rb +1 -6
  39. data/lib/nanoc3/extra/auto_compiler.rb +2 -3
  40. data/lib/nanoc3/extra/deployers/rsync.rb +1 -0
  41. data/lib/nanoc3/extra/validators/links.rb +45 -26
  42. data/lib/nanoc3/filters/asciidoc.rb +58 -0
  43. data/lib/nanoc3/filters/colorize_syntax.rb +47 -9
  44. data/lib/nanoc3/filters/less.rb +6 -0
  45. data/lib/nanoc3/filters/sass.rb +8 -5
  46. data/lib/nanoc3/filters.rb +2 -0
  47. data/lib/nanoc3/helpers/blogging.rb +8 -0
  48. data/lib/nanoc3/helpers/html_escape.rb +37 -7
  49. data/lib/nanoc3/helpers/link_to.rb +15 -4
  50. data/lib/nanoc3/helpers/rendering.rb +6 -2
  51. data/lib/nanoc3/tasks/clean.rb +0 -4
  52. data/lib/nanoc3.rb +1 -1
  53. data/nanoc3.gemspec +42 -0
  54. metadata +35 -19
  55. data/lib/nanoc3/base/checksummer.rb +0 -40
  56. data/lib/nanoc3/base/compiled_content_cache.rb +0 -86
  57. data/lib/nanoc3/base/item_rep.rb +0 -537
  58. data/lib/nanoc3/base/site.rb +0 -490
@@ -1,490 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Nanoc3
4
-
5
- # The in-memory representation of a nanoc site. It holds references to the
6
- # following site data:
7
- #
8
- # * {#items} - the list of items ({Nanoc3::Item})
9
- # * {#layouts} - the list of layouts ({Nanoc3::Layout})
10
- # * {#code_snippets} - the list of code snippets ({Nanoc3::CodeSnippet})
11
- #
12
- # In addition, each site has a {#config} hash which stores the site
13
- # configuration.
14
- #
15
- # A site also has several helper classes:
16
- #
17
- # * {#data_sources} (array of {Nanoc3::DataSource}) - A list of data sources
18
- # that are used for loading site data
19
- #
20
- # * {#compiler} ({Nanoc3::Compiler}) - The compiler that is used for
21
- # compiling items and their representations
22
- #
23
- # The physical representation of a {Nanoc3::Site} is usually a directory
24
- # that contains a configuration file, site data, a rakefile, a rules file,
25
- # etc. The way site data is stored depends on the data source.
26
- class Site
27
-
28
- # The default configuration for a data source. A data source's
29
- # configuration overrides these options.
30
- DEFAULT_DATA_SOURCE_CONFIG = {
31
- :type => 'filesystem_unified',
32
- :items_root => '/',
33
- :layouts_root => '/',
34
- :config => {}
35
- }
36
-
37
- # The default configuration for a site. A site's configuration overrides
38
- # these options: when a {Nanoc3::Site} is created with a configuration
39
- # that lacks some options, the default value will be taken from
40
- # `DEFAULT_CONFIG`.
41
- DEFAULT_CONFIG = {
42
- :text_extensions => %w( css erb haml htm html js less markdown md php rb sass txt xml ),
43
- :output_dir => 'output',
44
- :data_sources => [ {} ],
45
- :index_filenames => [ 'index.html' ],
46
- :enable_output_diff => false
47
- }
48
-
49
- # The name of the file where checksums will be stored.
50
- CHECKSUMS_FILE_NAME = 'tmp/checksums'
51
-
52
- # The site configuration. The configuration has the following keys:
53
- #
54
- # * `text_extensions` ({Array<String>}) - A list of file extensions that
55
- # will cause nanoc to threat the file as textual instead of binary. When
56
- # the data source finds a content file with an extension that is
57
- # included in this list, it will be marked as textual.
58
- #
59
- # * `output_dir` ({String}) - The directory to which compiled items will
60
- # be written. This path is relative to the current working directory,
61
- # but can also be an absolute path.
62
- #
63
- # * `data_sources` ({Array<Hash>}) - A list of data sources for this site.
64
- # See below for documentation on the structure of this list. By default,
65
- # there is only one data source of the filesystem type mounted at `/`.
66
- #
67
- # * `index_filenames` ({Array<String>}) - A list of filenames that will be
68
- # stripped off full item paths to create cleaner URLs. For example,
69
- # `/about/` will be used instead of `/about/index.html`). The default
70
- # value should be okay in most cases.
71
- #
72
- # * `enable_output_diff` ({Boolean}) - True when diffs should be generated
73
- # for the compiled content of this site; false otherwise.
74
- #
75
- # The list of data sources consists of hashes with the following keys:
76
- #
77
- # * `:type` ({String}) - The type of data source, i.e. its identifier.
78
- #
79
- # * `:items_root` ({String}) - The prefix that should be given to all
80
- # items returned by the {#items} method (comparable to mount points
81
- # for filesystems in Unix-ish OSes).
82
- #
83
- # * `:layouts_root` ({String}) - The prefix that should be given to all
84
- # layouts returned by the {#layouts} method (comparable to mount
85
- # points for filesystems in Unix-ish OSes).
86
- #
87
- # * `:config` ({Hash}) - A hash containing the configuration for this data
88
- # source. nanoc itself does not use this hash. This is especially
89
- # useful for online data sources; for example, a Twitter data source
90
- # would need the username of the account from which to fetch tweets.
91
- #
92
- # @return [Hash] The site configuration
93
- attr_reader :config
94
-
95
- # @return [String] The checksum of the site configuration that was in
96
- # effect during the previous site compilation
97
- attr_accessor :old_config_checksum
98
-
99
- # @return [String] The current, up-to-date checksum of the site
100
- # configuration
101
- attr_reader :new_config_checksum
102
-
103
- # @return [String] The checksum of the rules file that was in effect
104
- # during the previous site compilation
105
- attr_accessor :old_rules_checksum
106
-
107
- # @return [String] The current, up-to-date checksum of the rules file
108
- attr_reader :new_rules_checksum
109
-
110
- # @return [Proc] The code block that will be executed after all data is
111
- # loaded but before the site is compiled
112
- attr_accessor :preprocessor
113
-
114
- # Creates a site object for the site specified by the given
115
- # `dir_or_config_hash` argument.
116
- #
117
- # @param [Hash, String] dir_or_config_hash If a string, contains the path
118
- # to the site directory; if a hash, contains the site configuration.
119
- def initialize(dir_or_config_hash)
120
- @new_checksums = {}
121
-
122
- build_config(dir_or_config_hash)
123
-
124
- @code_snippets_loaded = false
125
- @items_loaded = false
126
- @layouts_loaded = false
127
- end
128
-
129
- # Returns the compiler for this site. Will create a new compiler if none
130
- # exists yet.
131
- #
132
- # @return [Nanoc3::Compiler] The compiler for this site
133
- def compiler
134
- @compiler ||= Compiler.new(self)
135
- end
136
-
137
- # Returns the data sources for this site. Will create a new data source if
138
- # none exists yet.
139
- #
140
- # @return [Array<Nanoc3::DataSource>] The list of data sources for this
141
- # site
142
- #
143
- # @raise [Nanoc3::Errors::UnknownDataSource] if the site configuration
144
- # specifies an unknown data source
145
- def data_sources
146
- @data_sources ||= begin
147
- @config[:data_sources].map do |data_source_hash|
148
- # Get data source class
149
- data_source_class = Nanoc3::DataSource.named(data_source_hash[:type])
150
- raise Nanoc3::Errors::UnknownDataSource.new(data_source_hash[:type]) if data_source_class.nil?
151
-
152
- # Warn about deprecated data sources
153
- # TODO [in nanoc 4.0] remove me
154
- case data_source_hash[:type]
155
- when 'filesystem'
156
- warn "Warning: the 'filesystem' data source has been renamed to 'filesystem_verbose'. Using 'filesystem' will work in nanoc 3.1.x, but it will likely not work anymore in a future release of nanoc. Please update your data source configuration and replace 'filesystem' with 'filesystem_verbose'."
157
- when 'filesystem_combined', 'filesystem_compact'
158
- warn "Warning: the 'filesystem_combined' and 'filesystem_compact' data source has been merged into the new 'filesystem_unified' data source. Using 'filesystem_combined' and 'filesystem_compact' will work in nanoc 3.1.x, but it will likely not work anymore in a future release of nanoc. Please update your data source configuration and replace 'filesystem_combined' and 'filesystem_compact with 'filesystem_unified'."
159
- end
160
-
161
- # Create data source
162
- data_source_class.new(
163
- self,
164
- data_source_hash[:items_root],
165
- data_source_hash[:layouts_root],
166
- data_source_hash[:config] || {}
167
- )
168
- end
169
- end
170
- end
171
-
172
- # Loads the site data. This will query the {Nanoc3::DataSource} associated
173
- # with the site and fetch all site data. The site data is cached, so
174
- # calling this method will not have any effect the second time, unless
175
- # the `force` parameter is true.
176
- #
177
- # @param [Boolean] force If true, will force load the site data even if it
178
- # has been loaded before, to circumvent caching issues
179
- #
180
- # @return [void]
181
- def load_data(force=false)
182
- # Don't load data twice
183
- return if instance_variable_defined?(:@data_loaded) && @data_loaded && !force
184
-
185
- # Load all data
186
- load_code_snippets(force)
187
- data_sources.each { |ds| ds.use }
188
- load_rules
189
- load_items
190
- load_layouts
191
- data_sources.each { |ds| ds.unuse }
192
-
193
- # Preprocess
194
- setup_child_parent_links
195
- preprocessor_context.instance_eval(&preprocessor) unless preprocessor.nil?
196
- link_everything_to_site
197
- setup_child_parent_links
198
- build_reps
199
- route_reps
200
-
201
- # Done
202
- @data_loaded = true
203
- end
204
-
205
- # Returns this site’s code snippets.
206
- #
207
- # @return [Array<Nanoc3::CodeSnippet>] The list of code snippets in this
208
- # site
209
- #
210
- # @raise [Nanoc3::Errors::DataNotYetAvailable] if the site data hasn’t
211
- # been loaded yet (call {#load_data} to load the site data)
212
- def code_snippets
213
- raise Nanoc3::Errors::DataNotYetAvailable.new('Code snippets', false) unless @code_snippets_loaded
214
- @code_snippets
215
- end
216
-
217
- # Returns this site’s items.
218
- #
219
- # @return [Array<Nanoc3::Item>] The list of items in this site
220
- #
221
- # @raise [Nanoc3::Errors::DataNotYetAvailable] if the site data hasn’t
222
- # been loaded yet (call {#load_data} to load the site data)
223
- def items
224
- raise Nanoc3::Errors::DataNotYetAvailable.new('Items', true) unless @items_loaded
225
- @items
226
- end
227
-
228
- # Returns this site’s layouts.
229
- #
230
- # @return [Array<Nanoc3::Layouts>] The list of layout in this site
231
- #
232
- # @raise [Nanoc3::Errors::DataNotYetAvailable] if the site data hasn’t
233
- # been loaded yet (call {#load_data} to load the site data)
234
- def layouts
235
- raise Nanoc3::Errors::DataNotYetAvailable.new('Layouts', true) unless @layouts_loaded
236
- @layouts
237
- end
238
-
239
- # Stores the checksums into the checksums file.
240
- #
241
- # @return [void]
242
- def store_checksums
243
- # Store
244
- FileUtils.mkdir_p(File.dirname(CHECKSUMS_FILE_NAME))
245
- store = PStore.new(CHECKSUMS_FILE_NAME)
246
- store.transaction do
247
- store[:checksums] = @new_checksums || {}
248
- end
249
- end
250
-
251
- # @return [Boolean] true if the site configuration was modified since the
252
- # site was last compiled, false otherwise
253
- def config_outdated?
254
- !self.old_config_checksum || !self.new_config_checksum || self.old_config_checksum != self.new_config_checksum
255
- end
256
-
257
- # @return [Boolean] true if the rules were modified since the site was
258
- # last compiled, false otherwise
259
- def rules_outdated?
260
- !self.old_rules_checksum || !self.new_rules_checksum || self.old_rules_checksum != self.new_rules_checksum
261
- end
262
-
263
- private
264
-
265
- # Returns the Nanoc3::CompilerDSL that should be used for this site.
266
- def dsl
267
- @dsl ||= Nanoc3::CompilerDSL.new(self)
268
- end
269
-
270
- # Loads this site’s code and executes it.
271
- def load_code_snippets(force=false)
272
- # Don't load code snippets twice
273
- return if @code_snippets_loaded and !force
274
-
275
- # Get code snippets
276
- @code_snippets = Dir['lib/**/*.rb'].sort.map do |filename|
277
- Nanoc3::CodeSnippet.new(
278
- File.read(filename),
279
- filename,
280
- :checksum => Nanoc3::Checksummer.checksum_for(filename)
281
- )
282
- end
283
-
284
- # Set checksums
285
- @code_snippets.each do |cs|
286
- cs.old_checksum = old_checksum_for(:code_snippet, cs.filename)
287
- @new_checksums[ [ :code_snippet, cs.filename ] ] = cs.new_checksum
288
- end
289
-
290
- # Execute code snippets
291
- @code_snippets.each { |cs| cs.load }
292
-
293
- @code_snippets_loaded = true
294
- end
295
-
296
- # Loads this site’s rules.
297
- def load_rules
298
- # Find rules file
299
- rules_filename = [ 'Rules', 'rules', 'Rules.rb', 'rules.rb' ].find { |f| File.file?(f) }
300
- raise Nanoc3::Errors::NoRulesFileFound.new if rules_filename.nil?
301
-
302
- # Get rule data
303
- @rules = File.read(rules_filename)
304
- @new_rules_checksum = Nanoc3::Checksummer.checksum_for(rules_filename)
305
- @old_rules_checksum = old_checksum_for(:misc, 'Rules')
306
- @new_checksums[ [ :misc, 'Rules' ] ] = @new_rules_checksum
307
-
308
- # Load DSL
309
- dsl.instance_eval(@rules, "./#{rules_filename}")
310
- end
311
-
312
- # Loads this site’s items, sets up item child-parent relationships and
313
- # builds each item's list of item representations.
314
- def load_items
315
- @items = []
316
- data_sources.each do |ds|
317
- items_in_ds = ds.items
318
- items_in_ds.each { |i| i.identifier = File.join(ds.items_root, i.identifier) }
319
- @items.concat(items_in_ds)
320
- end
321
-
322
- # Set checksums
323
- @items.each do |i|
324
- i.old_checksum = old_checksum_for(:item, i.identifier)
325
- @new_checksums[ [ :item, i.identifier ] ] = i.new_checksum
326
- end
327
-
328
- @items_loaded = true
329
- end
330
-
331
- # Loads this site’s layouts.
332
- def load_layouts
333
- @layouts = []
334
- data_sources.each do |ds|
335
- layouts_in_ds = ds.layouts
336
- layouts_in_ds.each { |i| i.identifier = File.join(ds.layouts_root, i.identifier) }
337
- @layouts.concat(layouts_in_ds)
338
- end
339
-
340
- # Set checksums
341
- @layouts.each do |l|
342
- l.old_checksum = old_checksum_for(:layout, l.identifier)
343
- @new_checksums[ [ :layout, l.identifier ] ] = l.new_checksum
344
- end
345
-
346
- @layouts_loaded = true
347
- end
348
-
349
- # Links items, layouts and code snippets to the site.
350
- def link_everything_to_site
351
- @items.each { |i| i.site = self }
352
- @layouts.each { |l| l.site = self }
353
- @code_snippets.each { |cs| cs.site = self }
354
- end
355
-
356
- # Fills each item's parent reference and children array with the
357
- # appropriate items.
358
- def setup_child_parent_links
359
- # Clear all links
360
- @items.each do |item|
361
- item.parent = nil
362
- item.children = []
363
- end
364
-
365
- @items.each do |item|
366
- # Get parent
367
- parent_identifier = item.identifier.sub(/[^\/]+\/$/, '')
368
- parent = @items.find { |p| p.identifier == parent_identifier }
369
- next if parent.nil? or item.identifier == '/'
370
-
371
- # Link
372
- item.parent = parent
373
- parent.children << item
374
- end
375
- end
376
-
377
- # Creates the representations of all items as defined by the compilation
378
- # rules.
379
- def build_reps
380
- @items.each do |item|
381
- # Find matching rules
382
- matching_rules = self.compiler.item_compilation_rules.select { |r| r.applicable_to?(item) }
383
- raise Nanoc3::Errors::NoMatchingCompilationRuleFound.new(item) if matching_rules.empty?
384
-
385
- # Create reps
386
- rep_names = matching_rules.map { |r| r.rep_name }.uniq
387
- rep_names.each do |rep_name|
388
- item.reps << ItemRep.new(item, rep_name)
389
- end
390
- end
391
- end
392
-
393
- # Determines the paths of all item representations.
394
- def route_reps
395
- reps = @items.map { |i| i.reps }.flatten
396
- reps.each do |rep|
397
- # Find matching rules
398
- rules = self.compiler.routing_rules_for(rep)
399
- raise Nanoc3::Errors::NoMatchingRoutingRuleFound.new(rep) if rules[:last].nil?
400
-
401
- rules.each_pair do |snapshot, rule|
402
- # Get basic path by applying matching rule
403
- basic_path = rule.apply_to(rep)
404
- next if basic_path.nil?
405
-
406
- # Get raw path by prepending output directory
407
- rep.raw_paths[snapshot] = self.config[:output_dir] + basic_path
408
-
409
- # Get normal path by stripping index filename
410
- rep.paths[snapshot] = basic_path
411
- self.config[:index_filenames].each do |index_filename|
412
- if rep.paths[snapshot][-index_filename.length..-1] == index_filename
413
- # Strip and stop
414
- rep.paths[snapshot] = rep.paths[snapshot][0..-index_filename.length-1]
415
- break
416
- end
417
- end
418
- end
419
- end
420
- end
421
-
422
- # Builds the configuration hash based on the given argument. Also see
423
- # {#initialize} for details.
424
- def build_config(dir_or_config_hash)
425
- if dir_or_config_hash.is_a? String
426
- # Read config from config.yaml in given dir
427
- config_path = File.join(dir_or_config_hash, 'config.yaml')
428
- @config = DEFAULT_CONFIG.merge(YAML.load_file(config_path).symbolize_keys)
429
- @config[:data_sources].map! { |ds| ds.symbolize_keys }
430
-
431
- @new_config_checksum = Nanoc3::Checksummer.checksum_for('config.yaml')
432
- @new_checksums[ [ :misc, 'config.yaml' ] ] = @new_config_checksum
433
- else
434
- # Use passed config hash
435
- @config = DEFAULT_CONFIG.merge(dir_or_config_hash)
436
- @new_config_checksum = nil
437
- end
438
-
439
- # Build checksum
440
- @old_config_checksum = old_checksum_for(:misc, 'config.yaml')
441
-
442
- # Merge data sources with default data source config
443
- @config[:data_sources].map! { |ds| DEFAULT_DATA_SOURCE_CONFIG.merge(ds) }
444
- end
445
-
446
- # Returns a preprocessor context, creating one if none exists yet.
447
- def preprocessor_context
448
- @preprocessor_context ||= Nanoc3::Context.new({
449
- :site => self,
450
- :config => self.config,
451
- :items => self.items,
452
- :layouts => self.layouts
453
- })
454
- end
455
-
456
- # Returns the checksums, loads the checksums from the cached checksums
457
- # file first if necessary. The checksums returned is a hash in th following
458
- # format:
459
- #
460
- # {
461
- # [ :layout, '/identifier/' ] => checksum,
462
- # [ :item, '/identifier/' ] => checksum,
463
- # [ :code_snippet, 'lib/filename.rb' ] => checksum,
464
- # }
465
- def checksums
466
- return @checksums if @checksums_loaded
467
-
468
- if !File.file?(CHECKSUMS_FILE_NAME)
469
- @checksums = {}
470
- else
471
- require 'pstore'
472
- store = PStore.new(CHECKSUMS_FILE_NAME)
473
- store.transaction do
474
- @checksums = store[:checksums] || {}
475
- end
476
- end
477
-
478
- @checksums_loaded = true
479
- @checksums
480
- end
481
-
482
- # Returns the old checksum for the given object.
483
- def old_checksum_for(type, identifier)
484
- key = [ type, identifier ]
485
- checksums[key]
486
- end
487
-
488
- end
489
-
490
- end