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
@@ -33,13 +33,13 @@ module Nanoc3
33
33
  # rep as a block argument.
34
34
  #
35
35
  # @param [String] identifier A pattern matching identifiers of items that
36
- # should be compiled using this rule
36
+ # should be compiled using this rule
37
37
  #
38
38
  # @option params [Symbol] :rep (:default) The name of the representation
39
- # that should be compiled using this rule
39
+ # that should be compiled using this rule
40
40
  #
41
41
  # @yield The block that will be executed when an item matching this
42
- # compilation rule needs to be compiled
42
+ # compilation rule needs to be compiled
43
43
  #
44
44
  # @return [void]
45
45
  #
@@ -77,13 +77,13 @@ module Nanoc3
77
77
  # and passing the rep as a block argument.
78
78
  #
79
79
  # @param [String] identifier A pattern matching identifiers of items that
80
- # should be routed using this rule
80
+ # should be routed using this rule
81
81
  #
82
82
  # @option params [Symbol] :rep (:default) The name of the representation
83
- # that should be routed using this rule
83
+ # that should be routed using this rule
84
84
  #
85
85
  # @yield The block that will be executed when an item matching this
86
- # compilation rule needs to be routed
86
+ # compilation rule needs to be routed
87
87
  #
88
88
  # @return [void]
89
89
  #
@@ -103,10 +103,11 @@ module Nanoc3
103
103
  raise ArgumentError.new("#route requires a block") unless block_given?
104
104
 
105
105
  # Get rep name
106
- rep_name = params[:rep] || :default
106
+ rep_name = params[:rep] || :default
107
+ snapshot_name = params[:snapshot] || :last
107
108
 
108
109
  # Create rule
109
- rule = Rule.new(identifier_to_regex(identifier), rep_name, block)
110
+ rule = Rule.new(identifier_to_regex(identifier), rep_name, block, :snapshot_name => snapshot_name)
110
111
  @site.compiler.item_routing_rules << rule
111
112
  end
112
113
 
@@ -117,13 +118,13 @@ module Nanoc3
117
118
  # contains filter arguments that will be passed to the filter.
118
119
  #
119
120
  # @param [String] identifier A pattern matching identifiers of layouts
120
- # that should be filtered using this rule
121
+ # that should be filtered using this rule
121
122
  #
122
123
  # @param [Symbol] filter_name The name of the filter that should be run
123
- # when processing the layout
124
+ # when processing the layout
124
125
  #
125
126
  # @param [Hash] params Extra filter arguments that should be passed to the
126
- # filter when processing the layout (see {Nanoc3::Filter#run})
127
+ # filter when processing the layout (see {Nanoc3::Filter#run})
127
128
  #
128
129
  # @return [void]
129
130
  #
@@ -18,12 +18,12 @@ module Nanoc3::StringExtensions
18
18
  # @return [String] The decomposed string
19
19
  def make_compatible_with_env
20
20
  # Check whether environment supports Unicode
21
- # TODO this is ugly, and there most likely are better ways to do this
21
+ # FIXME this is ugly, and there most likely are better ways to do this
22
22
  is_unicode_supported = %w( LC_ALL LC_CTYPE LANG ).any? { |e| ENV[e] =~ /UTF/ }
23
23
  return self if is_unicode_supported
24
24
 
25
25
  # Decompose if necessary
26
- # TODO this decomposition is not generally usable
26
+ # FIXME this decomposition is not generally usable
27
27
  self.gsub(/“|”/, '"').gsub(/‘|’/, '\'').gsub('…', '...')
28
28
  end
29
29
 
@@ -21,21 +21,22 @@ module Nanoc3
21
21
  # first time.
22
22
  #
23
23
  # @abstract Subclasses should at least implement {#items} and {#layouts}. If
24
- # the data source should support creating items and layouts using the
25
- # `create_item` and `create_layout` CLI commands, the {#setup},
26
- # {#create_item} and {#create_layout} methods should be implemented as well.
24
+ # the data source should support creating items and layouts using the
25
+ # `create_item` and `create_layout` CLI commands, the {#setup},
26
+ # {#create_item} and {#create_layout} methods should be implemented as
27
+ # well.
27
28
  class DataSource
28
29
 
29
30
  # @return [String] The root path where items returned by this data source
30
- # should be mounted.
31
+ # should be mounted.
31
32
  attr_reader :items_root
32
33
 
33
34
  # @return [String] The root path where layouts returned by this data
34
- # source should be mounted.
35
+ # source should be mounted.
35
36
  attr_reader :layouts_root
36
37
 
37
38
  # @return [Hash] The configuration for this data source. For example,
38
- # online data sources could contain authentication details.
39
+ # online data sources could contain authentication details.
39
40
  attr_reader :config
40
41
 
41
42
  extend Nanoc3::PluginRegistry::PluginMethods
@@ -45,12 +46,12 @@ module Nanoc3
45
46
  # @param [Nanoc3::Site] site The site this data source belongs to.
46
47
  #
47
48
  # @param [String] items_root The prefix that should be given to all items
48
- # returned by the #items method (comparable to mount points for
49
- # filesystems in Unix-ish OSes).
49
+ # returned by the #items method (comparable to mount points for
50
+ # filesystems in Unix-ish OSes).
50
51
  #
51
52
  # @param [String] layouts_root The prefix that should be given to all
52
- # layouts returned by the #layouts method (comparable to mount points
53
- # for filesystems in Unix-ish OSes).
53
+ # layouts returned by the #layouts method (comparable to mount points
54
+ # for filesystems in Unix-ish OSes).
54
55
  #
55
56
  # @param [Hash] config The configuration for this data source.
56
57
  def initialize(site, items_root, layouts_root, config)
@@ -189,9 +190,9 @@ module Nanoc3
189
190
  # @param [String] identifier
190
191
  #
191
192
  # @param [Hash] params Extra parameters to give to the data source. This
192
- # can be used to influence the way items are stored. For example,
193
- # filesystem data sources could use this to pass the extension of the
194
- # files that should be generated.
193
+ # can be used to influence the way items are stored. For example,
194
+ # filesystem data sources could use this to pass the extension of the
195
+ # files that should be generated.
195
196
  #
196
197
  # @return [void]
197
198
  def create_item(content, attributes, identifier, params={})
@@ -212,9 +213,9 @@ module Nanoc3
212
213
  # @param [String] identifier
213
214
  #
214
215
  # @param [Hash] params Extra parameters to give to the data source. This
215
- # can be used to influence the way items are stored. For example,
216
- # filesystem data sources could use this to pass the extension of the
217
- # files that should be generated.
216
+ # can be used to influence the way items are stored. For example,
217
+ # filesystem data sources could use this to pass the extension of the
218
+ # files that should be generated.
218
219
  #
219
220
  # @return [void]
220
221
  def create_layout(content, attributes, identifier, params={})
@@ -4,18 +4,18 @@ require 'pstore'
4
4
 
5
5
  module Nanoc3
6
6
 
7
- # Responsible for remembering dependencies between items. It is used to
8
- # speed up compilation by only letting an item be recompiled when it is
9
- # outdated or any of its dependencies (or dependencies’ dependencies, etc)
10
- # is outdated.
7
+ # Responsible for remembering dependencies between items and layouts. It is
8
+ # used to speed up compilation by only letting an item be recompiled when it
9
+ # is outdated or any of its dependencies (or dependencies’ dependencies,
10
+ # etc) is outdated.
11
11
  #
12
12
  # The dependencies tracked by the dependency tracker are not dependencies
13
- # based on an item’s content. When one item uses an attribute of another
14
- # item, then this is also treated as a dependency. While dependencies based
15
- # on an item’s content (handled in {Nanoc3::Compiler}) cannot be mutually
16
- # recursive, the more general dependencies in Nanoc3::DependencyTracker can
17
- # (e.g. item A can use an attribute of item B and vice versa without
18
- # problems).
13
+ # based on an item’s or a layout’s content. When one object uses an
14
+ # attribute of another object, then this is also treated as a dependency.
15
+ # While dependencies based on an item’s or layout’s content (handled in
16
+ # {Nanoc3::Compiler}) cannot be mutually recursive, the more general
17
+ # dependencies in Nanoc3::DependencyTracker can (e.g. item A can use an
18
+ # attribute of item B and vice versa without problems).
19
19
  #
20
20
  # The dependency tracker remembers the dependency information between runs.
21
21
  # Dependency information is stored in the `tmp/dependencies` file. This file
@@ -24,25 +24,26 @@ module Nanoc3
24
24
  class DependencyTracker
25
25
 
26
26
  # @return [String] The name of the file in which dependency information is
27
- # stored
27
+ # stored
28
28
  attr_accessor :filename
29
29
 
30
- # @return [Array<Nanoc3::Item>] The list of items that is being tracked
31
- # by the dependency tracker
32
- attr_reader :items
30
+ # @return [Array<Nanoc3::Item, Nanoc3::Layout>] The list of items and
31
+ # layouts that are being tracked by the dependency tracker
32
+ attr_reader :objects
33
33
 
34
34
  # The version of the file format used to store dependencies.
35
- STORE_VERSION = 2
35
+ STORE_VERSION = 3
36
36
 
37
- # Creates a new dependency tracker for the given items.
37
+ # Creates a new dependency tracker for the given items and layouts.
38
38
  #
39
- # @param [Array<Nanoc3::Item>] item The list of items whose dependencies
40
- # should be managed
41
- def initialize(items)
42
- @items = items
43
- @filename = 'tmp/dependencies'
44
- @graph = Nanoc3::DirectedGraph.new([ nil ] + @items)
45
- @previous_items = []
39
+ # @param [Array<Nanoc3::Item, Nanoc3::Layout>] objects The list of items
40
+ # and layouts whose dependencies should be managed
41
+ def initialize(objects)
42
+ @objects = objects
43
+
44
+ @filename = 'tmp/dependencies'
45
+ @graph = Nanoc3::DirectedGraph.new([ nil ] + @objects)
46
+ @previous_objects = []
46
47
  end
47
48
 
48
49
  # Starts listening for dependency messages (`:visit_started` and
@@ -50,24 +51,19 @@ module Nanoc3
50
51
  #
51
52
  # @return [void]
52
53
  def start
53
- # Initialize dependency stack. An item will be pushed onto this stack
54
- # when it is visited. Therefore, an item on the stack always depends on
55
- # all items pushed above it.
54
+ # Initialize dependency stack. An object will be pushed onto this stack
55
+ # when it is visited. Therefore, an object on the stack always depends
56
+ # on all objects pushed above it.
56
57
  @stack = []
57
58
 
58
59
  # Register start of visits
59
- Nanoc3::NotificationCenter.on(:visit_started, self) do |item|
60
- # Record possible dependency
61
- unless @stack.empty?
62
- $stderr.puts "*** Recording dependency on #{item.inspect}" if $DEBUG
63
- self.record_dependency(@stack[-1], item)
64
- end
65
-
66
- @stack.push(item)
60
+ Nanoc3::NotificationCenter.on(:visit_started, self) do |obj|
61
+ self.record_dependency(@stack[-1], obj) unless @stack.empty?
62
+ @stack.push(obj)
67
63
  end
68
64
 
69
65
  # Register end of visits
70
- Nanoc3::NotificationCenter.on(:visit_ended, self) do |item|
66
+ Nanoc3::NotificationCenter.on(:visit_ended, self) do |obj|
71
67
  @stack.pop
72
68
  end
73
69
  end
@@ -81,71 +77,78 @@ module Nanoc3
81
77
  Nanoc3::NotificationCenter.remove(:visit_ended, self)
82
78
  end
83
79
 
84
- # Returns the direct dependencies for `item`.
80
+ # Returns the direct dependencies for the given object.
85
81
  #
86
- # The direct dependencies of `item` include the items that, when outdated
87
- # will cause `item` to be marked as outdated. Indirect dependencies will
88
- # not be returned (e.g. if A depends on B which depends on C, then the
89
- # direct dependencies of A do not include C).
82
+ # The direct dependencies of the given object include the items
83
+ # and layouts that, when outdated will cause the given object to be marked
84
+ # as outdated. Indirect dependencies will not be returned (e.g. if A
85
+ # depends on B which depends on C, then the direct dependencies of A do
86
+ # not include C).
90
87
  #
91
- # @param [Nanoc3::Item] item The item for which to fetch the direct
92
- # predecessors
88
+ # @param [Nanoc3::Item, Nanoc3::Layout] object The object for
89
+ # which to fetch the direct predecessors
93
90
  #
94
- # @return [Array<Nanoc3::Item>] The direct predecessors of the given item
95
- def direct_predecessors_of(item)
96
- @graph.direct_predecessors_of(item).compact
91
+ # @return [Array<Nanoc3::Item, Nanoc3::Layout>] The direct predecessors of
92
+ # the given object
93
+ def direct_predecessors_of(object)
94
+ @graph.direct_predecessors_of(object).compact
97
95
  end
98
96
 
99
- # Returns all dependencies (direct and indirect) for `item`.
97
+ # Returns all dependencies (direct and indirect) for the given object.
100
98
  #
101
- # The dependencies of `item` include the items that, when outdated, will
102
- # cause `item` to be marked as outdated.
99
+ # The dependencies of given object include the objects that, when
100
+ # outdated, will cause the given object to be marked as outdated.
103
101
  #
104
- # @param [Nanoc3::Item] item The item for which to fetch all direct and
105
- # indirect predecessors
102
+ # @param [Nanoc3::Item, Nanoc3::Layout] object The object for which to
103
+ # fetch all direct and indirect predecessors
106
104
  #
107
- # @return [Array<Nanoc3::Item>] The predecessors of the given item
108
- def predecessors_of(item)
109
- @graph.predecessors_of(item).compact
105
+ # @return [Array<Nanoc3::Item, Nanoc3::Layout>] The predecessors of the
106
+ # given object
107
+ def predecessors_of(object)
108
+ @graph.predecessors_of(object).compact
110
109
  end
111
110
 
112
- # Returns the direct inverse dependencies for `item`.
111
+ # Returns the direct inverse dependencies for the given object.
113
112
  #
114
- # The direct inverse dependencies of `item` include the items that will be
115
- # marked as outdated when`+item` is outdated. Indirect dependencies will
116
- # not be returned (e.g. if A depends on B which depends on C, then the
117
- # direct inverse dependencies of C do not include A).
113
+ # The direct inverse dependencies of the given object include the objects
114
+ # that will be marked as outdated when the given object is outdated.
115
+ # Indirect dependencies will not be returned (e.g. if A depends on B which
116
+ # depends on C, then the direct inverse dependencies of C do not include
117
+ # A).
118
118
  #
119
- # @param [Nanoc3::Item] item The item for which to fetch the direct
120
- # successors
119
+ # @param [Nanoc3::Item, Nanoc3::Layout] object The object for which to
120
+ # fetch the direct successors
121
121
  #
122
- # @return [Array<Nanoc3::Item>] The direct successors of the given item
123
- def direct_successors_of(item)
124
- @graph.direct_successors_of(item).compact
122
+ # @return [Array<Nanoc3::Item, Nanoc3::Layout>] The direct successors of
123
+ # the given object
124
+ def direct_successors_of(object)
125
+ @graph.direct_successors_of(object).compact
125
126
  end
126
127
 
127
- # Returns all inverse dependencies (direct and indirect) for `item`.
128
+ # Returns all inverse dependencies (direct and indirect) for the given
129
+ # object.
128
130
  #
129
- # The inverse dependencies of `item` include the items that will be marked
130
- # as outdated when `item` is outdated.
131
+ # The inverse dependencies of the given object include the objects that
132
+ # will be marked as outdated when the given object is outdated.
131
133
  #
132
- # @param [Nanoc3::Item] item The item for which to fetch all direct and
133
- # indirect successors
134
+ # @param [Nanoc3::Item, Nanoc3::Layout] object The object for which to
135
+ # fetch all direct and indirect successors
134
136
  #
135
- # @return [Array<Nanoc3::Item>] The successors of the given item
136
- def successors_of(item)
137
- @graph.successors_of(item).compact
137
+ # @return [Array<Nanoc3::Item, Nanoc3::Layout>] The successors of the
138
+ # given object
139
+ def successors_of(object)
140
+ @graph.successors_of(object).compact
138
141
  end
139
142
 
140
143
  # Records a dependency from `src` to `dst` in the dependency graph. When
141
144
  # `dst` is oudated, `src` will also become outdated.
142
145
  #
143
- # @param [Nanoc3::Item] src The source of the dependency, i.e. the item
144
- # that will become outdated if dst is outdated
146
+ # @param [Nanoc3::Item, Nanoc3::Layout] src The source of the dependency,
147
+ # i.e. the object that will become outdated if dst is outdated
145
148
  #
146
- # @param [Nanoc3::Item] dst The destination of the dependency, i.e. the
147
- # item that will cause the source to become outdated if the destination
148
- # is outdated
149
+ # @param [Nanoc3::Item, Nanoc3::Layout] dst The destination of the
150
+ # dependency, i.e. the object that will cause the source to become
151
+ # outdated if the destination is outdated
149
152
  #
150
153
  # @return [void]
151
154
  def record_dependency(src, dst)
@@ -162,7 +165,7 @@ module Nanoc3
162
165
  store = PStore.new(self.filename)
163
166
  store.transaction do
164
167
  store[:version] = STORE_VERSION
165
- store[:vertices] = @graph.vertices.map { |i| i && i.identifier }
168
+ store[:vertices] = @graph.vertices.map { |obj| obj && obj.reference }
166
169
  store[:edges] = @graph.edges
167
170
  end
168
171
  end
@@ -173,7 +176,7 @@ module Nanoc3
173
176
  # @return [void]
174
177
  def load_graph
175
178
  # Create new graph
176
- @graph = Nanoc3::DirectedGraph.new([ nil ] + @items)
179
+ @graph = Nanoc3::DirectedGraph.new([ nil ] + @objects)
177
180
 
178
181
  # Get store
179
182
  return if !File.file?(self.filename)
@@ -185,44 +188,44 @@ module Nanoc3
185
188
  return if store[:version] != STORE_VERSION
186
189
 
187
190
  # Load vertices
188
- @previous_items = store[:vertices].map do |v|
189
- @items.find { |i| i.identifier == v }
191
+ @previous_objects = store[:vertices].map do |reference|
192
+ @objects.find { |obj| reference == obj.reference }
190
193
  end
191
194
 
192
195
  # Load edges
193
196
  store[:edges].each do |edge|
194
197
  from_index, to_index = *edge
195
- from, to = @previous_items[from_index], @previous_items[to_index]
198
+ from, to = @previous_objects[from_index], @previous_objects[to_index]
196
199
  @graph.add_edge(from, to)
197
200
  end
198
201
  end
199
202
  end
200
203
 
201
- # Traverses the dependency graph and marks all items that (directly or
202
- # indirectly) depend on an outdated item as outdated.
204
+ # Traverses the dependency graph and marks all objects that (directly or
205
+ # indirectly) depend on an outdated object as outdated.
203
206
  #
204
207
  # @return [void]
205
208
  def propagate_outdatedness
206
209
  # Unmark everything
207
- @items.each { |i| i.outdated_due_to_dependencies = false }
210
+ @objects.each { |o| o.outdated_due_to_dependencies = false }
208
211
 
209
- # Mark new items as outdated
210
- added_items = @items - @previous_items
211
- added_items.each { |i| i.outdated_due_to_dependencies = true }
212
+ # Mark new objects as outdated
213
+ added_objects = @objects - @previous_objects
214
+ added_objects.each { |o| o.outdated_due_to_dependencies = true }
212
215
 
213
216
  # Mark successors of nil as outdated
214
- self.successors_of(nil).each do |i|
215
- i.outdated_due_to_dependencies = true
217
+ self.successors_of(nil).each do |o|
218
+ o.outdated_due_to_dependencies = true
216
219
  end
217
220
 
218
- # Mark successors of outdated items as outdated
221
+ # Mark successors of outdated objects as outdated
219
222
  require 'set'
220
- unprocessed = @items.select { |i| i.outdated? }
223
+ unprocessed = @objects.select { |o| o.outdated? }
221
224
  seen = Set.new(unprocessed)
222
225
  until unprocessed.empty?
223
- item = unprocessed.shift
226
+ obj = unprocessed.shift
224
227
 
225
- self.direct_successors_of(item).each do |successor|
228
+ self.direct_successors_of(obj).each do |successor|
226
229
  next if seen.include?(successor)
227
230
  seen << successor
228
231
 
@@ -232,32 +235,17 @@ module Nanoc3
232
235
  end
233
236
  end
234
237
 
235
- # Empties the list of dependencies for the given item. This is necessary
236
- # before recompiling the given item, because otherwise old dependencies
238
+ # Empties the list of dependencies for the given object. This is necessary
239
+ # before recompiling the given object, because otherwise old dependencies
237
240
  # will stick around and new dependencies will appear twice. This function
238
241
  # removes all incoming edges for the given vertex.
239
242
  #
240
- # @param [Nanoc3::Item] item The item for which to forget all dependencies
241
- #
242
- # @return [void]
243
- def forget_dependencies_for(item)
244
- @graph.delete_edges_to(item)
245
- end
246
-
247
- # Prints the dependency graph in human-readable form.
243
+ # @param [Nanoc3::Item, Nanoc3::Layout] object The object for which to
244
+ # forget all dependencies
248
245
  #
249
246
  # @return [void]
250
- def print_graph
251
- @items.each do |item|
252
- puts "#{item.inspect} depends on:"
253
-
254
- predecessors = direct_predecessors_of(item)
255
- predecessors.each do |pred|
256
- puts " #{pred.inspect}"
257
- end
258
- puts " (nothing!)" if predecessors.empty?
259
- puts
260
- end
247
+ def forget_dependencies_for(object)
248
+ @graph.delete_edges_to(object)
261
249
  end
262
250
 
263
251
  # @deprecated Use {#propagate_outdatedness} instead.
@@ -265,13 +253,6 @@ module Nanoc3
265
253
  propagate_outdatedness
266
254
  end
267
255
 
268
- private
269
-
270
- # Returns the item with the given identifier, or nil if no item is found.
271
- def item_with_identifier(identifier)
272
- @items.find { |i| i.identifier == identifier }
273
- end
274
-
275
256
  end
276
257
 
277
258
  end
@@ -46,16 +46,19 @@ module Nanoc3
46
46
  @to_graph = {}
47
47
 
48
48
  @vertice_indexes = {}
49
- vertices.each_with_index do |v, i|
49
+ @vertices.each_with_index do |v, i|
50
50
  @vertice_indexes[v] = i
51
51
  end
52
52
 
53
+ @roots = Set.new(@vertices)
54
+
53
55
  invalidate_caches
54
56
  end
55
57
 
56
58
  # Adds an edge from the first vertex to the second vertex.
57
59
  #
58
60
  # @param from Vertex where the edge should start
61
+ #
59
62
  # @param to Vertex where the edge should end
60
63
  #
61
64
  # @return [void]
@@ -66,6 +69,8 @@ module Nanoc3
66
69
  @to_graph[to] ||= Set.new
67
70
  @to_graph[to] << from
68
71
 
72
+ @roots.delete(to)
73
+
69
74
  invalidate_caches
70
75
  end
71
76
 
@@ -73,30 +78,75 @@ module Nanoc3
73
78
  # edge does not exist, nothing is done.
74
79
  #
75
80
  # @param from Start vertex of the edge
81
+ #
76
82
  # @param to End vertex of the edge
77
83
  #
78
84
  # @return [void]
79
- def remove_edge(from, to)
85
+ def delete_edge(from, to)
80
86
  @from_graph[from] ||= Set.new
81
87
  @from_graph[from].delete(to)
82
88
 
83
89
  @to_graph[to] ||= Set.new
84
90
  @to_graph[to].delete(from)
85
91
 
92
+ @roots.add(to) if @to_graph[to].empty?
93
+
86
94
  invalidate_caches
87
95
  end
88
96
 
97
+ # Adds the given vertex to the graph.
98
+ #
99
+ # @param [Vertex] v The vertex to add to the graph
100
+ #
101
+ # @return [void]
102
+ def add_vertex(v)
103
+ return if @vertices.include?(v)
104
+
105
+ @vertices << v
106
+ @roots << v
107
+ end
108
+
109
+ # Deletes all edges coming from the given vertex.
110
+ #
111
+ # @param from Vertex from which all edges should be removed
112
+ #
113
+ # @return [void]
114
+ def delete_edges_from(from)
115
+ return if @from_graph[from].nil?
116
+
117
+ @from_graph[from].each do |to|
118
+ @to_graph[to].delete(from)
119
+ @roots.add(to) if @to_graph[to].empty?
120
+ end
121
+ @from_graph.delete(from)
122
+ end
123
+
89
124
  # Deletes all edges going to the given vertex.
90
125
  #
91
126
  # @param to Vertex to which all edges should be removed
92
127
  #
93
128
  # @return [void]
94
129
  def delete_edges_to(to)
95
- @to_graph[to] ||= Set.new
130
+ return if @to_graph[to].nil?
131
+
96
132
  @to_graph[to].each do |from|
97
133
  @from_graph[from].delete(to)
98
134
  end
99
135
  @to_graph.delete(to)
136
+ @roots.add(to)
137
+ end
138
+
139
+ # Removes the given vertex from the graph.
140
+ #
141
+ # @param v Vertex to remove from the graph
142
+ #
143
+ # @return [void]
144
+ def delete_vertex(v)
145
+ delete_edges_to(v)
146
+ delete_edges_from(v)
147
+
148
+ @vertices.delete(v)
149
+ @roots.delete(v)
100
150
  end
101
151
 
102
152
  # Returns the direct predecessors of the given vertex, i.e. the vertices
@@ -153,6 +203,18 @@ module Nanoc3
153
203
  result
154
204
  end
155
205
 
206
+ # Returns all root vertices, i.e. vertices where no edge points to.
207
+ #
208
+ # @return [Set] The set of all root vertices in this graph.
209
+ def roots
210
+ @roots
211
+ end
212
+
213
+ # @deprecated Use {#delete_edge} instead
214
+ def remove_edge(from, to)
215
+ delete_edge(from, to)
216
+ end
217
+
156
218
  private
157
219
 
158
220
  # Invalidates cached data. This method should be called when the internal