nanoc 4.1.6 → 4.2.0b1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -1
- data/NEWS.md +11 -4
- data/lib/nanoc/base/checksummer.rb +135 -46
- data/lib/nanoc/base/compilation/compiler.rb +18 -28
- data/lib/nanoc/base/compilation/dependency_tracker.rb +22 -32
- data/lib/nanoc/base/compilation/filter.rb +2 -4
- data/lib/nanoc/base/entities.rb +1 -0
- data/lib/nanoc/base/entities/content.rb +14 -3
- data/lib/nanoc/base/entities/document.rb +14 -6
- data/lib/nanoc/base/entities/item.rb +0 -31
- data/lib/nanoc/base/entities/item_rep.rb +1 -1
- data/lib/nanoc/base/entities/lazy_value.rb +36 -0
- data/lib/nanoc/base/entities/pattern.rb +3 -2
- data/lib/nanoc/base/entities/site.rb +2 -0
- data/lib/nanoc/base/memoization.rb +17 -10
- data/lib/nanoc/base/repos/compiled_content_cache.rb +1 -1
- data/lib/nanoc/base/repos/data_source.rb +10 -6
- data/lib/nanoc/base/services/executor.rb +22 -22
- data/lib/nanoc/base/services/item_rep_router.rb +4 -5
- data/lib/nanoc/base/views.rb +0 -1
- data/lib/nanoc/base/views/item_rep_view.rb +3 -9
- data/lib/nanoc/base/views/mixins/document_view_mixin.rb +4 -11
- data/lib/nanoc/base/views/view.rb +1 -0
- data/lib/nanoc/base/views/view_context.rb +5 -1
- data/lib/nanoc/cli/commands/compile.rb +0 -6
- data/lib/nanoc/data_sources.rb +5 -5
- data/lib/nanoc/data_sources/filesystem.rb +219 -90
- data/lib/nanoc/extra/checking/check.rb +1 -2
- data/lib/nanoc/extra/checking/checks.rb +2 -0
- data/lib/nanoc/extra/checking/checks/css.rb +6 -14
- data/lib/nanoc/extra/checking/checks/html.rb +6 -14
- data/lib/nanoc/extra/checking/checks/internal_links.rb +14 -3
- data/lib/nanoc/extra/checking/checks/w3c_validator.rb +28 -0
- data/lib/nanoc/extra/deployers/fog.rb +134 -78
- data/lib/nanoc/extra/link_collector.rb +14 -18
- data/lib/nanoc/filters/sass.rb +3 -3
- data/lib/nanoc/helpers.rb +1 -0
- data/lib/nanoc/helpers/capturing.rb +16 -58
- data/lib/nanoc/helpers/child_parent.rb +51 -0
- data/lib/nanoc/helpers/filtering.rb +0 -1
- data/lib/nanoc/helpers/html_escape.rb +5 -0
- data/lib/nanoc/helpers/link_to.rb +2 -0
- data/lib/nanoc/helpers/rendering.rb +3 -4
- data/lib/nanoc/rule_dsl/action_provider.rb +20 -4
- data/lib/nanoc/rule_dsl/recording_executor.rb +3 -1
- data/lib/nanoc/rule_dsl/rule_context.rb +0 -1
- data/lib/nanoc/rule_dsl/rule_memory_calculator.rb +4 -1
- data/lib/nanoc/spec.rb +217 -0
- data/lib/nanoc/version.rb +1 -1
- data/test/base/test_data_source.rb +4 -2
- data/test/base/test_dependency_tracker.rb +5 -11
- data/test/data_sources/test_filesystem.rb +605 -69
- data/test/extra/checking/checks/test_internal_links.rb +25 -0
- data/test/extra/deployers/test_fog.rb +0 -177
- data/test/filters/test_less.rb +9 -4
- data/test/helpers/test_capturing.rb +38 -212
- data/test/helpers/test_link_to.rb +0 -205
- data/test/helpers/test_xml_sitemap.rb +2 -1
- metadata +7 -12
- data/lib/nanoc/base/views/site_view.rb +0 -14
- data/lib/nanoc/data_sources/filesystem_unified.rb +0 -101
- data/test/data_sources/test_filesystem_unified.rb +0 -559
- data/test/helpers/test_breadcrumbs.rb +0 -60
- data/test/helpers/test_filtering.rb +0 -112
- data/test/helpers/test_html_escape.rb +0 -26
- data/test/helpers/test_rendering.rb +0 -147
- data/test/helpers/test_tagging.rb +0 -92
- data/test/helpers/test_text.rb +0 -18
data/lib/nanoc/base/entities.rb
CHANGED
@@ -27,7 +27,7 @@ module Nanoc
|
|
27
27
|
@filename.freeze
|
28
28
|
end
|
29
29
|
|
30
|
-
# @param [String] content The uncompiled item content (if it is textual
|
30
|
+
# @param [String, Proc] content The uncompiled item content (if it is textual
|
31
31
|
# content) or the path to the filename containing the content (if this
|
32
32
|
# is binary content).
|
33
33
|
#
|
@@ -58,11 +58,13 @@ module Nanoc
|
|
58
58
|
# @api private
|
59
59
|
class TextualContent < Content
|
60
60
|
# @return [String]
|
61
|
-
|
61
|
+
def string
|
62
|
+
@string.value
|
63
|
+
end
|
62
64
|
|
63
65
|
def initialize(string, filename: nil)
|
64
66
|
super(filename)
|
65
|
-
@string = string
|
67
|
+
@string = Nanoc::Int::LazyValue.new(string)
|
66
68
|
end
|
67
69
|
|
68
70
|
def freeze
|
@@ -73,6 +75,15 @@ module Nanoc
|
|
73
75
|
def binary?
|
74
76
|
false
|
75
77
|
end
|
78
|
+
|
79
|
+
def marshal_dump
|
80
|
+
[filename, string]
|
81
|
+
end
|
82
|
+
|
83
|
+
def marshal_load(array)
|
84
|
+
@filename = array[0]
|
85
|
+
@string = Nanoc::Int::LazyValue.new(array[1])
|
86
|
+
end
|
76
87
|
end
|
77
88
|
|
78
89
|
# @api private
|
@@ -6,27 +6,35 @@ module Nanoc
|
|
6
6
|
attr_reader :content
|
7
7
|
|
8
8
|
# @return [Hash]
|
9
|
-
|
9
|
+
def attributes
|
10
|
+
@attributes.value
|
11
|
+
end
|
10
12
|
|
11
13
|
# @return [Nanoc::Identifier]
|
12
14
|
attr_accessor :identifier
|
13
15
|
|
16
|
+
# @return [String, nil]
|
17
|
+
attr_accessor :checksum_data
|
18
|
+
|
14
19
|
# @param [String, Nanoc::Int::Content] content
|
15
20
|
#
|
16
|
-
# @param [Hash] attributes
|
21
|
+
# @param [Hash, Proc] attributes
|
17
22
|
#
|
18
23
|
# @param [String, Nanoc::Identifier] identifier
|
19
|
-
|
24
|
+
#
|
25
|
+
# @param [String, nil] checksum_data Used to determine whether the document has changed
|
26
|
+
def initialize(content, attributes, identifier, checksum_data: nil)
|
20
27
|
@content = Nanoc::Int::Content.create(content)
|
21
|
-
@attributes = attributes.__nanoc_symbolize_keys_recursively
|
28
|
+
@attributes = Nanoc::Int::LazyValue.new(attributes).map(&:__nanoc_symbolize_keys_recursively)
|
22
29
|
@identifier = Nanoc::Identifier.from(identifier)
|
30
|
+
@checksum_data = checksum_data
|
23
31
|
end
|
24
32
|
|
25
33
|
# @return [void]
|
26
34
|
def freeze
|
27
35
|
super
|
28
|
-
|
29
|
-
|
36
|
+
@content.freeze
|
37
|
+
@attributes.freeze
|
30
38
|
end
|
31
39
|
|
32
40
|
# @abstract
|
@@ -1,13 +1,6 @@
|
|
1
1
|
module Nanoc::Int
|
2
2
|
# @api private
|
3
3
|
class Item < ::Nanoc::Int::Document
|
4
|
-
# @see Document#initialize
|
5
|
-
def initialize(content, attributes, identifier)
|
6
|
-
super
|
7
|
-
|
8
|
-
@forced_outdated_status = ForcedOutdatedStatus.new
|
9
|
-
end
|
10
|
-
|
11
4
|
# Returns an object that can be used for uniquely identifying objects.
|
12
5
|
#
|
13
6
|
# @api private
|
@@ -16,29 +9,5 @@ module Nanoc::Int
|
|
16
9
|
def reference
|
17
10
|
[:item, identifier.to_s]
|
18
11
|
end
|
19
|
-
|
20
|
-
# Hack to allow a frozen item to still have modifiable frozen status.
|
21
|
-
#
|
22
|
-
# FIXME: Remove this.
|
23
|
-
class ForcedOutdatedStatus
|
24
|
-
attr_accessor :bool
|
25
|
-
|
26
|
-
def initialize
|
27
|
-
@bool = false
|
28
|
-
end
|
29
|
-
|
30
|
-
def freeze
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# @api private
|
35
|
-
def forced_outdated=(bool)
|
36
|
-
@forced_outdated_status.bool = bool
|
37
|
-
end
|
38
|
-
|
39
|
-
# @api private
|
40
|
-
def forced_outdated?
|
41
|
-
@forced_outdated_status.bool
|
42
|
-
end
|
43
12
|
end
|
44
13
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Nanoc::Int
|
2
|
+
# Holds a value that might be generated lazily.
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
class LazyValue
|
6
|
+
# @param [Object, Proc] value_or_proc A value or a proc to generate the value
|
7
|
+
def initialize(value_or_proc)
|
8
|
+
@value = { raw: value_or_proc }
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Object] The value, generated when needed
|
12
|
+
def value
|
13
|
+
if @value.key?(:raw)
|
14
|
+
value = @value.delete(:raw)
|
15
|
+
@value[:final] = value.respond_to?(:call) ? value.call : value
|
16
|
+
@value.__nanoc_freeze_recursively if frozen?
|
17
|
+
end
|
18
|
+
@value[:final]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns a new lazy value that will apply the given transformation when the value is requested.
|
22
|
+
#
|
23
|
+
# @yield resolved value
|
24
|
+
#
|
25
|
+
# @return [Nanoc::Int::LazyValue]
|
26
|
+
def map
|
27
|
+
Nanoc::Int::LazyValue.new(-> { yield(value) })
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [void]
|
31
|
+
def freeze
|
32
|
+
super
|
33
|
+
@value.__nanoc_freeze_recursively unless @value[:raw]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -29,13 +29,14 @@ module Nanoc::Int
|
|
29
29
|
|
30
30
|
# @api private
|
31
31
|
class StringPattern
|
32
|
+
MATCH_OPTS = File::FNM_PATHNAME | File::FNM_EXTGLOB
|
33
|
+
|
32
34
|
def initialize(string)
|
33
35
|
@string = string
|
34
36
|
end
|
35
37
|
|
36
38
|
def match?(identifier)
|
37
|
-
|
38
|
-
File.fnmatch(@string, identifier.to_s, opts)
|
39
|
+
File.fnmatch(@string, identifier.to_s, MATCH_OPTS)
|
39
40
|
end
|
40
41
|
|
41
42
|
def captures(_identifier)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'weakref'
|
2
|
+
|
1
3
|
module Nanoc::Int
|
2
4
|
# Adds support for memoizing functions.
|
3
5
|
#
|
@@ -5,6 +7,14 @@ module Nanoc::Int
|
|
5
7
|
#
|
6
8
|
# @since 3.2.0
|
7
9
|
module Memoization
|
10
|
+
class Wrapper
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
def initialize(value)
|
14
|
+
@value = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
8
18
|
# Memoizes the method with the given name. The modified method will cache
|
9
19
|
# the results of the original method, so that calling a method twice with
|
10
20
|
# the same arguments will short-circuit and return the cached results
|
@@ -39,24 +49,21 @@ module Nanoc::Int
|
|
39
49
|
#
|
40
50
|
# @return [void]
|
41
51
|
def memoize(method_name)
|
42
|
-
# Alias
|
43
52
|
original_method_name = '__nonmemoized_' + method_name.to_s
|
44
53
|
alias_method original_method_name, method_name
|
45
54
|
|
46
|
-
# Redefine
|
47
55
|
define_method(method_name) do |*args|
|
48
|
-
# Get cache
|
49
56
|
@__memoization_cache ||= {}
|
50
57
|
@__memoization_cache[method_name] ||= {}
|
58
|
+
method_cache = @__memoization_cache[method_name]
|
51
59
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
60
|
+
if method_cache.key?(args) && method_cache[args].weakref_alive?
|
61
|
+
method_cache[args].value
|
62
|
+
else
|
63
|
+
send(original_method_name, *args).tap do |r|
|
64
|
+
method_cache[args] = WeakRef.new(Wrapper.new(r))
|
65
|
+
end
|
56
66
|
end
|
57
|
-
|
58
|
-
# Done
|
59
|
-
@__memoization_cache[method_name][args]
|
60
67
|
end
|
61
68
|
end
|
62
69
|
end
|
@@ -130,18 +130,20 @@ module Nanoc
|
|
130
130
|
# Creates a new in-memory item instance. This is intended for use within
|
131
131
|
# the {#items} method.
|
132
132
|
#
|
133
|
-
# @param [String] content The uncompiled item content
|
133
|
+
# @param [String, Proc] content The uncompiled item content
|
134
134
|
# (if it is a textual item) or the path to the filename containing the
|
135
135
|
# content (if it is a binary item).
|
136
136
|
#
|
137
|
-
# @param [Hash] attributes A hash containing this item's attributes.
|
137
|
+
# @param [Hash, Proc] attributes A hash containing this item's attributes.
|
138
138
|
#
|
139
139
|
# @param [String] identifier This item's identifier.
|
140
140
|
#
|
141
141
|
# @param [Boolean] binary Whether or not this item is binary
|
142
|
-
|
142
|
+
#
|
143
|
+
# @param [String, nil] checksum_data Used to determine whether the item has changed
|
144
|
+
def new_item(content, attributes, identifier, binary: false, checksum_data: nil)
|
143
145
|
content = Nanoc::Int::Content.create(content, binary: binary)
|
144
|
-
Nanoc::Int::Item.new(content, attributes, identifier)
|
146
|
+
Nanoc::Int::Item.new(content, attributes, identifier, checksum_data: checksum_data)
|
145
147
|
end
|
146
148
|
|
147
149
|
# Creates a new in-memory layout instance. This is intended for use within
|
@@ -152,8 +154,10 @@ module Nanoc
|
|
152
154
|
# @param [Hash] attributes A hash containing this layout's attributes.
|
153
155
|
#
|
154
156
|
# @param [String] identifier This layout's identifier.
|
155
|
-
|
156
|
-
|
157
|
+
#
|
158
|
+
# @param [String, nil] checksum_data Used to determine whether the layout has changed
|
159
|
+
def new_layout(raw_content, attributes, identifier, checksum_data: nil)
|
160
|
+
Nanoc::Int::Layout.new(raw_content, attributes, identifier, checksum_data: checksum_data)
|
157
161
|
end
|
158
162
|
end
|
159
163
|
end
|
@@ -7,49 +7,36 @@ module Nanoc
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
def initialize(compiler)
|
10
|
+
def initialize(compiler, dependency_tracker)
|
11
11
|
@compiler = compiler
|
12
|
+
@dependency_tracker = dependency_tracker
|
12
13
|
end
|
13
14
|
|
14
15
|
def filter(rep, filter_name, filter_args = {})
|
15
|
-
|
16
|
-
klass = Nanoc::Filter.named(filter_name)
|
17
|
-
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil?
|
18
|
-
|
19
|
-
# Check whether filter can be applied
|
20
|
-
if klass.from_binary? && !rep.binary?
|
21
|
-
raise Nanoc::Int::Errors::CannotUseBinaryFilter.new(rep, klass)
|
22
|
-
elsif !klass.from_binary? && rep.binary?
|
23
|
-
raise Nanoc::Int::Errors::CannotUseTextualFilter.new(rep, klass)
|
24
|
-
end
|
16
|
+
filter = filter_for_filtering(rep, filter_name)
|
25
17
|
|
26
18
|
begin
|
27
|
-
# Notify start
|
28
19
|
Nanoc::Int::NotificationCenter.post(:filtering_started, rep, filter_name)
|
29
20
|
|
30
|
-
# Create filter
|
31
|
-
filter = klass.new(assigns_for(rep))
|
32
|
-
|
33
21
|
# Run filter
|
34
22
|
last = rep.snapshot_contents[:last]
|
35
23
|
source = rep.binary? ? last.filename : last.string
|
36
24
|
result = filter.setup_and_run(source, filter_args)
|
37
25
|
rep.snapshot_contents[:last] =
|
38
|
-
if
|
26
|
+
if filter.class.to_binary?
|
39
27
|
Nanoc::Int::BinaryContent.new(filter.output_filename).tap(&:freeze)
|
40
28
|
else
|
41
29
|
Nanoc::Int::TextualContent.new(result).tap(&:freeze)
|
42
30
|
end
|
43
31
|
|
44
32
|
# Check whether file was written
|
45
|
-
if
|
33
|
+
if filter.class.to_binary? && !File.file?(filter.output_filename)
|
46
34
|
raise OutputNotWrittenError.new(filter_name, filter.output_filename)
|
47
35
|
end
|
48
36
|
|
49
37
|
# Create snapshot
|
50
38
|
snapshot(rep, rep.snapshot_contents[:post] ? :post : :pre, final: false) unless rep.binary?
|
51
39
|
ensure
|
52
|
-
# Notify end
|
53
40
|
Nanoc::Int::NotificationCenter.post(:filtering_ended, rep, filter_name)
|
54
41
|
end
|
55
42
|
end
|
@@ -73,12 +60,12 @@ module Nanoc
|
|
73
60
|
# Create filter
|
74
61
|
klass = Nanoc::Filter.named(filter_name)
|
75
62
|
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil?
|
76
|
-
|
63
|
+
view_context = @compiler.create_view_context(@dependency_tracker)
|
64
|
+
layout_view = Nanoc::LayoutView.new(layout, view_context)
|
77
65
|
filter = klass.new(assigns_for(rep).merge({ layout: layout_view }))
|
78
66
|
|
79
67
|
# Visit
|
80
|
-
|
81
|
-
Nanoc::Int::NotificationCenter.post(:visit_ended, layout)
|
68
|
+
@dependency_tracker.bounce(layout)
|
82
69
|
|
83
70
|
begin
|
84
71
|
# Notify start
|
@@ -120,7 +107,7 @@ module Nanoc
|
|
120
107
|
end
|
121
108
|
|
122
109
|
def assigns_for(rep)
|
123
|
-
@compiler.assigns_for(rep)
|
110
|
+
@compiler.assigns_for(rep, @dependency_tracker)
|
124
111
|
end
|
125
112
|
|
126
113
|
def layouts
|
@@ -141,6 +128,19 @@ module Nanoc
|
|
141
128
|
raise Nanoc::Int::Errors::UnknownLayout.new(arg)
|
142
129
|
end
|
143
130
|
|
131
|
+
def filter_for_filtering(rep, filter_name)
|
132
|
+
klass = Nanoc::Filter.named(filter_name)
|
133
|
+
raise Nanoc::Int::Errors::UnknownFilter.new(filter_name) if klass.nil?
|
134
|
+
|
135
|
+
if klass.from_binary? && !rep.binary?
|
136
|
+
raise Nanoc::Int::Errors::CannotUseBinaryFilter.new(rep, klass)
|
137
|
+
elsif !klass.from_binary? && rep.binary?
|
138
|
+
raise Nanoc::Int::Errors::CannotUseTextualFilter.new(rep, klass)
|
139
|
+
end
|
140
|
+
|
141
|
+
klass.new(assigns_for(rep))
|
142
|
+
end
|
143
|
+
|
144
144
|
def use_globs?
|
145
145
|
@compiler.site.config[:string_pattern_type] == 'glob'
|
146
146
|
end
|
@@ -28,7 +28,6 @@ module Nanoc::Int
|
|
28
28
|
def route_rep(rep, snapshot_action, paths_to_reps)
|
29
29
|
basic_path = snapshot_action.path
|
30
30
|
return if basic_path.nil?
|
31
|
-
basic_path = basic_path.encode('UTF-8')
|
32
31
|
|
33
32
|
# Check for duplicate paths
|
34
33
|
if paths_to_reps.key?(basic_path)
|
@@ -43,10 +42,10 @@ module Nanoc::Int
|
|
43
42
|
|
44
43
|
def strip_index_filename(basic_path)
|
45
44
|
@site.config[:index_filenames].each do |index_filename|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
rep_path_ending = basic_path[-index_filename.length..-1]
|
46
|
+
next unless rep_path_ending == index_filename
|
47
|
+
|
48
|
+
return basic_path[0..-index_filename.length - 1]
|
50
49
|
end
|
51
50
|
|
52
51
|
basic_path
|
data/lib/nanoc/base/views.rb
CHANGED
@@ -21,7 +21,6 @@ require_relative 'views/mutable_item_view'
|
|
21
21
|
require_relative 'views/mutable_item_collection_view'
|
22
22
|
require_relative 'views/mutable_layout_view'
|
23
23
|
require_relative 'views/mutable_layout_collection_view'
|
24
|
-
require_relative 'views/site_view'
|
25
24
|
|
26
25
|
require_relative 'views/post_compile_item_view'
|
27
26
|
require_relative 'views/post_compile_item_collection_view'
|
@@ -36,9 +36,7 @@ module Nanoc
|
|
36
36
|
#
|
37
37
|
# @return [String] The content at the given snapshot.
|
38
38
|
def compiled_content(snapshot: nil)
|
39
|
-
|
40
|
-
Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item)
|
41
|
-
|
39
|
+
@context.dependency_tracker.bounce(unwrap.item)
|
42
40
|
@item_rep.compiled_content(snapshot: snapshot)
|
43
41
|
end
|
44
42
|
|
@@ -52,9 +50,7 @@ module Nanoc
|
|
52
50
|
#
|
53
51
|
# @return [String] The item rep’s path.
|
54
52
|
def path(snapshot: :last)
|
55
|
-
|
56
|
-
Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item)
|
57
|
-
|
53
|
+
@context.dependency_tracker.bounce(unwrap.item)
|
58
54
|
@item_rep.path(snapshot: snapshot)
|
59
55
|
end
|
60
56
|
|
@@ -67,9 +63,7 @@ module Nanoc
|
|
67
63
|
|
68
64
|
# @api private
|
69
65
|
def raw_path(snapshot: :last)
|
70
|
-
|
71
|
-
Nanoc::Int::NotificationCenter.post(:visit_ended, unwrap.item)
|
72
|
-
|
66
|
+
@context.dependency_tracker.bounce(unwrap.item)
|
73
67
|
@item_rep.raw_path(snapshot: snapshot)
|
74
68
|
end
|
75
69
|
|