nanoc2 2.2.3

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.
Files changed (100) hide show
  1. data/ChangeLog +3 -0
  2. data/LICENSE +19 -0
  3. data/README +75 -0
  4. data/Rakefile +76 -0
  5. data/bin/nanoc2 +26 -0
  6. data/lib/nanoc2.rb +73 -0
  7. data/lib/nanoc2/base.rb +26 -0
  8. data/lib/nanoc2/base/asset.rb +117 -0
  9. data/lib/nanoc2/base/asset_defaults.rb +21 -0
  10. data/lib/nanoc2/base/asset_rep.rb +282 -0
  11. data/lib/nanoc2/base/binary_filter.rb +44 -0
  12. data/lib/nanoc2/base/code.rb +41 -0
  13. data/lib/nanoc2/base/compiler.rb +67 -0
  14. data/lib/nanoc2/base/core_ext.rb +2 -0
  15. data/lib/nanoc2/base/core_ext/hash.rb +78 -0
  16. data/lib/nanoc2/base/core_ext/string.rb +8 -0
  17. data/lib/nanoc2/base/data_source.rb +286 -0
  18. data/lib/nanoc2/base/defaults.rb +30 -0
  19. data/lib/nanoc2/base/filter.rb +93 -0
  20. data/lib/nanoc2/base/layout.rb +91 -0
  21. data/lib/nanoc2/base/notification_center.rb +66 -0
  22. data/lib/nanoc2/base/page.rb +132 -0
  23. data/lib/nanoc2/base/page_defaults.rb +20 -0
  24. data/lib/nanoc2/base/page_rep.rb +324 -0
  25. data/lib/nanoc2/base/plugin.rb +71 -0
  26. data/lib/nanoc2/base/proxies.rb +5 -0
  27. data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
  28. data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
  29. data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
  30. data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
  31. data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
  32. data/lib/nanoc2/base/proxy.rb +37 -0
  33. data/lib/nanoc2/base/router.rb +72 -0
  34. data/lib/nanoc2/base/site.rb +274 -0
  35. data/lib/nanoc2/base/template.rb +64 -0
  36. data/lib/nanoc2/binary_filters.rb +1 -0
  37. data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
  38. data/lib/nanoc2/cli.rb +9 -0
  39. data/lib/nanoc2/cli/base.rb +132 -0
  40. data/lib/nanoc2/cli/commands.rb +10 -0
  41. data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
  42. data/lib/nanoc2/cli/commands/compile.rb +312 -0
  43. data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
  44. data/lib/nanoc2/cli/commands/create_page.rb +85 -0
  45. data/lib/nanoc2/cli/commands/create_site.rb +323 -0
  46. data/lib/nanoc2/cli/commands/create_template.rb +76 -0
  47. data/lib/nanoc2/cli/commands/help.rb +69 -0
  48. data/lib/nanoc2/cli/commands/info.rb +125 -0
  49. data/lib/nanoc2/cli/commands/switch.rb +141 -0
  50. data/lib/nanoc2/cli/commands/update.rb +91 -0
  51. data/lib/nanoc2/cli/logger.rb +72 -0
  52. data/lib/nanoc2/data_sources.rb +2 -0
  53. data/lib/nanoc2/data_sources/filesystem.rb +707 -0
  54. data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
  55. data/lib/nanoc2/extra.rb +6 -0
  56. data/lib/nanoc2/extra/auto_compiler.rb +285 -0
  57. data/lib/nanoc2/extra/context.rb +22 -0
  58. data/lib/nanoc2/extra/core_ext.rb +2 -0
  59. data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
  60. data/lib/nanoc2/extra/core_ext/time.rb +13 -0
  61. data/lib/nanoc2/extra/file_proxy.rb +29 -0
  62. data/lib/nanoc2/extra/vcs.rb +48 -0
  63. data/lib/nanoc2/extra/vcses.rb +5 -0
  64. data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
  65. data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
  66. data/lib/nanoc2/extra/vcses/git.rb +21 -0
  67. data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
  68. data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
  69. data/lib/nanoc2/filters.rb +16 -0
  70. data/lib/nanoc2/filters/bluecloth.rb +13 -0
  71. data/lib/nanoc2/filters/erb.rb +19 -0
  72. data/lib/nanoc2/filters/erubis.rb +14 -0
  73. data/lib/nanoc2/filters/haml.rb +21 -0
  74. data/lib/nanoc2/filters/markaby.rb +14 -0
  75. data/lib/nanoc2/filters/maruku.rb +14 -0
  76. data/lib/nanoc2/filters/old.rb +19 -0
  77. data/lib/nanoc2/filters/rainpress.rb +13 -0
  78. data/lib/nanoc2/filters/rdiscount.rb +13 -0
  79. data/lib/nanoc2/filters/rdoc.rb +23 -0
  80. data/lib/nanoc2/filters/redcloth.rb +14 -0
  81. data/lib/nanoc2/filters/relativize_paths.rb +16 -0
  82. data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
  83. data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
  84. data/lib/nanoc2/filters/rubypants.rb +14 -0
  85. data/lib/nanoc2/filters/sass.rb +18 -0
  86. data/lib/nanoc2/helpers.rb +9 -0
  87. data/lib/nanoc2/helpers/blogging.rb +217 -0
  88. data/lib/nanoc2/helpers/capturing.rb +63 -0
  89. data/lib/nanoc2/helpers/filtering.rb +54 -0
  90. data/lib/nanoc2/helpers/html_escape.rb +25 -0
  91. data/lib/nanoc2/helpers/link_to.rb +113 -0
  92. data/lib/nanoc2/helpers/render.rb +49 -0
  93. data/lib/nanoc2/helpers/tagging.rb +56 -0
  94. data/lib/nanoc2/helpers/text.rb +38 -0
  95. data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
  96. data/lib/nanoc2/routers.rb +3 -0
  97. data/lib/nanoc2/routers/default.rb +54 -0
  98. data/lib/nanoc2/routers/no_dirs.rb +66 -0
  99. data/lib/nanoc2/routers/versioned.rb +79 -0
  100. metadata +185 -0
@@ -0,0 +1,64 @@
1
+ module Nanoc2
2
+
3
+ # A Nanoc2::Template represents a template, which can be used for creating
4
+ # new pages. Pages don't necessary have to be created using templates, but
5
+ # they can be useful for generating pages where you only have to "fill in
6
+ # the blanks".
7
+ class Template
8
+
9
+ # The Nanoc2::Site this template belongs to.
10
+ attr_accessor :site
11
+
12
+ # The name of this template.
13
+ attr_reader :name
14
+
15
+ # The raw content a page created using this template will have.
16
+ attr_reader :page_content
17
+
18
+ # A hash containing the attributes a page created using this template will
19
+ # have.
20
+ attr_reader :page_attributes
21
+
22
+ # Creates a new template.
23
+ #
24
+ # +name+:: The name of this template.
25
+ #
26
+ # +page_content+:: The raw content a page created using this template will
27
+ # have.
28
+ #
29
+ # +page_attributes+:: A hash containing the attributes a page created
30
+ # using this template will have.
31
+ def initialize(page_content, page_attributes, name)
32
+ @page_content = page_content
33
+ @page_attributes = page_attributes.clean
34
+ @name = name
35
+ end
36
+
37
+ # Saves the template in the database, creating it if it doesn't exist yet
38
+ # or updating it if it already exists. Tells the site's data source to
39
+ # save the template.
40
+ def save
41
+ @site.data_source.loading do
42
+ @site.data_source.save_template(self)
43
+ end
44
+ end
45
+
46
+ # Renames the template. Tells the site's data source to rename the
47
+ # template.
48
+ def move_to(new_name)
49
+ @site.data_source.loading do
50
+ @site.data_source.move_template(self, new_name)
51
+ end
52
+ end
53
+
54
+ # Deletes the template. Tells the site's data source to delete the
55
+ # template.
56
+ def delete
57
+ @site.data_source.loading do
58
+ @site.data_source.delete_template(self)
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1 @@
1
+ require 'nanoc2/binary_filters/image_science_thumbnail'
@@ -0,0 +1,28 @@
1
+ module Nanoc2::BinaryFilters
2
+
3
+ class ImageScienceThumbnail < Nanoc2::BinaryFilter
4
+
5
+ identifier :image_science_thumbnail
6
+
7
+ def run(file)
8
+ require 'image_science'
9
+
10
+ # Get temporary file path
11
+ tmp_file = Tempfile.new('filter')
12
+ tmp_path = tmp_file.path
13
+ tmp_file.close
14
+
15
+ # Create thumbnail
16
+ ImageScience.with_image(file.path) do |img|
17
+ img.thumbnail(@asset_rep.thumbnail_size || 150) do |thumbnail|
18
+ thumbnail.save(tmp_path)
19
+ end
20
+ end
21
+
22
+ # Return thumbnail file
23
+ File.open(tmp_path)
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + '/../../vendor/cri/lib'))
2
+ require 'cri'
3
+
4
+ module Nanoc2::CLI # :nodoc:
5
+ end
6
+
7
+ require 'nanoc2/cli/base'
8
+ require 'nanoc2/cli/commands'
9
+ require 'nanoc2/cli/logger'
@@ -0,0 +1,132 @@
1
+ module Nanoc2::CLI
2
+
3
+ # Nanoc2::CLI::Base is the central class representing a commandline nanoc
4
+ # tool. It has a list of commands, and is linked to a specific nanoc site.
5
+ class Base < Cri::Base
6
+
7
+ attr_reader :commands, :site
8
+
9
+ # Creates a new instance of the commandline nanoc tool.
10
+ def initialize
11
+ super('nanoc2')
12
+
13
+ # Add help command
14
+ self.help_command = Nanoc2::CLI::HelpCommand.new
15
+ add_command(self.help_command)
16
+
17
+ # Add other commands
18
+ add_command(Nanoc2::CLI::AutocompileCommand.new)
19
+ add_command(Nanoc2::CLI::CompileCommand.new)
20
+ add_command(Nanoc2::CLI::CreateLayoutCommand.new)
21
+ add_command(Nanoc2::CLI::CreatePageCommand.new)
22
+ add_command(Nanoc2::CLI::CreateSiteCommand.new)
23
+ add_command(Nanoc2::CLI::CreateTemplateCommand.new)
24
+ add_command(Nanoc2::CLI::InfoCommand.new)
25
+ add_command(Nanoc2::CLI::SwitchCommand.new)
26
+ add_command(Nanoc2::CLI::UpdateCommand.new)
27
+ end
28
+
29
+ # Helper function which can be called when a command is executed that
30
+ # requires a site, such as the compile command.
31
+ def require_site
32
+ if site.nil?
33
+ $stderr.puts 'The current working directory does not seem to be a ' +
34
+ 'valid/complete nanoc site directory; aborting.'
35
+ exit 1
36
+ end
37
+ end
38
+
39
+ # Gets the site (Nanoc2::Site) in the current directory and loads its data.
40
+ def site
41
+ # Load site if possible
42
+ if File.file?('config.yaml') and @site.nil?
43
+ begin
44
+ @site = Nanoc2::Site.new(YAML.load_file('config.yaml'))
45
+ @site.load_data
46
+ rescue Nanoc2::Errors::UnknownDataSourceError => e
47
+ $stderr.puts "Unknown data source: #{e}"
48
+ exit 1
49
+ rescue Nanoc2::Errors::UnknownRouterError => e
50
+ $stderr.puts "Unknown router: #{e}"
51
+ exit 1
52
+ rescue Exception => e
53
+ $stderr.puts "ERROR: An exception occured while loading this site."
54
+ $stderr.puts
55
+ $stderr.puts "If you think this is a bug in nanoc, please do report it at " +
56
+ "<http://nanoc.stoneship.org/trac/newticket> -- thanks!"
57
+ $stderr.puts
58
+ $stderr.puts 'Message:'
59
+ $stderr.puts ' ' + e.message
60
+ $stderr.puts
61
+ $stderr.puts 'Backtrace:'
62
+ $stderr.puts e.backtrace.map { |t| ' - ' + t }.join("\n")
63
+ exit 1
64
+ end
65
+ end
66
+
67
+ @site
68
+ end
69
+
70
+ # Sets the data source's VCS to the VCS with the given name. Does nothing
71
+ # when the site's data source does not support VCSes (i.e. does not
72
+ # implement #vcs=).
73
+ def set_vcs(vcs_name)
74
+ # Skip if not possible
75
+ return if vcs_name.nil?
76
+ return if site.nil? or !site.data_source.respond_to?(:vcs=)
77
+
78
+ # Find VCS
79
+ vcs_class = Nanoc2::Extra::VCS.named(vcs_name.to_sym)
80
+ if vcs_class.nil?
81
+ $stderr.puts "A VCS named #{vcs_name} was not found; aborting."
82
+ exit 1
83
+ end
84
+
85
+ # Set VCS
86
+ site.data_source.vcs = vcs_class.new
87
+ end
88
+
89
+ # Returns the list of global option definitionss.
90
+ def global_option_definitions
91
+ [
92
+ {
93
+ :long => 'help', :short => 'h', :argument => :forbidden,
94
+ :desc => 'show this help message and quit'
95
+ },
96
+ {
97
+ :long => 'no-color', :short => 'C', :argument => :forbidden,
98
+ :desc => 'disable color'
99
+ },
100
+ {
101
+ :long => 'verbose', :short => 'V', :argument => :forbidden,
102
+ :desc => 'make nanoc output more detailed'
103
+ },
104
+ {
105
+ :long => 'version', :short => 'v', :argument => :forbidden,
106
+ :desc => 'show version information and quit'
107
+ }
108
+ ]
109
+ end
110
+
111
+ def handle_option(option)
112
+ # Handle no-color option
113
+ if option == :'no-color'
114
+ Nanoc2::CLI::Logger.instance.color = false
115
+ # Handle verbose option
116
+ elsif option == :verbose
117
+ Nanoc2::CLI::Logger.instance.level = :low
118
+ # Handle version option
119
+ elsif option == :version
120
+ puts "nanoc #{Nanoc2::VERSION} (c) 2007-2010 Denis Defreyne."
121
+ puts "Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) running on #{RUBY_PLATFORM}"
122
+ exit 0
123
+ # Handle help option
124
+ elsif option == :help
125
+ show_help
126
+ exit 0
127
+ end
128
+ end
129
+
130
+ end
131
+
132
+ end
@@ -0,0 +1,10 @@
1
+ require 'nanoc2/cli/commands/autocompile'
2
+ require 'nanoc2/cli/commands/compile'
3
+ require 'nanoc2/cli/commands/create_layout'
4
+ require 'nanoc2/cli/commands/create_page'
5
+ require 'nanoc2/cli/commands/create_site'
6
+ require 'nanoc2/cli/commands/create_template'
7
+ require 'nanoc2/cli/commands/help'
8
+ require 'nanoc2/cli/commands/info'
9
+ require 'nanoc2/cli/commands/switch'
10
+ require 'nanoc2/cli/commands/update'
@@ -0,0 +1,80 @@
1
+ module Nanoc2::CLI
2
+
3
+ class AutocompileCommand < Cri::Command # :nodoc:
4
+
5
+ def name
6
+ 'autocompile'
7
+ end
8
+
9
+ def aliases
10
+ [ 'aco', 'autocompile_site' ]
11
+ end
12
+
13
+ def short_desc
14
+ 'start the autocompiler'
15
+ end
16
+
17
+ def long_desc
18
+ handler_names = Nanoc2::Extra::AutoCompiler::HANDLER_NAMES.join(', ')
19
+
20
+ 'Start the autocompiler web server. Unless specified, the web ' +
21
+ 'server will run on port 3000 and listen on all IP addresses. ' +
22
+ 'Running the autocompiler requires \'mime/types\' and \'rack\'.' +
23
+ "\n" +
24
+ 'Available handlers are (in order of preference): ' + handler_names +
25
+ ' (default is ' + Nanoc2::Extra::AutoCompiler::HANDLER_NAMES[0].to_s + ').'
26
+ end
27
+
28
+ def usage
29
+ "nanoc2 autocompile [options]"
30
+ end
31
+
32
+ def option_definitions
33
+ [
34
+ # --all
35
+ {
36
+ :long => 'all', :short => 'a', :argument => :forbidden,
37
+ :desc => 'compile all pages, even those that aren\'t outdated'
38
+ },
39
+ # --port
40
+ {
41
+ :long => 'port', :short => 'p', :argument => :required,
42
+ :desc => 'specify a port number for the autocompiler'
43
+ },
44
+ # --handler
45
+ {
46
+ :long => 'handler', :short => 'H', :argument => :required,
47
+ :desc => 'specify the handler to use'
48
+ }
49
+ ]
50
+ end
51
+
52
+ def run(options, arguments)
53
+ # Check arguments
54
+ if arguments.size != 0
55
+ $stderr.puts "usage: #{usage}"
56
+ exit 1
57
+ end
58
+
59
+ # Make sure we are in a nanoc site directory
60
+ @base.require_site
61
+
62
+ # Autocompile site
63
+ begin
64
+ autocompiler = Nanoc2::Extra::AutoCompiler.new(@base.site, options.has_key?(:all))
65
+ autocompiler.start(
66
+ options[:port],
67
+ options[:handler]
68
+ )
69
+ rescue LoadError
70
+ $stderr.puts "'mime/types' and 'rack' are required to autocompile sites. " +
71
+ "You may want to install the 'mime-types' and 'rack' gems by " +
72
+ "running 'gem install mime-types' and 'gem install rack'."
73
+ rescue Nanoc2::Extra::AutoCompiler::UnknownHandlerError
74
+ $stderr.puts "The requested handler, #{options[:handler]}, is not available."
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,312 @@
1
+ module Nanoc2::CLI
2
+
3
+ class CompileCommand < Cri::Command # :nodoc:
4
+
5
+ def name
6
+ 'compile'
7
+ end
8
+
9
+ def aliases
10
+ []
11
+ end
12
+
13
+ def short_desc
14
+ 'compile pages and assets of this site'
15
+ end
16
+
17
+ def long_desc
18
+ 'Compile all pages and all assets of the current site. If a path is ' +
19
+ 'given, only the page or asset with the given path will be compiled. ' +
20
+ 'Additionally, only pages and assets that are outdated will be ' +
21
+ 'compiled, unless specified otherwise with the -a option.'
22
+ end
23
+
24
+ def usage
25
+ "nanoc2 compile [options] [path]"
26
+ end
27
+
28
+ def option_definitions
29
+ [
30
+ # --all
31
+ {
32
+ :long => 'all', :short => 'a', :argument => :forbidden,
33
+ :desc => 'compile all pages and assets, even those that aren\'t outdated'
34
+ },
35
+ # --pages
36
+ {
37
+ :long => 'pages', :short => 'P', :argument => :forbidden,
38
+ :desc => 'only compile pages (no assets)'
39
+ },
40
+ # --assets
41
+ {
42
+ :long => 'assets', :short => 'A', :argument => :forbidden,
43
+ :desc => 'only compile assets (no pages)'
44
+ },
45
+ # --only-outdated
46
+ {
47
+ :long => 'only-outdated', :short => 'o', :argument => :forbidden,
48
+ :desc => 'only compile outdated pages and assets'
49
+ },
50
+ ]
51
+ end
52
+
53
+ def run(options, arguments)
54
+ # Make sure we are in a nanoc site directory
55
+ @base.require_site
56
+
57
+ # Find object with given path
58
+ if arguments.size == 0
59
+ # Find all pages and/or assets
60
+ if options.has_key?(:pages)
61
+ objs = @base.site.pages
62
+ elsif options.has_key?(:assets)
63
+ objs = @base.site.assets
64
+ else
65
+ objs = nil
66
+ end
67
+ else
68
+ objs = arguments.map do |path|
69
+ # Find object
70
+ path = path.cleaned_path
71
+ obj = @base.site.pages.find { |page| page.path == path }
72
+ obj = @base.site.assets.find { |asset| asset.path == path } if obj.nil?
73
+
74
+ # Ensure object
75
+ if obj.nil?
76
+ $stderr.puts "Unknown page or asset: #{path}"
77
+ exit 1
78
+ end
79
+
80
+ obj
81
+ end
82
+ end
83
+
84
+ # Compile site
85
+ begin
86
+ # Give feedback
87
+ puts "Compiling #{objs.nil? ? 'site' : 'objects'}..."
88
+
89
+ # Initialize profiling stuff
90
+ time_before = Time.now
91
+ @filter_times ||= {}
92
+ @times_stack ||= []
93
+ setup_notifications
94
+
95
+ # Parse all/only-outdated options
96
+ if options.has_key?(:all)
97
+ warn "WARNING: The --all option is no longer necessary as nanoc " +
98
+ "2.2 compiles all pages and assets by default. To change this " +
99
+ "behaviour, use the --only-outdated option."
100
+ end
101
+ compile_all = options.has_key?(:'only-outdated') ? false : true
102
+
103
+ # Compile
104
+ @base.site.compiler.run(
105
+ objs,
106
+ :even_when_not_outdated => compile_all
107
+ )
108
+
109
+ # Find reps
110
+ page_reps = @base.site.pages.map { |p| p.reps }.flatten
111
+ asset_reps = @base.site.assets.map { |a| a.reps }.flatten
112
+ reps = page_reps + asset_reps
113
+
114
+ # Show skipped reps
115
+ reps.select { |r| !r.compiled? }.each do |rep|
116
+ duration = @rep_times[rep.disk_path]
117
+ Nanoc2::CLI::Logger.instance.file(:low, :skip, rep.disk_path, duration)
118
+ end
119
+
120
+ # Show non-written reps
121
+ reps.select { |r| r.compiled? && r.attribute_named(:skip_output) }.each do |rep|
122
+ duration = @rep_times[rep.disk_path]
123
+ Nanoc2::CLI::Logger.instance.file(:low, :'not written', rep.disk_path, duration)
124
+ end
125
+
126
+ # Give general feedback
127
+ puts
128
+ puts "No objects were modified." unless reps.any? { |r| r.modified? }
129
+ puts "#{objs.nil? ? 'Site' : 'Object'} compiled in #{format('%.2f', Time.now - time_before)}s."
130
+
131
+ if options.has_key?(:verbose)
132
+ print_state_feedback(reps)
133
+ print_profiling_feedback(reps)
134
+ end
135
+ rescue Interrupt => e
136
+ exit(1)
137
+ rescue Exception => e
138
+ print_error(e)
139
+ exit(1)
140
+ end
141
+ end
142
+
143
+ private
144
+
145
+ def setup_notifications
146
+ Nanoc2::NotificationCenter.on(:compilation_started) do |rep|
147
+ rep_compilation_started(rep)
148
+ end
149
+ Nanoc2::NotificationCenter.on(:compilation_ended) do |rep|
150
+ rep_compilation_ended(rep)
151
+ end
152
+ Nanoc2::NotificationCenter.on(:filtering_started) do |rep, filter_name|
153
+ rep_filtering_started(rep, filter_name)
154
+ end
155
+ Nanoc2::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
156
+ rep_filtering_ended(rep, filter_name)
157
+ end
158
+ end
159
+
160
+ def print_state_feedback(reps)
161
+ # Categorise reps
162
+ rest = reps
163
+ created, rest = *rest.partition { |r| r.created? }
164
+ modified, rest = *rest.partition { |r| r.modified? }
165
+ skipped, rest = *rest.partition { |r| !r.compiled? }
166
+ not_written, rest = *rest.partition { |r| r.compiled? && r.attribute_named(:skip_output) }
167
+ identical = rest
168
+
169
+ # Print
170
+ puts
171
+ puts format(' %4d created', created.size)
172
+ puts format(' %4d modified', modified.size)
173
+ puts format(' %4d skipped', skipped.size)
174
+ puts format(' %4d not written', not_written.size)
175
+ puts format(' %4d identical', identical.size)
176
+ end
177
+
178
+ def print_profiling_feedback(reps)
179
+ # Get max filter length
180
+ max_filter_name_length = @filter_times.keys.map { |k| k.to_s.size }.max
181
+ return if max_filter_name_length.nil?
182
+
183
+ # Print warning if necessary
184
+ if reps.any? { |r| !r.compiled? }
185
+ $stderr.puts
186
+ $stderr.puts "Warning: profiling information may not be accurate because " +
187
+ "some objects were not compiled."
188
+ end
189
+
190
+ # Print header
191
+ puts
192
+ puts ' ' * max_filter_name_length + ' | count min avg max tot'
193
+ puts '-' * max_filter_name_length + '-+-----------------------------------'
194
+
195
+ @filter_times.to_a.sort_by { |r| r[1] }.each do |row|
196
+ # Extract data
197
+ filter_name, samples = *row
198
+
199
+ # Calculate stats
200
+ count = samples.size
201
+ min = samples.min
202
+ tot = samples.inject { |memo, i| memo + i}
203
+ avg = tot/count
204
+ max = samples.max
205
+
206
+ # Format stats
207
+ count = format('%4d', count)
208
+ min = format('%4.2f', min)
209
+ avg = format('%4.2f', avg)
210
+ max = format('%4.2f', max)
211
+ tot = format('%5.2f', tot)
212
+
213
+ # Output stats
214
+ filter_name = format("%#{max_filter_name_length}s", filter_name)
215
+ puts "#{filter_name} | #{count} #{min}s #{avg}s #{max}s #{tot}s"
216
+ end
217
+ end
218
+
219
+ def print_error(error)
220
+ # Get rep
221
+ rep = @base.site.compiler.stack.select { |i| i.is_a?(Nanoc2::PageRep) || i.is_a?(Nanoc2::AssetRep) }[-1]
222
+ rep_name = rep.nil? ? 'the site' : "#{rep.is_a?(Nanoc2::PageRep) ? rep.page.path : rep.asset.path} (rep #{rep.name})"
223
+
224
+ # Build message
225
+ case error
226
+ when Nanoc2::Errors::UnknownLayoutError
227
+ message = "Unknown layout: #{error.message}"
228
+ when Nanoc2::Errors::UnknownFilterError
229
+ message = "Unknown filter: #{error.message}"
230
+ when Nanoc2::Errors::CannotDetermineFilterError
231
+ message = "Cannot determine filter for layout: #{error.message}"
232
+ when Nanoc2::Errors::RecursiveCompilationError
233
+ message = "Recursive call to page content."
234
+ when Nanoc2::Errors::NoLongerSupportedError
235
+ message = "No longer supported: #{error.message}"
236
+ else
237
+ message = "Error: #{error.message}"
238
+ end
239
+
240
+ # Print message
241
+ $stderr.puts
242
+ $stderr.puts "ERROR: An exception occured while compiling #{rep_name}."
243
+ $stderr.puts
244
+ $stderr.puts "If you think this is a bug in nanoc, please do report it at " +
245
+ "<http://nanoc.stoneship.org/trac/newticket> -- thanks!"
246
+ $stderr.puts
247
+ $stderr.puts 'Message:'
248
+ $stderr.puts ' ' + message
249
+ $stderr.puts
250
+ $stderr.puts 'Compilation stack:'
251
+ @base.site.compiler.stack.reverse.each do |item|
252
+ if item.is_a?(Nanoc2::PageRep) # page rep
253
+ $stderr.puts " - [page] #{item.page.path} (rep #{item.name})"
254
+ elsif item.is_a?(Nanoc2::AssetRep) # asset rep
255
+ $stderr.puts " - [asset] #{item.asset.path} (rep #{item.name})"
256
+ else # layout
257
+ $stderr.puts " - [layout] #{item.path}"
258
+ end
259
+ end
260
+ $stderr.puts
261
+ $stderr.puts 'Backtrace:'
262
+ $stderr.puts error.backtrace.map { |t| ' - ' + t }.join("\n")
263
+ end
264
+
265
+ def rep_compilation_started(rep)
266
+ # Profile compilation
267
+ @rep_times ||= {}
268
+ @rep_times[rep.disk_path] = Time.now
269
+ end
270
+
271
+ def rep_compilation_ended(rep)
272
+ # Profile compilation
273
+ @rep_times ||= {}
274
+ @rep_times[rep.disk_path] = Time.now - @rep_times[rep.disk_path]
275
+
276
+ # Skip if not outputted
277
+ return if rep.attribute_named(:skip_output)
278
+
279
+ # Get action and level
280
+ action, level = *if rep.created?
281
+ [ :create, :high ]
282
+ elsif rep.modified?
283
+ [ :update, :high ]
284
+ elsif !rep.compiled?
285
+ [ nil, nil ]
286
+ else
287
+ [ :identical, :low ]
288
+ end
289
+
290
+ # Log
291
+ unless action.nil?
292
+ duration = @rep_times[rep.disk_path]
293
+ Nanoc2::CLI::Logger.instance.file(level, action, rep.disk_path, duration)
294
+ end
295
+ end
296
+
297
+ def rep_filtering_started(rep, filter_name)
298
+ @times_stack.push(Time.now)
299
+ end
300
+
301
+ def rep_filtering_ended(rep, filter_name)
302
+ # Get last time
303
+ time_start = @times_stack.pop
304
+
305
+ # Update times
306
+ @filter_times[filter_name.to_sym] ||= []
307
+ @filter_times[filter_name.to_sym] << Time.now - time_start
308
+ end
309
+
310
+ end
311
+
312
+ end