nanoc3 3.0.9 → 3.1.0a1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/NEWS.md +360 -0
- data/README.md +85 -0
- data/Rakefile +2 -2
- data/bin/nanoc3 +0 -4
- data/lib/nanoc3/base/code_snippet.rb +14 -6
- data/lib/nanoc3/base/compiler.rb +68 -49
- data/lib/nanoc3/base/compiler_dsl.rb +70 -29
- data/lib/nanoc3/base/context.rb +47 -0
- data/lib/nanoc3/base/core_ext/array.rb +4 -0
- data/lib/nanoc3/base/core_ext/hash.rb +5 -1
- data/lib/nanoc3/base/core_ext/string.rb +2 -0
- data/lib/nanoc3/base/data_source.rb +132 -96
- data/lib/nanoc3/base/dependency_tracker.rb +160 -185
- data/lib/nanoc3/base/directed_graph.rb +252 -0
- data/lib/nanoc3/base/errors.rb +52 -4
- data/lib/nanoc3/base/filter.rb +43 -28
- data/lib/nanoc3/base/item.rb +93 -25
- data/lib/nanoc3/base/item_rep.rb +166 -55
- data/lib/nanoc3/base/layout.rb +16 -13
- data/lib/nanoc3/base/notification_center.rb +28 -12
- data/lib/nanoc3/base/plugin_registry.rb +158 -0
- data/lib/nanoc3/base/rule.rb +27 -8
- data/lib/nanoc3/base/rule_context.rb +59 -46
- data/lib/nanoc3/base/site.rb +124 -77
- data/lib/nanoc3/base.rb +7 -2
- data/lib/nanoc3/cli/base.rb +4 -1
- data/lib/nanoc3/cli/commands/autocompile.rb +5 -4
- data/lib/nanoc3/cli/commands/compile.rb +28 -7
- data/lib/nanoc3/cli/commands/create_item.rb +1 -1
- data/lib/nanoc3/cli/commands/create_layout.rb +1 -1
- data/lib/nanoc3/cli/commands/create_site.rb +46 -22
- data/lib/nanoc3/cli/commands/debug.rb +100 -0
- data/lib/nanoc3/cli/commands/help.rb +1 -1
- data/lib/nanoc3/cli/commands/info.rb +1 -1
- data/lib/nanoc3/cli/commands/view.rb +85 -0
- data/lib/nanoc3/cli/commands.rb +2 -0
- data/lib/nanoc3/cli/logger.rb +7 -0
- data/lib/nanoc3/cli.rb +0 -3
- data/lib/nanoc3/data_sources/{delicious.rb → deprecated/delicious.rb} +1 -24
- data/lib/nanoc3/data_sources/{last_fm.rb → deprecated/last_fm.rb} +1 -27
- data/lib/nanoc3/data_sources/{twitter.rb → deprecated/twitter.rb} +1 -14
- data/lib/nanoc3/data_sources/filesystem.rb +188 -176
- data/lib/nanoc3/data_sources/filesystem_unified.rb +107 -0
- data/lib/nanoc3/data_sources/filesystem_verbose.rb +80 -0
- data/lib/nanoc3/data_sources.rb +18 -9
- data/lib/nanoc3/extra/core_ext/enumerable.rb +39 -0
- data/lib/nanoc3/extra/core_ext/time.rb +2 -2
- data/lib/nanoc3/extra/core_ext.rb +1 -0
- data/lib/nanoc3/extra/deployers/rsync.rb +49 -3
- data/lib/nanoc3/extra/file_proxy.rb +7 -0
- data/lib/nanoc3/extra/vcs.rb +25 -24
- data/lib/nanoc3/extra/vcses/bazaar.rb +4 -0
- data/lib/nanoc3/extra/vcses/dummy.rb +4 -0
- data/lib/nanoc3/extra/vcses/git.rb +4 -0
- data/lib/nanoc3/extra/vcses/mercurial.rb +4 -0
- data/lib/nanoc3/extra/vcses/subversion.rb +4 -0
- data/lib/nanoc3/extra.rb +4 -1
- data/lib/nanoc3/filters/erb.rb +1 -1
- data/lib/nanoc3/filters/erubis.rb +1 -1
- data/lib/nanoc3/filters/haml.rb +1 -1
- data/lib/nanoc3/filters/kramdown.rb +14 -0
- data/lib/nanoc3/filters/maruku.rb +1 -1
- data/lib/nanoc3/filters/rainpress.rb +1 -1
- data/lib/nanoc3/filters/rdiscount.rb +3 -1
- data/lib/nanoc3/filters.rb +2 -0
- data/lib/nanoc3/helpers/blogging.rb +91 -75
- data/lib/nanoc3/helpers/breadcrumbs.rb +18 -10
- data/lib/nanoc3/helpers/capturing.rb +24 -29
- data/lib/nanoc3/helpers/filtering.rb +20 -17
- data/lib/nanoc3/helpers/html_escape.rb +7 -4
- data/lib/nanoc3/helpers/link_to.rb +51 -41
- data/lib/nanoc3/helpers/rendering.rb +15 -8
- data/lib/nanoc3/helpers/tagging.rb +27 -21
- data/lib/nanoc3/helpers/text.rb +12 -8
- data/lib/nanoc3/helpers/xml_sitemap.rb +13 -15
- data/lib/nanoc3/tasks/deploy/rsync.rake +4 -1
- data/lib/nanoc3/tasks.rb +2 -1
- data/lib/nanoc3.rb +24 -1
- metadata +43 -87
- data/NEWS.rdoc +0 -328
- data/README.rdoc +0 -83
- data/lib/nanoc3/base/plugin.rb +0 -88
- data/lib/nanoc3/base/preprocessor_context.rb +0 -37
- data/lib/nanoc3/data_sources/filesystem_combined.rb +0 -214
- data/lib/nanoc3/data_sources/filesystem_common.rb +0 -22
- data/lib/nanoc3/data_sources/filesystem_compact.rb +0 -256
- data/lib/nanoc3/extra/context.rb +0 -24
- data/lib/nanoc3/package.rb +0 -107
- data/vendor/cri/ChangeLog +0 -0
- data/vendor/cri/LICENSE +0 -19
- data/vendor/cri/NEWS +0 -0
- data/vendor/cri/README +0 -4
- data/vendor/cri/Rakefile +0 -25
- data/vendor/cri/lib/cri/base.rb +0 -153
- data/vendor/cri/lib/cri/command.rb +0 -105
- data/vendor/cri/lib/cri/core_ext/string.rb +0 -41
- data/vendor/cri/lib/cri/core_ext.rb +0 -8
- data/vendor/cri/lib/cri/option_parser.rb +0 -186
- data/vendor/cri/lib/cri.rb +0 -12
- data/vendor/cri/test/test_base.rb +0 -6
- data/vendor/cri/test/test_command.rb +0 -6
- data/vendor/cri/test/test_core_ext.rb +0 -21
- data/vendor/cri/test/test_option_parser.rb +0 -279
data/lib/nanoc3/base/item_rep.rb
CHANGED
@@ -2,70 +2,73 @@
|
|
2
2
|
|
3
3
|
module Nanoc3
|
4
4
|
|
5
|
-
# A
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# layout.
|
5
|
+
# A single representation (rep) of an item ({Nanoc3::Item}). An item can
|
6
|
+
# have multiple representations. A representation has its own output file.
|
7
|
+
# A single item can therefore have multiple output files, each run through
|
8
|
+
# a different set of filters with a different layout.
|
10
9
|
#
|
11
10
|
# An item representation is observable. The following events will be
|
12
11
|
# notified:
|
13
12
|
#
|
14
|
-
# *
|
15
|
-
# *
|
16
|
-
# *
|
17
|
-
# *
|
13
|
+
# * `:compilation_started`
|
14
|
+
# * `:compilation_ended`
|
15
|
+
# * `:filtering_started`
|
16
|
+
# * `:filtering_ended`
|
18
17
|
#
|
19
18
|
# The compilation-related events have one parameters (the item
|
20
19
|
# representation); the filtering-related events have two (the item
|
21
20
|
# representation, and a symbol containing the filter class name).
|
22
21
|
class ItemRep
|
23
22
|
|
24
|
-
#
|
23
|
+
# @return [Nanoc3::Item] The item to which this rep belongs
|
25
24
|
attr_reader :item
|
26
25
|
|
27
|
-
#
|
26
|
+
# @return [Symbol] The representation's unique name
|
28
27
|
attr_reader :name
|
29
28
|
|
30
|
-
#
|
29
|
+
# @return [Boolean] true if this rep is forced to be dirty (e.g. because
|
30
|
+
# of the `--force` commandline option); false otherwise
|
31
31
|
attr_accessor :force_outdated
|
32
32
|
|
33
|
-
#
|
34
|
-
# was compiled
|
33
|
+
# @return [Boolean] true if this rep’s output file has changed since the
|
34
|
+
# last time it was compiled; false otherwise
|
35
35
|
attr_accessor :modified
|
36
36
|
alias_method :modified?, :modified
|
37
37
|
|
38
|
-
#
|
39
|
-
#
|
38
|
+
# @return [Boolean] true if this rep’s output file was created during the
|
39
|
+
# current or last compilation session; false otherwise
|
40
40
|
attr_accessor :created
|
41
41
|
alias_method :created?, :created
|
42
42
|
|
43
|
-
#
|
43
|
+
# @return [Boolean] true if this representation has already been
|
44
|
+
# compiled during the current or last compilation session; false
|
45
|
+
# otherwise
|
44
46
|
attr_accessor :compiled
|
45
47
|
alias_method :compiled?, :compiled
|
46
48
|
|
47
|
-
#
|
48
|
-
# the current or last compilation session
|
49
|
+
# @return [Boolean] true if this representation’s compiled content has
|
50
|
+
# been written during the current or last compilation session; false
|
51
|
+
# otherwise
|
49
52
|
attr_reader :written
|
50
53
|
alias_method :written?, :written
|
51
54
|
|
52
|
-
# The item rep's path, as used when being linked to. It
|
53
|
-
# slash and it is relative to the output directory. It
|
54
|
-
# the path to the output directory. It will not include
|
55
|
-
# the filename is an index filename.
|
55
|
+
# @return [String] The item rep's path, as used when being linked to. It
|
56
|
+
# starts with a slash and it is relative to the output directory. It
|
57
|
+
# does not include the path to the output directory. It will not include
|
58
|
+
# the filename if the filename is an index filename.
|
56
59
|
attr_accessor :path
|
57
60
|
|
58
|
-
# The item rep's raw path. It is relative to the current
|
59
|
-
# and includes the path to the output directory. It
|
60
|
-
# filename, even if it is an index filename.
|
61
|
+
# @return [String] The item rep's raw path. It is relative to the current
|
62
|
+
# working directory and includes the path to the output directory. It
|
63
|
+
# also includes the filename, even if it is an index filename.
|
61
64
|
attr_accessor :raw_path
|
62
65
|
|
63
66
|
# Creates a new item representation for the given item.
|
64
67
|
#
|
65
|
-
#
|
66
|
-
#
|
68
|
+
# @param [Nanoc3::Item] item The item to which the new representation will
|
69
|
+
# belong.
|
67
70
|
#
|
68
|
-
#
|
71
|
+
# @param [Symbol] name The unique name for the new item representation.
|
69
72
|
def initialize(item, name)
|
70
73
|
# Set primary attributes
|
71
74
|
@item = item
|
@@ -77,6 +80,7 @@ module Nanoc3
|
|
77
80
|
:last => @item.raw_content,
|
78
81
|
:pre => @item.raw_content
|
79
82
|
}
|
83
|
+
@old_content = nil
|
80
84
|
|
81
85
|
# Reset flags
|
82
86
|
@compiled = false
|
@@ -86,8 +90,8 @@ module Nanoc3
|
|
86
90
|
@force_outdated = false
|
87
91
|
end
|
88
92
|
|
89
|
-
#
|
90
|
-
# regenerated, false otherwise
|
93
|
+
# @return [Boolean] true if this item rep's output file is outdated and
|
94
|
+
# must be regenerated, false otherwise
|
91
95
|
def outdated?
|
92
96
|
# Outdated if we don't know
|
93
97
|
return true if @item.mtime.nil?
|
@@ -126,7 +130,8 @@ module Nanoc3
|
|
126
130
|
return false
|
127
131
|
end
|
128
132
|
|
129
|
-
#
|
133
|
+
# @return [Hash] The assignments that should be available when compiling
|
134
|
+
# the content.
|
130
135
|
def assigns
|
131
136
|
{
|
132
137
|
:content => @content[:last],
|
@@ -139,22 +144,54 @@ module Nanoc3
|
|
139
144
|
}
|
140
145
|
end
|
141
146
|
|
142
|
-
# Returns the
|
147
|
+
# Returns the compiled content from a given snapshot.
|
143
148
|
#
|
144
|
-
#
|
145
|
-
#
|
146
|
-
|
149
|
+
# @option params [String] :snapshot The name of the snapshot from
|
150
|
+
# which to fetch the compiled content. By default, the returned compiled
|
151
|
+
# content will be the content compiled right before the first layout
|
152
|
+
# call (if any).
|
153
|
+
def compiled_content(params={})
|
154
|
+
# Notify
|
147
155
|
Nanoc3::NotificationCenter.post(:visit_started, self.item)
|
148
156
|
Nanoc3::NotificationCenter.post(:visit_ended, self.item)
|
149
157
|
|
158
|
+
# Debug
|
150
159
|
puts "*** Attempting to fetch content for #{self.inspect}" if $DEBUG
|
151
160
|
|
161
|
+
# Require compilation
|
152
162
|
raise Nanoc3::Errors::UnmetDependency.new(self) unless compiled?
|
153
163
|
|
154
|
-
|
164
|
+
# Get name of last pre-layout snapshot
|
165
|
+
snapshot_name = params[:snapshot]
|
166
|
+
if @content[:pre]
|
167
|
+
snapshot_name ||= :pre
|
168
|
+
else
|
169
|
+
snapshot_name ||= :last
|
170
|
+
end
|
171
|
+
|
172
|
+
# Get content
|
173
|
+
@content[snapshot_name]
|
174
|
+
end
|
175
|
+
|
176
|
+
# @deprecated Use {Nanoc3::ItemRep#compiled_content} instead.
|
177
|
+
def content_at_snapshot(snapshot=:pre)
|
178
|
+
compiled_content(:snapshot => snapshot)
|
155
179
|
end
|
156
180
|
|
157
181
|
# Runs the item content through the given filter with the given arguments.
|
182
|
+
# This method will replace the content of the `:last` snapshot with the
|
183
|
+
# filtered content of the last snapshot.
|
184
|
+
#
|
185
|
+
# This method is supposed to be called only in a compilation rule block
|
186
|
+
# (see {Nanoc3::CompilerDSL#compile}).
|
187
|
+
#
|
188
|
+
# @param [Symbol] filter_name The name of the filter to run the item
|
189
|
+
# representations' content through
|
190
|
+
#
|
191
|
+
# @param [Hash] filter_args The filter arguments that should be passed to
|
192
|
+
# the filter's #run method
|
193
|
+
#
|
194
|
+
# @return [void]
|
158
195
|
def filter(filter_name, filter_args={})
|
159
196
|
# Create filter
|
160
197
|
klass = Nanoc3::Filter.named(filter_name)
|
@@ -170,25 +207,24 @@ module Nanoc3
|
|
170
207
|
snapshot(@content[:post] ? :post : :pre)
|
171
208
|
end
|
172
209
|
|
173
|
-
# Lays out the item using the given layout.
|
210
|
+
# Lays out the item using the given layout. This method will replace the
|
211
|
+
# content of the `:last` snapshot with the laid out content of the last
|
212
|
+
# snapshot.
|
213
|
+
#
|
214
|
+
# This method is supposed to be called only in a compilation rule block
|
215
|
+
# (see {Nanoc3::CompilerDSL#compile}).
|
216
|
+
#
|
217
|
+
# @param [String] layout_identifier The identifier of the layout the ite
|
218
|
+
# should be laid out with
|
219
|
+
#
|
220
|
+
# @return [void]
|
174
221
|
def layout(layout_identifier)
|
175
|
-
#
|
176
|
-
|
177
|
-
raise Nanoc3::Errors::UnknownLayout.new(layout_identifier) if layout.nil?
|
178
|
-
|
179
|
-
# Get filter
|
180
|
-
filter_name, filter_args = @item.site.compiler.filter_for_layout(layout)
|
181
|
-
raise Nanoc3::Errors::CannotDetermineFilter.new(layout_identifier) if filter_name.nil?
|
182
|
-
|
183
|
-
# Get filter class
|
184
|
-
filter_class = Nanoc3::Filter.named(filter_name)
|
185
|
-
raise Nanoc3::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
|
222
|
+
# Create "pre" snapshot
|
223
|
+
snapshot(:pre) unless @content[:pre]
|
186
224
|
|
187
225
|
# Create filter
|
188
|
-
|
189
|
-
|
190
|
-
# Create "pre" snapshot
|
191
|
-
snapshot(:pre)
|
226
|
+
layout = layout_with_identifier(layout_identifier)
|
227
|
+
filter, filter_name, filter_args = filter_for_layout(layout)
|
192
228
|
|
193
229
|
# Layout
|
194
230
|
@item.site.compiler.stack.push(layout)
|
@@ -202,11 +238,20 @@ module Nanoc3
|
|
202
238
|
end
|
203
239
|
|
204
240
|
# Creates a snapshot of the current compiled item content.
|
241
|
+
#
|
242
|
+
# @param [Symbol] snapshot_name The name of the snapshot to create
|
243
|
+
#
|
244
|
+
# @return [void]
|
205
245
|
def snapshot(snapshot_name)
|
206
246
|
@content[snapshot_name] = @content[:last]
|
207
247
|
end
|
208
248
|
|
209
249
|
# Writes the item rep's compiled content to the rep's output file.
|
250
|
+
#
|
251
|
+
# This method should not be called directly, even in a compilation block;
|
252
|
+
# the compiler is responsible for calling this method.
|
253
|
+
#
|
254
|
+
# @return [void]
|
210
255
|
def write
|
211
256
|
# Create parent directory
|
212
257
|
FileUtils.mkdir_p(File.dirname(self.raw_path))
|
@@ -216,7 +261,7 @@ module Nanoc3
|
|
216
261
|
|
217
262
|
# Remember old content
|
218
263
|
if File.file?(self.raw_path)
|
219
|
-
old_content = File.read(self.raw_path)
|
264
|
+
@old_content = File.read(self.raw_path)
|
220
265
|
end
|
221
266
|
|
222
267
|
# Write
|
@@ -224,13 +269,79 @@ module Nanoc3
|
|
224
269
|
@written = true
|
225
270
|
|
226
271
|
# Check if file was modified
|
227
|
-
@modified = File.read(self.raw_path) != old_content
|
272
|
+
@modified = File.read(self.raw_path) != @old_content
|
273
|
+
end
|
274
|
+
|
275
|
+
# Creates and returns a diff between the compiled content before the
|
276
|
+
# current compilation session and the content compiled in the current
|
277
|
+
# compilation session.
|
278
|
+
#
|
279
|
+
# @return [String, nil] The difference between the old and new compiled
|
280
|
+
# content in `diff(1)` format, or nil if there is no previous compiled
|
281
|
+
# content
|
282
|
+
def diff
|
283
|
+
# Check if old content exists
|
284
|
+
if @old_content.nil? or self.raw_path.nil?
|
285
|
+
nil
|
286
|
+
else
|
287
|
+
diff_strings(@old_content, @content[:last])
|
288
|
+
end
|
228
289
|
end
|
229
290
|
|
230
291
|
def inspect
|
231
292
|
"<#{self.class}:0x#{self.object_id.to_s(16)} name=#{self.name} item.identifier=#{self.item.identifier}>"
|
232
293
|
end
|
233
294
|
|
295
|
+
private
|
296
|
+
|
297
|
+
def layout_with_identifier(layout_identifier)
|
298
|
+
layout ||= @item.site.layouts.find { |l| l.identifier == layout_identifier.cleaned_identifier }
|
299
|
+
raise Nanoc3::Errors::UnknownLayout.new(layout_identifier) if layout.nil?
|
300
|
+
layout
|
301
|
+
end
|
302
|
+
|
303
|
+
def filter_for_layout(layout)
|
304
|
+
# Get filter name and args
|
305
|
+
filter_name, filter_args = @item.site.compiler.filter_for_layout(layout)
|
306
|
+
raise Nanoc3::Errors::CannotDetermineFilter.new(layout_identifier) if filter_name.nil?
|
307
|
+
|
308
|
+
# Get filter class
|
309
|
+
filter_class = Nanoc3::Filter.named(filter_name)
|
310
|
+
raise Nanoc3::Errors::UnknownFilter.new(filter_name) if filter_class.nil?
|
311
|
+
|
312
|
+
# Create filter
|
313
|
+
filter = filter_class.new(assigns.merge({ :layout => layout }))
|
314
|
+
|
315
|
+
# Done
|
316
|
+
[ filter, filter_name, filter_args ]
|
317
|
+
end
|
318
|
+
|
319
|
+
def diff_strings(a, b)
|
320
|
+
# TODO Rewrite this string-diffing method in pure Ruby. It should not
|
321
|
+
# use the "diff" executable, because this will most likely not work on
|
322
|
+
# operating systems without it, such as Windows.
|
323
|
+
|
324
|
+
require 'tempfile'
|
325
|
+
require 'open3'
|
326
|
+
|
327
|
+
# Create files
|
328
|
+
old_file = Tempfile.new('old')
|
329
|
+
new_file = Tempfile.new('new')
|
330
|
+
|
331
|
+
# Write files
|
332
|
+
old_file.write(a)
|
333
|
+
new_file.write(b)
|
334
|
+
|
335
|
+
# Diff
|
336
|
+
stdin, stdout, stderr = Open3.popen3('diff', '-u', old_file.path, new_file.path)
|
337
|
+
result = stdout.read
|
338
|
+
result == '' ? nil : result
|
339
|
+
rescue Errno::ENOENT
|
340
|
+
warn 'Failed to run `diff`, so no diff with the previously compiled ' \
|
341
|
+
'content will be available.'
|
342
|
+
nil
|
343
|
+
end
|
344
|
+
|
234
345
|
end
|
235
346
|
|
236
347
|
end
|
data/lib/nanoc3/base/layout.rb
CHANGED
@@ -2,36 +2,35 @@
|
|
2
2
|
|
3
3
|
module Nanoc3
|
4
4
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# an identifier (because layouts are organised hierarchically), and a
|
8
|
-
# modification time (to speed up compilation).
|
5
|
+
# Represents a layout in a nanoc site. It has content, attributes, an
|
6
|
+
# identifier and a modification time (to speed up compilation).
|
9
7
|
class Layout
|
10
8
|
|
11
|
-
#
|
9
|
+
# @return [Nanoc3::Site] The site this layout belongs to
|
12
10
|
attr_accessor :site
|
13
11
|
|
14
|
-
# The raw content of this layout
|
12
|
+
# @return [String] The raw content of this layout
|
15
13
|
attr_reader :raw_content
|
16
14
|
|
17
|
-
#
|
15
|
+
# @return [Hash] This layout's attributes
|
18
16
|
attr_reader :attributes
|
19
17
|
|
20
|
-
# This layout's identifier, starting and ending with a
|
18
|
+
# @return [String] This layout's identifier, starting and ending with a
|
19
|
+
# slash
|
21
20
|
attr_accessor :identifier
|
22
21
|
|
23
|
-
# The time when this layout was last modified
|
22
|
+
# @return [Time] The time when this layout was last modified
|
24
23
|
attr_reader :mtime
|
25
24
|
|
26
25
|
# Creates a new layout.
|
27
26
|
#
|
28
|
-
#
|
27
|
+
# @param [String] content The raw content of this layout.
|
29
28
|
#
|
30
|
-
#
|
29
|
+
# @param [Hash] attributes A hash containing this layout's attributes.
|
31
30
|
#
|
32
|
-
#
|
31
|
+
# @param [String] identifier This layout's identifier.
|
33
32
|
#
|
34
|
-
#
|
33
|
+
# @param [Time, nil] mtime The time when this layout was last modified.
|
35
34
|
def initialize(raw_content, attributes, identifier, mtime=nil)
|
36
35
|
@raw_content = raw_content
|
37
36
|
@attributes = attributes.symbolize_keys
|
@@ -40,6 +39,10 @@ module Nanoc3
|
|
40
39
|
end
|
41
40
|
|
42
41
|
# Requests the attribute with the given key.
|
42
|
+
#
|
43
|
+
# @param [Symbol] key The name of the attribute to fetch.
|
44
|
+
#
|
45
|
+
# @return [Object] The value of the requested attribute.
|
43
46
|
def [](key)
|
44
47
|
@attributes[key]
|
45
48
|
end
|
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
module Nanoc3
|
4
4
|
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# given name is posted.
|
5
|
+
# Provides a way to send notifications between objects. It allows blocks
|
6
|
+
# associated with a certain notification name to be registered; these blocks
|
7
|
+
# will be called when the notification with the given name is posted.
|
9
8
|
#
|
10
9
|
# It is a slightly different implementation of the Observer pattern; the
|
11
10
|
# table of subscribers is not stored in the observable object itself, but in
|
@@ -17,11 +16,17 @@ module Nanoc3
|
|
17
16
|
# Adds the given block to the list of blocks that should be called when
|
18
17
|
# the notification with the given name is received.
|
19
18
|
#
|
20
|
-
#
|
19
|
+
# @param [String, Symbol] name The name of the notification that will
|
20
|
+
# cause the given block to be called.
|
21
21
|
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
22
|
+
# @param [String, Symbol, nil] id An identifier for the block. This is
|
23
|
+
# only used to be able to remove the block (using the remove method)
|
24
|
+
# later. Can be nil, but this is not recommended because it prevents
|
25
|
+
# the given notification block from being unregistered.
|
26
|
+
#
|
27
|
+
# @yield [*args] Will be executed with the arguments passed to {.post}
|
28
|
+
#
|
29
|
+
# @return [void]
|
25
30
|
def on(name, id=nil, &block)
|
26
31
|
initialize_if_necessary(name)
|
27
32
|
|
@@ -29,8 +34,15 @@ module Nanoc3
|
|
29
34
|
@notifications[name] << { :id => id, :block => block }
|
30
35
|
end
|
31
36
|
|
32
|
-
# Posts a notification with the given name
|
33
|
-
#
|
37
|
+
# Posts a notification with the given name and the given arguments.
|
38
|
+
#
|
39
|
+
# @param [String, Symbol] name The name of the notification that should
|
40
|
+
# be posted.
|
41
|
+
#
|
42
|
+
# @param args Arguments that wil be passed to the blocks handling the
|
43
|
+
# notification.
|
44
|
+
#
|
45
|
+
# @return [void]
|
34
46
|
def post(name, *args)
|
35
47
|
initialize_if_necessary(name)
|
36
48
|
|
@@ -44,9 +56,13 @@ module Nanoc3
|
|
44
56
|
# that should be called when the notification with the given name is
|
45
57
|
# posted.
|
46
58
|
#
|
47
|
-
#
|
59
|
+
# @param [String, Symbol] name The name of the notification that should
|
60
|
+
# no longer be registered.
|
61
|
+
#
|
62
|
+
# @param [String, Symbol] id The identifier of the block that should be
|
63
|
+
# removed.
|
48
64
|
#
|
49
|
-
#
|
65
|
+
# @return [void]
|
50
66
|
def remove(name, id)
|
51
67
|
initialize_if_necessary(name)
|
52
68
|
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Nanoc3
|
4
|
+
|
5
|
+
# The class responsible for keeping track of all loaded plugins, such as
|
6
|
+
# filters ({Nanoc3::Filter}), data sources ({Nanoc3::DataSource}) and VCSes
|
7
|
+
# ({Nanoc3::Extra::VCS}).
|
8
|
+
class PluginRegistry
|
9
|
+
|
10
|
+
# A module that contains class methods for plugins. It provides functions
|
11
|
+
# for setting identifiers, registering plugins and finding plugins. Plugin
|
12
|
+
# classes should extend this module.
|
13
|
+
module PluginMethods
|
14
|
+
# Sets the identifiers for this plugin.
|
15
|
+
#
|
16
|
+
# @param [Array<Symbol>] identifier A list of identifiers to assign to
|
17
|
+
# this plugin.
|
18
|
+
#
|
19
|
+
# @return [void]
|
20
|
+
def identifiers(*identifiers)
|
21
|
+
register(self, *identifiers)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sets the identifier for this plugin.
|
25
|
+
#
|
26
|
+
# @param [Symbol] identifier An identifier to assign to this plugin.
|
27
|
+
#
|
28
|
+
# @return [void]
|
29
|
+
def identifier(identifier)
|
30
|
+
register(self, identifier)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Registers the given class as a plugin with the given identifier.
|
34
|
+
#
|
35
|
+
# @param [Class, String] class_or_name The class to register, or a
|
36
|
+
# string containing the class name to register.
|
37
|
+
#
|
38
|
+
# @param [Array<Symbol>] identifiers A list of identifiers to assign to
|
39
|
+
# this plugin.
|
40
|
+
#
|
41
|
+
# @return [void]
|
42
|
+
def register(class_or_name, *identifiers)
|
43
|
+
Nanoc3::Plugin.register(self, class_or_name, *identifiers)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the plugin with the given name (identifier)
|
47
|
+
#
|
48
|
+
# @param [String] name The name of the plugin class to find
|
49
|
+
#
|
50
|
+
# @return [Class] The plugin class with the given name
|
51
|
+
def named(name)
|
52
|
+
Nanoc3::Plugin.find(self, name)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the shared {PluginRegistry} instance, creating it if none exists
|
58
|
+
# yet.
|
59
|
+
#
|
60
|
+
# @return [Nanoc3::PluginRegistry] The shared plugin registry
|
61
|
+
def self.instance
|
62
|
+
@instance ||= self.new
|
63
|
+
end
|
64
|
+
|
65
|
+
# Creates a new plugin registry. This should usually not be necessary; it
|
66
|
+
# is recommended to use the shared instance (obtained from
|
67
|
+
# {Nanoc3::PluginRegistry.instance}).
|
68
|
+
def initialize
|
69
|
+
@map = {}
|
70
|
+
end
|
71
|
+
|
72
|
+
# Registers the given class as a plugin.
|
73
|
+
#
|
74
|
+
# @param [Class] superclass The superclass of the plugin. For example:
|
75
|
+
# {Nanoc3::Filter}, {Nanoc3::Extra::VCS}.
|
76
|
+
#
|
77
|
+
# @param [Class, String] class_or_name The class to register. This can be
|
78
|
+
# a string, in which case it will be automatically converted to a proper
|
79
|
+
# class at lookup. For example: `Nanoc3::Filters::ERB`,
|
80
|
+
# `"Nanoc3::Filters::Haml"`.
|
81
|
+
#
|
82
|
+
# @param [Symbol] identifiers One or more symbols identifying the class.
|
83
|
+
# For example: `:haml`, :`erb`.
|
84
|
+
#
|
85
|
+
# @return [void]
|
86
|
+
def register(superclass, class_or_name, *identifiers)
|
87
|
+
@map[superclass] ||= {}
|
88
|
+
|
89
|
+
identifiers.each do |identifier|
|
90
|
+
@map[superclass][identifier.to_sym] = class_or_name
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Finds the plugin that is a subclass of the given class and has the given
|
95
|
+
# name.
|
96
|
+
#
|
97
|
+
# @param [Symbol] name The name of the plugin to return
|
98
|
+
#
|
99
|
+
# @return [Class, nil] The plugin with the given name
|
100
|
+
def find(klass, name)
|
101
|
+
# Initialize
|
102
|
+
@map[klass] ||= {}
|
103
|
+
|
104
|
+
# Lookup
|
105
|
+
class_or_name = @map[klass][name.to_sym]
|
106
|
+
|
107
|
+
# Get class
|
108
|
+
if class_or_name.is_a?(String)
|
109
|
+
class_or_name.scan(/\w+/).inject(klass) { |memo, part| memo.const_get(part) }
|
110
|
+
else
|
111
|
+
class_or_name
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Returns a list of all plugins. The returned list of plugins is an array
|
116
|
+
# with array elements in the following format:
|
117
|
+
#
|
118
|
+
# { :class => ..., :superclass => ..., :identifiers => ... }
|
119
|
+
#
|
120
|
+
# @return [Array<Hash>] A list of all plugins in the format described
|
121
|
+
def all
|
122
|
+
plugins = []
|
123
|
+
@map.each_pair do |superclass, submap|
|
124
|
+
submap.each_pair do |identifier, klass|
|
125
|
+
# Find existing plugin
|
126
|
+
existing_plugin = plugins.find do |p|
|
127
|
+
p[:class] == klass && p[:superclass] == superclass
|
128
|
+
end
|
129
|
+
|
130
|
+
if existing_plugin
|
131
|
+
# Add identifier to existing plugin
|
132
|
+
existing_plugin[:identifiers] << identifier
|
133
|
+
existing_plugin[:identifiers] = existing_plugin[:identifiers].sort_by { |s| s.to_s }
|
134
|
+
else
|
135
|
+
# Create new plugin
|
136
|
+
plugins << {
|
137
|
+
:class => klass,
|
138
|
+
:superclass => superclass,
|
139
|
+
:identifiers => [ identifier ]
|
140
|
+
}
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
plugins
|
146
|
+
end
|
147
|
+
|
148
|
+
# @deprecated Use {Nanoc3::PluginRegistry#find} instead
|
149
|
+
def named(name)
|
150
|
+
find(self, name)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
# @deprecated Use {Nanoc3::PluginRegistry.instace} instead
|
156
|
+
Plugin = PluginRegistry.instance
|
157
|
+
|
158
|
+
end
|
data/lib/nanoc3/base/rule.rb
CHANGED
@@ -2,19 +2,30 @@
|
|
2
2
|
|
3
3
|
module Nanoc3
|
4
4
|
|
5
|
-
#
|
5
|
+
# Contains the processing information for a item.
|
6
6
|
class Rule
|
7
7
|
|
8
|
-
# The regex that determines which items this rule can be
|
9
|
-
# rule can be applied to items with a identifier
|
8
|
+
# @return [Regexp] The regex that determines which items this rule can be
|
9
|
+
# applied to. This rule can be applied to items with a identifier
|
10
|
+
# matching this regex.
|
10
11
|
attr_reader :identifier_regex
|
11
12
|
|
12
|
-
# The name of the representation that will be compiled
|
13
|
+
# @return [Symbol] The name of the representation that will be compiled
|
14
|
+
# using this rule
|
13
15
|
attr_reader :rep_name
|
14
16
|
|
15
|
-
# Creates a new item compilation rule with the given identifier regex,
|
16
|
-
# and block. The block will be called during compilation with the
|
17
|
-
# as its argument.
|
17
|
+
# Creates a new item compilation rule with the given identifier regex,
|
18
|
+
# compiler and block. The block will be called during compilation with the
|
19
|
+
# item rep as its argument.
|
20
|
+
#
|
21
|
+
# @param [Regexp] identifier_regex A regular expression that will be used
|
22
|
+
# to determine whether this rule is applicable to certain items.
|
23
|
+
#
|
24
|
+
# @param [String, Symbol] rep_name The name of the item representation
|
25
|
+
# where this rule can be applied to
|
26
|
+
#
|
27
|
+
# @param [Proc] block A block that will be called when matching items are
|
28
|
+
# compiled
|
18
29
|
def initialize(identifier_regex, rep_name, block)
|
19
30
|
@identifier_regex = identifier_regex
|
20
31
|
@rep_name = rep_name.to_sym
|
@@ -22,12 +33,20 @@ module Nanoc3
|
|
22
33
|
@block = block
|
23
34
|
end
|
24
35
|
|
25
|
-
#
|
36
|
+
# @param [Nanoc3::Item] item The item to check
|
37
|
+
#
|
38
|
+
# @return [Boolean] true if this rule can be applied to the given item
|
39
|
+
# rep, false otherwise
|
26
40
|
def applicable_to?(item)
|
27
41
|
item.identifier =~ @identifier_regex
|
28
42
|
end
|
29
43
|
|
30
44
|
# Applies this rule to the given item rep.
|
45
|
+
#
|
46
|
+
# @param [Nanoc3::ItemRep] rep The item representation where this rule
|
47
|
+
# should be applied to
|
48
|
+
#
|
49
|
+
# @return [void]
|
31
50
|
def apply_to(rep)
|
32
51
|
Nanoc3::RuleContext.new(rep).instance_eval &@block
|
33
52
|
end
|