nanoc3 3.1.0b2 → 3.1.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -144,7 +144,7 @@ module Nanoc3
144
144
  # @param [Nanoc3::ItemRep] The item representation that was attempted to
145
145
  # be laid out
146
146
  def initialize(rep)
147
- super("The “{rep.item.identifier}” item (rep “#{rep.name}”) cannot be laid out because it is a binary item.".make_compatible_with_env)
147
+ super("The “{rep.item.identifier}” item (rep “#{rep.name}”) cannot be laid out because it is a binary item. If you are getting this error for an item that should be textual instead of binary, make sure that its extension is included in the text_extensions array in the site configuration.".make_compatible_with_env)
148
148
  end
149
149
 
150
150
  end
@@ -172,7 +172,7 @@ module Nanoc3
172
172
  #
173
173
  # @param [Class] filter_class The filter class that was used
174
174
  def initialize(rep, filter_class)
175
- super("The “#{filter_class.inspect}” filter cannot be used to filter the “#{rep.item.identifier}” item (rep “#{rep.name}”), because binary filters cannot be used on textual items.".make_compatible_with_env)
175
+ super("The “#{filter_class.inspect}” filter cannot be used to filter the “#{rep.item.identifier}” item (rep “#{rep.name}”), because binary filters cannot be used on textual items. If you are getting this error for an item that should be textual instead of binary, make sure that its extension is included in the text_extensions array in the site configuration.".make_compatible_with_env)
176
176
  end
177
177
 
178
178
  end
@@ -20,6 +20,20 @@ module Nanoc3
20
20
  # representation, and a symbol containing the filter class name).
21
21
  class ItemRep
22
22
 
23
+ # The descriptive strings for each outdatedness reason. This hash is used
24
+ # by the {#outdatedness_reason} method.
25
+ OUTDATEDNESS_REASON_DESCRIPTIONS = {
26
+ :no_mtime => 'No file modification time is available.',
27
+ :forced => 'All pages are recompiled because of a `--force` flag given to the compilation command.',
28
+ :no_raw_path => 'The routing rules do not specify a path where this item should be written to, i.e. the item representation will never be written to the output directory.',
29
+ :not_written => 'This item representation has not yet been written to the output directory (but it does have a path).',
30
+ :source_modified => 'The source file of this item has been modified since the last time this item representation was compiled.',
31
+ :layouts_outdated => 'The source of one or more layouts has been modified since the last time this item representation was compiled.',
32
+ :code_outdated => 'The code snippets in the `lib/` directory have been modified since the last time this item representation was compiled.',
33
+ :config_outdated => 'The site configuration has been modified since the last time this item representation was compiled.',
34
+ :rules_outdated => 'The rules file has been modified since the last time this item representation was compiled.',
35
+ }
36
+
23
37
  # @return [Nanoc3::Item] The item to which this rep belongs
24
38
  attr_reader :item
25
39
 
@@ -105,44 +119,72 @@ module Nanoc3
105
119
  @force_outdated = false
106
120
  end
107
121
 
108
- # @return [Boolean] true if this item rep's output file is outdated and
109
- # must be regenerated, false otherwise
110
- def outdated?
111
- # Outdated if we don't know
112
- return true if @item.mtime.nil?
113
-
114
- # Outdated if the dependency tracker says so
115
- return true if @force_outdated
122
+ # Calculates the reason why this item representation is outdated. The
123
+ # output will be a hash with a `:type` key, containing the reason why the
124
+ # item is outdated in the form of a symbol, and a `:description` key,
125
+ # containing a descriptive string that can be printed if necessary.
126
+ #
127
+ # For documentation on the types that this method can return, check the
128
+ # {OUTDATEDNESS_REASON_DESCRIPTIONS} hash in this class.
129
+ #
130
+ # @return [Hash, nil] A hash containing the reason why this item rep is
131
+ # outdated, both in the form of a symbol and as a descriptive string, or
132
+ # nil if the item representation is not outdated.
133
+ def outdatedness_reason
134
+ # Get reason symbol
135
+ reason = lambda do
136
+ # Outdated if we don't know
137
+ return :no_mtime if @item.mtime.nil?
138
+
139
+ # Outdated if the dependency tracker says so
140
+ return :forced if @force_outdated
141
+
142
+ # Outdated if compiled file doesn't exist (yet)
143
+ return :no_raw_path if self.raw_path.nil?
144
+ return :not_written if !File.file?(self.raw_path)
145
+
146
+ # Get compiled mtime
147
+ compiled_mtime = File.stat(self.raw_path).mtime
148
+
149
+ # Outdated if file too old
150
+ return :source_modified if @item.mtime > compiled_mtime
151
+
152
+ # Outdated if layouts outdated
153
+ return :layouts_outdated if @item.site.layouts.any? do |l|
154
+ l.mtime.nil? || l.mtime > compiled_mtime
155
+ end
116
156
 
117
- # Outdated if compiled file doesn't exist
118
- return true if self.raw_path.nil?
119
- return true if !File.file?(self.raw_path)
157
+ # Outdated if code outdated
158
+ return :code_outdated if @item.site.code_snippets.any? do |cs|
159
+ cs.mtime.nil? || cs.mtime > compiled_mtime
160
+ end
120
161
 
121
- # Get compiled mtime
122
- compiled_mtime = File.stat(self.raw_path).mtime
162
+ # Outdated if config outdated
163
+ return :config_outdated if @item.site.config_mtime.nil?
164
+ return :config_outdated if @item.site.config_mtime > compiled_mtime
123
165
 
124
- # Outdated if file too old
125
- return true if @item.mtime > compiled_mtime
166
+ # Outdated if rules outdated
167
+ return :rules_outdated if @item.site.rules_mtime.nil?
168
+ return :rules_outdated if @item.site.rules_mtime > compiled_mtime
126
169
 
127
- # Outdated if layouts outdated
128
- return true if @item.site.layouts.any? do |l|
129
- l.mtime.nil? || l.mtime > compiled_mtime
130
- end
170
+ return nil
171
+ end[]
131
172
 
132
- # Outdated if code outdated
133
- return true if @item.site.code_snippets.any? do |cs|
134
- cs.mtime.nil? || cs.mtime > compiled_mtime
173
+ # Build reason symbol and description
174
+ if reason.nil?
175
+ nil
176
+ else
177
+ {
178
+ :type => reason,
179
+ :description => OUTDATEDNESS_REASON_DESCRIPTIONS[reason]
180
+ }
135
181
  end
182
+ end
136
183
 
137
- # Outdated if config outdated
138
- return true if @item.site.config_mtime.nil?
139
- return true if @item.site.config_mtime > compiled_mtime
140
-
141
- # Outdated if rules outdated
142
- return true if @item.site.rules_mtime.nil?
143
- return true if @item.site.rules_mtime > compiled_mtime
144
-
145
- return false
184
+ # @return [Boolean] true if this item rep's output file is outdated and
185
+ # must be regenerated, false otherwise
186
+ def outdated?
187
+ !outdatedness_reason.nil?
146
188
  end
147
189
 
148
190
  # @return [Hash] The assignments that should be available when compiling
@@ -354,7 +396,7 @@ module Nanoc3
354
396
  # content in `diff(1)` format, or nil if there is no previous compiled
355
397
  # content
356
398
  def diff
357
- # TODO allow binary diffs
399
+ return nil if !@item.site.config[:enable_output_diff]
358
400
 
359
401
  if self.binary?
360
402
  nil
@@ -409,10 +451,6 @@ module Nanoc3
409
451
  end
410
452
 
411
453
  def diff_strings(a, b)
412
- # TODO Rewrite this string-diffing method in pure Ruby. It should not
413
- # use the "diff" executable, because this will most likely not work on
414
- # operating systems without it, such as Windows.
415
-
416
454
  require 'tempfile'
417
455
  require 'open3'
418
456
 
@@ -39,14 +39,20 @@ 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 js less markdown md php rb sass txt ),
43
- :output_dir => 'output',
44
- :data_sources => [ {} ],
45
- :index_filenames => [ 'index.html' ]
42
+ :text_extensions => %w( css erb haml htm html js less markdown md php rb sass txt ),
43
+ :output_dir => 'output',
44
+ :data_sources => [ {} ],
45
+ :index_filenames => [ 'index.html' ],
46
+ :enable_output_diff => false
46
47
  }
47
48
 
48
49
  # The site configuration. The configuration has the following keys:
49
50
  #
51
+ # * `text_extensions` ({Array<String>}) - A list of file extensions that
52
+ # will cause nanoc to threat the file as textual instead of binary. When
53
+ # the data source finds a content file with an extension that is
54
+ # included in this list, it will be marked as textual.
55
+ #
50
56
  # * `output_dir` ({String}) - The directory to which compiled items will
51
57
  # be written. This path is relative to the current working directory,
52
58
  # but can also be an absolute path.
@@ -60,6 +66,9 @@ module Nanoc3
60
66
  # `/about/` will be used instead of `/about/index.html`). The default
61
67
  # value should be okay in most cases.
62
68
  #
69
+ # * `enable_output_diff` ({Boolean}) - True when diffs should be generated
70
+ # for the compiled content of this site; false otherwise.
71
+ #
63
72
  # The list of data sources consists of hashes with the following keys:
64
73
  #
65
74
  # * `:type` ({String}) - The type of data source, i.e. its identifier.
@@ -32,6 +32,11 @@ output_dir: #{Nanoc3::Site::DEFAULT_CONFIG[:output_dir]}
32
32
  # such as “default.htm”. This list is used by nanoc to generate pretty URLs.
33
33
  index_filenames: #{array_to_yaml(Nanoc3::Site::DEFAULT_CONFIG[:index_filenames])}
34
34
 
35
+ # Whether or not to generate a diff of the compiled content when compiling a
36
+ # site. The diff will contain the differences between the compiled content
37
+ # before and after the last site compilation.
38
+ enable_output_diff: false
39
+
35
40
  # The data sources where nanoc loads its data from. This is an array of
36
41
  # hashes; each array element represents a single data source. By default,
37
42
  # there is only a single data source that reads data from the “content/” and
@@ -47,31 +47,45 @@ module Nanoc3::CLI::Commands
47
47
  dependency_tracker = @base.site.compiler.send(:dependency_tracker)
48
48
  dependency_tracker.load_graph
49
49
 
50
- # Print items
51
- puts '=== Items'
50
+ # Print item dependencies
51
+ puts '=== Item dependencies ======================================================='
52
52
  puts
53
- row = 0
54
53
  items.sort_by { |i| i.identifier }.each do |item|
55
- puts "item #{item.identifier}:"
56
-
57
- # Print item dependencies
58
- puts " dependencies:"
54
+ puts "item #{item.identifier} depends on:"
59
55
  predecessors = dependency_tracker.direct_predecessors_of(item).sort_by { |i| i.identifier }
60
56
  predecessors.each do |pred|
61
- puts " #{pred.identifier}"
57
+ puts " #{pred.identifier}"
62
58
  end
63
- puts " (nothing)" if predecessors.empty?
59
+ puts " (nothing)" if predecessors.empty?
60
+ puts
61
+ end
64
62
 
65
- # Print item representations
66
- puts " representations:"
63
+ # Print representation paths
64
+ puts '=== Representation paths ===================================================='
65
+ puts
66
+ items.sort_by { |i| i.identifier }.each do |item|
67
67
  item.reps.sort_by { |r| r.name.to_s }.each do |rep|
68
- puts " #{rep.name} -> #{rep.raw_path || '(not written)'}"
68
+ puts "item #{item.identifier}, rep #{rep.name}:"
69
+ puts " #{rep.raw_path || '(not written)'}"
69
70
  end
70
-
71
- # Done
72
71
  puts
73
72
  end
73
+
74
+ # Print representation outdatedness
75
+ puts '=== Representation outdatedness ============================================='
74
76
  puts
77
+ items.sort_by { |i| i.identifier }.each do |item|
78
+ item.reps.sort_by { |r| r.name.to_s }.each do |rep|
79
+ puts "item #{item.identifier}, rep #{rep.name}:"
80
+ outdatedness_reason = rep.outdatedness_reason
81
+ if outdatedness_reason
82
+ puts " is outdated: #{outdatedness_reason[:type]} (#{outdatedness_reason[:description]})"
83
+ else
84
+ puts " is not outdated"
85
+ end
86
+ end
87
+ puts
88
+ end
75
89
 
76
90
  # Print layouts
77
91
  puts '=== Layouts'
@@ -12,11 +12,58 @@ module Nanoc3::Filters
12
12
  def run(content, params={})
13
13
  require 'sass'
14
14
 
15
+ # Add imported_filename read accessor to ImportNode
16
+ # … but… but… nex3 said I could monkey patch it! :(
17
+ methods = ::Sass::Tree::ImportNode.instance_methods
18
+ if !methods.include?(:import_filename) && !methods.include?('import_filename')
19
+ ::Sass::Tree::ImportNode.send(:attr_reader, :imported_filename)
20
+ end
21
+
15
22
  # Get options
16
23
  options = params.merge(:filename => filename)
17
24
 
18
- # Get result
19
- ::Sass::Engine.new(content, options).render
25
+ # Build engine
26
+ engine = ::Sass::Engine.new(content, options)
27
+
28
+ # Get import nodes
29
+ require 'set'
30
+ imported_nodes = []
31
+ unprocessed_nodes = Set.new([ engine.to_tree ])
32
+ until unprocessed_nodes.empty?
33
+ # Get an unprocessed node
34
+ node = unprocessed_nodes.each { |n| break n }
35
+ unprocessed_nodes.delete(node)
36
+
37
+ # Add to list of import nodes if necessary
38
+ imported_nodes << node if node.is_a?(::Sass::Tree::ImportNode)
39
+
40
+ # Mark children of this node for processing
41
+ node.children.each { |c| unprocessed_nodes << c }
42
+ end
43
+
44
+ # Get import paths
45
+ import_paths = (options[:load_paths] || []).dup
46
+ import_paths.unshift(File.dirname(options[:filename])) if options[:filename]
47
+
48
+ # Get imported filenames
49
+ imported_filenames = imported_nodes.map do |node|
50
+ ::Sass::Files.find_file_to_import(node.imported_filename, import_paths)
51
+ end
52
+
53
+ # Convert to items
54
+ imported_items = imported_filenames.map do |filename|
55
+ normalized_filename = Pathname.new(filename).realpath
56
+ @items.find { |i| Pathname.new(i[:filename]).realpath == normalized_filename }
57
+ end
58
+
59
+ # Require compilation of each item
60
+ imported_items.each do |item|
61
+ any_uncompiled_rep = item.reps.find { |r| !r.compiled? }
62
+ raise Nanoc3::Errors::UnmetDependency.new(any_uncompiled_rep) if any_uncompiled_rep
63
+ end
64
+
65
+ # Done
66
+ engine.render
20
67
  end
21
68
 
22
69
  end
data/lib/nanoc3.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Nanoc3
4
4
 
5
5
  # The current nanoc version.
6
- VERSION = '3.1.0b2'
6
+ VERSION = '3.1.0rc1'
7
7
 
8
8
  end
9
9
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 3
7
7
  - 1
8
- - 0b2
9
- version: 3.1.0b2
8
+ - 0rc1
9
+ version: 3.1.0rc1
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-03-22 00:00:00 +01:00
17
+ date: 2010-03-27 00:00:00 +01:00
18
18
  default_executable: nanoc3
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency