nanoc3 3.1.9 → 3.2.0a1

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