nanoc3 3.1.0a2 → 3.1.0a3
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/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
|
#
|