nanoc3 3.1.0a2 → 3.1.0a3
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/NEWS.md +12 -2
- data/README.md +2 -0
- data/lib/nanoc3/base/code_snippet.rb +6 -2
- data/lib/nanoc3/base/compiler.rb +9 -6
- data/lib/nanoc3/base/compiler_dsl.rb +15 -9
- data/lib/nanoc3/base/data_source.rb +14 -14
- data/lib/nanoc3/base/dependency_tracker.rb +9 -9
- data/lib/nanoc3/base/directed_graph.rb +6 -7
- data/lib/nanoc3/base/errors.rb +48 -13
- data/lib/nanoc3/base/filter.rb +62 -16
- data/lib/nanoc3/base/item.rb +63 -20
- data/lib/nanoc3/base/item_rep.rb +117 -48
- data/lib/nanoc3/base/layout.rb +18 -5
- data/lib/nanoc3/base/notification_center.rb +8 -8
- data/lib/nanoc3/base/plugin_registry.rb +9 -9
- data/lib/nanoc3/base/rule.rb +8 -8
- data/lib/nanoc3/base/rule_context.rb +5 -5
- data/lib/nanoc3/base/site.rb +33 -29
- data/lib/nanoc3/cli/base.rb +1 -1
- data/lib/nanoc3/cli/commands/create_site.rb +12 -14
- data/lib/nanoc3/cli.rb +0 -1
- data/lib/nanoc3/data_sources/filesystem.rb +12 -2
- data/lib/nanoc3/data_sources/filesystem_unified.rb +22 -19
- data/lib/nanoc3/extra/auto_compiler.rb +12 -3
- data/lib/nanoc3/extra/chick.rb +12 -6
- data/lib/nanoc3/extra/deployers/rsync.rb +30 -27
- data/lib/nanoc3/extra/deployers.rb +0 -1
- data/lib/nanoc3/extra/file_proxy.rb +2 -15
- data/lib/nanoc3/extra/validators/links.rb +242 -0
- data/lib/nanoc3/extra/validators/w3c.rb +49 -25
- data/lib/nanoc3/extra/validators.rb +2 -2
- data/lib/nanoc3/extra/vcs.rb +1 -1
- data/lib/nanoc3/extra.rb +4 -1
- data/lib/nanoc3/helpers/blogging.rb +57 -29
- data/lib/nanoc3/helpers/breadcrumbs.rb +1 -1
- data/lib/nanoc3/helpers/capturing.rb +4 -2
- data/lib/nanoc3/helpers/filtering.rb +2 -1
- data/lib/nanoc3/helpers/link_to.rb +13 -6
- data/lib/nanoc3/helpers/rendering.rb +4 -3
- data/lib/nanoc3/helpers/tagging.rb +7 -6
- data/lib/nanoc3/helpers/text.rb +2 -2
- data/lib/nanoc3/tasks/validate.rake +62 -5
- data/lib/nanoc3.rb +2 -2
- metadata +23 -11
- data/lib/nanoc3/base/core_ext/enumerable.rb +0 -41
@@ -0,0 +1,242 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Nanoc3::Extra::Validators
|
4
|
+
|
5
|
+
# A validator that verifies that all links (`<a href="…">…</a>`) point to a
|
6
|
+
# location that exists.
|
7
|
+
class Links
|
8
|
+
|
9
|
+
# @param [String] dir The directory that will be searched for HTML files
|
10
|
+
# to validate
|
11
|
+
#
|
12
|
+
# @param [Array<String>] index_filenames An array of index filenames that
|
13
|
+
# will be appended to URLs by web servers if a directory is requested
|
14
|
+
# instead of a file
|
15
|
+
#
|
16
|
+
# @option params [Boolean] :internal (false) True if internal links should
|
17
|
+
# be checked; false if they should not
|
18
|
+
#
|
19
|
+
# @option params [Boolean] :external (false) True if external links should
|
20
|
+
# be checked; false if they should not
|
21
|
+
def initialize(dir, index_filenames, params={})
|
22
|
+
@dir = dir
|
23
|
+
@index_filenames = index_filenames
|
24
|
+
@include_internal = params.has_key?(:internal) && params[:internal]
|
25
|
+
@include_external = params.has_key?(:external) && params[:external]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Starts the validator. The results will be printed to stdout.
|
29
|
+
#
|
30
|
+
# @return [void]
|
31
|
+
def run
|
32
|
+
require 'nokogiri'
|
33
|
+
|
34
|
+
@delegate = self
|
35
|
+
links = all_broken_hrefs
|
36
|
+
if links.empty?
|
37
|
+
puts "No broken links found!"
|
38
|
+
else
|
39
|
+
links.each_pair do |href, origins|
|
40
|
+
puts "Broken link: #{href} -- referenced from:"
|
41
|
+
origins.each do |origin|
|
42
|
+
puts " #{origin}"
|
43
|
+
end
|
44
|
+
puts
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def all_broken_hrefs
|
52
|
+
broken_hrefs = {}
|
53
|
+
|
54
|
+
internal_hrefs = {}
|
55
|
+
external_hrefs = {}
|
56
|
+
|
57
|
+
# Split into internal and external hrefs
|
58
|
+
all_hrefs_per_filename.each_pair do |filename, hrefs|
|
59
|
+
hrefs.each do |href|
|
60
|
+
if is_external_href?(href)
|
61
|
+
external_hrefs[href] ||= []
|
62
|
+
external_hrefs[href] << filename
|
63
|
+
else
|
64
|
+
internal_hrefs[href] ||= []
|
65
|
+
internal_hrefs[href] << filename
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Validate hrefs
|
71
|
+
validate_internal_hrefs(internal_hrefs, broken_hrefs) if @include_internal
|
72
|
+
validate_external_hrefs(external_hrefs, broken_hrefs) if @include_external
|
73
|
+
|
74
|
+
# Done
|
75
|
+
broken_hrefs
|
76
|
+
end
|
77
|
+
|
78
|
+
def all_files
|
79
|
+
Dir[@dir + '/**/*.html']
|
80
|
+
end
|
81
|
+
|
82
|
+
def all_hrefs_per_filename
|
83
|
+
hrefs = {}
|
84
|
+
all_files.each do |filename|
|
85
|
+
hrefs[filename] ||= all_hrefs_in_file(filename)
|
86
|
+
end
|
87
|
+
hrefs
|
88
|
+
end
|
89
|
+
|
90
|
+
def all_hrefs_in_file(filename)
|
91
|
+
doc = Nokogiri::HTML(::File.read(filename))
|
92
|
+
doc.css('a').map { |l| l[:href] }.compact
|
93
|
+
end
|
94
|
+
|
95
|
+
def is_external_href?(href)
|
96
|
+
!!(href =~ %r{^[a-z\-]+:})
|
97
|
+
end
|
98
|
+
|
99
|
+
def is_valid_internal_href?(href, origin)
|
100
|
+
# Skip hrefs that point to self
|
101
|
+
# FIXME this is ugly and won’t always be correct
|
102
|
+
return true if href == '.'
|
103
|
+
|
104
|
+
# Remove target
|
105
|
+
path = href.sub(/#.*$/, '')
|
106
|
+
return true if path.empty?
|
107
|
+
|
108
|
+
# Make absolute
|
109
|
+
if path[0, 1] == '/'
|
110
|
+
path = @dir + path
|
111
|
+
else
|
112
|
+
path = ::File.expand_path(path, ::File.dirname(origin))
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check whether file exists
|
116
|
+
return true if File.file?(path)
|
117
|
+
|
118
|
+
# Check whether directory with index file exists
|
119
|
+
return true if File.directory?(path) && @index_filenames.any? { |fn| File.file?(File.join(path, fn)) }
|
120
|
+
|
121
|
+
# Nope :(
|
122
|
+
return false
|
123
|
+
end
|
124
|
+
|
125
|
+
def is_valid_external_href?(href)
|
126
|
+
require 'net/http'
|
127
|
+
require 'uri'
|
128
|
+
|
129
|
+
# Parse
|
130
|
+
uri = URI.parse(href)
|
131
|
+
|
132
|
+
# Skip non-HTTP URLs
|
133
|
+
return true if uri.scheme != 'http'
|
134
|
+
|
135
|
+
# Get status
|
136
|
+
status = fetch_http_status_for(uri)
|
137
|
+
is_valid = (status && status >= 200 && status <= 299)
|
138
|
+
|
139
|
+
# Notify
|
140
|
+
@delegate.send(:external_href_validated, href, is_valid)
|
141
|
+
|
142
|
+
# Done
|
143
|
+
is_valid
|
144
|
+
end
|
145
|
+
|
146
|
+
def validate_internal_hrefs(hrefs, broken_hrefs)
|
147
|
+
hrefs.each_pair do |href, filenames|
|
148
|
+
filenames.each do |filename|
|
149
|
+
if !is_valid_internal_href?(href, filename)
|
150
|
+
broken_hrefs[href] = filenames
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class EachPairEnumerator
|
157
|
+
|
158
|
+
def initialize(hash)
|
159
|
+
@hash = hash
|
160
|
+
@unprocessed_keys = @hash.keys.dup
|
161
|
+
@mutex = Mutex.new
|
162
|
+
end
|
163
|
+
|
164
|
+
def next_pair
|
165
|
+
@mutex.synchronize do
|
166
|
+
key = @unprocessed_keys.shift
|
167
|
+
return (key ? [ key, @hash[key] ] : nil)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
def validate_external_hrefs(hrefs, broken_hrefs)
|
174
|
+
@mutex = Mutex.new
|
175
|
+
|
176
|
+
enum = EachPairEnumerator.new(hrefs)
|
177
|
+
|
178
|
+
threads = []
|
179
|
+
10.times do
|
180
|
+
threads << Thread.new do
|
181
|
+
loop do
|
182
|
+
# Get next pair
|
183
|
+
pair = enum.next_pair
|
184
|
+
break if pair.nil?
|
185
|
+
href, filenames = pair[0], pair[1]
|
186
|
+
|
187
|
+
# Validate
|
188
|
+
if !is_valid_external_href?(href)
|
189
|
+
@mutex.synchronize do
|
190
|
+
broken_hrefs[href] = filenames
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
threads.each { |t| t.join }
|
197
|
+
end
|
198
|
+
|
199
|
+
def fetch_http_status_for(url, params={})
|
200
|
+
5.times do |i|
|
201
|
+
begin
|
202
|
+
res = request_url_once(url)
|
203
|
+
|
204
|
+
if res.code =~ /^3..$/
|
205
|
+
url = URI.parse(res['location'])
|
206
|
+
return nil if i == 5
|
207
|
+
else
|
208
|
+
return res.code.to_i
|
209
|
+
end
|
210
|
+
rescue
|
211
|
+
nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def request_url_once(url)
|
217
|
+
path = (url.path.nil? || url.path.empty? ? '/' : url.path)
|
218
|
+
req = Net::HTTP::Head.new(path)
|
219
|
+
res = Net::HTTP.start(url.host, url.port) { |h| h.request(req) }
|
220
|
+
res
|
221
|
+
end
|
222
|
+
|
223
|
+
def external_href_validated(href, is_valid)
|
224
|
+
texts = {
|
225
|
+
true => 'ok',
|
226
|
+
false => ' ERROR '
|
227
|
+
}
|
228
|
+
|
229
|
+
colors = {
|
230
|
+
true => "\e[32m",
|
231
|
+
false => "\e[41m\e[37m",
|
232
|
+
:off => "\033[0m"
|
233
|
+
}
|
234
|
+
|
235
|
+
@mutex.synchronize do
|
236
|
+
puts href + ': ' + colors[is_valid] + texts[is_valid] + colors[:off]
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
@@ -2,55 +2,79 @@
|
|
2
2
|
|
3
3
|
module Nanoc3::Extra::Validators
|
4
4
|
|
5
|
-
#
|
6
|
-
# service to validate HTML and CSS files.
|
5
|
+
# A validator that uses the W3C web service to validate HTML and CSS files.
|
7
6
|
class W3C
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# @param [String] dir The directory that will be searched for HTML and/or
|
9
|
+
# CSS files to validate
|
10
|
+
#
|
11
|
+
# @param [Array<Symbol>] types A list of types to check. Allowed types are
|
12
|
+
# `:html` and `:css`.
|
13
|
+
def initialize(dir, types)
|
14
|
+
@dir = dir
|
15
|
+
@types = types
|
12
16
|
end
|
13
17
|
|
18
|
+
# Starts the validator. The results will be printed to stdout.
|
19
|
+
#
|
20
|
+
# @return [void]
|
14
21
|
def run
|
15
22
|
# Load validator
|
16
23
|
require 'w3c_validators'
|
17
24
|
|
18
|
-
# Make sure config is loaded
|
19
|
-
@site.load_data
|
20
|
-
|
21
25
|
# Find all files
|
22
|
-
|
26
|
+
filenames = []
|
27
|
+
extensions = types_to_extensions(@types)
|
28
|
+
extensions.each { |extension| filenames.concat(Dir[@dir + '/**/*.' + extension]) }
|
23
29
|
|
24
30
|
# Validate each file
|
25
|
-
|
26
|
-
validation_started(
|
27
|
-
|
28
|
-
|
31
|
+
filenames.each do |filename|
|
32
|
+
validation_started(filename)
|
33
|
+
|
34
|
+
extension = File.extname(filename)[1..-1]
|
35
|
+
results = validator_for(extension).validate_file(filename)
|
36
|
+
|
37
|
+
validation_ended(filename, results.errors)
|
29
38
|
end
|
30
39
|
end
|
31
40
|
|
32
41
|
private
|
33
42
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
# Returns all extensions for the given types
|
44
|
+
def types_to_extensions(types)
|
45
|
+
extensions = []
|
46
|
+
types.each { |type| extensions.concat(type_to_extensions(type)) }
|
47
|
+
extensions
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns all extensions for the given type
|
51
|
+
def type_to_extensions(type)
|
52
|
+
case type
|
53
|
+
when :html
|
54
|
+
[ 'html', 'htm' ]
|
55
|
+
when :css
|
56
|
+
[ 'css' ]
|
57
|
+
else
|
58
|
+
raise RuntimeError, "unknown type: #{type}"
|
40
59
|
end
|
41
60
|
end
|
42
61
|
|
43
|
-
|
44
|
-
|
45
|
-
|
62
|
+
# Returns the validator class for the given extension
|
63
|
+
def validator_class_for(extension)
|
64
|
+
case extension
|
65
|
+
when 'html', 'htm'
|
46
66
|
::W3CValidators::MarkupValidator
|
47
|
-
when
|
67
|
+
when 'css'
|
48
68
|
::W3CValidators::CSSValidator
|
69
|
+
else
|
70
|
+
raise RuntimeError, "unknown extension: #{extension}"
|
49
71
|
end
|
50
72
|
end
|
51
73
|
|
52
|
-
|
53
|
-
|
74
|
+
# Returns the validator for the given extension
|
75
|
+
def validator_for(extension)
|
76
|
+
@validators ||= {}
|
77
|
+
@validators[extension] ||= validator_class_for(extension).new
|
54
78
|
end
|
55
79
|
|
56
80
|
def validation_started(file)
|
@@ -2,10 +2,10 @@
|
|
2
2
|
|
3
3
|
module Nanoc3::Extra
|
4
4
|
|
5
|
-
# Nanoc3::Extra::Validators is the name for all validators.
|
6
5
|
module Validators
|
7
6
|
|
8
|
-
autoload 'W3C',
|
7
|
+
autoload 'W3C', 'nanoc3/extra/validators/w3c'
|
8
|
+
autoload 'Links', 'nanoc3/extra/validators/links'
|
9
9
|
|
10
10
|
end
|
11
11
|
|
data/lib/nanoc3/extra/vcs.rb
CHANGED
data/lib/nanoc3/extra.rb
CHANGED
@@ -5,13 +5,16 @@ module Nanoc3::Extra
|
|
5
5
|
autoload 'AutoCompiler', 'nanoc3/extra/auto_compiler'
|
6
6
|
autoload 'CHiCk', 'nanoc3/extra/chick'
|
7
7
|
autoload 'Deployers', 'nanoc3/extra/deployers'
|
8
|
-
autoload 'FileProxy', 'nanoc3/extra/file_proxy'
|
9
8
|
autoload 'Validators', 'nanoc3/extra/validators'
|
10
9
|
|
11
10
|
# Deprecated; use {Nanoc3::Context} instead
|
12
11
|
# TODO [in nanoc 4.0] remove me
|
13
12
|
Context = ::Nanoc3::Context
|
14
13
|
|
14
|
+
# Deprecated
|
15
|
+
# TODO [in nanoc 4.0] remove me
|
16
|
+
autoload 'FileProxy', 'nanoc3/extra/file_proxy'
|
17
|
+
|
15
18
|
end
|
16
19
|
|
17
20
|
require 'nanoc3/extra/core_ext'
|
@@ -10,13 +10,16 @@ module Nanoc3::Helpers
|
|
10
10
|
#
|
11
11
|
# * `kind` — Set to `"article"`
|
12
12
|
#
|
13
|
-
# * `created_at` — The article’s publication timestamp
|
14
|
-
# be in any format parseable by `Time.parse`.
|
13
|
+
# * `created_at` — The article’s publication timestamp
|
15
14
|
#
|
16
15
|
# Some functions in this blogging helper, such as the {#atom_feed} function,
|
17
16
|
# require additional attributes to be set; these attributes are described in
|
18
17
|
# the documentation for these functions.
|
19
18
|
#
|
19
|
+
# All “time” item attributes, site configuration attributes or method
|
20
|
+
# parameters can either be a `Time` instance or a string in any format
|
21
|
+
# parseable by `Time.parse`.
|
22
|
+
#
|
20
23
|
# The two main functions are {#sorted_articles} and {#atom_feed}.
|
21
24
|
module Blogging
|
22
25
|
|
@@ -35,7 +38,10 @@ module Nanoc3::Helpers
|
|
35
38
|
# @return [Array] A sorted array containing all articles
|
36
39
|
def sorted_articles
|
37
40
|
require 'time'
|
38
|
-
articles.sort_by
|
41
|
+
articles.sort_by do |a|
|
42
|
+
time = a[:created_at]
|
43
|
+
time.is_a?(String) ? Time.parse(time) : time
|
44
|
+
end.reverse
|
39
45
|
end
|
40
46
|
|
41
47
|
# Returns a string representing the atom feed containing recent articles,
|
@@ -58,10 +64,9 @@ module Nanoc3::Helpers
|
|
58
64
|
# non-outputted items in a feed; such items could have their custom feed
|
59
65
|
# path set to the blog path instead, for example.
|
60
66
|
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
# instance).
|
67
|
+
# * `updated_at` — The time when the article was last modified. If this
|
68
|
+
# attribute is not present, the `created_at` attribute will be used as
|
69
|
+
# the time when the article was last modified.
|
65
70
|
#
|
66
71
|
# The site configuration will need to have the following attributes:
|
67
72
|
#
|
@@ -69,7 +74,10 @@ module Nanoc3::Helpers
|
|
69
74
|
# example, if the site is at “http://example.com/”, the `base_url`
|
70
75
|
# would be “http://example.com”.
|
71
76
|
#
|
72
|
-
# The feed item will need to
|
77
|
+
# The feed item will need to know about the feed title, the feed author
|
78
|
+
# name, and the URI corresponding to the author. These can be specified
|
79
|
+
# using parameters, as attributes in the feed item, or in the site
|
80
|
+
# configuration.
|
73
81
|
#
|
74
82
|
# * `title` — The title of the feed, which is usually also the title of
|
75
83
|
# the blog.
|
@@ -107,19 +115,28 @@ module Nanoc3::Helpers
|
|
107
115
|
# <%= atom_feed :limit => 5 %>
|
108
116
|
#
|
109
117
|
# @option params [Number] :limit (5) The maximum number of articles to
|
110
|
-
#
|
118
|
+
# show
|
111
119
|
#
|
112
120
|
# @option params [Array] :articles (sorted_articles) A list of articles to
|
113
|
-
#
|
121
|
+
# include in the feed
|
114
122
|
#
|
115
123
|
# @option params [Proc] :content_proc (->{ |article|
|
116
|
-
#
|
117
|
-
#
|
118
|
-
#
|
124
|
+
# article.compiled_content(:snapshot => :pre) }) A proc that returns the
|
125
|
+
# content of the given article, which is passed as a parameter. This
|
126
|
+
# function may not return nil.
|
119
127
|
#
|
120
128
|
# @option params [proc] :excerpt_proc (->{ |article| article[:excerpt] })
|
121
|
-
#
|
122
|
-
#
|
129
|
+
# A proc that returns the excerpt of the given article, passed as a
|
130
|
+
# parameter. This function should return nil if there is no excerpt.
|
131
|
+
#
|
132
|
+
# @option params [String] :title The feed’s title, if it is not given in
|
133
|
+
# the item attributes.
|
134
|
+
#
|
135
|
+
# @option params [String] :author_name The name of the feed’s author, if
|
136
|
+
# it is not given in the item attributes.
|
137
|
+
#
|
138
|
+
# @option params [String] :author_uri The URI of the feed’s author, if it
|
139
|
+
# is not given in the item attributes.
|
123
140
|
#
|
124
141
|
# @return [String] The generated feed content
|
125
142
|
def atom_feed(params={})
|
@@ -138,14 +155,17 @@ module Nanoc3::Helpers
|
|
138
155
|
end
|
139
156
|
|
140
157
|
# Check feed item attributes
|
141
|
-
|
142
|
-
|
158
|
+
title = params[:title] || @item[:title] || @site.config[:title]
|
159
|
+
if title.nil?
|
160
|
+
raise RuntimeError.new('Cannot build Atom feed: no title in params, item or site config')
|
143
161
|
end
|
144
|
-
|
145
|
-
|
162
|
+
author_name = params[:author_name] || @item[:author_name] || @site.config[:author_name]
|
163
|
+
if author_name.nil?
|
164
|
+
raise RuntimeError.new('Cannot build Atom feed: no author_name in params, item or site config')
|
146
165
|
end
|
147
|
-
|
148
|
-
|
166
|
+
author_uri = params[:author_uri] || @item[:author_uri] || @site.config[:author_uri]
|
167
|
+
if author_uri.nil?
|
168
|
+
raise RuntimeError.new('Cannot build Atom feed: no author_uri in params, item or site config')
|
149
169
|
end
|
150
170
|
|
151
171
|
# Check article attributes
|
@@ -157,7 +177,10 @@ module Nanoc3::Helpers
|
|
157
177
|
end
|
158
178
|
|
159
179
|
# Get sorted relevant articles
|
160
|
-
sorted_relevant_articles = relevant_articles.sort_by
|
180
|
+
sorted_relevant_articles = relevant_articles.sort_by do |a|
|
181
|
+
time = a[:created_at]
|
182
|
+
time.is_a?(String) ? Time.parse(time) : time
|
183
|
+
end.reverse.first(limit)
|
161
184
|
|
162
185
|
# Get most recent article
|
163
186
|
last_article = sorted_relevant_articles.first
|
@@ -173,10 +196,11 @@ module Nanoc3::Helpers
|
|
173
196
|
|
174
197
|
# Add primary attributes
|
175
198
|
xml.id root_url
|
176
|
-
xml.title
|
199
|
+
xml.title title
|
177
200
|
|
178
201
|
# Add date
|
179
|
-
|
202
|
+
time = last_article[:created_at]
|
203
|
+
xml.updated((time.is_a?(String) ? Time.parse(time) : time).to_iso8601_time)
|
180
204
|
|
181
205
|
# Add links
|
182
206
|
xml.link(:rel => 'alternate', :href => root_url)
|
@@ -184,8 +208,8 @@ module Nanoc3::Helpers
|
|
184
208
|
|
185
209
|
# Add author information
|
186
210
|
xml.author do
|
187
|
-
xml.name
|
188
|
-
xml.uri
|
211
|
+
xml.name author_name
|
212
|
+
xml.uri author_uri
|
189
213
|
end
|
190
214
|
|
191
215
|
# Add articles
|
@@ -200,8 +224,10 @@ module Nanoc3::Helpers
|
|
200
224
|
xml.title a[:title], :type => 'html'
|
201
225
|
|
202
226
|
# Add dates
|
203
|
-
|
204
|
-
|
227
|
+
create_time = a[:created_at]
|
228
|
+
update_time = a[:updated_at] || a[:created_at]
|
229
|
+
xml.published ((create_time.is_a?(String) ? Time.parse(create_time) : create_time).to_iso8601_time)
|
230
|
+
xml.updated ((update_time.is_a?(String) ? Time.parse(update_time) : update_time).to_iso8601_time)
|
205
231
|
|
206
232
|
# Add link
|
207
233
|
xml.link(:rel => 'alternate', :href => url)
|
@@ -263,7 +289,9 @@ module Nanoc3::Helpers
|
|
263
289
|
require 'time'
|
264
290
|
|
265
291
|
hostname, base_dir = %r{^.+?://([^/]+)(.*)$}.match(@site.config[:base_url])[1..2]
|
266
|
-
|
292
|
+
|
293
|
+
time = item[:created_at]
|
294
|
+
formatted_date = (time.is_a?(String) ? Time.parse(time) : time).to_iso8601_date
|
267
295
|
|
268
296
|
'tag:' + hostname + ',' + formatted_date + ':' + base_dir + (item.path || item.identifier)
|
269
297
|
end
|
@@ -13,7 +13,7 @@ module Nanoc3::Helpers
|
|
13
13
|
# will contain a `nil` element.
|
14
14
|
#
|
15
15
|
# @return [Array] The breadcrumbs, starting with the root item and ending
|
16
|
-
#
|
16
|
+
# with the item itself
|
17
17
|
def breadcrumbs_trail
|
18
18
|
trail = []
|
19
19
|
current_identifier = @item.identifier
|
@@ -13,11 +13,13 @@ module Nanoc3::Helpers
|
|
13
13
|
# the sidebar layout.
|
14
14
|
#
|
15
15
|
# @example Capturing content into a `content_for_summary` attribute
|
16
|
+
#
|
16
17
|
# <% content_for :summary do %>
|
17
18
|
# <p>On this item, nanoc is introduced, blah blah.</p>
|
18
19
|
# <% end %>
|
19
20
|
#
|
20
21
|
# @example Showing captured content in a sidebar
|
22
|
+
#
|
21
23
|
# <div id="sidebar">
|
22
24
|
# <h3>Summary</h3>
|
23
25
|
# <%= @item[:content_for_summary] || '(no summary)' %>
|
@@ -28,8 +30,8 @@ module Nanoc3::Helpers
|
|
28
30
|
# attribute named `content_for_` followed by the given name. The
|
29
31
|
# content of the block itself will not be outputted.
|
30
32
|
#
|
31
|
-
# @param [Symbol, String] The base name of the attribute into which
|
32
|
-
#
|
33
|
+
# @param [Symbol, String] The base name of the attribute into which the
|
34
|
+
# content should be stored
|
33
35
|
#
|
34
36
|
# @return [void]
|
35
37
|
def content_for(name, &block)
|
@@ -16,13 +16,14 @@ module Nanoc3::Helpers
|
|
16
16
|
# work correctly.
|
17
17
|
#
|
18
18
|
# @example Running a filter on a part of an item or layout
|
19
|
+
#
|
19
20
|
# <p>Lorem ipsum dolor sit amet...</p>
|
20
21
|
# <% filter :rubypants do %>
|
21
22
|
# <p>Consectetur adipisicing elit...</p>
|
22
23
|
# <% end %>
|
23
24
|
#
|
24
25
|
# @param [Symbol] filter_name The name of the filter to run on the
|
25
|
-
#
|
26
|
+
# contents of the block
|
26
27
|
#
|
27
28
|
# @param [Hash] argument Arguments to pass to the filter
|
28
29
|
#
|