nanoc3 3.2.0a3 → 3.2.0a4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gemtest +0 -0
- data/LICENSE +1 -1
- data/NEWS.md +23 -4
- data/README.md +7 -0
- data/lib/nanoc3/base/compilation/checksum_store.rb +17 -90
- data/lib/nanoc3/base/compilation/compiled_content_cache.rb +5 -0
- data/lib/nanoc3/base/compilation/compiler.rb +112 -175
- data/lib/nanoc3/base/compilation/compiler_dsl.rb +54 -11
- data/lib/nanoc3/base/compilation/dependency_tracker.rb +32 -65
- data/lib/nanoc3/base/compilation/filter.rb +4 -3
- data/lib/nanoc3/base/compilation/item_rep_proxy.rb +19 -4
- data/lib/nanoc3/base/compilation/item_rep_recorder_proxy.rb +90 -0
- data/lib/nanoc3/base/compilation/outdatedness_checker.rb +152 -15
- data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +12 -9
- data/lib/nanoc3/base/compilation/rule.rb +3 -1
- data/lib/nanoc3/base/compilation/rule_memory_calculator.rb +42 -0
- data/lib/nanoc3/base/compilation/rule_memory_store.rb +53 -0
- data/lib/nanoc3/base/compilation/rules_collection.rb +205 -0
- data/lib/nanoc3/base/core_ext/array.rb +20 -0
- data/lib/nanoc3/base/core_ext/hash.rb +30 -0
- data/lib/nanoc3/base/core_ext/pathname.rb +26 -0
- data/lib/nanoc3/base/core_ext/string.rb +12 -0
- data/lib/nanoc3/base/core_ext.rb +1 -0
- data/lib/nanoc3/base/directed_graph.rb +11 -3
- data/lib/nanoc3/base/errors.rb +0 -4
- data/lib/nanoc3/base/memoization.rb +72 -0
- data/lib/nanoc3/base/result_data/item_rep.rb +64 -25
- data/lib/nanoc3/base/source_data/code_snippet.rb +9 -0
- data/lib/nanoc3/base/source_data/configuration.rb +20 -0
- data/lib/nanoc3/base/source_data/item.rb +29 -4
- data/lib/nanoc3/base/source_data/layout.rb +20 -1
- data/lib/nanoc3/base/source_data/site.rb +49 -26
- data/lib/nanoc3/base/store.rb +10 -1
- data/lib/nanoc3/base.rb +6 -1
- data/lib/nanoc3/cli/base.rb +20 -7
- data/lib/nanoc3/cli/commands/compile.rb +0 -2
- data/lib/nanoc3/cli/commands/create_site.rb +16 -7
- data/lib/nanoc3/cli/commands/debug.rb +3 -3
- data/lib/nanoc3/cli/commands/view.rb +1 -0
- data/lib/nanoc3/cli/commands/watch.rb +2 -1
- data/lib/nanoc3/data_sources/deprecated/delicious.rb +0 -2
- data/lib/nanoc3/data_sources/deprecated/last_fm.rb +0 -2
- data/lib/nanoc3/data_sources/deprecated/twitter.rb +0 -2
- data/lib/nanoc3/data_sources/filesystem.rb +17 -3
- data/lib/nanoc3/data_sources/filesystem_unified.rb +17 -17
- data/lib/nanoc3/extra/auto_compiler.rb +5 -1
- data/lib/nanoc3/extra/core_ext/time.rb +1 -1
- data/lib/nanoc3/extra/file_proxy.rb +11 -1
- data/lib/nanoc3/extra/validators/links.rb +1 -1
- data/lib/nanoc3/filters/asciidoc.rb +3 -3
- data/lib/nanoc3/filters/colorize_syntax.rb +106 -27
- data/lib/nanoc3/filters/erb.rb +16 -6
- data/lib/nanoc3/filters/erubis.rb +5 -1
- data/lib/nanoc3/filters/haml.rb +2 -1
- data/lib/nanoc3/filters/less.rb +3 -6
- data/lib/nanoc3/filters/mustache.rb +3 -0
- data/lib/nanoc3/filters/redcarpet.rb +27 -0
- data/lib/nanoc3/filters/sass.rb +1 -5
- data/lib/nanoc3/filters/slim.rb +25 -0
- data/lib/nanoc3/filters/typogruby.rb +23 -0
- data/lib/nanoc3/filters.rb +6 -0
- data/lib/nanoc3/helpers/blogging.rb +22 -26
- data/lib/nanoc3/helpers/rendering.rb +1 -1
- data/lib/nanoc3/helpers/xml_sitemap.rb +11 -2
- data/lib/nanoc3.rb +24 -3
- data/nanoc3.gemspec +4 -3
- data/tasks/clean.rake +11 -0
- data/tasks/doc.rake +14 -0
- data/tasks/test.rake +38 -0
- data/test/base/core_ext/array_spec.rb +55 -0
- data/test/base/core_ext/hash_spec.rb +82 -0
- data/test/base/core_ext/pathname_spec.rb +29 -0
- data/test/base/core_ext/string_spec.rb +39 -0
- data/test/base/test_checksum_store.rb +37 -0
- data/test/base/test_code_snippet.rb +33 -0
- data/test/base/test_compiler.rb +303 -0
- data/test/base/test_compiler_dsl.rb +156 -0
- data/test/base/test_context.rb +33 -0
- data/test/base/test_data_source.rb +48 -0
- data/test/base/test_dependency_tracker.rb +264 -0
- data/test/base/test_directed_graph.rb +285 -0
- data/test/base/test_filter.rb +85 -0
- data/test/base/test_item.rb +164 -0
- data/test/base/test_item_rep.rb +555 -0
- data/test/base/test_layout.rb +44 -0
- data/test/base/test_memoization.rb +53 -0
- data/test/base/test_notification_center.rb +36 -0
- data/test/base/test_outdatedness_checker.rb +365 -0
- data/test/base/test_plugin.rb +32 -0
- data/test/base/test_rule.rb +21 -0
- data/test/base/test_rule_context.rb +67 -0
- data/test/base/test_site.rb +144 -0
- data/test/cli/commands/test_compile.rb +12 -0
- data/test/cli/commands/test_create_item.rb +12 -0
- data/test/cli/commands/test_create_layout.rb +28 -0
- data/test/cli/commands/test_create_site.rb +24 -0
- data/test/cli/commands/test_help.rb +12 -0
- data/test/cli/commands/test_info.rb +12 -0
- data/test/cli/commands/test_update.rb +12 -0
- data/test/cli/test_logger.rb +12 -0
- data/test/data_sources/test_filesystem.rb +420 -0
- data/test/data_sources/test_filesystem_unified.rb +562 -0
- data/test/data_sources/test_filesystem_verbose.rb +359 -0
- data/test/extra/core_ext/test_enumerable.rb +32 -0
- data/test/extra/core_ext/test_time.rb +17 -0
- data/test/extra/deployers/test_rsync.rb +234 -0
- data/test/extra/test_auto_compiler.rb +417 -0
- data/test/extra/test_file_proxy.rb +21 -0
- data/test/extra/test_vcs.rb +24 -0
- data/test/extra/validators/test_links.rb +53 -0
- data/test/extra/validators/test_w3c.rb +49 -0
- data/test/filters/test_asciidoc.rb +22 -0
- data/test/filters/test_bluecloth.rb +20 -0
- data/test/filters/test_coderay.rb +46 -0
- data/test/filters/test_colorize_syntax.rb +149 -0
- data/test/filters/test_erb.rb +101 -0
- data/test/filters/test_erubis.rb +72 -0
- data/test/filters/test_haml.rb +98 -0
- data/test/filters/test_kramdown.rb +20 -0
- data/test/filters/test_less.rb +59 -0
- data/test/filters/test_markaby.rb +26 -0
- data/test/filters/test_maruku.rb +20 -0
- data/test/filters/test_mustache.rb +27 -0
- data/test/filters/test_rainpress.rb +31 -0
- data/test/filters/test_rdiscount.rb +33 -0
- data/test/filters/test_rdoc.rb +18 -0
- data/test/filters/test_redcarpet.rb +63 -0
- data/test/filters/test_redcloth.rb +35 -0
- data/test/filters/test_relativize_paths.rb +231 -0
- data/test/filters/test_rubypants.rb +20 -0
- data/test/filters/test_sass.rb +103 -0
- data/test/filters/test_slim.rb +37 -0
- data/test/filters/test_typogruby.rb +23 -0
- data/test/helper.rb +161 -0
- data/test/helpers/test_blogging.rb +756 -0
- data/test/helpers/test_breadcrumbs.rb +83 -0
- data/test/helpers/test_capturing.rb +43 -0
- data/test/helpers/test_filtering.rb +108 -0
- data/test/helpers/test_html_escape.rb +34 -0
- data/test/helpers/test_link_to.rb +251 -0
- data/test/helpers/test_rendering.rb +90 -0
- data/test/helpers/test_tagging.rb +89 -0
- data/test/helpers/test_text.rb +26 -0
- data/test/helpers/test_xml_sitemap.rb +105 -0
- data/test/tasks/test_clean.rb +69 -0
- metadata +96 -27
- data/lib/nanoc3/base/compilation/checksummer.rb +0 -68
@@ -5,16 +5,14 @@ module Nanoc3
|
|
5
5
|
# Contains methods that will be executed by the site’s `Rules` file.
|
6
6
|
class CompilerDSL
|
7
7
|
|
8
|
-
#
|
9
|
-
attr_reader :compiler
|
10
|
-
|
11
|
-
# Creates a new compiler DSL for the given compiler.
|
8
|
+
# Creates a new compiler DSL for the given collection of rules.
|
12
9
|
#
|
13
10
|
# @api private
|
14
11
|
#
|
15
|
-
# @param [Nanoc3::
|
16
|
-
|
17
|
-
|
12
|
+
# @param [Nanoc3::RulesCollection] rules_collection The collection of rules
|
13
|
+
# to modify when loading this DSL
|
14
|
+
def initialize(rules_collection)
|
15
|
+
@rules_collection = rules_collection
|
18
16
|
end
|
19
17
|
|
20
18
|
# Creates a preprocessor block that will be executed after all data is
|
@@ -24,7 +22,7 @@ module Nanoc3
|
|
24
22
|
#
|
25
23
|
# @return [void]
|
26
24
|
def preprocess(&block)
|
27
|
-
|
25
|
+
@rules_collection.preprocessor = block
|
28
26
|
end
|
29
27
|
|
30
28
|
# Creates a compilation rule for all items whose identifier match the
|
@@ -68,7 +66,7 @@ module Nanoc3
|
|
68
66
|
|
69
67
|
# Create rule
|
70
68
|
rule = Rule.new(identifier_to_regex(identifier), rep_name, block)
|
71
|
-
|
69
|
+
@rules_collection.add_item_compilation_rule(rule)
|
72
70
|
end
|
73
71
|
|
74
72
|
# Creates a routing rule for all items whose identifier match the
|
@@ -113,7 +111,7 @@ module Nanoc3
|
|
113
111
|
|
114
112
|
# Create rule
|
115
113
|
rule = Rule.new(identifier_to_regex(identifier), rep_name, block, :snapshot_name => snapshot_name)
|
116
|
-
|
114
|
+
@rules_collection.add_item_routing_rule(rule)
|
117
115
|
end
|
118
116
|
|
119
117
|
# Creates a layout rule for all layouts whose identifier match the given
|
@@ -141,7 +139,52 @@ module Nanoc3
|
|
141
139
|
#
|
142
140
|
# layout '/custom/', :haml, :format => :html5
|
143
141
|
def layout(identifier, filter_name, params={})
|
144
|
-
|
142
|
+
@rules_collection.layout_filter_mapping[identifier_to_regex(identifier)] = [ filter_name, params ]
|
143
|
+
end
|
144
|
+
|
145
|
+
# Creates a pair of compilation and routing rules that indicate that the
|
146
|
+
# specified item(s) should be copied to the output folder as-is. The items
|
147
|
+
# are selected using an identifier, which may either be a string
|
148
|
+
# containing the `*` wildcard, or a regular expression.
|
149
|
+
#
|
150
|
+
# This meta-rule will be applicable to reps with a name equal to
|
151
|
+
# `:default`; this can be changed by giving an explicit `:rep` parameter.
|
152
|
+
#
|
153
|
+
# @param [String] identifier A pattern matching identifiers of items that
|
154
|
+
# should be processed using this meta-rule
|
155
|
+
#
|
156
|
+
# @option params [Symbol] :rep (:default) The name of the representation
|
157
|
+
# that should be routed using this rule
|
158
|
+
#
|
159
|
+
# @return [void]
|
160
|
+
#
|
161
|
+
# @since 3.2.0
|
162
|
+
#
|
163
|
+
# @example Copying the `/foo/` item as-is
|
164
|
+
#
|
165
|
+
# passthrough '/foo/'
|
166
|
+
#
|
167
|
+
# @example Copying the `:raw` rep of the `/bar/` item as-is
|
168
|
+
#
|
169
|
+
# passthrough '/bar/', :rep => :raw
|
170
|
+
def passthrough(identifier, params={})
|
171
|
+
# Require no block
|
172
|
+
raise ArgumentError.new("#passthrough does not require a block") if block_given?
|
173
|
+
|
174
|
+
# Get rep name
|
175
|
+
rep_name = params[:rep] || :default
|
176
|
+
|
177
|
+
# Create compilation rule
|
178
|
+
compilation_block = proc { }
|
179
|
+
compilation_rule = Rule.new(identifier_to_regex(identifier), rep_name, compilation_block)
|
180
|
+
@rules_collection.add_item_compilation_rule(compilation_rule, :before)
|
181
|
+
|
182
|
+
# Create routing rule
|
183
|
+
routing_block = proc do
|
184
|
+
item.identifier.chop + '.' + item[:extension]
|
185
|
+
end
|
186
|
+
routing_rule = Rule.new(identifier_to_regex(identifier), rep_name, routing_block)
|
187
|
+
@rules_collection.add_item_routing_rule(routing_rule, :before)
|
145
188
|
end
|
146
189
|
|
147
190
|
private
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'pstore'
|
4
|
-
|
5
3
|
module Nanoc3
|
6
4
|
|
7
5
|
# Responsible for remembering dependencies between items and layouts. It is
|
@@ -18,9 +16,9 @@ module Nanoc3
|
|
18
16
|
# attribute of item B and vice versa without problems).
|
19
17
|
#
|
20
18
|
# The dependency tracker remembers the dependency information between runs.
|
21
|
-
# Dependency information is stored in the `tmp/dependencies` file.
|
22
|
-
#
|
23
|
-
#
|
19
|
+
# Dependency information is stored in the `tmp/dependencies` file.
|
20
|
+
#
|
21
|
+
# @api private
|
24
22
|
class DependencyTracker < ::Nanoc3::Store
|
25
23
|
|
26
24
|
# @return [Array<Nanoc3::Item, Nanoc3::Layout>] The list of items and
|
@@ -41,8 +39,6 @@ module Nanoc3
|
|
41
39
|
@objects = objects
|
42
40
|
|
43
41
|
@graph = Nanoc3::DirectedGraph.new([ nil ] + @objects)
|
44
|
-
@previous_objects = []
|
45
|
-
@objects_outdated_due_to_dependencies = Set.new
|
46
42
|
end
|
47
43
|
|
48
44
|
# Starts listening for dependency messages (`:visit_started` and
|
@@ -76,33 +72,25 @@ module Nanoc3
|
|
76
72
|
Nanoc3::NotificationCenter.remove(:visit_ended, self)
|
77
73
|
end
|
78
74
|
|
79
|
-
# Checks whether the given object is outdated due to dependencies, i.e.
|
80
|
-
# check whether there are other objects that are outdated that cause this
|
81
|
-
# object to be outdated.
|
82
|
-
#
|
83
|
-
# @param [Nanoc3::Item, Nanoc3::Layout] obj The object to check
|
84
|
-
#
|
85
|
-
# @return [Boolean] true if the given object is outdated due to
|
86
|
-
# dependencies, false if not.
|
87
|
-
def outdated_due_to_dependencies?(obj)
|
88
|
-
@objects_outdated_due_to_dependencies.include?(obj)
|
89
|
-
end
|
90
|
-
|
91
75
|
# Returns the direct dependencies for the given object.
|
92
76
|
#
|
93
|
-
# The direct dependencies of the given object include the items
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
77
|
+
# The direct dependencies of the given object include the items and
|
78
|
+
# layouts that, when outdated will cause the given object to be marked as
|
79
|
+
# outdated. Indirect dependencies will not be returned (e.g. if A depends
|
80
|
+
# on B which depends on C, then the direct dependencies of A do not
|
81
|
+
# include C).
|
82
|
+
#
|
83
|
+
# The direct predecessors can include nil, which indicates an item that is
|
84
|
+
# no longer present in the site.
|
98
85
|
#
|
99
86
|
# @param [Nanoc3::Item, Nanoc3::Layout] object The object for
|
100
87
|
# which to fetch the direct predecessors
|
101
88
|
#
|
102
|
-
# @return [Array<Nanoc3::Item, Nanoc3::Layout>] The direct
|
89
|
+
# @return [Array<Nanoc3::Item, Nanoc3::Layout, nil>] The direct
|
90
|
+
# predecessors of
|
103
91
|
# the given object
|
104
|
-
def
|
105
|
-
@graph.direct_predecessors_of(object)
|
92
|
+
def objects_causing_outdatedness_of(object)
|
93
|
+
@graph.direct_predecessors_of(object)
|
106
94
|
end
|
107
95
|
|
108
96
|
# Returns the direct inverse dependencies for the given object.
|
@@ -118,7 +106,7 @@ module Nanoc3
|
|
118
106
|
#
|
119
107
|
# @return [Array<Nanoc3::Item, Nanoc3::Layout>] The direct successors of
|
120
108
|
# the given object
|
121
|
-
def
|
109
|
+
def objects_outdated_due_to(object)
|
122
110
|
@graph.direct_successors_of(object).compact
|
123
111
|
end
|
124
112
|
|
@@ -138,35 +126,6 @@ module Nanoc3
|
|
138
126
|
@graph.add_edge(dst, src) unless src == dst
|
139
127
|
end
|
140
128
|
|
141
|
-
# Traverses the dependency graph and marks all objects that (directly or
|
142
|
-
# indirectly) depend on an outdated object as outdated.
|
143
|
-
#
|
144
|
-
# @return [void]
|
145
|
-
def propagate_outdatedness
|
146
|
-
# Unmark everything
|
147
|
-
@objects_outdated_due_to_dependencies.clear
|
148
|
-
|
149
|
-
# Mark new objects as outdated
|
150
|
-
added_objects = @objects - @previous_objects
|
151
|
-
@objects_outdated_due_to_dependencies.merge(added_objects)
|
152
|
-
|
153
|
-
# Mark successors of outdated objects as outdated
|
154
|
-
require 'set'
|
155
|
-
unprocessed = [ nil ] + @objects.select { |o| compiler.outdated?(o) }
|
156
|
-
seen = Set.new(unprocessed)
|
157
|
-
until unprocessed.empty?
|
158
|
-
obj = unprocessed.shift
|
159
|
-
|
160
|
-
self.direct_successors_of(obj).each do |successor|
|
161
|
-
next if seen.include?(successor)
|
162
|
-
seen << successor
|
163
|
-
|
164
|
-
@objects_outdated_due_to_dependencies << successor
|
165
|
-
unprocessed << successor
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
129
|
# Empties the list of dependencies for the given object. This is necessary
|
171
130
|
# before recompiling the given object, because otherwise old dependencies
|
172
131
|
# will stick around and new dependencies will appear twice. This function
|
@@ -182,11 +141,6 @@ module Nanoc3
|
|
182
141
|
@graph.delete_edges_to(object)
|
183
142
|
end
|
184
143
|
|
185
|
-
# @deprecated Use {#propagate_outdatedness} instead.
|
186
|
-
def mark_outdated_items
|
187
|
-
propagate_outdatedness
|
188
|
-
end
|
189
|
-
|
190
144
|
# @deprecated Use {#store} instead
|
191
145
|
def store_graph
|
192
146
|
self.store
|
@@ -197,6 +151,11 @@ module Nanoc3
|
|
197
151
|
self.load
|
198
152
|
end
|
199
153
|
|
154
|
+
# @see Nanoc3::Store#unload
|
155
|
+
def unload
|
156
|
+
@graph = Nanoc3::DirectedGraph.new([ nil ] + @objects)
|
157
|
+
end
|
158
|
+
|
200
159
|
protected
|
201
160
|
|
202
161
|
def data
|
@@ -211,17 +170,25 @@ module Nanoc3
|
|
211
170
|
@graph = Nanoc3::DirectedGraph.new([ nil ] + @objects)
|
212
171
|
|
213
172
|
# Load vertices
|
214
|
-
|
173
|
+
previous_objects = new_data[:vertices].map do |reference|
|
215
174
|
@objects.find { |obj| reference == obj.reference }
|
216
175
|
end
|
217
176
|
|
218
177
|
# Load edges
|
219
178
|
new_data[:edges].each do |edge|
|
220
179
|
from_index, to_index = *edge
|
221
|
-
from = from_index &&
|
222
|
-
to = to_index &&
|
180
|
+
from = from_index && previous_objects[from_index]
|
181
|
+
to = to_index && previous_objects[to_index]
|
223
182
|
@graph.add_edge(from, to)
|
224
183
|
end
|
184
|
+
|
185
|
+
# Record dependency from all items on new items
|
186
|
+
new_objects = (@objects - previous_objects)
|
187
|
+
new_objects.each do |new_obj|
|
188
|
+
@objects.each do |obj|
|
189
|
+
@graph.add_edge(new_obj, obj)
|
190
|
+
end
|
191
|
+
end
|
225
192
|
end
|
226
193
|
|
227
194
|
end
|
@@ -110,17 +110,18 @@ module Nanoc3
|
|
110
110
|
# used on binary items. When running a binary filter on a file, the
|
111
111
|
# resulting file must end up in the location returned by this method.
|
112
112
|
#
|
113
|
+
# The returned filename will be absolute, so it is safe to change to
|
114
|
+
# another directory inside the filter.
|
115
|
+
#
|
113
116
|
# @return [String] The output filename
|
114
117
|
def output_filename
|
115
118
|
@output_filename ||= begin
|
116
|
-
require 'tempfile'
|
117
|
-
|
118
119
|
FileUtils.mkdir_p(TMP_BINARY_ITEMS_DIR)
|
119
120
|
tempfile = Tempfile.new(filename.gsub(/[^a-z]/, '-'), TMP_BINARY_ITEMS_DIR)
|
120
121
|
new_filename = tempfile.path
|
121
122
|
tempfile.close!
|
122
123
|
|
123
|
-
new_filename
|
124
|
+
File.expand_path(new_filename)
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
3
|
module Nanoc3
|
6
4
|
|
7
5
|
# Represents an item representation, but provides an interface that is
|
@@ -65,19 +63,36 @@ module Nanoc3
|
|
65
63
|
set_assigns
|
66
64
|
|
67
65
|
layout = layout_with_identifier(layout_identifier)
|
68
|
-
filter_name, filter_args = @compiler.filter_for_layout(layout)
|
66
|
+
filter_name, filter_args = @compiler.rules_collection.filter_for_layout(layout)
|
69
67
|
|
70
68
|
@item_rep.layout(layout, filter_name, filter_args)
|
71
69
|
end
|
72
70
|
|
71
|
+
# Returns true because this item is already a proxy, and therefore doesn’t
|
72
|
+
# need to be wrapped anymore.
|
73
|
+
#
|
74
|
+
# @api private
|
75
|
+
#
|
76
|
+
# @return [true]
|
77
|
+
#
|
78
|
+
# @see Nanoc3::ItemRep#is_proxy?
|
79
|
+
# @see Nanoc3::ItemRepRecorderProxy#is_proxy?
|
80
|
+
def is_proxy?
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
73
84
|
private
|
74
85
|
|
75
86
|
def set_assigns
|
76
87
|
@item_rep.assigns = @compiler.assigns_for(@item_rep)
|
77
88
|
end
|
78
89
|
|
90
|
+
def layouts
|
91
|
+
@compiler.site.layouts
|
92
|
+
end
|
93
|
+
|
79
94
|
def layout_with_identifier(layout_identifier)
|
80
|
-
layout ||=
|
95
|
+
layout ||= layouts.find { |l| l.identifier == layout_identifier.cleaned_identifier }
|
81
96
|
raise Nanoc3::Errors::UnknownLayout.new(layout_identifier) if layout.nil?
|
82
97
|
layout
|
83
98
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Nanoc3
|
4
|
+
|
5
|
+
# Represents a fake iem representation that does not actually perform any
|
6
|
+
# actual filtering, layouting or snapshotting, but instead keeps track of
|
7
|
+
# what would happen if a real item representation would have been used
|
8
|
+
# instead. It therefore “records” the actions that happens upon it.
|
9
|
+
#
|
10
|
+
# The list of recorded actions is used during compilation to determine
|
11
|
+
# whether an item representation needs to be recompiled: if the list of
|
12
|
+
# actions is different from the list of actions from the previous
|
13
|
+
# compilation run, the item needs to be recompiled; if it is the same, it
|
14
|
+
# may not need to be recompiled.
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
class ItemRepRecorderProxy
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
def_delegators :@item_rep, :item, :name, :binary, :binary?, :compiled_content, :has_snapshot?, :raw_path, :path, :assigns, :assigns=
|
22
|
+
|
23
|
+
# @return The list of recorded actions (“rule memory”)
|
24
|
+
#
|
25
|
+
# @example The compilation rule and the corresponding rule memory
|
26
|
+
#
|
27
|
+
# # rule
|
28
|
+
# compile '/foo/' do
|
29
|
+
# filter :erb
|
30
|
+
# filter :myfilter, :arg1 => 'stuff'
|
31
|
+
# layout 'meh'
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # memory
|
35
|
+
# [
|
36
|
+
# [ :filter, :erb, {} ],
|
37
|
+
# [ :filter, :myfilter, { :arg1 => 'stuff' } ],
|
38
|
+
# [ :layout, 'meh' ]
|
39
|
+
# ]
|
40
|
+
#
|
41
|
+
# @return [Array] The rule memory
|
42
|
+
attr_reader :rule_memory
|
43
|
+
|
44
|
+
# @param [Nanoc3::ItemRep] item_rep The item representation that this
|
45
|
+
# proxy should behave like
|
46
|
+
def initialize(item_rep)
|
47
|
+
@item_rep = item_rep
|
48
|
+
@rule_memory = []
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [void]
|
52
|
+
#
|
53
|
+
# @see Nanoc3::ItemRepProxy#filter, Nanoc3::ItemRep#filter
|
54
|
+
def filter(name, args={})
|
55
|
+
@rule_memory << [ :filter, name, args ]
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [void]
|
59
|
+
#
|
60
|
+
# @see Nanoc3::ItemRepProxy#layout, Nanoc3::ItemRep#layout
|
61
|
+
def layout(layout_identifier)
|
62
|
+
@rule_memory << [ :layout, layout_identifier ]
|
63
|
+
end
|
64
|
+
|
65
|
+
# @return [void]
|
66
|
+
#
|
67
|
+
# @see Nanoc3::ItemRep#snapshot
|
68
|
+
def snapshot(snapshot_name, params={})
|
69
|
+
@rule_memory << [ :snapshot, snapshot_name, params ]
|
70
|
+
end
|
71
|
+
|
72
|
+
# @return [{}]
|
73
|
+
def content
|
74
|
+
{}
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns true because this item is already a proxy, and therefore doesn’t
|
78
|
+
# need to be wrapped anymore.
|
79
|
+
#
|
80
|
+
# @return [true]
|
81
|
+
#
|
82
|
+
# @see Nanoc3::ItemRep#is_proxy?
|
83
|
+
# @see Nanoc3::ItemRepProxy#is_proxy?
|
84
|
+
def is_proxy?
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
@@ -7,16 +7,27 @@ module Nanoc3
|
|
7
7
|
# @api private
|
8
8
|
class OutdatednessChecker
|
9
9
|
|
10
|
+
extend Nanoc3::Memoization
|
11
|
+
|
10
12
|
# @option params [Nanoc3::Site] :site (nil) The site this outdatedness
|
11
13
|
# checker belongs to.
|
12
14
|
#
|
13
15
|
# @option params [Nanoc3::ChecksumStore] :checksum_store (nil) The
|
14
16
|
# checksum store where checksums of items, layouts, … are stored.
|
17
|
+
#
|
18
|
+
# @option params [Nanoc3::DependencyTracker] :dependency_tracker (nil) The
|
19
|
+
# dependency tracker for the given site.
|
15
20
|
def initialize(params={})
|
16
|
-
@site
|
17
|
-
|
18
|
-
|
21
|
+
@site = params[:site] or raise ArgumentError,
|
22
|
+
'Nanoc3::OutdatednessChecker#initialize needs a :site parameter'
|
23
|
+
@checksum_store = params[:checksum_store] or raise ArgumentError,
|
24
|
+
'Nanoc3::OutdatednessChecker#initialize needs a :checksum_store parameter'
|
25
|
+
@dependency_tracker = params[:dependency_tracker] or raise ArgumentError,
|
26
|
+
'Nanoc3::OutdatednessChecker#initialize needs a :dependency_tracker parameter'
|
27
|
+
|
28
|
+
@basic_outdatedness_reasons = {}
|
19
29
|
@outdatedness_reasons = {}
|
30
|
+
@objects_outdated_due_to_dependencies = {}
|
20
31
|
end
|
21
32
|
|
22
33
|
# Checks whether the given object is outdated and therefore needs to be
|
@@ -38,34 +49,72 @@ module Nanoc3
|
|
38
49
|
# @return [Nanoc3::OutdatednessReasons::Generic, nil] The reason why the
|
39
50
|
# given object is outdated, or nil if the object is not outdated.
|
40
51
|
def outdatedness_reason_for(obj)
|
52
|
+
reason = basic_outdatedness_reason_for(obj)
|
53
|
+
if reason.nil? && outdated_due_to_dependencies?(obj)
|
54
|
+
reason = Nanoc3::OutdatednessReasons::DependenciesOutdated
|
55
|
+
end
|
56
|
+
reason
|
57
|
+
end
|
58
|
+
memoize :outdatedness_reason_for
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Checks whether the given object is outdated and therefore needs to be
|
63
|
+
# recompiled. This method does not take dependencies into account; use
|
64
|
+
# {#outdated?} if you want to include dependencies in the outdatedness
|
65
|
+
# check.
|
66
|
+
#
|
67
|
+
# @param [Nanoc3::Item, Nanoc3::ItemRep, Nanoc3::Layout] obj The object
|
68
|
+
# whose outdatedness should be checked.
|
69
|
+
#
|
70
|
+
# @return [Boolean] true if the object is outdated, false otherwise
|
71
|
+
def basic_outdated?(obj)
|
72
|
+
!basic_outdatedness_reason_for(obj).nil?
|
73
|
+
end
|
74
|
+
|
75
|
+
# Calculates the reason why the given object is outdated. This method does
|
76
|
+
# not take dependencies into account; use {#outdatedness_reason_for?} if
|
77
|
+
# you want to include dependencies in the outdatedness check.
|
78
|
+
#
|
79
|
+
# @param [Nanoc3::Item, Nanoc3::ItemRep, Nanoc3::Layout] obj The object
|
80
|
+
# whose outdatedness reason should be calculated.
|
81
|
+
#
|
82
|
+
# @return [Nanoc3::OutdatednessReasons::Generic, nil] The reason why the
|
83
|
+
# given object is outdated, or nil if the object is not outdated.
|
84
|
+
def basic_outdatedness_reason_for(obj)
|
41
85
|
case obj.type
|
42
86
|
when :item_rep
|
87
|
+
# Outdated if rules outdated
|
88
|
+
return Nanoc3::OutdatednessReasons::RulesModified if
|
89
|
+
rule_memory_differs_for(obj)
|
90
|
+
|
43
91
|
# Outdated if checksums are missing or different
|
44
|
-
return Nanoc3::OutdatednessReasons::NotEnoughData if !
|
45
|
-
return Nanoc3::OutdatednessReasons::SourceModified if !
|
92
|
+
return Nanoc3::OutdatednessReasons::NotEnoughData if !checksums_available?(obj.item)
|
93
|
+
return Nanoc3::OutdatednessReasons::SourceModified if !checksums_identical?(obj.item)
|
46
94
|
|
47
95
|
# Outdated if compiled file doesn't exist (yet)
|
48
96
|
return Nanoc3::OutdatednessReasons::NotWritten if obj.raw_path && !File.file?(obj.raw_path)
|
49
97
|
|
50
98
|
# Outdated if code snippets outdated
|
51
|
-
return Nanoc3::OutdatednessReasons::CodeSnippetsModified if
|
52
|
-
|
99
|
+
return Nanoc3::OutdatednessReasons::CodeSnippetsModified if site.code_snippets.any? do |cs|
|
100
|
+
object_modified?(cs)
|
53
101
|
end
|
54
102
|
|
55
103
|
# Outdated if configuration outdated
|
56
|
-
return Nanoc3::OutdatednessReasons::ConfigurationModified if
|
57
|
-
|
58
|
-
# Outdated if rules outdated
|
59
|
-
return Nanoc3::OutdatednessReasons::RulesModified if checksum_store.object_modified?(@site.compiler.rules_with_reference)
|
104
|
+
return Nanoc3::OutdatednessReasons::ConfigurationModified if object_modified?(site.config)
|
60
105
|
|
61
106
|
# Not outdated
|
62
107
|
return nil
|
63
108
|
when :item
|
64
|
-
obj.reps.find { |rep|
|
109
|
+
obj.reps.find { |rep| basic_outdatedness_reason_for(rep) }
|
65
110
|
when :layout
|
111
|
+
# Outdated if rules outdated
|
112
|
+
return Nanoc3::OutdatednessReasons::RulesModified if
|
113
|
+
rule_memory_differs_for(obj)
|
114
|
+
|
66
115
|
# Outdated if checksums are missing or different
|
67
|
-
return Nanoc3::OutdatednessReasons::NotEnoughData if !
|
68
|
-
return Nanoc3::OutdatednessReasons::SourceModified if !
|
116
|
+
return Nanoc3::OutdatednessReasons::NotEnoughData if !checksums_available?(obj)
|
117
|
+
return Nanoc3::OutdatednessReasons::SourceModified if !checksums_identical?(obj)
|
69
118
|
|
70
119
|
# Not outdated
|
71
120
|
return nil
|
@@ -73,14 +122,102 @@ module Nanoc3
|
|
73
122
|
raise RuntimeError, "do not know how to check outdatedness of #{obj.inspect}"
|
74
123
|
end
|
75
124
|
end
|
125
|
+
memoize :basic_outdatedness_reason_for
|
76
126
|
|
77
|
-
|
127
|
+
# Checks whether the given object is outdated due to dependencies.
|
128
|
+
#
|
129
|
+
# @param [Nanoc3::Item, Nanoc3::ItemRep, Nanoc3::Layout] obj The object
|
130
|
+
# whose outdatedness should be checked.
|
131
|
+
#
|
132
|
+
# @param [Set] processed The collection of items that has been visited
|
133
|
+
# during this outdatedness check. This is used to prevent checks for
|
134
|
+
# items that (indirectly) depend on their own from looping
|
135
|
+
# indefinitely. It should not be necessary to pass this a custom value.
|
136
|
+
#
|
137
|
+
# @return [Boolean] true if the object is outdated, false otherwise
|
138
|
+
def outdated_due_to_dependencies?(obj, processed=Set.new)
|
139
|
+
# Convert from rep to item if necessary
|
140
|
+
obj = obj.item if obj.type == :item_rep
|
141
|
+
|
142
|
+
# Get from cache
|
143
|
+
if @objects_outdated_due_to_dependencies.has_key?(obj)
|
144
|
+
return @objects_outdated_due_to_dependencies[obj]
|
145
|
+
end
|
146
|
+
|
147
|
+
# Check processed
|
148
|
+
# Don’t return true; the false will be or’ed into a true if there
|
149
|
+
# really is a dependency that is causing outdatedness.
|
150
|
+
return false if processed.include?(obj)
|
151
|
+
|
152
|
+
# Calculate
|
153
|
+
is_outdated = dependency_tracker.objects_causing_outdatedness_of(obj).any? do |other|
|
154
|
+
other.nil? || basic_outdated?(other) || outdated_due_to_dependencies?(other, processed.merge([obj]))
|
155
|
+
end
|
156
|
+
|
157
|
+
# Cache
|
158
|
+
@objects_outdated_due_to_dependencies[obj] = is_outdated
|
159
|
+
|
160
|
+
# Done
|
161
|
+
is_outdated
|
162
|
+
end
|
163
|
+
|
164
|
+
# @param [Nanoc3::ItemRep, Nanoc3::Layout] obj The layout or item
|
165
|
+
# representation to check the rule memory for
|
166
|
+
#
|
167
|
+
# @return [Boolean] true if the rule memory for the given item
|
168
|
+
# represenation has changed, false otherwise
|
169
|
+
def rule_memory_differs_for(obj)
|
170
|
+
rules_collection.rule_memory_differs_for(obj)
|
171
|
+
end
|
172
|
+
memoize :rule_memory_differs_for
|
173
|
+
|
174
|
+
# @param obj
|
175
|
+
#
|
176
|
+
# @return [Boolean] false if either the new or the old checksum for the
|
177
|
+
# given object is not available, true if both checksums are available
|
178
|
+
def checksums_available?(obj)
|
179
|
+
!!checksum_store[obj] && obj.checksum
|
180
|
+
end
|
181
|
+
memoize :checksums_available?
|
182
|
+
|
183
|
+
# @param obj
|
184
|
+
#
|
185
|
+
# @return [Boolean] false if the old and new checksums for the given
|
186
|
+
# object differ, true if they are identical
|
187
|
+
def checksums_identical?(obj)
|
188
|
+
checksum_store[obj] == obj.checksum
|
189
|
+
end
|
190
|
+
memoize :checksums_identical?
|
191
|
+
|
192
|
+
# @param obj
|
193
|
+
#
|
194
|
+
# @return [Boolean] true if the old and new checksums for the given object
|
195
|
+
# are available and identical, false otherwise
|
196
|
+
def object_modified?(obj)
|
197
|
+
!checksums_available?(obj) || !checksums_identical?(obj)
|
198
|
+
end
|
199
|
+
memoize :object_modified?
|
78
200
|
|
79
201
|
# @return [Nanoc3::ChecksumStore] The checksum store
|
80
202
|
def checksum_store
|
81
203
|
@checksum_store
|
82
204
|
end
|
83
205
|
|
206
|
+
# TODO document
|
207
|
+
def rules_collection
|
208
|
+
site.compiler.rules_collection
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [Nanoc3::DependencyTracker] The dependency tracker
|
212
|
+
def dependency_tracker
|
213
|
+
@dependency_tracker
|
214
|
+
end
|
215
|
+
|
216
|
+
# @return [Nanoc3::Site] The site
|
217
|
+
def site
|
218
|
+
@site
|
219
|
+
end
|
220
|
+
|
84
221
|
end
|
85
222
|
|
86
223
|
end
|
@@ -20,24 +20,27 @@ module Nanoc3
|
|
20
20
|
|
21
21
|
end
|
22
22
|
|
23
|
-
NotEnoughData = Generic.new(
|
24
|
-
'Not enough data is present to correctly determine whether the item is outdated.')
|
25
|
-
|
26
|
-
NotWritten = Generic.new(
|
27
|
-
'This item representation has not yet been written to the output directory (but it does have a path).')
|
28
|
-
|
29
|
-
SourceModified = Generic.new(
|
30
|
-
'The source file of this item has been modified since the last time the site was compiled.')
|
31
|
-
|
32
23
|
CodeSnippetsModified = Generic.new(
|
33
24
|
'The code snippets have been modified since the last time the site was compiled.')
|
34
25
|
|
35
26
|
ConfigurationModified = Generic.new(
|
36
27
|
'The site configuration has been modified since the last time the site was compiled.')
|
37
28
|
|
29
|
+
DependenciesOutdated = Generic.new(
|
30
|
+
'This item uses content or attributes that have changed since the last time the site was compiled.')
|
31
|
+
|
32
|
+
NotEnoughData = Generic.new(
|
33
|
+
'Not enough data is present to correctly determine whether the item is outdated.')
|
34
|
+
|
35
|
+
NotWritten = Generic.new(
|
36
|
+
'This item representation has not yet been written to the output directory (but it does have a path).')
|
37
|
+
|
38
38
|
RulesModified = Generic.new(
|
39
39
|
'The rules file has been modified since the last time the site was compiled.')
|
40
40
|
|
41
|
+
SourceModified = Generic.new(
|
42
|
+
'The source file of this item has been modified since the last time the site was compiled.')
|
43
|
+
|
41
44
|
end
|
42
45
|
|
43
46
|
end
|