nanoc2 2.2.3

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