nanoc3 3.1.0a3 → 3.1.0b1

Sign up to get free protection for your applications and to get access to all the features.
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