nanoc3 3.2.0a3 → 3.2.0a4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/.gemtest +0 -0
  2. data/LICENSE +1 -1
  3. data/NEWS.md +23 -4
  4. data/README.md +7 -0
  5. data/lib/nanoc3/base/compilation/checksum_store.rb +17 -90
  6. data/lib/nanoc3/base/compilation/compiled_content_cache.rb +5 -0
  7. data/lib/nanoc3/base/compilation/compiler.rb +112 -175
  8. data/lib/nanoc3/base/compilation/compiler_dsl.rb +54 -11
  9. data/lib/nanoc3/base/compilation/dependency_tracker.rb +32 -65
  10. data/lib/nanoc3/base/compilation/filter.rb +4 -3
  11. data/lib/nanoc3/base/compilation/item_rep_proxy.rb +19 -4
  12. data/lib/nanoc3/base/compilation/item_rep_recorder_proxy.rb +90 -0
  13. data/lib/nanoc3/base/compilation/outdatedness_checker.rb +152 -15
  14. data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +12 -9
  15. data/lib/nanoc3/base/compilation/rule.rb +3 -1
  16. data/lib/nanoc3/base/compilation/rule_memory_calculator.rb +42 -0
  17. data/lib/nanoc3/base/compilation/rule_memory_store.rb +53 -0
  18. data/lib/nanoc3/base/compilation/rules_collection.rb +205 -0
  19. data/lib/nanoc3/base/core_ext/array.rb +20 -0
  20. data/lib/nanoc3/base/core_ext/hash.rb +30 -0
  21. data/lib/nanoc3/base/core_ext/pathname.rb +26 -0
  22. data/lib/nanoc3/base/core_ext/string.rb +12 -0
  23. data/lib/nanoc3/base/core_ext.rb +1 -0
  24. data/lib/nanoc3/base/directed_graph.rb +11 -3
  25. data/lib/nanoc3/base/errors.rb +0 -4
  26. data/lib/nanoc3/base/memoization.rb +72 -0
  27. data/lib/nanoc3/base/result_data/item_rep.rb +64 -25
  28. data/lib/nanoc3/base/source_data/code_snippet.rb +9 -0
  29. data/lib/nanoc3/base/source_data/configuration.rb +20 -0
  30. data/lib/nanoc3/base/source_data/item.rb +29 -4
  31. data/lib/nanoc3/base/source_data/layout.rb +20 -1
  32. data/lib/nanoc3/base/source_data/site.rb +49 -26
  33. data/lib/nanoc3/base/store.rb +10 -1
  34. data/lib/nanoc3/base.rb +6 -1
  35. data/lib/nanoc3/cli/base.rb +20 -7
  36. data/lib/nanoc3/cli/commands/compile.rb +0 -2
  37. data/lib/nanoc3/cli/commands/create_site.rb +16 -7
  38. data/lib/nanoc3/cli/commands/debug.rb +3 -3
  39. data/lib/nanoc3/cli/commands/view.rb +1 -0
  40. data/lib/nanoc3/cli/commands/watch.rb +2 -1
  41. data/lib/nanoc3/data_sources/deprecated/delicious.rb +0 -2
  42. data/lib/nanoc3/data_sources/deprecated/last_fm.rb +0 -2
  43. data/lib/nanoc3/data_sources/deprecated/twitter.rb +0 -2
  44. data/lib/nanoc3/data_sources/filesystem.rb +17 -3
  45. data/lib/nanoc3/data_sources/filesystem_unified.rb +17 -17
  46. data/lib/nanoc3/extra/auto_compiler.rb +5 -1
  47. data/lib/nanoc3/extra/core_ext/time.rb +1 -1
  48. data/lib/nanoc3/extra/file_proxy.rb +11 -1
  49. data/lib/nanoc3/extra/validators/links.rb +1 -1
  50. data/lib/nanoc3/filters/asciidoc.rb +3 -3
  51. data/lib/nanoc3/filters/colorize_syntax.rb +106 -27
  52. data/lib/nanoc3/filters/erb.rb +16 -6
  53. data/lib/nanoc3/filters/erubis.rb +5 -1
  54. data/lib/nanoc3/filters/haml.rb +2 -1
  55. data/lib/nanoc3/filters/less.rb +3 -6
  56. data/lib/nanoc3/filters/mustache.rb +3 -0
  57. data/lib/nanoc3/filters/redcarpet.rb +27 -0
  58. data/lib/nanoc3/filters/sass.rb +1 -5
  59. data/lib/nanoc3/filters/slim.rb +25 -0
  60. data/lib/nanoc3/filters/typogruby.rb +23 -0
  61. data/lib/nanoc3/filters.rb +6 -0
  62. data/lib/nanoc3/helpers/blogging.rb +22 -26
  63. data/lib/nanoc3/helpers/rendering.rb +1 -1
  64. data/lib/nanoc3/helpers/xml_sitemap.rb +11 -2
  65. data/lib/nanoc3.rb +24 -3
  66. data/nanoc3.gemspec +4 -3
  67. data/tasks/clean.rake +11 -0
  68. data/tasks/doc.rake +14 -0
  69. data/tasks/test.rake +38 -0
  70. data/test/base/core_ext/array_spec.rb +55 -0
  71. data/test/base/core_ext/hash_spec.rb +82 -0
  72. data/test/base/core_ext/pathname_spec.rb +29 -0
  73. data/test/base/core_ext/string_spec.rb +39 -0
  74. data/test/base/test_checksum_store.rb +37 -0
  75. data/test/base/test_code_snippet.rb +33 -0
  76. data/test/base/test_compiler.rb +303 -0
  77. data/test/base/test_compiler_dsl.rb +156 -0
  78. data/test/base/test_context.rb +33 -0
  79. data/test/base/test_data_source.rb +48 -0
  80. data/test/base/test_dependency_tracker.rb +264 -0
  81. data/test/base/test_directed_graph.rb +285 -0
  82. data/test/base/test_filter.rb +85 -0
  83. data/test/base/test_item.rb +164 -0
  84. data/test/base/test_item_rep.rb +555 -0
  85. data/test/base/test_layout.rb +44 -0
  86. data/test/base/test_memoization.rb +53 -0
  87. data/test/base/test_notification_center.rb +36 -0
  88. data/test/base/test_outdatedness_checker.rb +365 -0
  89. data/test/base/test_plugin.rb +32 -0
  90. data/test/base/test_rule.rb +21 -0
  91. data/test/base/test_rule_context.rb +67 -0
  92. data/test/base/test_site.rb +144 -0
  93. data/test/cli/commands/test_compile.rb +12 -0
  94. data/test/cli/commands/test_create_item.rb +12 -0
  95. data/test/cli/commands/test_create_layout.rb +28 -0
  96. data/test/cli/commands/test_create_site.rb +24 -0
  97. data/test/cli/commands/test_help.rb +12 -0
  98. data/test/cli/commands/test_info.rb +12 -0
  99. data/test/cli/commands/test_update.rb +12 -0
  100. data/test/cli/test_logger.rb +12 -0
  101. data/test/data_sources/test_filesystem.rb +420 -0
  102. data/test/data_sources/test_filesystem_unified.rb +562 -0
  103. data/test/data_sources/test_filesystem_verbose.rb +359 -0
  104. data/test/extra/core_ext/test_enumerable.rb +32 -0
  105. data/test/extra/core_ext/test_time.rb +17 -0
  106. data/test/extra/deployers/test_rsync.rb +234 -0
  107. data/test/extra/test_auto_compiler.rb +417 -0
  108. data/test/extra/test_file_proxy.rb +21 -0
  109. data/test/extra/test_vcs.rb +24 -0
  110. data/test/extra/validators/test_links.rb +53 -0
  111. data/test/extra/validators/test_w3c.rb +49 -0
  112. data/test/filters/test_asciidoc.rb +22 -0
  113. data/test/filters/test_bluecloth.rb +20 -0
  114. data/test/filters/test_coderay.rb +46 -0
  115. data/test/filters/test_colorize_syntax.rb +149 -0
  116. data/test/filters/test_erb.rb +101 -0
  117. data/test/filters/test_erubis.rb +72 -0
  118. data/test/filters/test_haml.rb +98 -0
  119. data/test/filters/test_kramdown.rb +20 -0
  120. data/test/filters/test_less.rb +59 -0
  121. data/test/filters/test_markaby.rb +26 -0
  122. data/test/filters/test_maruku.rb +20 -0
  123. data/test/filters/test_mustache.rb +27 -0
  124. data/test/filters/test_rainpress.rb +31 -0
  125. data/test/filters/test_rdiscount.rb +33 -0
  126. data/test/filters/test_rdoc.rb +18 -0
  127. data/test/filters/test_redcarpet.rb +63 -0
  128. data/test/filters/test_redcloth.rb +35 -0
  129. data/test/filters/test_relativize_paths.rb +231 -0
  130. data/test/filters/test_rubypants.rb +20 -0
  131. data/test/filters/test_sass.rb +103 -0
  132. data/test/filters/test_slim.rb +37 -0
  133. data/test/filters/test_typogruby.rb +23 -0
  134. data/test/helper.rb +161 -0
  135. data/test/helpers/test_blogging.rb +756 -0
  136. data/test/helpers/test_breadcrumbs.rb +83 -0
  137. data/test/helpers/test_capturing.rb +43 -0
  138. data/test/helpers/test_filtering.rb +108 -0
  139. data/test/helpers/test_html_escape.rb +34 -0
  140. data/test/helpers/test_link_to.rb +251 -0
  141. data/test/helpers/test_rendering.rb +90 -0
  142. data/test/helpers/test_tagging.rb +89 -0
  143. data/test/helpers/test_text.rb +26 -0
  144. data/test/helpers/test_xml_sitemap.rb +105 -0
  145. data/test/tasks/test_clean.rb +69 -0
  146. metadata +96 -27
  147. data/lib/nanoc3/base/compilation/checksummer.rb +0 -68
@@ -16,6 +16,8 @@ module Nanoc3
16
16
 
17
17
  # @return [Symbol] The name of the snapshot this rule will apply to.
18
18
  # Ignored for compilation rules, but used for routing rules.
19
+ #
20
+ # @since 3.2.0
19
21
  attr_reader :snapshot_name
20
22
 
21
23
  # Creates a new item compilation rule with the given identifier regex,
@@ -62,7 +64,7 @@ module Nanoc3
62
64
  # @return [void]
63
65
  def apply_to(rep, params={})
64
66
  compiler = params[:compiler] or raise ArgumentError, "Required :compiler option is missing"
65
- rep = Nanoc3::ItemRepProxy.new(rep, compiler)
67
+ rep = Nanoc3::ItemRepProxy.new(rep, compiler) unless rep.is_proxy?
66
68
  Nanoc3::RuleContext.new(:rep => rep, :compiler => compiler).instance_eval &@block
67
69
  end
68
70
 
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3
4
+
5
+ # Calculates rule memories for objects that can be run through a rule (item
6
+ # representations and layouts).
7
+ #
8
+ # @api private
9
+ class RuleMemoryCalculator
10
+
11
+ extend Nanoc3::Memoization
12
+
13
+ # @option params [Nanoc3::RulesCollection] rules_collection The rules
14
+ # collection
15
+ def initialize(params={})
16
+ super('tmp/rule_memory', 1)
17
+
18
+ @rules_collection = params[:rules_collection] or
19
+ raise ArgumentError, "Required :rules_collection option is missing"
20
+ end
21
+
22
+ # @param [#reference] obj The object to calculate the rule memory for
23
+ #
24
+ # @return [Array] The caluclated rule memory for the given object
25
+ def [](obj)
26
+ result = case obj.type
27
+ when :item_rep
28
+ @rules_collection.new_rule_memory_for_rep(obj)
29
+ when :layout
30
+ @rules_collection.new_rule_memory_for_layout(obj)
31
+ else
32
+ raise RuntimeError,
33
+ "Do not know how to calculate the rule memory for #{obj.inspect}"
34
+ end
35
+
36
+ result
37
+ end
38
+ memoize :[]
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3
4
+
5
+ # Stores rule memories for objects that can be run through a rule (item
6
+ # representations and layouts).
7
+ #
8
+ # @api private
9
+ class RuleMemoryStore < ::Nanoc3::Store
10
+
11
+ # @option params [Nanoc3::Site] site The site where this rule memory store
12
+ # belongs to
13
+ def initialize(params={})
14
+ super('tmp/rule_memory', 1)
15
+
16
+ @site = params[:site] if params.has_key?(:site)
17
+
18
+ @rule_memories = {}
19
+ end
20
+
21
+ # @param [Nanoc3::ItemRep, Nanoc3::Layout] obj The item representation or
22
+ # the layout to get the rule memory for
23
+ #
24
+ # @return [Array] The rule memory for the given object
25
+ def [](obj)
26
+ @rule_memories[obj.reference]
27
+ end
28
+
29
+ # @param [Nanoc3::ItemRep, Nanoc3::Layout] obj The item representation or
30
+ # the layout to set the rule memory for
31
+ #
32
+ # @param [Array] rule_memory The new rule memory to be stored
33
+ #
34
+ # @return [void]
35
+ def []=(obj, rule_memory)
36
+ @rule_memories[obj.reference] = rule_memory
37
+ end
38
+
39
+ protected
40
+
41
+ # @see Nanoc3::Store#data
42
+ def data
43
+ @rule_memories
44
+ end
45
+
46
+ # @see Nanoc3::Store#data=
47
+ def data=(new_data)
48
+ @rule_memories = new_data
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,205 @@
1
+ module Nanoc3
2
+
3
+ # Keeps track of the rules in a site.
4
+ #
5
+ # @api private
6
+ class RulesCollection
7
+
8
+ extend Nanoc3::Memoization
9
+
10
+ # @return [Array<Nanoc3::Rule>] The list of item compilation rules that
11
+ # will be used to compile items.
12
+ attr_reader :item_compilation_rules
13
+
14
+ # @return [Array<Nanoc3::Rule>] The list of routing rules that will be
15
+ # used to give all items a path.
16
+ attr_reader :item_routing_rules
17
+
18
+ # The hash containing layout-to-filter mapping rules. This hash is
19
+ # ordered: iterating over the hash will happen in insertion order.
20
+ #
21
+ # @return [Hash] The layout-to-filter mapping rules
22
+ attr_reader :layout_filter_mapping
23
+
24
+ # @return [Proc] The code block that will be executed after all data is
25
+ # loaded but before the site is compiled
26
+ attr_accessor :preprocessor
27
+
28
+ # @param [Nanoc3::Compiler] compiler The site’s compiler
29
+ def initialize(compiler)
30
+ @compiler = compiler
31
+
32
+ @item_compilation_rules = []
33
+ @item_routing_rules = []
34
+ @layout_filter_mapping = OrderedHash.new
35
+ end
36
+
37
+ # TODO document
38
+ def add_item_compilation_rule(rule, position=:after)
39
+ case position
40
+ when :before
41
+ @item_compilation_rules.unshift(rule)
42
+ when :after
43
+ @item_compilation_rules << rule
44
+ else
45
+ raise "#add_item_routing_rule expected position to be :after or :before"
46
+ end
47
+ end
48
+
49
+ # TODO document
50
+ def add_item_routing_rule(rule, position=:after)
51
+ case position
52
+ when :before
53
+ @item_routing_rules.unshift(rule)
54
+ when :after
55
+ @item_routing_rules << rule
56
+ else
57
+ raise "#add_item_routing_rule expected position to be :after or :before"
58
+ end
59
+ end
60
+
61
+ # TODO document
62
+ def item_compilation_rules_for(item)
63
+ @item_compilation_rules.select { |r| r.applicable_to?(item) }
64
+ end
65
+
66
+ # Loads this site’s rules.
67
+ def load
68
+ # Find rules file
69
+ rules_filenames = [ 'Rules', 'rules', 'Rules.rb', 'rules.rb' ]
70
+ rules_filename = rules_filenames.find { |f| File.file?(f) }
71
+ raise Nanoc3::Errors::NoRulesFileFound.new if rules_filename.nil?
72
+
73
+ # Get rule data
74
+ @data = File.read(rules_filename)
75
+
76
+ # Load DSL
77
+ dsl.instance_eval(@data, "./#{rules_filename}")
78
+ end
79
+
80
+ # Unloads this site’s rules.
81
+ def unload
82
+ @item_compilation_rules = []
83
+ @item_routing_rules = []
84
+ @layout_filter_mapping = OrderedHash.new
85
+ end
86
+
87
+ # Finds the first matching compilation rule for the given item
88
+ # representation.
89
+ #
90
+ # @param [Nanoc3::ItemRep] rep The item rep for which to fetch the rule
91
+ #
92
+ # @return [Nanoc3::Rule, nil] The compilation rule for the given item rep,
93
+ # or nil if no rules have been found
94
+ def compilation_rule_for(rep)
95
+ @item_compilation_rules.find do |rule|
96
+ rule.applicable_to?(rep.item) && rule.rep_name == rep.name
97
+ end
98
+ end
99
+
100
+ # Finds the first matching routing rule for the given item representation.
101
+ #
102
+ # @param [Nanoc3::ItemRep] rep The item rep for which to fetch the rule
103
+ #
104
+ # @return [Nanoc3::Rule, nil] The routing rule for the given item rep, or
105
+ # nil if no rules have been found
106
+ def routing_rule_for(rep)
107
+ @item_routing_rules.find do |rule|
108
+ rule.applicable_to?(rep.item) && rule.rep_name == rep.name
109
+ end
110
+ end
111
+
112
+ # Returns the list of routing rules that can be applied to the given item
113
+ # representation. For each snapshot, the first matching rule will be
114
+ # returned. The result is a hash containing the corresponding rule for
115
+ # each snapshot.
116
+ #
117
+ # @return [Hash<Symbol, Nanoc3::Rule>] The routing rules for the given rep
118
+ def routing_rules_for(rep)
119
+ rules = {}
120
+ @item_routing_rules.each do |rule|
121
+ next if !rule.applicable_to?(rep.item)
122
+ next if rule.rep_name != rep.name
123
+ next if rules.has_key?(rule.snapshot_name)
124
+
125
+ rules[rule.snapshot_name] = rule
126
+ end
127
+ rules
128
+ end
129
+
130
+ # Finds the filter name and arguments to use for the given layout.
131
+ #
132
+ # @param [Nanoc3::Layout] layout The layout for which to fetch the filter.
133
+ #
134
+ # @return [Array, nil] A tuple containing the filter name and the filter
135
+ # arguments for the given layout.
136
+ def filter_for_layout(layout)
137
+ @layout_filter_mapping.each_pair do |layout_identifier, filter_name_and_args|
138
+ return filter_name_and_args if layout.identifier =~ layout_identifier
139
+ end
140
+ nil
141
+ end
142
+
143
+ # Returns the Nanoc3::CompilerDSL that should be used for this site.
144
+ def dsl
145
+ Nanoc3::CompilerDSL.new(self)
146
+ end
147
+ memoize :dsl
148
+
149
+ # TODO document
150
+ def reference
151
+ :rules
152
+ end
153
+
154
+ # TODO document
155
+ def data
156
+ @data
157
+ end
158
+
159
+ # TODO document
160
+ def checksum
161
+ @data.checksum
162
+ end
163
+
164
+ def inspect
165
+ "<#{self.class}:0x#{self.object_id.to_s(16)}>"
166
+ end
167
+
168
+ # @param [Nanoc3::ItemRep] rep The item representation to get the rule
169
+ # memory for
170
+ #
171
+ # @return [Array] The rule memory for the given item representation
172
+ def new_rule_memory_for_rep(rep)
173
+ recording_proxy = rep.to_recording_proxy
174
+ compilation_rule_for(rep).apply_to(recording_proxy, :compiler => @compiler)
175
+ recording_proxy.rule_memory
176
+ end
177
+ memoize :new_rule_memory_for_rep
178
+
179
+ # @param [Nanoc3::Layout] layout The layout to get the rule memory for
180
+ #
181
+ # @return [Array] The rule memory for the given layout
182
+ def new_rule_memory_for_layout(layout)
183
+ filter_for_layout(layout)
184
+ end
185
+ memoize :new_rule_memory_for_layout
186
+
187
+ # TODO document
188
+ def rule_memory_differs_for(obj)
189
+ !rule_memory_store[obj].eql?(rule_memory_calculator[obj])
190
+ end
191
+ memoize :rule_memory_differs_for
192
+
193
+ # TODO document
194
+ def rule_memory_store
195
+ @compiler.rule_memory_store
196
+ end
197
+
198
+ # TODO document
199
+ def rule_memory_calculator
200
+ @compiler.rule_memory_calculator
201
+ end
202
+
203
+ end
204
+
205
+ end
@@ -24,6 +24,26 @@ module Nanoc3::ArrayExtensions
24
24
  end
25
25
  end
26
26
 
27
+ # Freezes the contents of the array, as well as all array elements. The
28
+ # array elements will be frozen using {#freeze_recursively} if they respond
29
+ # to that message, or #freeze if they do not.
30
+ #
31
+ # @see Hash#freeze_recursively
32
+ #
33
+ # @return [void]
34
+ #
35
+ # @since 3.2.0
36
+ def freeze_recursively
37
+ freeze
38
+ each do |value|
39
+ if value.respond_to?(:freeze_recursively)
40
+ value.freeze_recursively
41
+ else
42
+ value.freeze
43
+ end
44
+ end
45
+ end
46
+
27
47
  end
28
48
 
29
49
  class Array
@@ -24,6 +24,36 @@ module Nanoc3::HashExtensions
24
24
  end
25
25
  end
26
26
 
27
+ # Freezes the contents of the hash, as well as all hash values. The hash
28
+ # values will be frozen using {#freeze_recursively} if they respond to
29
+ # that message, or #freeze if they do not.
30
+ #
31
+ # @see Array#freeze_recursively
32
+ #
33
+ # @return [void]
34
+ #
35
+ # @since 3.2.0
36
+ def freeze_recursively
37
+ freeze
38
+ each_pair do |key, value|
39
+ if value.respond_to?(:freeze_recursively)
40
+ value.freeze_recursively
41
+ else
42
+ value.freeze
43
+ end
44
+ end
45
+ end
46
+
47
+ # Calculates the checksum for this hash. Any change to this hash will result
48
+ # in a different checksum.
49
+ #
50
+ # @return [String] The checksum for this hash
51
+ #
52
+ # @api private
53
+ def checksum
54
+ Marshal.dump(self).checksum
55
+ end
56
+
27
57
  end
28
58
 
29
59
  class Hash
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::PathnameExtensions
4
+
5
+ # Calculates the checksum for the file referenced to by this pathname. Any
6
+ # change to the file contents will result in a different checksum.
7
+ #
8
+ # @return [String] The checksum for this file
9
+ #
10
+ # @api private
11
+ def checksum
12
+ digest = Digest::SHA1.new
13
+ File.open(self.to_s, 'r') do |io|
14
+ until io.eof
15
+ data = io.readpartial(2**10)
16
+ digest.update(data)
17
+ end
18
+ end
19
+ digest.hexdigest
20
+ end
21
+
22
+ end
23
+
24
+ class Pathname
25
+ include Nanoc3::PathnameExtensions
26
+ end
@@ -27,6 +27,18 @@ module Nanoc3::StringExtensions
27
27
  self.gsub(/“|”/, '"').gsub(/‘|’/, '\'').gsub('…', '...')
28
28
  end
29
29
 
30
+ # Calculates the checksum for this string. Any change to this string will
31
+ # result in a different checksum.
32
+ #
33
+ # @return [String] The checksum for this string
34
+ #
35
+ # @api private
36
+ def checksum
37
+ digest = Digest::SHA1.new
38
+ digest.update(self)
39
+ digest.hexdigest
40
+ end
41
+
30
42
  end
31
43
 
32
44
  class String
@@ -2,4 +2,5 @@
2
2
 
3
3
  require 'nanoc3/base/core_ext/array'
4
4
  require 'nanoc3/base/core_ext/hash'
5
+ require 'nanoc3/base/core_ext/pathname'
5
6
  require 'nanoc3/base/core_ext/string'
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'set'
4
-
5
3
  module Nanoc3
6
4
 
7
5
  # Represents a directed graph. It is used by the dependency tracker for
@@ -89,6 +87,8 @@ module Nanoc3
89
87
  # @param to End vertex of the edge
90
88
  #
91
89
  # @return [void]
90
+ #
91
+ # @since 3.2.0
92
92
  def delete_edge(from, to)
93
93
  @from_graph[from] ||= Set.new
94
94
  @from_graph[from].delete(to)
@@ -103,9 +103,11 @@ module Nanoc3
103
103
 
104
104
  # Adds the given vertex to the graph.
105
105
  #
106
- # @param [Vertex] v The vertex to add to the graph
106
+ # @param v The vertex to add to the graph
107
107
  #
108
108
  # @return [void]
109
+ #
110
+ # @since 3.2.0
109
111
  def add_vertex(v)
110
112
  return if @vertices.include?(v)
111
113
 
@@ -120,6 +122,8 @@ module Nanoc3
120
122
  # @param from Vertex from which all edges should be removed
121
123
  #
122
124
  # @return [void]
125
+ #
126
+ # @since 3.2.0
123
127
  def delete_edges_from(from)
124
128
  return if @from_graph[from].nil?
125
129
 
@@ -150,6 +154,8 @@ module Nanoc3
150
154
  # @param v Vertex to remove from the graph
151
155
  #
152
156
  # @return [void]
157
+ #
158
+ # @since 3.2.0
153
159
  def delete_vertex(v)
154
160
  delete_edges_to(v)
155
161
  delete_edges_from(v)
@@ -217,6 +223,8 @@ module Nanoc3
217
223
  # Returns all root vertices, i.e. vertices where no edge points to.
218
224
  #
219
225
  # @return [Set] The set of all root vertices in this graph.
226
+ #
227
+ # @since 3.2.0
220
228
  def roots
221
229
  @roots
222
230
  end
@@ -160,10 +160,6 @@ module Nanoc3
160
160
 
161
161
  end
162
162
 
163
- # Error that is raised when an internal consistency error is detected.
164
- class InternalConsistency < Generic
165
- end
166
-
167
163
  # @deprecated No longer necessary, but kept for backwards compatibility.
168
164
  class DataNotYetAvailable < Generic
169
165
 
@@ -0,0 +1,72 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3
4
+
5
+ # Adds support for memoizing functions.
6
+ #
7
+ # @since 3.2.0
8
+ module Memoization
9
+
10
+ CACHE = {}
11
+
12
+ # Memoizes the method with the given name. The modified method will cache
13
+ # the results of the original method, so that calling a method twice with
14
+ # the same arguments will short-circuit and return the cached results
15
+ # immediately.
16
+ #
17
+ # Memoization assumes that the current object as well as the function
18
+ # arguments are immutable. Mutating the object or the arguments will not
19
+ # cause memoized methods to recalculate their results. There is no way to
20
+ # un-memoize a result, and calculation results will remain in memory even
21
+ # if they are no longer needed.
22
+ #
23
+ # @example A fast fib function due to memoization
24
+ #
25
+ # class FibFast
26
+ #
27
+ # extend Nanoc3::Memoization
28
+ #
29
+ # def run(n)
30
+ # if n == 0
31
+ # 0
32
+ # elsif n == 1
33
+ # 1
34
+ # else
35
+ # run(n-1) + run(n-2)
36
+ # end
37
+ # end
38
+ # memoize :run
39
+ #
40
+ # end
41
+ #
42
+ # @param [Symbol, String] method_name The name of the method to memoize
43
+ #
44
+ # @return [void]
45
+ def memoize(method_name)
46
+ # Alias
47
+ original_method_name = '__nonmemoized_' + method_name.to_s
48
+ alias_method original_method_name, method_name
49
+
50
+ # Redefine
51
+ define_method(method_name) do |*args|
52
+ # Get method-specific cache
53
+ key = [ self, method_name ]
54
+ if !CACHE.has_key?(key)
55
+ CACHE[key] = {}
56
+ end
57
+ method_cache = CACHE[key]
58
+
59
+ # Recalculate if necessary
60
+ if !method_cache.has_key?(args)
61
+ result = send(original_method_name, *args)
62
+ method_cache[args] = result
63
+ end
64
+
65
+ # Done
66
+ method_cache[args]
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ end