nanoc3 3.2.0a2 → 3.2.0a3

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
@@ -5,6 +5,30 @@
5
5
  * Sped up nanoc quite a bit
6
6
  * Added AsciiDoc filter
7
7
  * Added progress indicator for long-running filters
8
+ * Made all source data, such as item attributes, frozen during compilation
9
+ * Exposed RedCloth parameters in the filter [Vincent Driessen]
10
+ * Added --color option to force color on
11
+ * Cleaned up internals, deprecating several parts and/or marking them as
12
+ private in the progress
13
+
14
+ ## 3.1.5 (???)
15
+
16
+ * Improved `#render` documentation
17
+ * Improved metadata section check so that e.g. raw diffs are handled properly
18
+ * Deprecated using `Nanoc3::Site#initialize` with a non-`"."` argument
19
+ * Added Ruby engine to version string
20
+ * Allowed the `created_at` and `updated_at` attributes used in the`Blogging`
21
+ helper to be `Date` instances
22
+
23
+ ## 3.1.4 (2010-07-25)
24
+
25
+ * Made INT and TERM signals always quit the CLI
26
+ * Allowed relative imports in LESS
27
+ * Made sure modification times are unchanged for identical recompiled items
28
+ * Improved link validator error handling
29
+ * Made pygmentize not output extra divs and pres
30
+ * Allowed colorizers to be specified using symbols instead of strings
31
+ * Added scss to the default list of text extensions
8
32
 
9
33
  ## 3.1.3 (2010-04-25)
10
34
 
data/README.md CHANGED
@@ -74,9 +74,11 @@ may be interested in the development dependencies:
74
74
  * Dmitry Bilunov
75
75
  * Devon Luke Buchanan
76
76
  * Brian Candler
77
+ * Vincent Driessen
77
78
  * Chris Eppstein
78
79
  * Felix Hanley
79
80
  * Starr Horne
81
+ * Tuomas Kareinen
80
82
  * Ale Muñoz
81
83
  * Nicky Peeters
82
84
  * Christian Plessl
data/lib/nanoc3.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Nanoc3
4
4
 
5
5
  # The current nanoc version.
6
- VERSION = '3.2.0a2'
6
+ VERSION = '3.2.0a3'
7
7
 
8
8
  end
9
9
 
@@ -82,7 +82,7 @@ module Nanoc3
82
82
  #
83
83
  # @return [void]
84
84
  def calculate_all_checksums
85
- @site.objects.each { |obj| new_checksum_for(obj) }
85
+ @site.compiler.objects.each { |obj| new_checksum_for(obj) }
86
86
  end
87
87
 
88
88
  # @param [#reference] obj
@@ -40,6 +40,8 @@ module Nanoc3
40
40
  # the specified object.
41
41
  class Compiler
42
42
 
43
+ # @group Accessors
44
+
43
45
  # @return [Nanoc3::Site] The site this compiler belongs to
44
46
  attr_reader :site
45
47
 
@@ -68,6 +70,8 @@ module Nanoc3
68
70
  # loaded but before the site is compiled
69
71
  attr_accessor :preprocessor
70
72
 
73
+ # @group Public instance methods
74
+
71
75
  # Creates a new compiler fo the given site
72
76
  #
73
77
  # @param [Nanoc3::Site] site The site this compiler belongs to
@@ -96,6 +100,7 @@ module Nanoc3
96
100
 
97
101
  # Compile reps
98
102
  load
103
+ @site.freeze
99
104
  dependency_tracker.start
100
105
  compile_reps(reps)
101
106
  dependency_tracker.stop
@@ -105,6 +110,8 @@ module Nanoc3
105
110
  FileUtils.rm_rf(Nanoc3::Filter::TMP_BINARY_ITEMS_DIR)
106
111
  end
107
112
 
113
+ # @group Private instance methods
114
+
108
115
  # Load the helper data that is used for compiling the site.
109
116
  #
110
117
  # @api private
@@ -114,7 +121,8 @@ module Nanoc3
114
121
  return if @loaded
115
122
  @loaded = true
116
123
 
117
- stores.each { |s| s.load }
124
+ # Load site if necessary
125
+ @site.load
118
126
 
119
127
  # Preprocess
120
128
  load_rules
@@ -123,6 +131,9 @@ module Nanoc3
123
131
  build_reps
124
132
  route_reps
125
133
 
134
+ # Load auxiliary stores
135
+ stores.each { |s| s.load }
136
+
126
137
  # Determine which reps need to be recompiled
127
138
  dependency_tracker.propagate_outdatedness
128
139
  forget_dependencies_if_outdated(items)
@@ -260,7 +271,17 @@ module Nanoc3
260
271
  preprocessor_context.instance_eval(&preprocessor) if preprocessor
261
272
  end
262
273
 
263
- # FIXME get rid of this
274
+ # Returns all objects managed by the site (items, layouts, code snippets,
275
+ # site configuration and the rules).
276
+ #
277
+ # @api private
278
+ def objects
279
+ # FIXME remove reference to rules
280
+ site.items + site.layouts + site.code_snippets + [ site.config, self.rules_with_reference ]
281
+ end
282
+
283
+ # Returns the rules along with an unique reference (`:rules`) so that the
284
+ # outdatedness checker can use them.
264
285
  #
265
286
  # @api private
266
287
  def rules_with_reference
@@ -306,6 +327,9 @@ module Nanoc3
306
327
  # Get basic path by applying matching rule
307
328
  basic_path = rule.apply_to(rep, :compiler => self)
308
329
  next if basic_path.nil?
330
+ if basic_path !~ %r{^/}
331
+ raise RuntimeError, "The path returned for the #{rep.inspect} item representation, “#{basic_path}”, does not start with a slash. Please ensure that all routing rules return a path that starts with a slash.".make_compatible_with_env
332
+ end
309
333
 
310
334
  # Get raw path by prepending output directory
311
335
  rep.raw_paths[snapshot] = @site.config[:output_dir] + basic_path
@@ -328,9 +352,11 @@ module Nanoc3
328
352
  #
329
353
  # @return [Hash] The assigns that should be used in the next filter/layout
330
354
  # operation
355
+ #
356
+ # @api private
331
357
  def assigns_for(rep)
332
358
  if rep.binary?
333
- content_or_filename_assigns = { :filename => rep.filenames[:last] }
359
+ content_or_filename_assigns = { :filename => rep.temporary_filenames[:last] }
334
360
  else
335
361
  content_or_filename_assigns = { :content => rep.content[:last] }
336
362
  end
@@ -218,7 +218,8 @@ module Nanoc3
218
218
  # Load edges
219
219
  new_data[:edges].each do |edge|
220
220
  from_index, to_index = *edge
221
- from, to = @previous_objects[from_index], @previous_objects[to_index]
221
+ from = from_index && @previous_objects[from_index]
222
+ to = to_index && @previous_objects[to_index]
222
223
  @graph.add_edge(from, to)
223
224
  end
224
225
  end
@@ -67,6 +67,9 @@ module Nanoc3
67
67
  #
68
68
  # @return [void]
69
69
  def add_edge(from, to)
70
+ add_vertex(from)
71
+ add_vertex(to)
72
+
70
73
  @from_graph[from] ||= Set.new
71
74
  @from_graph[from] << to
72
75
 
@@ -107,6 +110,8 @@ module Nanoc3
107
110
  return if @vertices.include?(v)
108
111
 
109
112
  @vertices << v
113
+ @vertice_indexes[v] = @vertices.size-1
114
+
110
115
  @roots << v
111
116
  end
112
117
 
@@ -22,20 +22,11 @@ module Nanoc3
22
22
  #
23
23
  # @param [String] filename The filename corresponding to this code snippet
24
24
  #
25
- # @param [Time, Hash] params Extra parameters. For backwards
26
- # compatibility, this can be a Time instance indicating the time when
27
- # this code snippet was last modified (mtime).
28
- #
29
- # @option params [Time, nil] :mtime (nil) The time when this code snippet
30
- # was last modified
25
+ # @param [Time, Hash] params Extra parameters. Ignored by nanoc; it is
26
+ # only included for backwards compatibility.
31
27
  def initialize(data, filename, params=nil)
32
- # Parse params
33
- params ||= {}
34
- params = { :mtime => params } if params.is_a?(Time)
35
-
36
28
  @data = data
37
29
  @filename = filename
38
- @mtime = params[:mtime]
39
30
  end
40
31
 
41
32
  # Loads the code by executing it.
@@ -23,9 +23,6 @@ module Nanoc3
23
23
  # @return [String] This item's identifier
24
24
  attr_accessor :identifier
25
25
 
26
- # @return [Time] The time where this item was last modified
27
- attr_reader :mtime
28
-
29
26
  # @return [Array<Nanoc3::ItemRep>] This item’s list of item reps
30
27
  attr_reader :reps
31
28
 
@@ -60,7 +57,8 @@ module Nanoc3
60
57
  # this item was last modified (mtime).
61
58
  #
62
59
  # @option params [Time, nil] :mtime (nil) The time when this item was last
63
- # modified
60
+ # modified. Deprecated; pass the modification time as the `:mtime`
61
+ # attribute instead.
64
62
  #
65
63
  # @option params [Symbol, nil] :binary (true) Whether or not this item is
66
64
  # binary
@@ -70,9 +68,6 @@ module Nanoc3
70
68
  params = { :mtime => params } if params.is_a?(Time)
71
69
  params[:binary] = false unless params.has_key?(:binary)
72
70
 
73
- # Get mtime
74
- @mtime = params[:mtime]
75
-
76
71
  # Get type and raw content or raw filename
77
72
  @is_binary = params[:binary]
78
73
  if @is_binary
@@ -85,6 +80,9 @@ module Nanoc3
85
80
  @attributes = attributes.symbolize_keys
86
81
  @identifier = identifier.cleaned_identifier
87
82
 
83
+ # Set mtime
84
+ @attributes.merge(:mtime => params[:mtime]) if params[:mtime]
85
+
88
86
  @parent = nil
89
87
  @children = []
90
88
 
@@ -163,6 +161,19 @@ module Nanoc3
163
161
  Nanoc3::NotificationCenter.post(:visit_started, self)
164
162
  Nanoc3::NotificationCenter.post(:visit_ended, self)
165
163
 
164
+ # Get captured content (hax)
165
+ # TODO [in nanoc 4.0] remove me
166
+ if key.to_s =~ /^content_for_(.*)$/
167
+ # Include capturing helper if necessary
168
+ unless @_Nanoc3_Helpers_Capturing_included
169
+ self.class.send(:include, ::Nanoc3::Helpers::Capturing)
170
+ @_Nanoc3_Helpers_Capturing_included = true
171
+ end
172
+
173
+ # Get content
174
+ return content_for(self, $1.to_sym)
175
+ end
176
+
166
177
  @attributes[key]
167
178
  end
168
179
 
@@ -199,10 +210,21 @@ module Nanoc3
199
210
  [ type, self.identifier ]
200
211
  end
201
212
 
213
+ # Prevents all further modifications to itself, its items, its layouts etc.
214
+ #
215
+ # @return [void]
216
+ def freeze
217
+ attributes.freeze
218
+ end
202
219
  def inspect
203
220
  "<#{self.class}:0x#{self.object_id.to_s(16)} identifier=#{self.identifier} binary?=#{self.binary?}>"
204
221
  end
205
222
 
223
+ # @deprecated Access the modification time using `item[:mtime]` instead.
224
+ def mtime
225
+ self[:mtime]
226
+ end
227
+
206
228
  end
207
229
 
208
230
  end
@@ -16,9 +16,6 @@ module Nanoc3
16
16
  # slash
17
17
  attr_accessor :identifier
18
18
 
19
- # @return [Time] The time where this layout was last modified
20
- attr_reader :mtime
21
-
22
19
  # Creates a new layout.
23
20
  #
24
21
  # @param [String] raw_content The raw content of this layout.
@@ -32,18 +29,17 @@ module Nanoc3
32
29
  # this layout was last modified (mtime).
33
30
  #
34
31
  # @option params [Time, nil] :mtime (nil) The time when this layout was
35
- # last modified
32
+ # last modified. Deprecated; pass the modification time as the `:mtime`
33
+ # attribute instead.
36
34
  def initialize(raw_content, attributes, identifier, params=nil)
37
- # Parse params
38
- params ||= {}
39
- params = { :mtime => params } if params.is_a?(Time)
40
-
41
- # Get mtime
42
- @mtime = params[:mtime]
43
-
44
35
  @raw_content = raw_content
45
36
  @attributes = attributes.symbolize_keys
46
37
  @identifier = identifier.cleaned_identifier
38
+
39
+ # Set mtime
40
+ params ||= {}
41
+ params = { :mtime => params } if params.is_a?(Time)
42
+ @attributes.merge(:mtime => params[:mtime]) if params[:mtime]
47
43
  end
48
44
 
49
45
  # Requests the attribute with the given key.
@@ -78,6 +74,11 @@ module Nanoc3
78
74
  "<#{self.class}:0x#{self.object_id.to_s(16)} identifier=#{self.identifier}>"
79
75
  end
80
76
 
77
+ # @deprecated Access the modification time using `layout[:mtime]` instead.
78
+ def mtime
79
+ self[:mtime]
80
+ end
81
+
81
82
  end
82
83
 
83
84
  end
@@ -208,12 +208,16 @@ module Nanoc3
208
208
  end
209
209
  end
210
210
 
211
- # TODO document
211
+ # Prevents all further modifications to itself, its items, its layouts etc.
212
212
  #
213
- # @api private
214
- def objects
215
- # FIXME remove reference to rules
216
- items + layouts + code_snippets + [ config, compiler.rules_with_reference ]
213
+ # @return [void]
214
+ def freeze
215
+ super
216
+
217
+ config.freeze
218
+ items.each { |i| i.freeze }
219
+ layouts.each { |l| l.freeze }
220
+ code_snippets.each { |cs| cs.freeze }
217
221
  end
218
222
 
219
223
  # @deprecated It is no longer necessary to explicitly load site data. It
@@ -222,10 +226,12 @@ module Nanoc3
222
226
  warn 'It is no longer necessary to call Nanoc3::Site#load_data. This method no longer has any effect. All calls to this method can be safely removed.'
223
227
  end
224
228
 
225
- private
226
-
227
229
  # Loads the site data. It is not necessary to call this method explicitly;
228
230
  # it will be called when it is necessary.
231
+ #
232
+ # @api private
233
+ #
234
+ # @return [void]
229
235
  def load
230
236
  return if @data_loaded
231
237
  @data_loaded = true
@@ -243,6 +249,8 @@ module Nanoc3
243
249
  compiler.load
244
250
  end
245
251
 
252
+ private
253
+
246
254
  # Loads this site’s code and executes it.
247
255
  def load_code_snippets
248
256
  @code_snippets_loaded ||= false
@@ -296,6 +304,11 @@ module Nanoc3
296
304
  # {#initialize} for details.
297
305
  def build_config(dir_or_config_hash)
298
306
  if dir_or_config_hash.is_a? String
307
+ # Check whether it is supported
308
+ if dir_or_config_hash != '.'
309
+ warn 'WARNING: Calling Nanoc3::Site.new with a directory that is not the current working directory is not supported. It is recommended to change the directory before calling Nanoc3::Site.new. For example, instead of Nanoc3::Site.new(\'abc\'), use Dir.chdir(\'abc\') { Nanoc3::Site.new(\'.\') }.'
310
+ end
311
+
299
312
  # Read config from config.yaml in given dir
300
313
  config_path = File.join(dir_or_config_hash, 'config.yaml')
301
314
  @config = DEFAULT_CONFIG.merge(YAML.load_file(config_path).symbolize_keys)
@@ -55,6 +55,11 @@ module Nanoc3::CLI
55
55
  add_command(Nanoc3::CLI::Commands::Watch.new)
56
56
  end
57
57
 
58
+ # Returns a fully initialised base instance. It is recommended to use this
59
+ # shared instance than to create new ones, as this will be the instance
60
+ # that will be used when reading all code from the `lib/` directory.
61
+ #
62
+ # @return [Nanoc3::CLI::Base]
58
63
  def self.shared_base
59
64
  @shared_base ||= Nanoc3::CLI::Base.new
60
65
  end
@@ -64,8 +69,11 @@ module Nanoc3::CLI
64
69
  @debug
65
70
  end
66
71
 
67
- # Helper function which can be called when a command is executed that
68
- # requires a site, such as the compile command.
72
+ # Asserts that the current working directory contains a site
73
+ # ({Nanoc3::Site} instance). If no site is present, prints an error
74
+ # message and exits.
75
+ #
76
+ # @return [void]
69
77
  def require_site
70
78
  if site.nil?
71
79
  $stderr.puts 'The current working directory does not seem to be a ' +
@@ -74,7 +82,10 @@ module Nanoc3::CLI
74
82
  end
75
83
  end
76
84
 
77
- # Gets the site (Nanoc3::Site) in the current directory and loads its data.
85
+ # Gets the site ({Nanoc3::Site} instance) in the current directory and
86
+ # loads its data.
87
+ #
88
+ # @return [Nanoc3::Site] The site in the current working directory
78
89
  def site
79
90
  # Load site if possible
80
91
  if File.file?('config.yaml') && (!self.instance_variable_defined?(:@site) || @site.nil?)
@@ -89,8 +100,16 @@ module Nanoc3::CLI
89
100
  @site
90
101
  end
91
102
 
92
- # Inherited from ::Cri::Base
103
+ # @see ::Cri::Base#run
93
104
  def run(args)
105
+ # Set exit handler
106
+ [ 'INT', 'TERM' ].each do |signal|
107
+ Signal.trap(signal) do
108
+ puts
109
+ exit!(0)
110
+ end
111
+ end
112
+
94
113
  super(args)
95
114
  rescue Interrupt => e
96
115
  exit(1)
@@ -99,8 +118,12 @@ module Nanoc3::CLI
99
118
  exit(1)
100
119
  end
101
120
 
102
- # Prints the given error to stderr. Includes message, possible resolution,
103
- # compilation stack, backtrace, etc.
121
+ # Prints the given error to stderr. Includes message, possible resolution
122
+ # (see {#resolution_for}), compilation stack, backtrace, etc.
123
+ #
124
+ # @param [Error] error The error that should be described
125
+ #
126
+ # @return [void]
104
127
  def print_error(error)
105
128
  $stderr.puts
106
129
 
@@ -144,8 +167,12 @@ module Nanoc3::CLI
144
167
  $stderr.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
145
168
  end
146
169
 
147
- # Returns a string containing hints for resolving the given error, or nil
148
- # if no resolution can be automatically obtained.
170
+ # Attempts to find a resolution for the given error, or nil if no
171
+ # resolution can be automatically obtained.
172
+ #
173
+ # @param [Error] error The error to find a resolution for
174
+ #
175
+ # @return [String] The resolution for the given error
149
176
  def resolution_for(error)
150
177
  case error
151
178
  when LoadError
@@ -157,12 +184,22 @@ module Nanoc3::CLI
157
184
  if gem_name
158
185
  "Try installing the '#{gem_name}' gem (`gem install #{gem_name}`) and then re-running the command."
159
186
  end
187
+ when RuntimeError
188
+ if error.message =~ /^can't modify frozen/
189
+ "You attempted to modify immutable data. Some data, such as " \
190
+ "item/layout attributes and raw item/layout content, can no " \
191
+ "longer be modified once compilation has started."
192
+ end
160
193
  end
161
194
  end
162
195
 
163
196
  # Sets the data source's VCS to the VCS with the given name. Does nothing
164
197
  # when the site's data source does not support VCSes (i.e. does not
165
198
  # implement #vcs=).
199
+ #
200
+ # @param [String] vcs_name The name of the VCS that should be used
201
+ #
202
+ # @return [void]
166
203
  def set_vcs(vcs_name)
167
204
  # Skip if not possible
168
205
  return if vcs_name.nil? || site.nil?
@@ -183,13 +220,17 @@ module Nanoc3::CLI
183
220
  end
184
221
  end
185
222
 
186
- # Returns the list of global option definitionss.
223
+ # @return [Array] The list of global option definitions
187
224
  def global_option_definitions
188
225
  [
189
226
  {
190
227
  :long => 'help', :short => 'h', :argument => :forbidden,
191
228
  :desc => 'show this help message and quit'
192
229
  },
230
+ {
231
+ :long => 'color', :short => 'l', :argument => :forbidden,
232
+ :desc => 'enable color'
233
+ },
193
234
  {
194
235
  :long => 'no-color', :short => 'C', :argument => :forbidden,
195
236
  :desc => 'disable color'
@@ -213,13 +254,15 @@ module Nanoc3::CLI
213
254
  ]
214
255
  end
215
256
 
257
+ # @see Cri::Base#handle_option
216
258
  def handle_option(option)
217
259
  case option
218
260
  when :version
219
261
  gem_info = defined?(Gem) ? "with RubyGems #{Gem::VERSION}" : "without RubyGems"
262
+ engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
220
263
 
221
264
  puts "nanoc #{Nanoc3::VERSION} (c) 2007-2010 Denis Defreyne."
222
- puts "Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) running on #{RUBY_PLATFORM} #{gem_info}"
265
+ puts "Running #{engine} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) on #{RUBY_PLATFORM} #{gem_info}"
223
266
  exit 0
224
267
  when :verbose
225
268
  Nanoc3::CLI::Logger.instance.level = :low
@@ -227,6 +270,8 @@ module Nanoc3::CLI
227
270
  @debug = true
228
271
  when :warn
229
272
  $-w = true
273
+ when :color
274
+ Nanoc3::CLI::Logger.instance.color = true
230
275
  when :'no-color'
231
276
  Nanoc3::CLI::Logger.instance.color = false
232
277
  when :help
@@ -53,7 +53,7 @@ module Nanoc3::CLI::Commands
53
53
  puts "item #{item.identifier} depends on:"
54
54
  predecessors = dependency_tracker.direct_predecessors_of(item).sort_by { |i| i.identifier }
55
55
  predecessors.each do |pred|
56
- puts " #{pred.identifier}"
56
+ puts " [ #{format '%6s', pred.type} ] #{pred.identifier}"
57
57
  end
58
58
  puts " (nothing)" if predecessors.empty?
59
59
  puts
@@ -31,11 +31,7 @@ module Nanoc3::CLI::Commands
31
31
 
32
32
  def run(options, arguments)
33
33
  require 'fssm'
34
-
35
- Signal.trap("INT") do
36
- puts
37
- exit
38
- end
34
+ require 'pathname'
39
35
 
40
36
  @notifier = Notifier.new
41
37
 
@@ -119,7 +115,7 @@ module Nanoc3::CLI::Commands
119
115
  # @param [String] message The message to include in the notification
120
116
  def notify(message)
121
117
  return if tool.nil?
122
- send(tool.tr('-', '_'), message, params)
118
+ send(tool.tr('-', '_'), message)
123
119
  end
124
120
 
125
121
  private
@@ -128,11 +124,11 @@ module Nanoc3::CLI::Commands
128
124
  @tool ||= TOOLS.find { |t| !`which #{t}`.empty? }
129
125
  end
130
126
 
131
- def growlnotify(message, params={})
127
+ def growlnotify(message)
132
128
  system('growlnotify', '-m', message)
133
129
  end
134
130
 
135
- def notify_send(message, params={})
131
+ def notify_send(message)
136
132
  system('notify-send', message)
137
133
  end
138
134
 
@@ -8,6 +8,8 @@ module Nanoc3::CLI
8
8
  # feedback in the terminal.
9
9
  class Logger
10
10
 
11
+ # Maps actions (`:create`, `:update`, `:identical` and `:skip`) onto their
12
+ # ANSI color codes.
11
13
  ACTION_COLORS = {
12
14
  :create => "\e[1m" + "\e[32m", # bold + green
13
15
  :update => "\e[1m" + "\e[33m", # bold + yellow
@@ -17,35 +19,43 @@ module Nanoc3::CLI
17
19
 
18
20
  include Singleton
19
21
 
20
- # The log level, which can be :high, :low or :off (which will log all
21
- # messages, only high-priority messages, or no messages at all,
22
+ # Returns the log level, which can be :high, :low or :off (which will log
23
+ # all messages, only high-priority messages, or no messages at all,
22
24
  # respectively).
25
+ #
26
+ # @return [Symbol] The log level
23
27
  attr_accessor :level
24
28
 
25
- # Whether to use color in log messages or not
26
- attr_accessor :color
27
- alias_method :color?, :color
29
+ # @return [Boolean] True if color should be used, false otherwise
30
+ attr_writer :color
28
31
 
29
32
  def initialize
30
33
  @level = :high
31
- @color = $stdout.tty?
34
+ end
32
35
 
33
- # Try enabling color support on Windows
34
- begin
35
- require 'Win32/Console/ANSI' if RUBY_PLATFORM =~/mswin|mingw/
36
- rescue LoadError
37
- @color = false
36
+ # @return [Boolean] true if colors are enabled, false otherwise
37
+ def color?
38
+ if @color.nil?
39
+ @color = true
40
+ begin
41
+ require 'Win32/Console/ANSI' if RUBY_PLATFORM =~ /mswin|mingw/
42
+ rescue LoadError
43
+ @color = false
44
+ end
38
45
  end
46
+
47
+ @color
39
48
  end
40
49
 
41
50
  # Logs a file-related action.
42
51
  #
43
- # +level+:: The importance of this action. Can be :high or :low.
52
+ # @param [:high, :low] level The importance of this action
44
53
  #
45
- # +action+:: The kind of file action. Can be :create, :update or
46
- # :identical.
54
+ # @param [:create, :update, :identical] action The kind of file action
47
55
  #
48
- # +identifier+:: The identifier of the item the action was performed on.
56
+ # @param [String] name The name of the file the action was performed on
57
+ #
58
+ # @return [void]
49
59
  def file(level, action, identifier, duration=nil)
50
60
  log(
51
61
  level,
@@ -61,18 +71,19 @@ module Nanoc3::CLI
61
71
 
62
72
  # Logs a message.
63
73
  #
64
- # +level+:: The importance of this message. Can be :high or :low.
74
+ # @param [:high, :low] level The importance of this message
75
+ #
76
+ # @param [String] message The message to be logged
65
77
  #
66
- # +s+:: The message to be logged.
78
+ # @param [#puts] io The stream to which the message should be written
67
79
  #
68
- # +io+:: The IO instance to which the message will be written. Defaults to
69
- # standard output.
70
- def log(level, s, io=$stdout)
80
+ # @return [void]
81
+ def log(level, message, io=$stdout)
71
82
  # Don't log when logging is disabled
72
83
  return if @level == :off
73
84
 
74
85
  # Log when level permits it
75
- io.puts(s) if (@level == :low or @level == level)
86
+ io.puts(message) if (@level == :low or @level == level)
76
87
  end
77
88
 
78
89
  end
@@ -238,7 +238,7 @@ module Nanoc3::DataSources
238
238
  data = File.read(content_filename)
239
239
 
240
240
  # Check presence of metadata section
241
- if data[0, 3] != '-'*3 && data[0, 5] != '-'*5
241
+ if data !~ /\A-{3,5}\s*$/
242
242
  return [ {}, data ]
243
243
  end
244
244
 
@@ -107,8 +107,23 @@ module Nanoc3::Filters
107
107
  code
108
108
  end
109
109
 
110
+ # Runs the content through [pygmentize](http://pygments.org/docs/cmdline/),
111
+ # the commandline frontend for [Pygments](http://pygments.org/).
112
+ #
113
+ # @api private
114
+ #
115
+ # @param [String] code The code to colorize
116
+ #
117
+ # @param [String] language The language the code is written in
118
+ #
119
+ # @option params [String, Symbol] :encoding The encoding of the code block
120
+ #
121
+ # @return [String] The colorized output
110
122
  def pygmentize(code, language, params={})
111
- IO.popen("pygmentize -l #{language} -f html", "r+") do |io|
123
+ enc = ""
124
+ enc = "-O encoding=" + params[:encoding] if params[:encoding]
125
+
126
+ IO.popen("pygmentize -l #{language} -f html #{enc}", "r+") do |io|
112
127
  io.write(code)
113
128
  io.close_write
114
129
  highlighted_code = io.read
@@ -3,8 +3,22 @@
3
3
  module Nanoc3::Filters
4
4
  class RedCloth < Nanoc3::Filter
5
5
 
6
- # Runs the content through [RedCloth](http://redcloth.org/).
7
- # This method takes no options.
6
+ # Runs the content through [RedCloth](http://redcloth.org/). This method
7
+ # takes the following options:
8
+ #
9
+ # * `:filter_class`
10
+ # * `:filter_html`
11
+ # * `:filter_ids`
12
+ # * `:filter_style`
13
+ # * `:hard_breaks`
14
+ # * `:lite_mode`
15
+ # * `:no_span_caps`
16
+ # * `:sanitize_htm`
17
+ #
18
+ # Each of these options sets the corresponding attribute on the `RedCloth`
19
+ # instance. For example, when the `:hard_breaks => false` option is passed
20
+ # to this filter, the filter will call `r.hard_breaks = false` (with `r`
21
+ # being the `RedCloth` instance).
8
22
  #
9
23
  # @param [String] content The content to filter
10
24
  #
@@ -12,8 +26,21 @@ module Nanoc3::Filters
12
26
  def run(content, params={})
13
27
  require 'redcloth'
14
28
 
29
+ # Create formatter
30
+ r = ::RedCloth.new(content)
31
+
32
+ # Set options
33
+ r.filter_classes = params[:filter_classes] if params.has_key?(:filter_classes)
34
+ r.filter_html = params[:filter_html] if params.has_key?(:filter_html)
35
+ r.filter_ids = params[:filter_ids] if params.has_key?(:filter_ids)
36
+ r.filter_styles = params[:filter_styles] if params.has_key?(:filter_styles)
37
+ r.hard_breaks = params[:hard_breaks] if params.has_key?(:hard_breaks)
38
+ r.lite_mode = params[:lite_mode] if params.has_key?(:lite_mode)
39
+ r.no_span_caps = params[:no_span_caps] if params.has_key?(:no_span_caps)
40
+ r.sanitize_html = params[:sanitize_html] if params.has_key?(:sanitize_html)
41
+
15
42
  # Get result
16
- ::RedCloth.new(content).to_html
43
+ r.to_html
17
44
  end
18
45
 
19
46
  end
@@ -39,8 +39,7 @@ module Nanoc3::Helpers
39
39
  def sorted_articles
40
40
  require 'time'
41
41
  articles.sort_by do |a|
42
- time = a[:created_at]
43
- time.is_a?(String) ? Time.parse(time) : time
42
+ attribute_to_time(a[:created_at])
44
43
  end.reverse
45
44
  end
46
45
 
@@ -183,8 +182,7 @@ module Nanoc3::Helpers
183
182
 
184
183
  # Get sorted relevant articles
185
184
  sorted_relevant_articles = relevant_articles.sort_by do |a|
186
- time = a[:created_at]
187
- time.is_a?(String) ? Time.parse(time) : time
185
+ attribute_to_time(a[:created_at])
188
186
  end.reverse.first(limit)
189
187
 
190
188
  # Get most recent article
@@ -204,8 +202,7 @@ module Nanoc3::Helpers
204
202
  xml.title title
205
203
 
206
204
  # Add date
207
- time = last_article[:created_at]
208
- xml.updated((time.is_a?(String) ? Time.parse(time) : time).to_iso8601_time)
205
+ xml.updated(attribute_to_time(last_article[:created_at]).to_iso8601_time)
209
206
 
210
207
  # Add links
211
208
  xml.link(:rel => 'alternate', :href => root_url)
@@ -229,10 +226,8 @@ module Nanoc3::Helpers
229
226
  xml.title a[:title], :type => 'html'
230
227
 
231
228
  # Add dates
232
- create_time = a[:created_at]
233
- update_time = a[:updated_at] || a[:created_at]
234
- xml.published((create_time.is_a?(String) ? Time.parse(create_time) : create_time).to_iso8601_time)
235
- xml.updated( (update_time.is_a?(String) ? Time.parse(update_time) : update_time).to_iso8601_time)
229
+ xml.published attribute_to_time(a[:created_at]).to_iso8601_time
230
+ xml.updated attribute_to_time(a[:updated_at] || a[:created_at]).to_iso8601_time
236
231
 
237
232
  # Add specific author information
238
233
  if a[:author_name] || a[:author_uri]
@@ -305,12 +300,24 @@ module Nanoc3::Helpers
305
300
 
306
301
  hostname, base_dir = %r{^.+?://([^/]+)(.*)$}.match(@site.config[:base_url])[1..2]
307
302
 
308
- time = item[:created_at]
309
- formatted_date = (time.is_a?(String) ? Time.parse(time) : time).to_iso8601_date
303
+ formatted_date = attribute_to_time(item[:created_at]).to_iso8601_date
310
304
 
311
305
  'tag:' + hostname + ',' + formatted_date + ':' + base_dir + (item.path || item.identifier)
312
306
  end
313
307
 
308
+ # Converts the given attribute (which can be a string, a Time or a Date)
309
+ # into a Time.
310
+ #
311
+ # @param [String, Time, Date] time Something that contains time
312
+ # information but is not necessarily a Time instance yet
313
+ #
314
+ # @return [Time] The Time instance corresponding to the given input
315
+ def attribute_to_time(time)
316
+ time = Time.local(time.year, time.month, time.day) if time.is_a?(Date)
317
+ time = Time.parse(time) if time.is_a?(String)
318
+ time
319
+ end
320
+
314
321
  end
315
322
 
316
323
  end
@@ -12,7 +12,10 @@ module Nanoc3::Helpers
12
12
  # you write the summary on the item itself, but capture it, and print it in
13
13
  # the sidebar layout.
14
14
  #
15
- # @example Capturing content into a `content_for_summary` attribute
15
+ # This helper has been tested with ERB and Haml. Other filters may not work
16
+ # correctly.
17
+ #
18
+ # @example Capturing content for a summary
16
19
  #
17
20
  # <% content_for :summary do %>
18
21
  # <p>On this item, nanoc is introduced, blah blah.</p>
@@ -22,28 +25,95 @@ module Nanoc3::Helpers
22
25
  #
23
26
  # <div id="sidebar">
24
27
  # <h3>Summary</h3>
28
+ # <%= content_for(@item, :summary) || '(no summary)' %>
29
+ # </div>
30
+ #
31
+ # @example Showing captured content in a sidebar the old, deprecated way (do not use or I will become very angry)
32
+ #
33
+ # <div id="sidebar">
34
+ # <h3>Summary</h3>
25
35
  # <%= @item[:content_for_summary] || '(no summary)' %>
26
36
  # </div>
27
37
  module Capturing
28
38
 
29
- # Captures the content inside the block and stores it in an item
30
- # attribute named `content_for_` followed by the given name. The
31
- # content of the block itself will not be outputted.
39
+ # @api private
40
+ class CapturesStore
41
+
42
+ require 'singleton'
43
+ include Singleton
44
+
45
+ def initialize
46
+ @store = {}
47
+ end
48
+
49
+ def []=(item, name, content)
50
+ @store[item.identifier] ||= {}
51
+ @store[item.identifier][name] = content
52
+ end
53
+
54
+ def [](item, name)
55
+ @store[item.identifier] ||= {}
56
+ @store[item.identifier][name]
57
+ end
58
+
59
+ end
60
+
61
+ # @overload content_for(name, &block)
62
+ #
63
+ # Captures the content inside the block and stores it so that it can be
64
+ # referenced later on. The same method, {#content_for}, is used for
65
+ # getting the captured content as well as setting it. When capturing,
66
+ # the content of the block itself will not be outputted.
67
+ #
68
+ # For backwards compatibility, it is also possible to fetch the captured
69
+ # content by getting the contents of the attribute named `content_for_`
70
+ # followed by the given name. This way of accessing captures is
71
+ # deprecated.
72
+ #
73
+ # @param [Symbol, String] name The base name of the attribute into which
74
+ # the content should be stored
75
+ #
76
+ # @return [void]
77
+ #
78
+ # @overload content_for(item, name)
32
79
  #
33
- # @param [Symbol, String] The base name of the attribute into which the
34
- # content should be stored
80
+ # Fetches the capture with the given name from the given item and
81
+ # returns it.
35
82
  #
36
- # @return [void]
37
- def content_for(name, &block)
38
- eval("@item[:content_for_#{name.to_s}] = capture(&block)")
83
+ # @param [Nanoc3::Item] item The item for which to get the capture
84
+ #
85
+ # @param [Symbol, String] name The name of the capture to fetch
86
+ #
87
+ # @return [String] The stored captured content
88
+ def content_for(*args, &block)
89
+ if block_given? # Set content
90
+ # Get args
91
+ if args.size != 1
92
+ raise ArgumentError, "expected 1 argument (the name " +
93
+ "of the capture) but got #{args.size} instead"
94
+ end
95
+ name = args[0]
96
+
97
+ # Capture and store
98
+ content = capture(&block)
99
+ CapturesStore.instance[@item, name.to_sym] = content
100
+ else # Get content
101
+ # Get args
102
+ if args.size != 2
103
+ raise ArgumentError, "expected 2 arguments (the item " +
104
+ "and the name of the capture) but got #{args.size} instead"
105
+ end
106
+ item = args[0]
107
+ name = args[1]
108
+
109
+ # Get content
110
+ CapturesStore.instance[item, name.to_sym]
111
+ end
39
112
  end
40
113
 
41
- # Evaluates the given block and returns the result. The contents of the
114
+ # Evaluates the given block and returns its contents. The contents of the
42
115
  # block is not outputted.
43
116
  #
44
- # This function has been tested with ERB and Haml. Other filters may not
45
- # work correctly.
46
- #
47
117
  # @return [String] The captured result
48
118
  def capture(&block)
49
119
  # Get erbout so far
@@ -7,7 +7,18 @@ module Nanoc3::Helpers
7
7
 
8
8
  include Nanoc3::Helpers::Capturing
9
9
 
10
- # Returns a string containing the rendered given layout.
10
+ # Returns a string containing the rendered given layout. The given layout
11
+ # will first be run through the matching layout rule.
12
+ #
13
+ # The assigns (`@item`, `@config`, …) will not be available in the
14
+ # partial, but it is possible to pass custom assigns to the method. These
15
+ # assigns will be made available as instance variables inside the partial.
16
+ #
17
+ # The method can also take a block. In this case, the content of the block
18
+ # will be captured (using the {Nanoc3::Helpers::Capturing} helper) and
19
+ # this content will be made available with `yield`. In other words, a
20
+ # `yield` inside the partial will output the content of the block passed
21
+ # to the method.
11
22
  #
12
23
  # @param [String] identifier The identifier of the layout that should be
13
24
  # rendered
@@ -29,6 +40,18 @@ module Nanoc3::Helpers
29
40
  # <%= render 'head', :title => 'Foo' %>
30
41
  # # => "<h1>Foo</h1>"
31
42
  #
43
+ # @example Yielding inside a partial
44
+ #
45
+ # # The 'box' partial
46
+ # <div class="box">
47
+ # <%= yield %>
48
+ # </div>
49
+ #
50
+ # # The item/layout where the partial is rendered
51
+ # <% render 'box' do %>
52
+ # I'm boxy! Luvz!
53
+ # <% end %>
54
+ #
32
55
  # @raise [Nanoc3::Errors::UnknownLayout] if the given layout does not
33
56
  # exist
34
57
  #
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nanoc3
3
3
  version: !ruby/object:Gem::Version
4
- hash: -142061300
4
+ hash: -794993270
5
5
  prerelease: true
6
6
  segments:
7
7
  - 3
8
8
  - 2
9
- - 0a2
10
- version: 3.2.0a2
9
+ - 0a3
10
+ version: 3.2.0a3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Denis Defreyne
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-09 00:00:00 +02:00
18
+ date: 2010-08-24 00:00:00 +02:00
19
19
  default_executable: nanoc3
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency