nanoc3 3.1.9 → 3.2.0a1

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 (136) hide show
  1. data/LICENSE +1 -1
  2. data/NEWS.md +0 -50
  3. data/README.md +3 -15
  4. data/bin/nanoc3 +2 -0
  5. data/lib/nanoc3/base/checksummer.rb +40 -0
  6. data/lib/nanoc3/base/code_snippet.rb +30 -12
  7. data/lib/nanoc3/base/compiled_content_cache.rb +86 -0
  8. data/lib/nanoc3/base/compiler.rb +134 -95
  9. data/lib/nanoc3/base/compiler_dsl.rb +12 -11
  10. data/lib/nanoc3/base/core_ext/string.rb +2 -2
  11. data/lib/nanoc3/base/data_source.rb +17 -16
  12. data/lib/nanoc3/base/dependency_tracker.rb +102 -121
  13. data/lib/nanoc3/base/directed_graph.rb +65 -3
  14. data/lib/nanoc3/base/errors.rb +20 -16
  15. data/lib/nanoc3/base/item.rb +58 -50
  16. data/lib/nanoc3/base/item_rep.rb +177 -150
  17. data/lib/nanoc3/base/layout.rb +51 -18
  18. data/lib/nanoc3/base/notification_center.rb +8 -8
  19. data/lib/nanoc3/base/plugin_registry.rb +9 -9
  20. data/lib/nanoc3/base/rule.rb +18 -9
  21. data/lib/nanoc3/base/rule_context.rb +5 -5
  22. data/lib/nanoc3/base/site.rb +135 -47
  23. data/lib/nanoc3/base.rb +21 -19
  24. data/lib/nanoc3/cli/base.rb +51 -74
  25. data/lib/nanoc3/cli/commands/autocompile.rb +3 -0
  26. data/lib/nanoc3/cli/commands/compile.rb +35 -74
  27. data/lib/nanoc3/cli/commands/create_site.rb +17 -5
  28. data/lib/nanoc3/cli/commands/debug.rb +11 -4
  29. data/lib/nanoc3/cli/commands/view.rb +0 -1
  30. data/lib/nanoc3/cli/commands/watch.rb +148 -0
  31. data/lib/nanoc3/cli/commands.rb +1 -0
  32. data/lib/nanoc3/cli/logger.rb +15 -21
  33. data/lib/nanoc3/data_sources/deprecated/twitter.rb +0 -1
  34. data/lib/nanoc3/data_sources/filesystem.rb +11 -40
  35. data/lib/nanoc3/data_sources/filesystem_unified.rb +22 -22
  36. data/lib/nanoc3/extra/auto_compiler.rb +1 -1
  37. data/lib/nanoc3/extra/chick.rb +8 -8
  38. data/lib/nanoc3/extra/deployers/rsync.rb +2 -3
  39. data/lib/nanoc3/extra/validators/links.rb +32 -51
  40. data/lib/nanoc3/extra/validators/w3c.rb +2 -2
  41. data/lib/nanoc3/extra/vcs.rb +1 -1
  42. data/lib/nanoc3/filters/colorize_syntax.rb +15 -19
  43. data/lib/nanoc3/filters/erb.rb +1 -5
  44. data/lib/nanoc3/filters/erubis.rb +1 -5
  45. data/lib/nanoc3/filters/haml.rb +1 -2
  46. data/lib/nanoc3/filters/less.rb +2 -51
  47. data/lib/nanoc3/filters/mustache.rb +21 -0
  48. data/lib/nanoc3/filters/rdiscount.rb +1 -2
  49. data/lib/nanoc3/filters/relativize_paths.rb +3 -2
  50. data/lib/nanoc3/filters/sass.rb +50 -56
  51. data/lib/nanoc3/filters.rb +2 -0
  52. data/lib/nanoc3/helpers/blogging.rb +22 -29
  53. data/lib/nanoc3/helpers/breadcrumbs.rb +1 -1
  54. data/lib/nanoc3/helpers/capturing.rb +1 -1
  55. data/lib/nanoc3/helpers/filtering.rb +1 -1
  56. data/lib/nanoc3/helpers/link_to.rb +10 -21
  57. data/lib/nanoc3/helpers/rendering.rb +5 -24
  58. data/lib/nanoc3/helpers/tagging.rb +6 -6
  59. data/lib/nanoc3/helpers/text.rb +2 -2
  60. data/lib/nanoc3.rb +1 -1
  61. metadata +35 -93
  62. data/.gemtest +0 -0
  63. data/doc/yardoc_templates/default/layout/html/footer.erb +0 -10
  64. data/nanoc3.gemspec +0 -41
  65. data/tasks/clean.rake +0 -11
  66. data/tasks/doc.rake +0 -14
  67. data/tasks/gem.rake +0 -13
  68. data/tasks/test.rake +0 -38
  69. data/test/base/core_ext/array_spec.rb +0 -23
  70. data/test/base/core_ext/hash_spec.rb +0 -41
  71. data/test/base/core_ext/string_spec.rb +0 -27
  72. data/test/base/test_code_snippet.rb +0 -33
  73. data/test/base/test_compiler.rb +0 -410
  74. data/test/base/test_compiler_dsl.rb +0 -121
  75. data/test/base/test_context.rb +0 -33
  76. data/test/base/test_data_source.rb +0 -48
  77. data/test/base/test_dependency_tracker.rb +0 -510
  78. data/test/base/test_directed_graph.rb +0 -91
  79. data/test/base/test_filter.rb +0 -85
  80. data/test/base/test_item.rb +0 -141
  81. data/test/base/test_item_rep.rb +0 -953
  82. data/test/base/test_layout.rb +0 -44
  83. data/test/base/test_notification_center.rb +0 -36
  84. data/test/base/test_plugin.rb +0 -32
  85. data/test/base/test_rule.rb +0 -21
  86. data/test/base/test_rule_context.rb +0 -63
  87. data/test/base/test_site.rb +0 -366
  88. data/test/cli/commands/test_compile.rb +0 -12
  89. data/test/cli/commands/test_create_item.rb +0 -12
  90. data/test/cli/commands/test_create_layout.rb +0 -28
  91. data/test/cli/commands/test_create_site.rb +0 -24
  92. data/test/cli/commands/test_help.rb +0 -12
  93. data/test/cli/commands/test_info.rb +0 -12
  94. data/test/cli/commands/test_update.rb +0 -12
  95. data/test/cli/test_logger.rb +0 -12
  96. data/test/data_sources/test_filesystem.rb +0 -420
  97. data/test/data_sources/test_filesystem_unified.rb +0 -538
  98. data/test/data_sources/test_filesystem_verbose.rb +0 -359
  99. data/test/extra/core_ext/test_enumerable.rb +0 -32
  100. data/test/extra/core_ext/test_time.rb +0 -17
  101. data/test/extra/deployers/test_rsync.rb +0 -234
  102. data/test/extra/test_auto_compiler.rb +0 -482
  103. data/test/extra/test_file_proxy.rb +0 -21
  104. data/test/extra/test_vcs.rb +0 -24
  105. data/test/extra/validators/test_links.rb +0 -53
  106. data/test/extra/validators/test_w3c.rb +0 -49
  107. data/test/filters/test_bluecloth.rb +0 -20
  108. data/test/filters/test_coderay.rb +0 -46
  109. data/test/filters/test_colorize_syntax.rb +0 -84
  110. data/test/filters/test_erb.rb +0 -72
  111. data/test/filters/test_erubis.rb +0 -72
  112. data/test/filters/test_haml.rb +0 -98
  113. data/test/filters/test_kramdown.rb +0 -20
  114. data/test/filters/test_less.rb +0 -118
  115. data/test/filters/test_markaby.rb +0 -26
  116. data/test/filters/test_maruku.rb +0 -20
  117. data/test/filters/test_rainpress.rb +0 -31
  118. data/test/filters/test_rdiscount.rb +0 -33
  119. data/test/filters/test_rdoc.rb +0 -18
  120. data/test/filters/test_redcloth.rb +0 -20
  121. data/test/filters/test_relativize_paths.rb +0 -231
  122. data/test/filters/test_rubypants.rb +0 -20
  123. data/test/filters/test_sass.rb +0 -235
  124. data/test/gem_loader.rb +0 -11
  125. data/test/helper.rb +0 -99
  126. data/test/helpers/test_blogging.rb +0 -808
  127. data/test/helpers/test_breadcrumbs.rb +0 -83
  128. data/test/helpers/test_capturing.rb +0 -42
  129. data/test/helpers/test_filtering.rb +0 -108
  130. data/test/helpers/test_html_escape.rb +0 -18
  131. data/test/helpers/test_link_to.rb +0 -251
  132. data/test/helpers/test_rendering.rb +0 -109
  133. data/test/helpers/test_tagging.rb +0 -89
  134. data/test/helpers/test_text.rb +0 -26
  135. data/test/helpers/test_xml_sitemap.rb +0 -69
  136. data/test/tasks/test_clean.rb +0 -71
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2007-2011 Denis Defreyne and contributors
1
+ Copyright (c) 2007-2010 Denis Defreyne and contributors
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/NEWS.md CHANGED
@@ -1,55 +1,5 @@
1
1
  # nanoc news
2
2
 
3
- ## 3.1.9 (2011-06-30)
4
-
5
- * Really fixed dependency generation between Sass partials this time
6
- * Updated Less filter to 2.0
7
- * Made colorize_syntax filter throw an error if pygmentize is not available
8
-
9
- ## 3.1.8 (2011-06-25)
10
-
11
- * Made link validator accept https: URLs
12
- * Fixed erronous handling of layouts with names ending in index
13
- * Fixed dependency generation between Sass partials
14
- * Fixed errors related to thread requires
15
- * Fixed crash while handling load errors
16
- * Improved encoding handling while reading files
17
-
18
- ## 3.1.7 (2011-05-03)
19
-
20
- * Restored compatibility with Sass 3.1
21
-
22
- ## 3.1.6 (2010-11-21)
23
-
24
- * Fixed issues with incompatible encodings
25
-
26
- ## 3.1.5 (2010-08-24)
27
-
28
- * Improved `#render` documentation
29
- * Improved metadata section check so that e.g. raw diffs are handled properly
30
- * Deprecated using `Nanoc3::Site#initialize` with a non-`"."` argument
31
- * Added Ruby engine to version string
32
- * Allowed the `created_at` and `updated_at` attributes used in the `Blogging`
33
- helper to be `Date` instances
34
-
35
- ## 3.1.4 (2010-07-25)
36
-
37
- * Made INT and TERM signals always quit the CLI
38
- * Allowed relative imports in LESS
39
- * Made sure modification times are unchanged for identical recompiled items
40
- * Improved link validator error handling
41
- * Made pygmentize not output extra divs and pres
42
- * Allowed colorizers to be specified using symbols instead of strings
43
- * Added scss to the default list of text extensions
44
-
45
- ## 3.1.3 (2010-04-25)
46
-
47
- * Removed annoying win32console warning [Eric Sunshine]
48
- * Removed color codes when not writing to a terminal, or when writing to
49
- Windows’ console when win32console is not installed [Eric Sunshine]
50
- * Added .xhtml and .xml to list of text extensions
51
- * Improved support for relative Sass @imports [Chris Eppstein]
52
-
53
3
  ## 3.1.2 (2010-04-07)
54
4
 
55
5
  * Fixed bug which could cause incorrect output when compilation of an item is
data/README.md CHANGED
@@ -52,38 +52,26 @@ you want to explore nanoc from a technical perspective.
52
52
 
53
53
  ## Dependencies
54
54
 
55
- nanoc has few dependencies. It is possible to use nanoc programmatically
56
- without any dependencies at all, but if you want to use nanoc in a proper way,
57
- you’ll likely need some dependencies:
55
+ nanoc itself can be used without installing any dependencies. Some
56
+ components, however, do have dependencies:
58
57
 
59
- * The **commandline frontend** depends on `cli`.
60
58
  * The **autocompiler** depends on `mime-types` and `rack`.
61
- * Filters and helpers likely have dependencies on their own too.
62
-
63
- If you’re developing for nanoc, such as writing custom filters or helpers, you
64
- may be interested in the development dependencies:
65
-
66
59
  * For **documentation generation** you’ll need `yard`.
67
60
  * For **packaging** you’ll need `rubygems` (1.3 or newer).
68
- * For **testing** you’ll need `mocha` and `minitest`.
61
+ * For **testing** you’ll need `mocha`.
69
62
 
70
63
  ## Contributors
71
64
 
72
65
  (In alphabetical order)
73
66
 
74
- * Ben Armston
75
67
  * Colin Barrett
76
68
  * Dmitry Bilunov
77
69
  * Brian Candler
78
70
  * Chris Eppstein
79
- * Felix Hanley
80
71
  * Starr Horne
81
- * Tuomas Kareinen
82
72
  * Nicky Peeters
83
73
  * Christian Plessl
84
74
  * Šime Ramov
85
- * Xavier Shay
86
- * Arnau Siches
87
75
  * “Soryu”
88
76
  * Eric Sunshine
89
77
  * Dennis Sutch
data/bin/nanoc3 CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
  # encoding: utf-8
3
3
 
4
+ puts "Starting..."
5
+
4
6
  # Add lib to load path
5
7
  $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../lib'))
6
8
 
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3
4
+
5
+ # Responsible for creating checksums of files. Such checksums are used to
6
+ # determine whether files have changed between compilations.
7
+ class Checksummer
8
+
9
+ # Returns a checksum for the specified file. Multiple invocations of this
10
+ # method with the same filename argument will yield the same result.
11
+ #
12
+ # The returned checksum has the property that for two given files with
13
+ # different content, the returned checksum will be different with a very
14
+ # high probability. In practice, this boils down to using a secure
15
+ # cryptographic hash function, such as SHA-1.
16
+ #
17
+ # The format of the resulting checksum should not be relied upon. In
18
+ # future versions of nanoc, this function may use a different method for
19
+ # generating checksums.
20
+ #
21
+ # @param [String] filename The name of the file for which the checksum
22
+ # should be calculated
23
+ #
24
+ # @return [String] The checksum for the given files
25
+ def self.checksum_for(filename)
26
+ require 'digest'
27
+
28
+ digest = Digest::SHA1.new
29
+ File.open(filename, 'r') do |io|
30
+ until io.eof
31
+ data = io.readpartial(2**10)
32
+ digest.update(data)
33
+ end
34
+ end
35
+ digest.hexdigest
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -2,9 +2,7 @@
2
2
 
3
3
  module Nanoc3
4
4
 
5
- # Nanoc3::CodeSnippet represent a piece of custom code of a nanoc site. It
6
- # contains the textual source code as well as a mtime, which is used to
7
- # speed up site compilation.
5
+ # Nanoc3::CodeSnippet represent a piece of custom code of a nanoc site.
8
6
  class CodeSnippet
9
7
 
10
8
  # The {Nanoc3::Site} this code snippet belongs to.
@@ -22,24 +20,38 @@ module Nanoc3
22
20
  # @return [String]
23
21
  attr_reader :filename
24
22
 
25
- # The time where this code snippet was last modified.
26
- #
27
- # @return [Time]
28
- attr_reader :mtime
23
+ # @return [String] The checksum of this code snippet that was in effect
24
+ # during the previous site compilation
25
+ attr_accessor :old_checksum
26
+
27
+ # @return [String] The current, up-to-date checksum of this code snippet
28
+ attr_reader :new_checksum
29
29
 
30
30
  # Creates a new code snippet.
31
31
  #
32
32
  # @param [String] data The raw source code which will be executed before
33
- # compilation
33
+ # compilation
34
34
  #
35
35
  # @param [String] filename The filename corresponding to this code snippet
36
36
  #
37
- # @param [Time] mtime The time when the code was last modified (can be
38
- # nil)
39
- def initialize(data, filename, mtime=nil)
37
+ # @param [Time, Hash] params Extra parameters. For backwards
38
+ # compatibility, this can be a Time instance indicating the time when
39
+ # this code snippet was last modified (mtime).
40
+ #
41
+ # @option params [Time, nil] :mtime (nil) The time when this code snippet
42
+ # was last modified
43
+ #
44
+ # @option params [String, nil] :checksum (nil) The current, up-to-date
45
+ # checksum of this code snippet
46
+ def initialize(data, filename, params=nil)
47
+ # Get mtime and checksum
48
+ params ||= {}
49
+ params = { :mtime => params } if params.is_a?(Time)
50
+ @new_checksum = params[:checksum]
51
+ @mtime = params[:mtime]
52
+
40
53
  @data = data
41
54
  @filename = filename
42
- @mtime = mtime
43
55
  end
44
56
 
45
57
  # Loads the code by executing it.
@@ -49,6 +61,12 @@ module Nanoc3
49
61
  eval(@data, TOPLEVEL_BINDING, @filename)
50
62
  end
51
63
 
64
+ # @return [Boolean] true if the code snippet was modified since it was
65
+ # last compiled, false otherwise
66
+ def outdated?
67
+ !self.old_checksum || !self.new_checksum || self.new_checksum != self.old_checksum
68
+ end
69
+
52
70
  end
53
71
 
54
72
  end
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3
4
+
5
+ # Represents a cache than can be used to store already compiled content,
6
+ # to prevent it from being needlessly recompiled.
7
+ #
8
+ # This class is intended for internal use only. Do not rely on its
9
+ # presence; future versions of nanoc, even in the 3.x branch, may no
10
+ # longer contain this class.
11
+ class CompiledContentCache
12
+
13
+ # @return [String] The filename where the cache will be loaded from
14
+ # and stored to
15
+ attr_reader :filename
16
+
17
+ # Creates a new cache for the given filename.
18
+ #
19
+ # @param [String] filename The filename where the cache will be loaded
20
+ # from and stored to
21
+ def initialize(filename)
22
+ require 'pstore'
23
+
24
+ @filename = filename
25
+ end
26
+
27
+ # Loads the cache from the filesystem into memory.
28
+ #
29
+ # @return [void]
30
+ def load
31
+ cache = nil
32
+ return if !File.file?(filename)
33
+ pstore.transaction { cache = pstore[:compiled_content] }
34
+ end
35
+
36
+ # Stores the content of the (probably modified) in-memory cache to the
37
+ # filesystem.
38
+ #
39
+ # @return [void]
40
+ def store
41
+ FileUtils.mkdir_p(File.dirname(filename))
42
+ pstore.transaction { pstore[:compiled_content] = cache }
43
+ end
44
+
45
+ # Returns the cached compiled content for the given item
46
+ # representation. This cached compiled content is a hash where the keys
47
+ # are the snapshot names and the values the compiled content at the
48
+ # given snapshot.
49
+ #
50
+ # @param [Nanoc3::ItemRep] rep The item rep to fetch the content for
51
+ #
52
+ # @return [Hash<Symbol,String>] A hash containing the cached compiled
53
+ # content for the given item representation
54
+ def [](rep)
55
+ item_cache = cache[rep.item.identifier] || {}
56
+ item_cache[rep.name]
57
+ end
58
+
59
+ # Sets the compiled content for the given representation.
60
+ #
61
+ # @param [Nanoc3::ItemRep] rep The item representation for which to set
62
+ # the compiled content
63
+ #
64
+ # @param [Hash<Symbol,String>] content A hash containing the compiled
65
+ # content of the given representation
66
+ #
67
+ # @return [void]
68
+ def []=(rep, content)
69
+ cache[rep.item.identifier] ||= {}
70
+ cache[rep.item.identifier][rep.name] = content
71
+ end
72
+
73
+ private
74
+
75
+ def cache
76
+ @cache ||= {}
77
+ end
78
+
79
+ def pstore
80
+ require 'pstore'
81
+ @store ||= PStore.new(@filename)
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -3,8 +3,38 @@
3
3
  module Nanoc3
4
4
 
5
5
  # Responsible for compiling a site’s item representations.
6
+ #
7
+ # The compilation process makes use of notifications (see
8
+ # {Nanoc3::NotificationCenter}) to track dependencies between items,
9
+ # layouts, etc. The following notifications are used:
10
+ #
11
+ # * `compilation_started` — indicates that the compiler has started
12
+ # compiling this item representation. Has one argument: the item
13
+ # representation itself. Only one item can be compiled at a given moment;
14
+ # therefore, it is not possible to get two consecutive
15
+ # `compilation_started` notifications without also getting a
16
+ # `compilation_ended` notification in between them.
17
+ #
18
+ # * `compilation_ended` — indicates that the compiler has finished compiling
19
+ # this item representation (either successfully or with failure). Has one
20
+ # argument: the item representation itself.
21
+ #
22
+ # * `visit_started` — indicates that the compiler requires content or
23
+ # attributes from the item representation that will be visited. Has one
24
+ # argument: the visited item identifier. This notification is used to
25
+ # track dependencies of items on other items; a `visit_started` event
26
+ # followed by another `visit_started` event indicates that the item
27
+ # corresponding to the former event will depend on the item from the
28
+ # latter event.
29
+ #
30
+ # * `visit_ended` — indicates that the compiler has finished visiting the
31
+ # item representation and that the requested attributes or content have
32
+ # been fetched (either successfully or with failure)
6
33
  class Compiler
7
34
 
35
+ # The name of the file where cached compiled content will be stored
36
+ COMPILED_CONTENT_CACHE_FILENAME = 'tmp/compiled_content'
37
+
8
38
  # The compilation stack. When the compiler begins compiling a rep or a
9
39
  # layout, it will be placed on the stack; when it is done compiling the
10
40
  # rep or layout, it will be removed from the stack.
@@ -47,34 +77,31 @@ module Nanoc3
47
77
  # representations.
48
78
  #
49
79
  # @param [Nanoc3::Item] item The item that should be compiled, along with
50
- # its dependencies. Pass `nil` if the entire site should be compiled.
80
+ # its dependencies. Pass `nil` if the entire site should be compiled.
51
81
  #
52
82
  # @option params [Boolean] :force (false) true if the rep should be
53
- # compiled even if it is not outdated, false if not
83
+ # compiled even if it is not outdated, false if not
54
84
  #
55
85
  # @return [void]
56
86
  def run(item=nil, params={})
87
+ # Parse params
88
+ params[:force] = false if !params.has_key?(:force)
89
+
57
90
  # Create output directory if necessary
58
91
  FileUtils.mkdir_p(@site.config[:output_dir])
59
92
 
60
- # Load dependencies
93
+ # Load necessary data
94
+ compiled_content_cache = CompiledContentCache.new(COMPILED_CONTENT_CACHE_FILENAME)
95
+ compiled_content_cache.load
61
96
  dependency_tracker.load_graph
62
97
 
63
98
  # Get items and reps to compile
64
- if item
65
- items = [ item ] + dependency_tracker.successors_of(item)
66
- items.uniq!
67
- else
68
- items = @site.items
69
- end
99
+ items = item ? ([ item ] + dependency_tracker.successors_of(item)).uniq : @site.items
70
100
  reps = items.map { |i| i.reps }.flatten
71
101
 
72
- # Prepare dependencies
73
- if params.has_key?(:force) && params[:force]
74
- reps.each { |r| r.force_outdated = true }
75
- else
76
- dependency_tracker.propagate_outdatedness
77
- end
102
+ # Determine which reps need to be recompiled
103
+ reps.each { |r| r.force_outdated = true } if params[:force]
104
+ dependency_tracker.propagate_outdatedness
78
105
  forget_dependencies_if_outdated(items)
79
106
 
80
107
  # Compile reps
@@ -82,11 +109,21 @@ module Nanoc3
82
109
  compile_reps(reps)
83
110
  dependency_tracker.stop
84
111
 
112
+ # Store necessary data
113
+ compiled_content_cache.store
114
+ @site.store_checksums
115
+ dependency_tracker.store_graph
116
+ ensure
85
117
  # Cleanup
86
118
  FileUtils.rm_rf(Nanoc3::Filter::TMP_BINARY_ITEMS_DIR)
119
+ end
87
120
 
88
- # Store dependencies
89
- dependency_tracker.store_graph
121
+ # Returns the dependency tracker for this site, creating it first if it
122
+ # does not yet exist.
123
+ #
124
+ # @return [Nanoc3::DependencyTracker] The dependency tracker for this site
125
+ def dependency_tracker
126
+ @dependency_tracker ||= Nanoc3::DependencyTracker.new(@site.items + @site.layouts)
90
127
  end
91
128
 
92
129
  # Finds the first matching compilation rule for the given item
@@ -95,7 +132,7 @@ module Nanoc3
95
132
  # @param [Nanoc3::ItemRep] rep The item rep for which to fetch the rule
96
133
  #
97
134
  # @return [Nanoc3::Rule, nil] The compilation rule for the given item rep,
98
- # or nil if no rules have been found
135
+ # or nil if no rules have been found
99
136
  def compilation_rule_for(rep)
100
137
  @item_compilation_rules.find do |rule|
101
138
  rule.applicable_to?(rep.item) && rule.rep_name == rep.name
@@ -107,19 +144,37 @@ module Nanoc3
107
144
  # @param [Nanoc3::ItemRep] rep The item rep for which to fetch the rule
108
145
  #
109
146
  # @return [Nanoc3::Rule, nil] The routing rule for the given item rep, or
110
- # nil if no rules have been found
147
+ # nil if no rules have been found
111
148
  def routing_rule_for(rep)
112
149
  @item_routing_rules.find do |rule|
113
150
  rule.applicable_to?(rep.item) && rule.rep_name == rep.name
114
151
  end
115
152
  end
116
153
 
154
+ # Returns the list of routing rules that can be applied to the given item
155
+ # representation. For each snapshot, the first matching rule will be
156
+ # returned. The result is a hash containing the corresponding rule for
157
+ # each snapshot.
158
+ #
159
+ # @return [Hash<Symbol, Nanoc3::Rule>] The routing rules for the given rep
160
+ def routing_rules_for(rep)
161
+ rules = {}
162
+ @item_routing_rules.each do |rule|
163
+ next if !rule.applicable_to?(rep.item)
164
+ next if rule.rep_name != rep.name
165
+ next if rules.has_key?(rule.snapshot_name)
166
+
167
+ rules[rule.snapshot_name] = rule
168
+ end
169
+ rules
170
+ end
171
+
117
172
  # Finds the filter name and arguments to use for the given layout.
118
173
  #
119
174
  # @param [Nanoc3::Layout] layout The layout for which to fetch the filter.
120
175
  #
121
176
  # @return [Array, nil] A tuple containing the filter name and the filter
122
- # arguments for the given layout.
177
+ # arguments for the given layout.
123
178
  def filter_for_layout(layout)
124
179
  @layout_filter_mapping.each_pair do |layout_identifier, filter_name_and_args|
125
180
  return filter_name_and_args if layout.identifier =~ layout_identifier
@@ -135,69 +190,41 @@ module Nanoc3
135
190
  #
136
191
  # @return [void]
137
192
  def compile_reps(reps)
138
- active_reps, skipped_reps = reps.partition { |rep| rep.outdated? || rep.item.outdated_due_to_dependencies? }
139
- inactive_reps = []
140
- compiled_reps = []
141
-
142
- # Repeat as long as something is successfully compiled...
143
- changed = true
144
- until !changed
145
- changed = false
146
-
147
- # Attempt to compile all active reps
148
- until active_reps.empty?
149
- @stack.clear
150
- begin
151
- rep = active_reps.shift
152
- puts "*** Attempting to compile #{rep.inspect}" if $DEBUG
153
-
154
- @stack.push(rep)
155
- compile_rep(rep)
156
- rescue Nanoc3::Errors::UnmetDependency => e
157
- puts "*** Attempt failed due to unmet dependency on #{e.rep.inspect}" if $DEBUG
158
-
159
- # Reinitialize rep
160
- rep.forget_progress
161
-
162
- # Save rep to compile it later
163
- inactive_reps << rep
164
-
165
- # Add dependency to list of items to compile
166
- unless active_reps.include?(e.rep) || inactive_reps.include?(e.rep)
167
- changed = true
168
- skipped_reps.delete(e.rep)
169
- inactive_reps.unshift(e.rep)
170
- end
171
- else
172
- puts "*** Attempt succeeded" if $DEBUG
173
-
174
- changed = true
175
- compiled_reps << rep
176
- end
177
- puts if $DEBUG
178
- end
179
-
180
- # Retry
181
- if inactive_reps.empty?
182
- puts "*** Nothing left to compile!" if $DEBUG
183
- break
184
- else
185
- puts "*** No active reps left; activating all (#{inactive_reps.size}) inactive reps" if $DEBUG
186
- puts if $DEBUG
187
- active_reps = inactive_reps
188
- inactive_reps = []
189
- end
193
+ require 'set'
194
+
195
+ # Partition in outdated and non-outdated
196
+ outdated_reps = Set.new
197
+ skipped_reps = Set.new
198
+ reps.each do |rep|
199
+ target = (rep.outdated? || rep.item.outdated_due_to_dependencies?) ? outdated_reps : skipped_reps
200
+ target.add(rep)
190
201
  end
191
202
 
192
- # Notify skipped reps
193
- skipped_reps.each do |rep|
194
- Nanoc3::NotificationCenter.post(:compilation_started, rep)
195
- Nanoc3::NotificationCenter.post(:compilation_ended, rep)
203
+ # Build graph for outdated reps
204
+ content_dependency_graph = Nanoc3::DirectedGraph.new(outdated_reps)
205
+
206
+ # Attempt to compile all active reps
207
+ loop do
208
+ # Find rep to compile
209
+ break if content_dependency_graph.roots.empty?
210
+ rep = content_dependency_graph.roots.each { |e| break e }
211
+ @stack = [ rep ]
212
+
213
+ begin
214
+ compile_rep(rep)
215
+ content_dependency_graph.delete_vertex(rep)
216
+ rescue Nanoc3::Errors::UnmetDependency => e
217
+ content_dependency_graph.add_edge(e.rep, rep)
218
+ unless content_dependency_graph.vertices.include?(e.rep)
219
+ skipped_reps.delete(e.rep)
220
+ content_dependency_graph.add_vertex(e.rep)
221
+ end
222
+ end
196
223
  end
197
224
 
198
- # Raise error if some active but non-compileable reps are left
199
- if !active_reps.empty?
200
- raise Nanoc3::Errors::RecursiveCompilation.new(active_reps)
225
+ # Check whether everything was compiled
226
+ if !content_dependency_graph.vertices.empty?
227
+ raise Nanoc3::Errors::RecursiveCompilation.new(content_dependency_graph.vertices)
201
228
  end
202
229
  end
203
230
 
@@ -211,34 +238,35 @@ module Nanoc3
211
238
  #
212
239
  # @return [void]
213
240
  def compile_rep(rep)
214
- # Start
215
241
  Nanoc3::NotificationCenter.post(:compilation_started, rep)
216
242
  Nanoc3::NotificationCenter.post(:visit_started, rep.item)
217
243
 
218
- # Apply matching rule
219
- compilation_rule_for(rep).apply_to(rep)
220
- rep.compiled = true
244
+ if !rep.outdated? && !rep.item.outdated_due_to_dependencies && compiled_content_cache[rep]
245
+ Nanoc3::NotificationCenter.post(:cached_content_used, rep)
246
+ rep.content = compiled_content_cache[rep]
247
+ else
248
+ rep.snapshot(:raw)
249
+ rep.snapshot(:pre, :final => false)
250
+ compilation_rule_for(rep).apply_to(rep)
251
+ rep.snapshot(:post) if rep.has_snapshot?(:post)
252
+ rep.snapshot(:last)
253
+ end
221
254
 
222
- # Write if rep is routed
223
- rep.write unless rep.raw_path.nil?
255
+ rep.compiled = true
256
+ compiled_content_cache[rep] = rep.content
257
+ rescue => e
258
+ rep.forget_progress
259
+ Nanoc3::NotificationCenter.post(:compilation_failed, rep)
260
+ raise e
224
261
  ensure
225
- # Stop
226
262
  Nanoc3::NotificationCenter.post(:visit_ended, rep.item)
227
263
  Nanoc3::NotificationCenter.post(:compilation_ended, rep)
228
264
  end
229
265
 
230
- # Returns the dependency tracker for this site, creating it first if it
231
- # does not yet exist.
232
- #
233
- # @return [Nanoc3::DependencyTracker] The dependency tracker for this site
234
- def dependency_tracker
235
- @dependency_tracker ||= Nanoc3::DependencyTracker.new(@site.items)
236
- end
237
-
238
266
  # Clears the list of dependencies for items that will be recompiled.
239
267
  #
240
268
  # @param [Array<Nanoc3::Item>] items The list of items for which to forget
241
- # the dependencies
269
+ # the dependencies
242
270
  #
243
271
  # @return [void]
244
272
  def forget_dependencies_if_outdated(items)
@@ -249,6 +277,17 @@ module Nanoc3
249
277
  end
250
278
  end
251
279
 
280
+ # Returns the cache used for storing compiled content.
281
+ #
282
+ # @return [CompiledContentCache] The compiled content cache
283
+ def compiled_content_cache
284
+ @compiled_content_cache ||= begin
285
+ cache = Nanoc3::CompiledContentCache.new(COMPILED_CONTENT_CACHE_FILENAME)
286
+ cache.load
287
+ cache
288
+ end
289
+ end
290
+
252
291
  end
253
292
 
254
293
  end