nanoc3 3.0.9 → 3.1.0a1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. data/LICENSE +1 -1
  2. data/NEWS.md +360 -0
  3. data/README.md +85 -0
  4. data/Rakefile +2 -2
  5. data/bin/nanoc3 +0 -4
  6. data/lib/nanoc3/base/code_snippet.rb +14 -6
  7. data/lib/nanoc3/base/compiler.rb +68 -49
  8. data/lib/nanoc3/base/compiler_dsl.rb +70 -29
  9. data/lib/nanoc3/base/context.rb +47 -0
  10. data/lib/nanoc3/base/core_ext/array.rb +4 -0
  11. data/lib/nanoc3/base/core_ext/hash.rb +5 -1
  12. data/lib/nanoc3/base/core_ext/string.rb +2 -0
  13. data/lib/nanoc3/base/data_source.rb +132 -96
  14. data/lib/nanoc3/base/dependency_tracker.rb +160 -185
  15. data/lib/nanoc3/base/directed_graph.rb +252 -0
  16. data/lib/nanoc3/base/errors.rb +52 -4
  17. data/lib/nanoc3/base/filter.rb +43 -28
  18. data/lib/nanoc3/base/item.rb +93 -25
  19. data/lib/nanoc3/base/item_rep.rb +166 -55
  20. data/lib/nanoc3/base/layout.rb +16 -13
  21. data/lib/nanoc3/base/notification_center.rb +28 -12
  22. data/lib/nanoc3/base/plugin_registry.rb +158 -0
  23. data/lib/nanoc3/base/rule.rb +27 -8
  24. data/lib/nanoc3/base/rule_context.rb +59 -46
  25. data/lib/nanoc3/base/site.rb +124 -77
  26. data/lib/nanoc3/base.rb +7 -2
  27. data/lib/nanoc3/cli/base.rb +4 -1
  28. data/lib/nanoc3/cli/commands/autocompile.rb +5 -4
  29. data/lib/nanoc3/cli/commands/compile.rb +28 -7
  30. data/lib/nanoc3/cli/commands/create_item.rb +1 -1
  31. data/lib/nanoc3/cli/commands/create_layout.rb +1 -1
  32. data/lib/nanoc3/cli/commands/create_site.rb +46 -22
  33. data/lib/nanoc3/cli/commands/debug.rb +100 -0
  34. data/lib/nanoc3/cli/commands/help.rb +1 -1
  35. data/lib/nanoc3/cli/commands/info.rb +1 -1
  36. data/lib/nanoc3/cli/commands/view.rb +85 -0
  37. data/lib/nanoc3/cli/commands.rb +2 -0
  38. data/lib/nanoc3/cli/logger.rb +7 -0
  39. data/lib/nanoc3/cli.rb +0 -3
  40. data/lib/nanoc3/data_sources/{delicious.rb → deprecated/delicious.rb} +1 -24
  41. data/lib/nanoc3/data_sources/{last_fm.rb → deprecated/last_fm.rb} +1 -27
  42. data/lib/nanoc3/data_sources/{twitter.rb → deprecated/twitter.rb} +1 -14
  43. data/lib/nanoc3/data_sources/filesystem.rb +188 -176
  44. data/lib/nanoc3/data_sources/filesystem_unified.rb +107 -0
  45. data/lib/nanoc3/data_sources/filesystem_verbose.rb +80 -0
  46. data/lib/nanoc3/data_sources.rb +18 -9
  47. data/lib/nanoc3/extra/core_ext/enumerable.rb +39 -0
  48. data/lib/nanoc3/extra/core_ext/time.rb +2 -2
  49. data/lib/nanoc3/extra/core_ext.rb +1 -0
  50. data/lib/nanoc3/extra/deployers/rsync.rb +49 -3
  51. data/lib/nanoc3/extra/file_proxy.rb +7 -0
  52. data/lib/nanoc3/extra/vcs.rb +25 -24
  53. data/lib/nanoc3/extra/vcses/bazaar.rb +4 -0
  54. data/lib/nanoc3/extra/vcses/dummy.rb +4 -0
  55. data/lib/nanoc3/extra/vcses/git.rb +4 -0
  56. data/lib/nanoc3/extra/vcses/mercurial.rb +4 -0
  57. data/lib/nanoc3/extra/vcses/subversion.rb +4 -0
  58. data/lib/nanoc3/extra.rb +4 -1
  59. data/lib/nanoc3/filters/erb.rb +1 -1
  60. data/lib/nanoc3/filters/erubis.rb +1 -1
  61. data/lib/nanoc3/filters/haml.rb +1 -1
  62. data/lib/nanoc3/filters/kramdown.rb +14 -0
  63. data/lib/nanoc3/filters/maruku.rb +1 -1
  64. data/lib/nanoc3/filters/rainpress.rb +1 -1
  65. data/lib/nanoc3/filters/rdiscount.rb +3 -1
  66. data/lib/nanoc3/filters.rb +2 -0
  67. data/lib/nanoc3/helpers/blogging.rb +91 -75
  68. data/lib/nanoc3/helpers/breadcrumbs.rb +18 -10
  69. data/lib/nanoc3/helpers/capturing.rb +24 -29
  70. data/lib/nanoc3/helpers/filtering.rb +20 -17
  71. data/lib/nanoc3/helpers/html_escape.rb +7 -4
  72. data/lib/nanoc3/helpers/link_to.rb +51 -41
  73. data/lib/nanoc3/helpers/rendering.rb +15 -8
  74. data/lib/nanoc3/helpers/tagging.rb +27 -21
  75. data/lib/nanoc3/helpers/text.rb +12 -8
  76. data/lib/nanoc3/helpers/xml_sitemap.rb +13 -15
  77. data/lib/nanoc3/tasks/deploy/rsync.rake +4 -1
  78. data/lib/nanoc3/tasks.rb +2 -1
  79. data/lib/nanoc3.rb +24 -1
  80. metadata +43 -87
  81. data/NEWS.rdoc +0 -328
  82. data/README.rdoc +0 -83
  83. data/lib/nanoc3/base/plugin.rb +0 -88
  84. data/lib/nanoc3/base/preprocessor_context.rb +0 -37
  85. data/lib/nanoc3/data_sources/filesystem_combined.rb +0 -214
  86. data/lib/nanoc3/data_sources/filesystem_common.rb +0 -22
  87. data/lib/nanoc3/data_sources/filesystem_compact.rb +0 -256
  88. data/lib/nanoc3/extra/context.rb +0 -24
  89. data/lib/nanoc3/package.rb +0 -107
  90. data/vendor/cri/ChangeLog +0 -0
  91. data/vendor/cri/LICENSE +0 -19
  92. data/vendor/cri/NEWS +0 -0
  93. data/vendor/cri/README +0 -4
  94. data/vendor/cri/Rakefile +0 -25
  95. data/vendor/cri/lib/cri/base.rb +0 -153
  96. data/vendor/cri/lib/cri/command.rb +0 -105
  97. data/vendor/cri/lib/cri/core_ext/string.rb +0 -41
  98. data/vendor/cri/lib/cri/core_ext.rb +0 -8
  99. data/vendor/cri/lib/cri/option_parser.rb +0 -186
  100. data/vendor/cri/lib/cri.rb +0 -12
  101. data/vendor/cri/test/test_base.rb +0 -6
  102. data/vendor/cri/test/test_command.rb +0 -6
  103. data/vendor/cri/test/test_core_ext.rb +0 -21
  104. data/vendor/cri/test/test_option_parser.rb +0 -279
@@ -2,70 +2,73 @@
2
2
 
3
3
  module Nanoc3
4
4
 
5
- # A Nanoc3::ItemRep is a single representation (rep) of an item
6
- # (Nanoc3::Item). An item can have multiple representations. A representation
7
- # has its own output file. A single item can therefore have multiple output
8
- # files, each run through a different set of filters with a different
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
- # * :compilation_started
15
- # * :compilation_ended
16
- # * :filtering_started
17
- # * :filtering_ended
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
- # The item (Nanoc3::Item) to which this representation belongs.
23
+ # @return [Nanoc3::Item] The item to which this rep belongs
25
24
  attr_reader :item
26
25
 
27
- # This item representation's unique name.
26
+ # @return [Symbol] The representation's unique name
28
27
  attr_reader :name
29
28
 
30
- # Indicates whether this rep is forced to be dirty by the user.
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
- # Indicates whether this rep's output file has changed the last time it
34
- # was compiled.
33
+ # @return [Boolean] true if this reps 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
- # Indicates whether this rep's output file was created the last time it
39
- # was compiled.
38
+ # @return [Boolean] true if this reps 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
- # Indicates whether this rep has already been compiled.
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
- # Indicates whether this rep's compiled content has been written during
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 starts with a
53
- # slash and it is relative to the output directory. It does not include
54
- # the path to the output directory. It will not include the filename if
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 working directory
59
- # and includes the path to the output directory. It also includes the
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
- # +item+:: The item (Nanoc3::Item) to which the new representation will
66
- # belong.
68
+ # @param [Nanoc3::Item] item The item to which the new representation will
69
+ # belong.
67
70
  #
68
- # +name+:: The unique name for the new item representation.
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
- # Returns true if this item rep's output file is outdated and must be
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
- # Returns the assignments that should be available when compiling the content.
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 item representation content at the given snapshot.
147
+ # Returns the compiled content from a given snapshot.
143
148
  #
144
- # +snapshot+:: The snapshot from which the content should be fetched. To
145
- # get the raw, uncompiled content, use +:raw+.
146
- def content_at_snapshot(snapshot=:pre)
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
- @content[snapshot]
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
- # Get layout
176
- layout ||= @item.site.layouts.find { |l| l.identifier == layout_identifier.cleaned_identifier }
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
- filter = filter_class.new(assigns.merge({ :layout => layout }))
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
@@ -2,36 +2,35 @@
2
2
 
3
3
  module Nanoc3
4
4
 
5
- # A Nanoc3::Layout represents a layout in a nanoc site. It has content,
6
- # attributes (for determining which filter to use for laying out an item),
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
- # The Nanoc3::Site this layout belongs to.
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
- # A hash containing this layout's attributes.
15
+ # @return [Hash] This layout's attributes
18
16
  attr_reader :attributes
19
17
 
20
- # This layout's identifier, starting and ending with a slash.
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
- # +content+:: The raw content of this layout.
27
+ # @param [String] content The raw content of this layout.
29
28
  #
30
- # +attributes+:: A hash containing this layout's attributes.
29
+ # @param [Hash] attributes A hash containing this layout's attributes.
31
30
  #
32
- # +identifier+:: This layout's identifier.
31
+ # @param [String] identifier This layout's identifier.
33
32
  #
34
- # +mtime+:: The time when this layout was last modified.
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
- # Nanoc3::NotificationCenter provides a way to send notifications between
6
- # objects. It allows blocks associated with a certain notification name to
7
- # be registered; these blocks will be called when the notification with the
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
- # +name+:: The name of the notification that will be posted.
19
+ # @param [String, Symbol] name The name of the notification that will
20
+ # cause the given block to be called.
21
21
  #
22
- # +id+:: An identifier for the block. This is only used to be able to
23
- # remove the block (using the remove method) later. Defaults to
24
- # nil.
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. All arguments wil be passed
33
- # to the blocks handling the notification.
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
- # +name+:: The name of the notification that will be posted.
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
- # +id+:: The identifier of the block that should be removed.
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
@@ -2,19 +2,30 @@
2
2
 
3
3
  module Nanoc3
4
4
 
5
- # Nanoc3::Rule contains the processing information for a item.
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 applied to. This
9
- # rule can be applied to items with a identifier matching this regex.
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 using this rule.
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, compiler
16
- # and block. The block will be called during compilation with the item rep
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
- # Returns true if this rule can be applied to the given item rep.
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