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.
- data/ChangeLog +3 -0
- data/LICENSE +19 -0
- data/README +75 -0
- data/Rakefile +76 -0
- data/bin/nanoc2 +26 -0
- data/lib/nanoc2.rb +73 -0
- data/lib/nanoc2/base.rb +26 -0
- data/lib/nanoc2/base/asset.rb +117 -0
- data/lib/nanoc2/base/asset_defaults.rb +21 -0
- data/lib/nanoc2/base/asset_rep.rb +282 -0
- data/lib/nanoc2/base/binary_filter.rb +44 -0
- data/lib/nanoc2/base/code.rb +41 -0
- data/lib/nanoc2/base/compiler.rb +67 -0
- data/lib/nanoc2/base/core_ext.rb +2 -0
- data/lib/nanoc2/base/core_ext/hash.rb +78 -0
- data/lib/nanoc2/base/core_ext/string.rb +8 -0
- data/lib/nanoc2/base/data_source.rb +286 -0
- data/lib/nanoc2/base/defaults.rb +30 -0
- data/lib/nanoc2/base/filter.rb +93 -0
- data/lib/nanoc2/base/layout.rb +91 -0
- data/lib/nanoc2/base/notification_center.rb +66 -0
- data/lib/nanoc2/base/page.rb +132 -0
- data/lib/nanoc2/base/page_defaults.rb +20 -0
- data/lib/nanoc2/base/page_rep.rb +324 -0
- data/lib/nanoc2/base/plugin.rb +71 -0
- data/lib/nanoc2/base/proxies.rb +5 -0
- data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
- data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
- data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
- data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
- data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
- data/lib/nanoc2/base/proxy.rb +37 -0
- data/lib/nanoc2/base/router.rb +72 -0
- data/lib/nanoc2/base/site.rb +274 -0
- data/lib/nanoc2/base/template.rb +64 -0
- data/lib/nanoc2/binary_filters.rb +1 -0
- data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
- data/lib/nanoc2/cli.rb +9 -0
- data/lib/nanoc2/cli/base.rb +132 -0
- data/lib/nanoc2/cli/commands.rb +10 -0
- data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
- data/lib/nanoc2/cli/commands/compile.rb +312 -0
- data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
- data/lib/nanoc2/cli/commands/create_page.rb +85 -0
- data/lib/nanoc2/cli/commands/create_site.rb +323 -0
- data/lib/nanoc2/cli/commands/create_template.rb +76 -0
- data/lib/nanoc2/cli/commands/help.rb +69 -0
- data/lib/nanoc2/cli/commands/info.rb +125 -0
- data/lib/nanoc2/cli/commands/switch.rb +141 -0
- data/lib/nanoc2/cli/commands/update.rb +91 -0
- data/lib/nanoc2/cli/logger.rb +72 -0
- data/lib/nanoc2/data_sources.rb +2 -0
- data/lib/nanoc2/data_sources/filesystem.rb +707 -0
- data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
- data/lib/nanoc2/extra.rb +6 -0
- data/lib/nanoc2/extra/auto_compiler.rb +285 -0
- data/lib/nanoc2/extra/context.rb +22 -0
- data/lib/nanoc2/extra/core_ext.rb +2 -0
- data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
- data/lib/nanoc2/extra/core_ext/time.rb +13 -0
- data/lib/nanoc2/extra/file_proxy.rb +29 -0
- data/lib/nanoc2/extra/vcs.rb +48 -0
- data/lib/nanoc2/extra/vcses.rb +5 -0
- data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
- data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
- data/lib/nanoc2/extra/vcses/git.rb +21 -0
- data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
- data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
- data/lib/nanoc2/filters.rb +16 -0
- data/lib/nanoc2/filters/bluecloth.rb +13 -0
- data/lib/nanoc2/filters/erb.rb +19 -0
- data/lib/nanoc2/filters/erubis.rb +14 -0
- data/lib/nanoc2/filters/haml.rb +21 -0
- data/lib/nanoc2/filters/markaby.rb +14 -0
- data/lib/nanoc2/filters/maruku.rb +14 -0
- data/lib/nanoc2/filters/old.rb +19 -0
- data/lib/nanoc2/filters/rainpress.rb +13 -0
- data/lib/nanoc2/filters/rdiscount.rb +13 -0
- data/lib/nanoc2/filters/rdoc.rb +23 -0
- data/lib/nanoc2/filters/redcloth.rb +14 -0
- data/lib/nanoc2/filters/relativize_paths.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
- data/lib/nanoc2/filters/rubypants.rb +14 -0
- data/lib/nanoc2/filters/sass.rb +18 -0
- data/lib/nanoc2/helpers.rb +9 -0
- data/lib/nanoc2/helpers/blogging.rb +217 -0
- data/lib/nanoc2/helpers/capturing.rb +63 -0
- data/lib/nanoc2/helpers/filtering.rb +54 -0
- data/lib/nanoc2/helpers/html_escape.rb +25 -0
- data/lib/nanoc2/helpers/link_to.rb +113 -0
- data/lib/nanoc2/helpers/render.rb +49 -0
- data/lib/nanoc2/helpers/tagging.rb +56 -0
- data/lib/nanoc2/helpers/text.rb +38 -0
- data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
- data/lib/nanoc2/routers.rb +3 -0
- data/lib/nanoc2/routers/default.rb +54 -0
- data/lib/nanoc2/routers/no_dirs.rb +66 -0
- data/lib/nanoc2/routers/versioned.rb +79 -0
- 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
|
data/lib/nanoc2/cli.rb
ADDED
@@ -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
|