nanoc3 3.1.0a3 → 3.1.0b1

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/NEWS.md CHANGED
@@ -2,17 +2,17 @@
2
2
 
3
3
  ## 3.1
4
4
 
5
- * An `Item#rep(name)` function for quickly getting a certain rep
5
+ * An `Item#rep_named(name)` function for quickly getting a certain rep
6
6
  * An `Item#compiled_content` function for quickly getting compiled content
7
7
  * An `Item#path` function for quickly getting the path of an item rep
8
8
  * A new “+” wildcard in rule patterns that matches one or more characters
9
9
  * A `view` command that starts a web server in the output directory
10
10
  * A `debug` command that shows information about the items, reps and layouts
11
11
  * A `kramdown` filter ([kramdown site](http://kramdown.rubyforge.org/))
12
- * A plugin-loading system
13
12
  * A diff between the previously compiled content and the last compiled content
14
13
  is now written to `output.diff`
15
14
  * Assigns, such as `@items`, `@layouts`, `@item`, … are accessible without `@`
15
+ * Support for binary items
16
16
 
17
17
  Changed:
18
18
 
@@ -3,27 +3,7 @@
3
3
  module Nanoc3
4
4
 
5
5
  # The current nanoc version.
6
- VERSION = '3.1.0a3'
7
-
8
- # Loads all nanoc3 plugins, i.e. requires all ruby gems whose name start
9
- # with `nanoc3-`.
10
- #
11
- # @return [Boolean] true if all plugins were loaded successfully, false if
12
- # rubygems isn’t loaded.
13
- def self.load_plugins
14
- # Don’t load if there’s no rubygems
15
- return false if !defined?(Gem)
16
-
17
- Gem.source_index.find_name('').each do |gem|
18
- # Skip irrelevant ones
19
- next if gem.name !~ /^nanoc3-/
20
-
21
- # Load plugin
22
- require gem.name
23
- end
24
-
25
- true
26
- end
6
+ VERSION = '3.1.0b1'
27
7
 
28
8
  end
29
9
 
@@ -37,6 +17,3 @@ require 'nanoc3/extra'
37
17
  require 'nanoc3/data_sources'
38
18
  require 'nanoc3/filters'
39
19
  require 'nanoc3/helpers'
40
-
41
- # Load plugins
42
- Nanoc3.load_plugins
@@ -74,7 +74,7 @@ module Nanoc3
74
74
  if params.has_key?(:force) && params[:force]
75
75
  reps.each { |r| r.force_outdated = true }
76
76
  else
77
- dependency_tracker.mark_outdated_items
77
+ dependency_tracker.propagate_outdatedness
78
78
  end
79
79
  forget_dependencies_if_outdated(items)
80
80
 
@@ -202,7 +202,7 @@ module Nanoc3
202
202
  # indirectly) depend on an outdated item as outdated.
203
203
  #
204
204
  # @return [void]
205
- def mark_outdated_items
205
+ def propagate_outdatedness
206
206
  # Unmark everything
207
207
  @items.each { |i| i.outdated_due_to_dependencies = false }
208
208
 
@@ -215,11 +215,19 @@ module Nanoc3
215
215
  i.outdated_due_to_dependencies = true
216
216
  end
217
217
 
218
- # For each outdated item...
219
- @items.select { |i| i.outdated? }.each do |outdated_item|
220
- # ... mark all its successors as outdated
221
- self.successors_of(outdated_item).each do |i|
222
- i.outdated_due_to_dependencies = true
218
+ # Mark successors of outdated items as outdated
219
+ require 'set'
220
+ processed = Set.new
221
+ unprocessed = @items.select { |i| i.outdated? }
222
+ until unprocessed.empty?
223
+ item = unprocessed.shift
224
+ processed << item
225
+
226
+ self.direct_successors_of(item).each do |successor|
227
+ next if successor.outdated? || processed.include?(successor)
228
+
229
+ successor.outdated_due_to_dependencies = true
230
+ unprocessed << successor
223
231
  end
224
232
  end
225
233
  end
@@ -254,6 +262,11 @@ module Nanoc3
254
262
  end
255
263
  end
256
264
 
265
+ # @deprecated Use {#propagate_outdatedness} instead.
266
+ def mark_outdated_items
267
+ propagate_outdatedness
268
+ end
269
+
257
270
  private
258
271
 
259
272
  # Returns the item with the given identifier, or nil if no item is found.
@@ -35,6 +35,10 @@ module Nanoc3
35
35
 
36
36
  # A square matrix that contains boolean values. It is used as an adjacency
37
37
  # matrix by the {Nanoc3::DirectedGraph} class.
38
+ #
39
+ # This class is a helper class, which means that it is not used directly
40
+ # by nanoc. Future versions of nanoc may no longer contain this class. Do
41
+ # not depend on this class to be available.
38
42
  class SquareBooleanMatrix
39
43
 
40
44
  # Creates a new matrix with the given number of rows/columns.
@@ -113,6 +117,8 @@ module Nanoc3
113
117
  # Creates a new directed graph with the given vertices.
114
118
  def initialize(vertices)
115
119
  @vertices = vertices
120
+ generate_indices
121
+ invalidate_successor_predecessor_cache
116
122
 
117
123
  @matrix = SquareBooleanMatrix.new(@vertices.size)
118
124
  end
@@ -126,6 +132,7 @@ module Nanoc3
126
132
  def add_edge(from, to)
127
133
  from_index, to_index = indices_of(from, to)
128
134
  @matrix[from_index, to_index] = true
135
+ invalidate_successor_predecessor_cache
129
136
  end
130
137
 
131
138
  # Removes the edge from the first vertex to the second vertex. If the
@@ -138,6 +145,7 @@ module Nanoc3
138
145
  def remove_edge(from, to)
139
146
  from_index, to_index = indices_of(from, to)
140
147
  @matrix[from_index, to_index] = false
148
+ invalidate_successor_predecessor_cache
141
149
  end
142
150
 
143
151
  # Returns the direct predecessors of the given vertex, i.e. the vertices
@@ -147,9 +155,11 @@ module Nanoc3
147
155
  #
148
156
  # @return [Array] Direct predecessors of the given vertex
149
157
  def direct_predecessors_of(to)
150
- @vertices.select do |from|
151
- from_index, to_index = indices_of(from, to)
152
- @matrix[from_index, to_index] == true
158
+ @direct_predecessors[to] ||= begin
159
+ @vertices.select do |from|
160
+ from_index, to_index = indices_of(from, to)
161
+ @matrix[from_index, to_index] == true
162
+ end
153
163
  end
154
164
  end
155
165
 
@@ -160,9 +170,11 @@ module Nanoc3
160
170
  #
161
171
  # @return [Array] Direct successors of the given vertex
162
172
  def direct_successors_of(from)
163
- @vertices.select do |to|
164
- from_index, to_index = indices_of(from, to)
165
- @matrix[from_index, to_index] == true
173
+ @direct_successors[from] ||= begin
174
+ @vertices.select do |to|
175
+ from_index, to_index = indices_of(from, to)
176
+ @matrix[from_index, to_index] == true
177
+ end
166
178
  end
167
179
  end
168
180
 
@@ -173,7 +185,7 @@ module Nanoc3
173
185
  #
174
186
  # @return [Array] Predecessors of the given vertex
175
187
  def predecessors_of(to)
176
- recursively_find_vertices(to, :direct_predecessors_of)
188
+ @predecessors[to] ||= recursively_find_vertices(to, :direct_predecessors_of)
177
189
  end
178
190
 
179
191
  # Returns the successors of the given vertex, i.e. the vertices y for
@@ -183,7 +195,7 @@ module Nanoc3
183
195
  #
184
196
  # @return [Array] Successors of the given vertex
185
197
  def successors_of(from)
186
- recursively_find_vertices(from, :direct_successors_of)
198
+ @successors[from] ||= recursively_find_vertices(from, :direct_successors_of)
187
199
  end
188
200
 
189
201
  # Returns an array of tuples representing the edges. The result of this
@@ -215,10 +227,27 @@ module Nanoc3
215
227
 
216
228
  private
217
229
 
230
+ # Generates vertex-to-index mapping
231
+ def generate_indices
232
+ @indices = {}
233
+ @vertices.each_with_index do |v, i|
234
+ @indices[v] = i
235
+ end
236
+ end
237
+
238
+ # Invalidates the cache that contains successors and predecessors (both
239
+ # direct and indirect).
240
+ def invalidate_successor_predecessor_cache
241
+ @successors = {}
242
+ @direct_successors = {}
243
+ @predecessors = {}
244
+ @direct_predecessors = {}
245
+ end
246
+
218
247
  # Returns an array of indices for the given vertices. Raises an error if
219
248
  # one or more given objects are not vertices.
220
249
  def indices_of(*vertices)
221
- vertices.map { |v| @vertices.index(v) or raise RuntimeError, "#{v.inspect} not a vertex" }
250
+ vertices.map { |v| @indices[v] or raise RuntimeError, "#{v.inspect} not a vertex" }
222
251
  end
223
252
 
224
253
  # Recursively finds vertices, starting at the vertex start, using the
@@ -149,6 +149,8 @@ module Nanoc3
149
149
 
150
150
  end
151
151
 
152
+ # Error that is raised when a textual filter is attempted to be applied to
153
+ # a binary item representation.
152
154
  class CannotUseTextualFilter < Generic
153
155
 
154
156
  # @param [Nanoc3::ItemRep] rep The item representation that was
@@ -161,6 +163,8 @@ module Nanoc3
161
163
 
162
164
  end
163
165
 
166
+ # Error that is raised when a binary filter is attempted to be applied to
167
+ # a textual item representation.
164
168
  class CannotUseBinaryFilter < Generic
165
169
 
166
170
  # @param [Nanoc3::ItemRep] rep The item representation that was
@@ -41,18 +41,40 @@ module Nanoc3
41
41
 
42
42
  class << self
43
43
 
44
- # Sets the new type for the filter (`:binary` or `:text`).
44
+ # Sets the new type for the filter. The type can be `:binary` (default)
45
+ # or `:text`. The given argument can either be a symbol indicating both
46
+ # “from” and “to” types, or a hash where the only key is the “from” type
47
+ # and the only value is the “to” type.
45
48
  #
46
- # @param [Symbol] arg The new type of this filter
49
+ # @example Specifying a text-to-text filter
50
+ #
51
+ # type :text
52
+ #
53
+ # @example Specifying a text-to-binary filter
54
+ #
55
+ # type :text => :binary
56
+ #
57
+ # @param [Symbol, Hash] arg The new type of this filter
47
58
  #
48
59
  # @return [void]
49
60
  def type(arg)
50
- @type = arg
61
+ if arg.is_a?(Hash)
62
+ @from, @to = arg.keys[0], arg.values[0]
63
+ else
64
+ @from, @to = arg, arg
65
+ end
66
+ end
67
+
68
+ # @return [Boolean] True if this filter can be applied to binary item
69
+ # representations, false otherwise
70
+ def from_binary?
71
+ (@from || :text) == :binary
51
72
  end
52
73
 
53
- # @return [Boolean] True if this is a binary filter, false otherwise
54
- def binary?
55
- (@type || :text) == :binary
74
+ # @return [Boolean] True if this filter results in a binary item
75
+ # representation, false otherwise
76
+ def to_binary?
77
+ (@to || :text) == :binary
56
78
  end
57
79
 
58
80
  end
@@ -77,7 +77,7 @@ module Nanoc3
77
77
  end
78
78
 
79
79
  # Get type and raw content or raw filename
80
- @is_binary = params.has_key?(:binary) ? params[:binary] : true
80
+ @is_binary = params.has_key?(:binary) ? params[:binary] : false
81
81
  if @is_binary
82
82
  @raw_filename = raw_content_or_raw_filename
83
83
  else
@@ -30,6 +30,10 @@ module Nanoc3
30
30
  # of the `--force` commandline option); false otherwise
31
31
  attr_accessor :force_outdated
32
32
 
33
+ # @return [Boolean] true if this rep is currently binary; false otherwise
34
+ attr_reader :binary
35
+ alias_method :binary?, :binary
36
+
33
37
  # @return [Boolean] true if this rep’s output file has changed since the
34
38
  # last time it was compiled; false otherwise
35
39
  attr_accessor :modified
@@ -70,21 +74,26 @@ module Nanoc3
70
74
  # @param [Symbol] name The unique name for the new item representation.
71
75
  def initialize(item, name)
72
76
  # Set primary attributes
73
- @item = item
74
- @name = name
77
+ @item = item
78
+ @name = name
79
+
80
+ # Set binary
81
+ @binary = @item.binary?
75
82
 
76
83
  # Initialize content and filenames
77
- if @item.binary?
84
+ if self.binary?
78
85
  @filenames = {
79
86
  :raw => @item.raw_filename,
80
87
  :last => @item.raw_filename
81
88
  }
89
+ @content = {}
82
90
  else
83
91
  @content = {
84
92
  :raw => @item.raw_content,
85
93
  :last => @item.raw_content,
86
94
  :pre => @item.raw_content
87
95
  }
96
+ @filenames = {}
88
97
  end
89
98
  @old_content = nil
90
99
 
@@ -139,7 +148,7 @@ module Nanoc3
139
148
  # @return [Hash] The assignments that should be available when compiling
140
149
  # the content.
141
150
  def assigns
142
- if item.binary?
151
+ if self.binary?
143
152
  content_or_filename_assigns = { :filename => @filenames[:last] }
144
153
  else
145
154
  content_or_filename_assigns = { :content => @content[:last] }
@@ -165,12 +174,6 @@ module Nanoc3
165
174
  # @return [String] The compiled content at the given snapshot (or the
166
175
  # default snapshot if no snapshot is specified)
167
176
  def compiled_content(params={})
168
- # Check whether content can be fetched
169
- # TODO get proper exception
170
- if @item.binary?
171
- raise RuntimeError, "attempted to fetch compiled content from a binary item"
172
- end
173
-
174
177
  # Notify
175
178
  Nanoc3::NotificationCenter.post(:visit_started, self.item)
176
179
  Nanoc3::NotificationCenter.post(:visit_ended, self.item)
@@ -213,30 +216,40 @@ module Nanoc3
213
216
  #
214
217
  # @return [void]
215
218
  def filter(filter_name, filter_args={})
216
- # Create filter
217
- klass = Nanoc3::Filter.named(filter_name)
219
+ # Get filter class
220
+ klass = filter_named(filter_name)
218
221
  raise Nanoc3::Errors::UnknownFilter.new(filter_name) if klass.nil?
219
- filter = klass.new(assigns)
220
222
 
221
223
  # Check whether filter can be applied
222
- if klass.binary? && !item.binary?
224
+ if klass.from_binary? && !self.binary?
223
225
  raise Nanoc3::Errors::CannotUseBinaryFilter.new(self, klass)
224
- elsif !klass.binary? && item.binary?
226
+ elsif !klass.from_binary? && self.binary?
225
227
  raise Nanoc3::Errors::CannotUseTextualFilter.new(self, klass)
226
228
  end
227
229
 
230
+ # Create filter
231
+ filter = klass.new(assigns)
232
+
228
233
  # Run filter
229
234
  Nanoc3::NotificationCenter.post(:filtering_started, self, filter_name)
230
- if item.binary?
231
- filter.run(@filenames[:last], filter_args)
235
+ source = self.binary? ? @filenames[:last] : @content[:last]
236
+ result = filter.run(source, filter_args)
237
+ if klass.to_binary?
232
238
  @filenames[:last] = filter.output_filename
233
239
  else
234
- @content[:last] = filter.run(@content[:last], filter_args)
240
+ @content[:last] = result
235
241
  end
242
+ @binary = klass.to_binary?
236
243
  Nanoc3::NotificationCenter.post(:filtering_ended, self, filter_name)
237
244
 
245
+ # Check whether file was written
246
+ if self.binary? && !File.file?(filter.output_filename)
247
+ raise RuntimeError,
248
+ "The #{filter_name.inspect} filter did not write anything to the required output file, #{filter.output_filename}."
249
+ end
250
+
238
251
  # Create snapshot
239
- snapshot(@content[:post] ? :post : :pre) unless item.binary?
252
+ snapshot(@content[:post] ? :post : :pre) unless self.binary?
240
253
  end
241
254
 
242
255
  # Lays out the item using the given layout. This method will replace the
@@ -252,7 +265,7 @@ module Nanoc3
252
265
  # @return [void]
253
266
  def layout(layout_identifier)
254
267
  # Check whether item can be laid out
255
- raise Nanoc3::Errors::CannotLayoutBinaryItem.new(self) if item.binary?
268
+ raise Nanoc3::Errors::CannotLayoutBinaryItem.new(self) if self.binary?
256
269
 
257
270
  # Create "pre" snapshot
258
271
  snapshot(:pre) unless @content[:pre]
@@ -278,7 +291,7 @@ module Nanoc3
278
291
  #
279
292
  # @return [void]
280
293
  def snapshot(snapshot_name)
281
- target = @item.binary? ? @filenames : @content
294
+ target = self.binary? ? @filenames : @content
282
295
  target[snapshot_name] = target[:last]
283
296
  end
284
297
 
@@ -295,7 +308,7 @@ module Nanoc3
295
308
  # Check if file will be created
296
309
  @created = !File.file?(self.raw_path)
297
310
 
298
- if @item.binary?
311
+ if self.binary?
299
312
  # Calculate hash of old content
300
313
  if File.file?(self.raw_path)
301
314
  hash_old = hash(self.raw_path)
@@ -335,7 +348,7 @@ module Nanoc3
335
348
  def diff
336
349
  # Check if content can be diffed
337
350
  # TODO allow binary diffs
338
- return nil if @item.binary?
351
+ return nil if self.binary?
339
352
 
340
353
  # Check if old content exists
341
354
  if @old_content.nil? or self.raw_path.nil?
@@ -346,11 +359,15 @@ module Nanoc3
346
359
  end
347
360
 
348
361
  def inspect
349
- "<#{self.class}:0x#{self.object_id.to_s(16)} name=#{self.name} item.identifier=#{self.item.identifier} item.binary?=#{@item.binary?}>"
362
+ "<#{self.class}:0x#{self.object_id.to_s(16)} name=#{self.name} binary=#{self.binary?} item.identifier=#{self.item.identifier}>"
350
363
  end
351
364
 
352
365
  private
353
366
 
367
+ def filter_named(name)
368
+ Nanoc3::Filter.named(name)
369
+ end
370
+
354
371
  def layout_with_identifier(layout_identifier)
355
372
  layout ||= @item.site.layouts.find { |l| l.identifier == layout_identifier.cleaned_identifier }
356
373
  raise Nanoc3::Errors::UnknownLayout.new(layout_identifier) if layout.nil?
@@ -382,17 +399,18 @@ module Nanoc3
382
399
  require 'open3'
383
400
 
384
401
  # Create files
385
- old_file = Tempfile.new('old')
386
- new_file = Tempfile.new('new')
387
-
388
- # Write files
389
- old_file.write(a)
390
- new_file.write(b)
391
-
392
- # Diff
393
- stdin, stdout, stderr = Open3.popen3('diff', '-u', old_file.path, new_file.path)
394
- result = stdout.read
395
- result == '' ? nil : result
402
+ Tempfile.open('old') do |old_file|
403
+ Tempfile.open('new') do |new_file|
404
+ # Write files
405
+ old_file.write(a)
406
+ new_file.write(b)
407
+
408
+ # Diff
409
+ stdin, stdout, stderr = Open3.popen3('diff', '-u', old_file.path, new_file.path)
410
+ result = stdout.read
411
+ result == '' ? nil : result
412
+ end
413
+ end
396
414
  rescue Errno::ENOENT
397
415
  warn 'Failed to run `diff`, so no diff with the previously compiled ' \
398
416
  'content will be available.'
@@ -39,7 +39,7 @@ module Nanoc3
39
39
  # that lacks some options, the default value will be taken from
40
40
  # `DEFAULT_CONFIG`.
41
41
  DEFAULT_CONFIG = {
42
- :text_extensions => %w( css erb haml htm html less markdown md sass txt ),
42
+ :text_extensions => %w( css erb haml htm html js less markdown md php rb sass txt ),
43
43
  :output_dir => 'output',
44
44
  :data_sources => [ {} ],
45
45
  :index_filenames => [ 'index.html' ]
@@ -153,6 +153,9 @@ module Nanoc3::Extra::Validators
153
153
  end
154
154
  end
155
155
 
156
+ # This class is a helper class, which means that it is not used directly
157
+ # by nanoc. Future versions of nanoc may no longer contain this class. Do
158
+ # not depend on this class to be available.
156
159
  class EachPairEnumerator
157
160
 
158
161
  def initialize(hash)
@@ -16,11 +16,15 @@ module Nanoc3::Filters
16
16
 
17
17
  # Colorize
18
18
  doc = Nokogiri::HTML.fragment(content)
19
- doc.css('pre > code[class^="lang"]').each do |element|
20
- language = element['class'][/lang-([^ ]+)/].sub(/^lang-/, '')
21
- element.parent.replace(
22
- Nokogiri.make(highlight(element.inner_text, language, params))
23
- )
19
+ doc.css('pre > code[class*="language-"]').each do |element|
20
+ # Get language
21
+ match = element['class'].match(/(^| )language-([^ ]+)/)
22
+ next if match.nil?
23
+ language = match[2]
24
+
25
+ # Highlight
26
+ highlighted_code = highlight(element.inner_text, language, params)
27
+ element.inner_html = highlighted_code
24
28
  end
25
29
 
26
30
  doc.to_s
@@ -33,7 +37,7 @@ module Nanoc3::Filters
33
37
  def highlight(code, language, params={})
34
38
  colorizer = @colorizers[language]
35
39
  if KNOWN_COLORIZERS.include?(colorizer)
36
- send(colorizer, code, language, params[colorizer])
40
+ send(colorizer, code, language, params[colorizer] || {})
37
41
  else
38
42
  raise RuntimeError, "I don’t know how to highlight code using the “#{colorizer}” colorizer"
39
43
  end
@@ -42,7 +46,7 @@ module Nanoc3::Filters
42
46
  def coderay(code, language, params={})
43
47
  require 'coderay'
44
48
 
45
- CodeRay.scan(code, language).div(params)
49
+ ::CodeRay.scan(code, language).html(params)
46
50
  end
47
51
 
48
52
  def dummy(code, language, params={})
@@ -226,8 +226,8 @@ module Nanoc3::Helpers
226
226
  # Add dates
227
227
  create_time = a[:created_at]
228
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)
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)
231
231
 
232
232
  # Add link
233
233
  xml.link(:rel => 'alternate', :href => url)
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 3
7
7
  - 1
8
- - 0a3
9
- version: 3.1.0a3
8
+ - 0b1
9
+ version: 3.1.0b1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Denis Defreyne
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-02-28 00:00:00 +01:00
17
+ date: 2010-03-07 00:00:00 +01:00
18
18
  default_executable: nanoc3
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency