nanoc 4.1.6 → 4.2.0b1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
@@ -20,13 +20,12 @@ module Nanoc::Extra::Checking
|
|
20
20
|
output_filenames = Dir[output_dir + '/**/*'].select { |f| File.file?(f) }
|
21
21
|
|
22
22
|
# FIXME: ugly
|
23
|
-
view_context = site.compiler.create_view_context
|
23
|
+
view_context = site.compiler.create_view_context(Nanoc::Int::DependencyTracker::Null.new)
|
24
24
|
|
25
25
|
context = {
|
26
26
|
items: Nanoc::ItemCollectionWithRepsView.new(site.items, view_context),
|
27
27
|
layouts: Nanoc::LayoutCollectionView.new(site.layouts, view_context),
|
28
28
|
config: Nanoc::ConfigView.new(site.config, view_context),
|
29
|
-
site: Nanoc::SiteView.new(site, view_context), # TODO: remove me
|
30
29
|
output_filenames: output_filenames,
|
31
30
|
}
|
32
31
|
|
@@ -1,22 +1,14 @@
|
|
1
1
|
module ::Nanoc::Extra::Checking::Checks
|
2
2
|
# @api private
|
3
|
-
class CSS < ::Nanoc::Extra::Checking::
|
3
|
+
class CSS < ::Nanoc::Extra::Checking::Checks::W3CValidator
|
4
4
|
identifier :css
|
5
5
|
|
6
|
-
def
|
7
|
-
|
6
|
+
def extension
|
7
|
+
'css'
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
lines = File.readlines(filename)
|
12
|
-
results.errors.each do |e|
|
13
|
-
line_num = e.line.to_i - 1
|
14
|
-
line = lines[line_num]
|
15
|
-
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
|
16
|
-
desc = "line #{line_num + 1}: #{message}: #{line}"
|
17
|
-
add_issue(desc, subject: filename)
|
18
|
-
end
|
19
|
-
end
|
10
|
+
def validator_class
|
11
|
+
::W3CValidators::CSSValidator
|
20
12
|
end
|
21
13
|
end
|
22
14
|
end
|
@@ -1,22 +1,14 @@
|
|
1
1
|
module ::Nanoc::Extra::Checking::Checks
|
2
2
|
# @api private
|
3
|
-
class HTML < ::Nanoc::Extra::Checking::
|
3
|
+
class HTML < ::Nanoc::Extra::Checking::Checks::W3CValidator
|
4
4
|
identifier :html
|
5
5
|
|
6
|
-
def
|
7
|
-
|
6
|
+
def extension
|
7
|
+
'{htm,html}'
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
lines = File.readlines(filename)
|
12
|
-
results.errors.each do |e|
|
13
|
-
line_num = e.line.to_i - 1
|
14
|
-
line = lines[line_num]
|
15
|
-
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
|
16
|
-
desc = "line #{line_num + 1}: #{message}: #{line}"
|
17
|
-
add_issue(desc, subject: filename)
|
18
|
-
end
|
19
|
-
end
|
10
|
+
def validator_class
|
11
|
+
::W3CValidators::MarkupValidator
|
20
12
|
end
|
21
13
|
end
|
22
14
|
end
|
@@ -34,7 +34,7 @@ module Nanoc::Extra::Checking::Checks
|
|
34
34
|
return true if href == '.'
|
35
35
|
|
36
36
|
# Skip hrefs that are specified in the exclude configuration
|
37
|
-
return true if excluded?(href)
|
37
|
+
return true if excluded?(href, origin)
|
38
38
|
|
39
39
|
# Remove target
|
40
40
|
path = href.sub(/#.*$/, '')
|
@@ -65,9 +65,20 @@ module Nanoc::Extra::Checking::Checks
|
|
65
65
|
false
|
66
66
|
end
|
67
67
|
|
68
|
-
def excluded?(href)
|
69
|
-
|
68
|
+
def excluded?(href, origin)
|
69
|
+
config = @config.fetch(:checks, {}).fetch(:internal_links, {})
|
70
|
+
excluded_target?(href, config) || excluded_origin?(origin, config)
|
71
|
+
end
|
72
|
+
|
73
|
+
def excluded_target?(href, config)
|
74
|
+
excludes = config.fetch(:exclude_targets, config.fetch(:exclude, []))
|
70
75
|
excludes.any? { |pattern| Regexp.new(pattern).match(href) }
|
71
76
|
end
|
77
|
+
|
78
|
+
def excluded_origin?(origin, config)
|
79
|
+
relative_origin = origin[@config[:output_dir].size..-1]
|
80
|
+
excludes = config.fetch(:exclude_origins, [])
|
81
|
+
excludes.any? { |pattern| Regexp.new(pattern).match(relative_origin) }
|
82
|
+
end
|
72
83
|
end
|
73
84
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ::Nanoc::Extra::Checking::Checks
|
2
|
+
# @api private
|
3
|
+
class W3CValidator < ::Nanoc::Extra::Checking::Check
|
4
|
+
def run
|
5
|
+
require 'w3c_validators'
|
6
|
+
|
7
|
+
Dir[@config[:output_dir] + '/**/*.' + extension].each do |filename|
|
8
|
+
results = validator_class.new.validate_file(filename)
|
9
|
+
lines = File.readlines(filename)
|
10
|
+
results.errors.each do |e|
|
11
|
+
line_num = e.line.to_i - 1
|
12
|
+
line = lines[line_num]
|
13
|
+
message = e.message.gsub(%r{\s+}, ' ').strip.sub(/\s+:$/, '')
|
14
|
+
desc = "line #{line_num + 1}: #{message}: #{line}"
|
15
|
+
add_issue(desc, subject: filename)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def extension
|
21
|
+
raise NotImplementedError
|
22
|
+
end
|
23
|
+
|
24
|
+
def validator_class
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -21,104 +21,165 @@ module Nanoc::Extra::Deployers
|
|
21
21
|
#
|
22
22
|
# @api private
|
23
23
|
class Fog < ::Nanoc::Extra::Deployer
|
24
|
+
class FogWrapper
|
25
|
+
def initialize(directory, is_dry_run)
|
26
|
+
@directory = directory
|
27
|
+
@is_dry_run = is_dry_run
|
28
|
+
end
|
29
|
+
|
30
|
+
def upload(source_filename, destination_key)
|
31
|
+
log_effectful("uploading #{source_filename} -> #{destination_key}")
|
32
|
+
|
33
|
+
unless dry_run?
|
34
|
+
# FIXME: source_filename file is never closed
|
35
|
+
@directory.files.create(
|
36
|
+
key: destination_key,
|
37
|
+
body: File.open(source_filename),
|
38
|
+
public: true,
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove(keys)
|
44
|
+
keys.each do |key|
|
45
|
+
log_effectful("removing #{key}")
|
46
|
+
|
47
|
+
unless dry_run?
|
48
|
+
@directory.files.get(key).destroy
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def invalidate(keys, cdn, distribution)
|
54
|
+
keys.each_slice(1000) do |keys_slice|
|
55
|
+
keys_slice.each do |key|
|
56
|
+
log_effectful("invalidating #{key}")
|
57
|
+
end
|
58
|
+
|
59
|
+
unless dry_run?
|
60
|
+
cdn.post_invalidation(distribution, keys_slice)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def dry_run?
|
66
|
+
@is_dry_run
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_effectful(s)
|
70
|
+
if @is_dry_run
|
71
|
+
puts "[dry run] #{s}"
|
72
|
+
else
|
73
|
+
puts s
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
24
78
|
# @see Nanoc::Extra::Deployer#run
|
25
79
|
def run
|
26
80
|
require 'fog'
|
27
81
|
|
28
|
-
# Get params, unsetting anything we don't want to pass through to fog.
|
29
82
|
src = File.expand_path(source_path)
|
30
|
-
bucket = config
|
31
|
-
path = config
|
32
|
-
cdn_id = config
|
83
|
+
bucket = config[:bucket] || config[:bucket_name]
|
84
|
+
path = config[:path]
|
85
|
+
cdn_id = config[:cdn_id]
|
33
86
|
|
34
|
-
|
87
|
+
# FIXME: confusing error message
|
88
|
+
raise 'The path requires no trailing slash' if path && path[-1, 1] == '/'
|
35
89
|
|
36
|
-
|
37
|
-
|
90
|
+
connection = connect
|
91
|
+
directory = get_or_create_bucket(connection, bucket, path)
|
92
|
+
wrapper = FogWrapper.new(directory, dry_run?)
|
38
93
|
|
39
|
-
|
40
|
-
|
41
|
-
puts 'Dry run - simulation'
|
42
|
-
::Fog.mock!
|
43
|
-
end
|
94
|
+
remote_files = list_remote_files(directory)
|
95
|
+
etags = read_etags(remote_files)
|
44
96
|
|
45
|
-
|
46
|
-
puts 'Connecting'
|
47
|
-
connection = ::Fog::Storage.new(config)
|
97
|
+
modified_keys, retained_keys = upload_all(src, path, etags, wrapper)
|
48
98
|
|
49
|
-
|
50
|
-
|
51
|
-
begin
|
52
|
-
directory = connection.directories.get(bucket, prefix: path)
|
53
|
-
rescue ::Excon::Errors::NotFound
|
54
|
-
should_create_bucket = true
|
55
|
-
end
|
56
|
-
should_create_bucket = true if directory.nil?
|
99
|
+
removed_keys = remote_files.map(&:key) - retained_keys - modified_keys
|
100
|
+
wrapper.remove(removed_keys)
|
57
101
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
102
|
+
if cdn_id
|
103
|
+
cdn = ::Fog::CDN.new(config_for_fog)
|
104
|
+
distribution = cdn.get_distribution(cdn_id)
|
105
|
+
wrapper.invalidate(modified_keys + removed_keys, cdn, distribution)
|
62
106
|
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
63
110
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
111
|
+
def config_for_fog
|
112
|
+
config.dup.tap do |c|
|
113
|
+
c.delete(:bucket)
|
114
|
+
c.delete(:bucket_name)
|
115
|
+
c.delete(:path)
|
116
|
+
c.delete(:cdn_id)
|
117
|
+
c.delete(:kind)
|
71
118
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
def connect
|
122
|
+
::Fog::Storage.new(config_for_fog)
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_or_create_bucket(connection, bucket, path)
|
126
|
+
directory =
|
127
|
+
begin
|
128
|
+
connection.directories.get(bucket, prefix: path)
|
129
|
+
rescue ::Excon::Errors::NotFound
|
130
|
+
nil
|
83
131
|
end
|
132
|
+
|
133
|
+
if directory
|
134
|
+
directory
|
135
|
+
elsif dry_run?
|
136
|
+
puts '[dry run] creating bucket'
|
137
|
+
else
|
138
|
+
puts 'creating bucket'
|
139
|
+
connection.directories.create(key: bucket, prefix: path)
|
84
140
|
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def remote_key_for_local_filename(local_filename, src, path)
|
144
|
+
relative_local_filename = local_filename.sub(/\A#{src}\//, '')
|
85
145
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
146
|
+
if path
|
147
|
+
File.join(path, relative_local_filename)
|
148
|
+
else
|
149
|
+
relative_local_filename
|
90
150
|
end
|
151
|
+
end
|
91
152
|
|
92
|
-
|
93
|
-
if
|
94
|
-
|
95
|
-
|
96
|
-
cdn = ::Fog::CDN.new(config)
|
97
|
-
# fog cannot mock CDN requests
|
98
|
-
unless dry_run?
|
99
|
-
distribution = cdn.get_distribution(cdn_id)
|
100
|
-
# usual limit per invalidation: 1000 objects
|
101
|
-
keys_to_invalidate.each_slice(1000) do |paths|
|
102
|
-
cdn.post_invalidation(distribution, paths)
|
103
|
-
end
|
153
|
+
def list_remote_files(directory)
|
154
|
+
if directory
|
155
|
+
[].tap do |files|
|
156
|
+
directory.files.each { |file| files << file }
|
104
157
|
end
|
158
|
+
else
|
159
|
+
[]
|
105
160
|
end
|
106
|
-
|
107
|
-
puts 'Done!'
|
108
161
|
end
|
109
162
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
keys_to_destroy.delete(key)
|
163
|
+
def list_local_files(src)
|
164
|
+
Dir[src + '/**/*'].select { |f| File.file?(f) }
|
165
|
+
end
|
114
166
|
|
115
|
-
|
167
|
+
def upload_all(src, path, etags, wrapper)
|
168
|
+
modified_keys = []
|
169
|
+
retained_keys = []
|
170
|
+
|
171
|
+
local_files = list_local_files(src)
|
172
|
+
local_files.each do |file_path|
|
173
|
+
key = remote_key_for_local_filename(file_path, src, path)
|
174
|
+
if needs_upload?(key, file_path, etags)
|
175
|
+
wrapper.upload(file_path, key)
|
176
|
+
modified_keys.push(key)
|
177
|
+
else
|
178
|
+
retained_keys.push(key)
|
179
|
+
end
|
180
|
+
end
|
116
181
|
|
117
|
-
|
118
|
-
key: key,
|
119
|
-
body: File.open(file_path),
|
120
|
-
public: true)
|
121
|
-
keys_to_invalidate.push(key)
|
182
|
+
[modified_keys, retained_keys]
|
122
183
|
end
|
123
184
|
|
124
185
|
def needs_upload?(key, file_path, etags)
|
@@ -146,10 +207,5 @@ module Nanoc::Extra::Deployers
|
|
146
207
|
Digest::MD5.file(file_path).hexdigest
|
147
208
|
end
|
148
209
|
end
|
149
|
-
|
150
|
-
# Prints the given message on stderr and exits.
|
151
|
-
def error(msg)
|
152
|
-
raise RuntimeError.new(msg)
|
153
|
-
end
|
154
210
|
end
|
155
211
|
end
|
@@ -32,27 +32,11 @@ module ::Nanoc::Extra
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def filenames_per_href
|
35
|
-
|
36
|
-
filenames_per_href = {}
|
37
|
-
@filenames.each do |filename|
|
38
|
-
hrefs_in_file(filename).each do |href|
|
39
|
-
filenames_per_href[href] ||= Set.new
|
40
|
-
filenames_per_href[href] << filename
|
41
|
-
end
|
42
|
-
end
|
43
|
-
filenames_per_href
|
35
|
+
grouped_filenames { |filename| hrefs_in_file(filename) }
|
44
36
|
end
|
45
37
|
|
46
38
|
def filenames_per_resource_uri
|
47
|
-
|
48
|
-
filenames_per_resource_uri = {}
|
49
|
-
@filenames.each do |filename|
|
50
|
-
resource_uris_in_file(filename).each do |resouce_uri|
|
51
|
-
filenames_per_resource_uri[resouce_uri] ||= Set.new
|
52
|
-
filenames_per_resource_uri[resouce_uri] << filename
|
53
|
-
end
|
54
|
-
end
|
55
|
-
filenames_per_resource_uri
|
39
|
+
grouped_filenames { |filename| resource_uris_in_file(filename) }
|
56
40
|
end
|
57
41
|
|
58
42
|
def external_href?(href)
|
@@ -69,6 +53,18 @@ module ::Nanoc::Extra
|
|
69
53
|
|
70
54
|
private
|
71
55
|
|
56
|
+
def grouped_filenames
|
57
|
+
require 'nokogiri'
|
58
|
+
grouped_filenames = {}
|
59
|
+
@filenames.each do |filename|
|
60
|
+
yield(filename).each do |resouce_uri|
|
61
|
+
grouped_filenames[resouce_uri] ||= Set.new
|
62
|
+
grouped_filenames[resouce_uri] << filename
|
63
|
+
end
|
64
|
+
end
|
65
|
+
grouped_filenames
|
66
|
+
end
|
67
|
+
|
72
68
|
def uris_in_file(filename, tag_names)
|
73
69
|
uris = Set.new
|
74
70
|
doc = Nokogiri::HTML(::File.read(filename))
|
data/lib/nanoc/filters/sass.rb
CHANGED
@@ -18,9 +18,9 @@ module Nanoc::Filters
|
|
18
18
|
engine.render
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.
|
21
|
+
def self.item_filename_map_for_config(config, items)
|
22
22
|
@item_filename_map ||= {}
|
23
|
-
@item_filename_map[
|
23
|
+
@item_filename_map[config] ||=
|
24
24
|
{}.tap do |map|
|
25
25
|
items.each do |item|
|
26
26
|
if item.raw_filename
|
@@ -34,7 +34,7 @@ module Nanoc::Filters
|
|
34
34
|
def imported_filename_to_item(filename)
|
35
35
|
realpath = Pathname.new(filename).realpath.to_s
|
36
36
|
|
37
|
-
map = self.class.
|
37
|
+
map = self.class.item_filename_map_for_config(@config, @items)
|
38
38
|
map[realpath]
|
39
39
|
end
|
40
40
|
end
|