nanoc3 3.2.0a1 → 3.2.0a2

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