nanoc3 3.2.0a1 → 3.2.0a2
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.
- data/ChangeLog +2 -2
- data/NEWS.md +14 -0
- data/README.md +20 -12
- data/doc/yardoc_templates/default/layout/html/footer.erb +10 -0
- data/lib/nanoc3/base/compilation/checksum_store.rb +130 -0
- data/lib/nanoc3/base/compilation/checksummer.rb +68 -0
- data/lib/nanoc3/base/compilation/compiled_content_cache.rb +57 -0
- data/lib/nanoc3/base/{compiler.rb → compilation/compiler.rb} +255 -55
- data/lib/nanoc3/base/{compiler_dsl.rb → compilation/compiler_dsl.rb} +11 -6
- data/lib/nanoc3/base/{dependency_tracker.rb → compilation/dependency_tracker.rb} +62 -92
- data/lib/nanoc3/base/{filter.rb → compilation/filter.rb} +0 -0
- data/lib/nanoc3/base/compilation/item_rep_proxy.rb +87 -0
- data/lib/nanoc3/base/compilation/outdatedness_checker.rb +86 -0
- data/lib/nanoc3/base/compilation/outdatedness_reasons.rb +43 -0
- data/lib/nanoc3/base/{rule.rb → compilation/rule.rb} +8 -2
- data/lib/nanoc3/base/{rule_context.rb → compilation/rule_context.rb} +17 -14
- data/lib/nanoc3/base/directed_graph.rb +8 -0
- data/lib/nanoc3/base/errors.rb +9 -17
- data/lib/nanoc3/base/plugin_registry.rb +1 -1
- data/lib/nanoc3/base/result_data/item_rep.rb +447 -0
- data/lib/nanoc3/base/{code_snippet.rb → source_data/code_snippet.rb} +7 -22
- data/lib/nanoc3/base/{data_source.rb → source_data/data_source.rb} +0 -0
- data/lib/nanoc3/base/{item.rb → source_data/item.rb} +20 -30
- data/lib/nanoc3/base/{layout.rb → source_data/layout.rb} +7 -26
- data/lib/nanoc3/base/source_data/site.rb +314 -0
- data/lib/nanoc3/base/store.rb +126 -0
- data/lib/nanoc3/base.rb +26 -15
- data/lib/nanoc3/cli/base.rb +2 -1
- data/lib/nanoc3/cli/commands/compile.rb +116 -48
- data/lib/nanoc3/cli/commands/create_item.rb +0 -1
- data/lib/nanoc3/cli/commands/create_layout.rb +0 -1
- data/lib/nanoc3/cli/commands/create_site.rb +11 -1
- data/lib/nanoc3/cli/commands/debug.rb +11 -6
- data/lib/nanoc3/cli/commands/info.rb +1 -2
- data/lib/nanoc3/cli/commands/update.rb +0 -1
- data/lib/nanoc3/cli/commands/watch.rb +27 -32
- data/lib/nanoc3/cli/logger.rb +2 -2
- data/lib/nanoc3/data_sources/filesystem.rb +1 -6
- data/lib/nanoc3/extra/auto_compiler.rb +2 -3
- data/lib/nanoc3/extra/deployers/rsync.rb +1 -0
- data/lib/nanoc3/extra/validators/links.rb +45 -26
- data/lib/nanoc3/filters/asciidoc.rb +58 -0
- data/lib/nanoc3/filters/colorize_syntax.rb +47 -9
- data/lib/nanoc3/filters/less.rb +6 -0
- data/lib/nanoc3/filters/sass.rb +8 -5
- data/lib/nanoc3/filters.rb +2 -0
- data/lib/nanoc3/helpers/blogging.rb +8 -0
- data/lib/nanoc3/helpers/html_escape.rb +37 -7
- data/lib/nanoc3/helpers/link_to.rb +15 -4
- data/lib/nanoc3/helpers/rendering.rb +6 -2
- data/lib/nanoc3/tasks/clean.rb +0 -4
- data/lib/nanoc3.rb +1 -1
- data/nanoc3.gemspec +42 -0
- metadata +35 -19
- data/lib/nanoc3/base/checksummer.rb +0 -40
- data/lib/nanoc3/base/compiled_content_cache.rb +0 -86
- data/lib/nanoc3/base/item_rep.rb +0 -537
- data/lib/nanoc3/base/site.rb +0 -490
@@ -17,17 +17,15 @@ module Nanoc3::CLI::Commands
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def long_desc
|
20
|
-
'Compile all items of the current site.
|
21
|
-
'only the item with the given identifier will be compiled. ' +
|
20
|
+
'Compile all items of the current site.' +
|
22
21
|
"\n\n" +
|
23
22
|
'By default, only item that are outdated will be compiled. This can ' +
|
24
23
|
'speed up the compilation process quite a bit, but items that include ' +
|
25
|
-
'content from other items may have to be recompiled manually.
|
26
|
-
'order to compile items even when they are outdated, use the --force option.'
|
24
|
+
'content from other items may have to be recompiled manually.'
|
27
25
|
end
|
28
26
|
|
29
27
|
def usage
|
30
|
-
"nanoc3 compile [options]
|
28
|
+
"nanoc3 compile [options]"
|
31
29
|
end
|
32
30
|
|
33
31
|
def option_definitions
|
@@ -35,12 +33,12 @@ module Nanoc3::CLI::Commands
|
|
35
33
|
# --all
|
36
34
|
{
|
37
35
|
:long => 'all', :short => 'a', :argument => :forbidden,
|
38
|
-
:desc => '
|
36
|
+
:desc => '(ignored)'
|
39
37
|
},
|
40
38
|
# --force
|
41
39
|
{
|
42
40
|
:long => 'force', :short => 'f', :argument => :forbidden,
|
43
|
-
:desc => '
|
41
|
+
:desc => '(ignored)'
|
44
42
|
}
|
45
43
|
]
|
46
44
|
end
|
@@ -49,30 +47,21 @@ module Nanoc3::CLI::Commands
|
|
49
47
|
# Make sure we are in a nanoc site directory
|
50
48
|
puts "Loading site data..."
|
51
49
|
@base.require_site
|
52
|
-
@base.site.load_data
|
53
50
|
|
54
51
|
# Check presence of --all option
|
55
|
-
if options.has_key?(:all)
|
56
|
-
$stderr.puts "Warning: the --
|
57
|
-
end
|
58
|
-
|
59
|
-
#
|
60
|
-
if arguments.size ==
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
identifier = arguments[0].cleaned_identifier
|
65
|
-
item = @base.site.items.find { |item| item.identifier == identifier }
|
66
|
-
|
67
|
-
# Ensure item
|
68
|
-
if item.nil?
|
69
|
-
$stderr.puts "Unknown item: #{identifier}"
|
70
|
-
exit 1
|
71
|
-
end
|
52
|
+
if options.has_key?(:all) || options.has_key?(:force)
|
53
|
+
$stderr.puts "Warning: the --force option (and its deprecated --all alias) are, as of nanoc 3.2, no longer supported and have no effect."
|
54
|
+
end
|
55
|
+
|
56
|
+
# Warn if trying to compile a single item
|
57
|
+
if arguments.size == 1
|
58
|
+
$stderr.puts '-' * 80
|
59
|
+
$stderr.puts 'Note: As of nanoc 3.2, it is no longer possible to compile a single item. When invoking the “compile” command, all items in the site will be compiled.'.make_compatible_with_env
|
60
|
+
$stderr.puts '-' * 80
|
72
61
|
end
|
73
62
|
|
74
63
|
# Give feedback
|
75
|
-
puts "Compiling
|
64
|
+
puts "Compiling site..."
|
76
65
|
|
77
66
|
# Initialize profiling stuff
|
78
67
|
time_before = Time.now
|
@@ -80,14 +69,14 @@ module Nanoc3::CLI::Commands
|
|
80
69
|
@filter_times = {}
|
81
70
|
setup_notifications
|
82
71
|
|
72
|
+
# Prepare for generating diffs
|
73
|
+
setup_diffs
|
74
|
+
|
83
75
|
# Compile
|
84
|
-
@base.site.
|
85
|
-
item,
|
86
|
-
:force => options.has_key?(:all) || options.has_key?(:force)
|
87
|
-
)
|
76
|
+
@base.site.compile
|
88
77
|
|
89
78
|
# Find reps
|
90
|
-
reps = @base.site.items.map
|
79
|
+
reps = @base.site.items.map { |i| i.reps }.flatten
|
91
80
|
|
92
81
|
# Show skipped reps
|
93
82
|
reps.select { |r| !r.compiled? }.each do |rep|
|
@@ -98,12 +87,12 @@ module Nanoc3::CLI::Commands
|
|
98
87
|
end
|
99
88
|
end
|
100
89
|
|
101
|
-
#
|
102
|
-
|
90
|
+
# Stop diffing
|
91
|
+
teardown_diffs
|
103
92
|
|
104
93
|
# Give general feedback
|
105
94
|
puts
|
106
|
-
puts "
|
95
|
+
puts "Site compiled in #{format('%.2f', Time.now - time_before)}s."
|
107
96
|
|
108
97
|
# Give detailed feedback
|
109
98
|
if options.has_key?(:verbose)
|
@@ -115,6 +104,9 @@ module Nanoc3::CLI::Commands
|
|
115
104
|
|
116
105
|
def setup_notifications
|
117
106
|
# File notifications
|
107
|
+
Nanoc3::NotificationCenter.on(:will_write_rep) do |rep, snapshot|
|
108
|
+
generate_diff_for(rep, snapshot)
|
109
|
+
end
|
118
110
|
Nanoc3::NotificationCenter.on(:rep_written) do |rep, path, is_created, is_modified|
|
119
111
|
action = (is_created ? :create : (is_modified ? :update : :identical))
|
120
112
|
duration = Time.now - @rep_times[rep.raw_path] if @rep_times[rep.raw_path]
|
@@ -144,36 +136,112 @@ module Nanoc3::CLI::Commands
|
|
144
136
|
end
|
145
137
|
Nanoc3::NotificationCenter.on(:filtering_started) do |rep, filter_name|
|
146
138
|
@filter_times[filter_name] = Time.now
|
139
|
+
start_filter_progress(rep, filter_name)
|
147
140
|
end
|
148
141
|
Nanoc3::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
|
149
142
|
@filter_times[filter_name] = Time.now - @filter_times[filter_name]
|
143
|
+
stop_filter_progress(rep, filter_name)
|
150
144
|
end
|
151
145
|
end
|
152
146
|
|
153
|
-
def
|
154
|
-
|
147
|
+
def setup_diffs
|
148
|
+
@diff_lock = Mutex.new
|
149
|
+
@diff_threads = []
|
155
150
|
FileUtils.rm('output.diff') if File.file?('output.diff')
|
151
|
+
end
|
152
|
+
|
153
|
+
def teardown_diffs
|
154
|
+
@diff_threads.each { |t| t.join }
|
155
|
+
end
|
156
156
|
|
157
|
-
|
157
|
+
def generate_diff_for(rep, snapshot)
|
158
158
|
return if !@base.site.config[:enable_output_diff]
|
159
|
+
return if !File.file?(rep.raw_path(:snapshot => snapshot))
|
160
|
+
return if rep.binary?
|
161
|
+
|
162
|
+
# Get old and new content
|
163
|
+
old_content = File.read(rep.raw_path(:snapshot => snapshot))
|
164
|
+
new_content = rep.compiled_content(:snapshot => snapshot, :force => true)
|
159
165
|
|
160
|
-
#
|
161
|
-
|
162
|
-
reps.each do |rep|
|
163
|
-
diff = rep.diff
|
164
|
-
next if diff.nil?
|
166
|
+
# Check whether there’s a different
|
167
|
+
return if old_content == new_content
|
165
168
|
|
166
|
-
|
167
|
-
|
169
|
+
require 'thread'
|
170
|
+
@diff_threads << Thread.new do
|
171
|
+
# Generate diff
|
172
|
+
diff = diff_strings(old_content, new_content)
|
168
173
|
diff.sub!(/^--- .*/, '--- ' + rep.raw_path)
|
169
174
|
diff.sub!(/^\+\+\+ .*/, '+++ ' + rep.raw_path)
|
170
175
|
|
171
|
-
#
|
172
|
-
|
176
|
+
# Write diff
|
177
|
+
@diff_lock.synchronize do
|
178
|
+
File.open('output.diff', 'a') { |io| io.write(diff) }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# TODO move this elsewhere
|
184
|
+
def diff_strings(a, b)
|
185
|
+
require 'tempfile'
|
186
|
+
require 'open3'
|
187
|
+
|
188
|
+
# Create files
|
189
|
+
Tempfile.open('old') do |old_file|
|
190
|
+
Tempfile.open('new') do |new_file|
|
191
|
+
# Write files
|
192
|
+
old_file.write(a)
|
193
|
+
old_file.flush
|
194
|
+
new_file.write(b)
|
195
|
+
new_file.flush
|
196
|
+
|
197
|
+
# Diff
|
198
|
+
cmd = [ 'diff', '-u', old_file.path, new_file.path ]
|
199
|
+
Open3.popen3(*cmd) do |stdin, stdout, stderr|
|
200
|
+
result = stdout.read
|
201
|
+
return (result == '' ? nil : result)
|
202
|
+
end
|
203
|
+
end
|
173
204
|
end
|
205
|
+
rescue Errno::ENOENT
|
206
|
+
warn 'Failed to run `diff`, so no diff with the previously compiled ' \
|
207
|
+
'content will be available.'
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
|
211
|
+
def start_filter_progress(rep, filter_name)
|
212
|
+
# Only show progress on terminals
|
213
|
+
return if !$stdout.tty?
|
214
|
+
|
215
|
+
@progress_thread = Thread.new do
|
216
|
+
delay = 1.0
|
217
|
+
step = 0
|
218
|
+
|
219
|
+
text = "Running #{filter_name} filter… ".make_compatible_with_env
|
220
|
+
|
221
|
+
while !Thread.current[:stopped]
|
222
|
+
sleep 0.1
|
223
|
+
|
224
|
+
# Wait for a while before showing text
|
225
|
+
delay -= 0.1
|
226
|
+
next if delay > 0.05
|
227
|
+
|
228
|
+
# Print progress
|
229
|
+
$stdout.print text + %w( | / - \\ )[step] + "\r"
|
230
|
+
step = (step + 1) % 4
|
231
|
+
end
|
232
|
+
|
233
|
+
# Clear text
|
234
|
+
if delay < 0.05
|
235
|
+
$stdout.print ' ' * (text.length + 1 + 1) + "\r"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def stop_filter_progress(rep, filter_name)
|
241
|
+
# Only show progress on terminals
|
242
|
+
return if !$stdout.tty?
|
174
243
|
|
175
|
-
|
176
|
-
File.open('output.diff', 'w') { |io| io.write(full_diff) }
|
244
|
+
@progress_thread[:stopped] = true
|
177
245
|
end
|
178
246
|
|
179
247
|
def print_profiling_feedback(reps)
|
@@ -71,6 +71,10 @@ watcher:
|
|
71
71
|
# A list of single files to watch for changes. As mentioned above, don’t put
|
72
72
|
# any files from the “output/” or “tmp/” directories in here.
|
73
73
|
files_to_watch: [ 'config.yaml', 'Rules' ]
|
74
|
+
|
75
|
+
# When to send notifications (using Growl or notify-send).
|
76
|
+
notify_on_compilation_success: true
|
77
|
+
notify_on_compilation_failure: true
|
74
78
|
EOS
|
75
79
|
|
76
80
|
DEFAULT_RULES = <<EOS
|
@@ -100,7 +104,13 @@ route '/stylesheet/' do
|
|
100
104
|
end
|
101
105
|
|
102
106
|
route '*' do
|
103
|
-
item.
|
107
|
+
if item.binary?
|
108
|
+
# /foo/ -> /foo.ext
|
109
|
+
item.identifier.chop + '.' + item[:extension]
|
110
|
+
else
|
111
|
+
# /foo/ -> /foo/index.html
|
112
|
+
item.identifier + 'index.html'
|
113
|
+
end
|
104
114
|
end
|
105
115
|
|
106
116
|
layout '*', :erb
|
@@ -33,7 +33,6 @@ module Nanoc3::CLI::Commands
|
|
33
33
|
# Make sure we are in a nanoc site directory
|
34
34
|
print "Loading site data... "
|
35
35
|
@base.require_site
|
36
|
-
@base.site.load_data
|
37
36
|
puts "done"
|
38
37
|
puts
|
39
38
|
|
@@ -43,8 +42,9 @@ module Nanoc3::CLI::Commands
|
|
43
42
|
layouts = @base.site.layouts
|
44
43
|
|
45
44
|
# Get dependency tracker
|
46
|
-
|
47
|
-
|
45
|
+
compiler = @base.site.compiler
|
46
|
+
compiler.load
|
47
|
+
dependency_tracker = compiler.dependency_tracker
|
48
48
|
|
49
49
|
# Print item dependencies
|
50
50
|
puts '=== Item dependencies ======================================================='
|
@@ -82,9 +82,9 @@ module Nanoc3::CLI::Commands
|
|
82
82
|
items.sort_by { |i| i.identifier }.each do |item|
|
83
83
|
item.reps.sort_by { |r| r.name.to_s }.each do |rep|
|
84
84
|
puts "item #{item.identifier}, rep #{rep.name}:"
|
85
|
-
outdatedness_reason = rep
|
85
|
+
outdatedness_reason = compiler.outdatedness_reason_for(rep)
|
86
86
|
if outdatedness_reason
|
87
|
-
puts " is outdated: #{outdatedness_reason
|
87
|
+
puts " is outdated: #{outdatedness_reason.message}"
|
88
88
|
else
|
89
89
|
puts " is not outdated"
|
90
90
|
end
|
@@ -97,7 +97,12 @@ module Nanoc3::CLI::Commands
|
|
97
97
|
puts
|
98
98
|
layouts.each do |layout|
|
99
99
|
puts "layout #{layout.identifier}:"
|
100
|
-
|
100
|
+
outdatedness_reason = compiler.outdatedness_reason_for(layout)
|
101
|
+
if outdatedness_reason
|
102
|
+
puts " is outdated: #{outdatedness_reason.message}"
|
103
|
+
else
|
104
|
+
puts " is not outdated"
|
105
|
+
end
|
101
106
|
puts
|
102
107
|
end
|
103
108
|
end
|
@@ -39,8 +39,7 @@ module Nanoc3::CLI::Commands
|
|
39
39
|
|
40
40
|
# Get list of plugins (before and after)
|
41
41
|
plugins_before = Nanoc3::Plugin.all
|
42
|
-
@base.site
|
43
|
-
@base.site.load_data if @base.site
|
42
|
+
@base.site.code_snippets if @base.site
|
44
43
|
plugins_after = Nanoc3::Plugin.all
|
45
44
|
|
46
45
|
# Divide list of plugins into builtin and custom
|
@@ -32,6 +32,11 @@ module Nanoc3::CLI::Commands
|
|
32
32
|
def run(options, arguments)
|
33
33
|
require 'fssm'
|
34
34
|
|
35
|
+
Signal.trap("INT") do
|
36
|
+
puts
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
35
40
|
@notifier = Notifier.new
|
36
41
|
|
37
42
|
# Define rebuilder
|
@@ -53,17 +58,27 @@ module Nanoc3::CLI::Commands
|
|
53
58
|
# Recompile
|
54
59
|
start = Time.now
|
55
60
|
site = Nanoc3::Site.new('.')
|
56
|
-
site.load_data
|
57
61
|
begin
|
58
|
-
site.
|
62
|
+
site.compile
|
59
63
|
|
60
64
|
# TODO include icon (--image misc/success-icon.png)
|
61
|
-
|
65
|
+
notify_on_compilation_success = site.config.has_key?(:notify_on_compilation_success) ?
|
66
|
+
site.config[:notify_on_compilation_success] :
|
67
|
+
true
|
68
|
+
if notify_on_compilation_success
|
69
|
+
@notifier.notify('Compilation complete')
|
70
|
+
end
|
62
71
|
|
63
|
-
|
72
|
+
time_spent = ((Time.now - start)*1000.0).round
|
73
|
+
puts "done in #{format '%is %ims', *(time_spent.divmod(1000))}"
|
64
74
|
rescue Exception => e
|
65
75
|
# TODO include icon (--image misc/error-icon.png)
|
66
|
-
|
76
|
+
notify_on_compilation_failure = site.config.has_key?(:notify_on_compilation_failure) ?
|
77
|
+
site.config[:notify_on_compilation_failure] :
|
78
|
+
true
|
79
|
+
if notify_on_compilation_failure
|
80
|
+
@notifier.notify('Compilation failed')
|
81
|
+
end
|
67
82
|
|
68
83
|
puts
|
69
84
|
@base.print_error(e)
|
@@ -96,35 +111,15 @@ module Nanoc3::CLI::Commands
|
|
96
111
|
class Notifier
|
97
112
|
|
98
113
|
# A list of commandline tool names that can be used to send notifications
|
99
|
-
TOOLS = %w( growlnotify
|
100
|
-
|
101
|
-
# Error that is raised when no notifier can be found.
|
102
|
-
class NoNotifierFound < ::StandardError
|
103
|
-
|
104
|
-
def initialize
|
105
|
-
super("Could not find a notifier that works on this system. I tried to find #{CrossPlatformNotifier::TOOLS.join(', ')} but found nothing.")
|
106
|
-
end
|
114
|
+
TOOLS = %w( growlnotify notify-send )
|
107
115
|
|
108
|
-
|
109
|
-
|
110
|
-
# Send a notification.
|
116
|
+
# Send a notification. If no notifier is found, no notification will be
|
117
|
+
# created.
|
111
118
|
#
|
112
119
|
# @param [String] message The message to include in the notification
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
def notify(message, params={})
|
117
|
-
params[:raise] = true if !params.has_key?(:raise)
|
118
|
-
|
119
|
-
if tool.nil?
|
120
|
-
if params[:raise]
|
121
|
-
raise NoNotifierFound
|
122
|
-
else
|
123
|
-
return
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
send(tool, message, params)
|
120
|
+
def notify(message)
|
121
|
+
return if tool.nil?
|
122
|
+
send(tool.tr('-', '_'), message, params)
|
128
123
|
end
|
129
124
|
|
130
125
|
private
|
@@ -138,7 +133,7 @@ module Nanoc3::CLI::Commands
|
|
138
133
|
end
|
139
134
|
|
140
135
|
def notify_send(message, params={})
|
141
|
-
system('
|
136
|
+
system('notify-send', message)
|
142
137
|
end
|
143
138
|
|
144
139
|
end
|
data/lib/nanoc3/cli/logger.rb
CHANGED
@@ -28,13 +28,13 @@ module Nanoc3::CLI
|
|
28
28
|
|
29
29
|
def initialize
|
30
30
|
@level = :high
|
31
|
-
@color =
|
31
|
+
@color = $stdout.tty?
|
32
32
|
|
33
33
|
# Try enabling color support on Windows
|
34
34
|
begin
|
35
35
|
require 'Win32/Console/ANSI' if RUBY_PLATFORM =~/mswin|mingw/
|
36
36
|
rescue LoadError
|
37
|
-
|
37
|
+
@color = false
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -123,15 +123,10 @@ module Nanoc3::DataSources
|
|
123
123
|
raise RuntimeError, "meta_mtime and content_mtime are both nil"
|
124
124
|
end
|
125
125
|
|
126
|
-
# Get checksum
|
127
|
-
meta_checksum = meta_filename ? Nanoc3::Checksummer.checksum_for(meta_filename) : nil
|
128
|
-
content_checksum = content_filename ? Nanoc3::Checksummer.checksum_for(content_filename) : nil
|
129
|
-
checksum = [ meta_checksum, content_checksum ].compact.join('-')
|
130
|
-
|
131
126
|
# Create layout object
|
132
127
|
klass.new(
|
133
128
|
content_or_filename, attributes, identifier,
|
134
|
-
:binary => is_binary, :mtime => mtime
|
129
|
+
:binary => is_binary, :mtime => mtime
|
135
130
|
)
|
136
131
|
end
|
137
132
|
end
|
@@ -43,8 +43,8 @@ module Nanoc3::Extra
|
|
43
43
|
r.raw_path == site.config[:output_dir] + path
|
44
44
|
end
|
45
45
|
|
46
|
-
# Recompile
|
47
|
-
site.
|
46
|
+
# Recompile
|
47
|
+
site.compile if rep
|
48
48
|
|
49
49
|
# Get paths by appending index filenames
|
50
50
|
if path =~ /\/$/
|
@@ -82,7 +82,6 @@ module Nanoc3::Extra
|
|
82
82
|
|
83
83
|
def build_site
|
84
84
|
@site = Nanoc3::Site.new(@site_path)
|
85
|
-
@site.load_data
|
86
85
|
end
|
87
86
|
|
88
87
|
def mime_type_of(path, fallback)
|
@@ -106,6 +106,7 @@ module Nanoc3::Extra::Deployers
|
|
106
106
|
# Runs the given shell command. This is a simple wrapper around Kernel#system.
|
107
107
|
def run_shell_cmd(args)
|
108
108
|
system(*args)
|
109
|
+
raise "command exited with a nonzero status code #{$?.exitstatus} (command: #{args.join(' ')})" if !$?.success?
|
109
110
|
end
|
110
111
|
|
111
112
|
end
|
@@ -48,6 +48,36 @@ module Nanoc3::Extra::Validators
|
|
48
48
|
|
49
49
|
private
|
50
50
|
|
51
|
+
# Enumerates all key-value pairs of a given hash in a thread-safe way.
|
52
|
+
#
|
53
|
+
# This class is a helper class, which means that it is not used directly
|
54
|
+
# by nanoc. Future versions of nanoc may no longer contain this class. Do
|
55
|
+
# not depend on this class to be available.
|
56
|
+
class ThreadsafeHashEnumerator
|
57
|
+
|
58
|
+
# Creates a new enumerator for the given hash.
|
59
|
+
#
|
60
|
+
# @param [Hash] hash The hash for which the enumerator should return
|
61
|
+
# key-value pairs
|
62
|
+
def initialize(hash)
|
63
|
+
@hash = hash
|
64
|
+
@unprocessed_keys = @hash.keys.dup
|
65
|
+
@mutex = Mutex.new
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns the next key-value pair in the hash.
|
69
|
+
#
|
70
|
+
# @return [Array] An array containing the key and the corresponding
|
71
|
+
# value of teh next key-value pair
|
72
|
+
def next_pair
|
73
|
+
@mutex.synchronize do
|
74
|
+
key = @unprocessed_keys.shift
|
75
|
+
return (key ? [ key, @hash[key] ] : nil)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
51
81
|
def all_broken_hrefs
|
52
82
|
broken_hrefs = {}
|
53
83
|
|
@@ -127,17 +157,23 @@ module Nanoc3::Extra::Validators
|
|
127
157
|
require 'uri'
|
128
158
|
|
129
159
|
# Parse
|
130
|
-
uri =
|
160
|
+
uri = nil
|
161
|
+
begin
|
162
|
+
uri = URI.parse(href)
|
163
|
+
rescue URI::InvalidURIError
|
164
|
+
@delegate && @delegate.send(:external_href_validated, href, false)
|
165
|
+
return false
|
166
|
+
end
|
131
167
|
|
132
168
|
# Skip non-HTTP URLs
|
133
169
|
return true if uri.scheme != 'http'
|
134
170
|
|
135
171
|
# Get status
|
136
172
|
status = fetch_http_status_for(uri)
|
137
|
-
is_valid = (status && status >= 200 && status <= 299)
|
173
|
+
is_valid = !!(status && status >= 200 && status <= 299)
|
138
174
|
|
139
175
|
# Notify
|
140
|
-
@delegate.send(:external_href_validated, href, is_valid)
|
176
|
+
@delegate && @delegate.send(:external_href_validated, href, is_valid)
|
141
177
|
|
142
178
|
# Done
|
143
179
|
is_valid
|
@@ -153,30 +189,10 @@ module Nanoc3::Extra::Validators
|
|
153
189
|
end
|
154
190
|
end
|
155
191
|
|
156
|
-
# This class is a helper class, which means that it is not used directly
|
157
|
-
# by nanoc. Future versions of nanoc may no longer contain this class. Do
|
158
|
-
# not depend on this class to be available.
|
159
|
-
class EachPairEnumerator
|
160
|
-
|
161
|
-
def initialize(hash)
|
162
|
-
@hash = hash
|
163
|
-
@unprocessed_keys = @hash.keys.dup
|
164
|
-
@mutex = Mutex.new
|
165
|
-
end
|
166
|
-
|
167
|
-
def next_pair
|
168
|
-
@mutex.synchronize do
|
169
|
-
key = @unprocessed_keys.shift
|
170
|
-
return (key ? [ key, @hash[key] ] : nil)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
end
|
175
|
-
|
176
192
|
def validate_external_hrefs(hrefs, broken_hrefs)
|
177
193
|
@mutex = Mutex.new
|
178
194
|
|
179
|
-
enum =
|
195
|
+
enum = ThreadsafeHashEnumerator.new(hrefs)
|
180
196
|
|
181
197
|
threads = []
|
182
198
|
10.times do
|
@@ -202,7 +218,10 @@ module Nanoc3::Extra::Validators
|
|
202
218
|
def fetch_http_status_for(url, params={})
|
203
219
|
5.times do |i|
|
204
220
|
begin
|
205
|
-
res =
|
221
|
+
res = nil
|
222
|
+
Timeout::timeout(10) do
|
223
|
+
res = request_url_once(url)
|
224
|
+
end
|
206
225
|
|
207
226
|
if res.code =~ /^3..$/
|
208
227
|
url = URI.parse(res['location'])
|
@@ -211,7 +230,7 @@ module Nanoc3::Extra::Validators
|
|
211
230
|
return res.code.to_i
|
212
231
|
end
|
213
232
|
rescue
|
214
|
-
nil
|
233
|
+
return nil
|
215
234
|
end
|
216
235
|
end
|
217
236
|
end
|