nanoc 2.0 → 2.1
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 +54 -0
- data/LICENSE +1 -1
- data/README +63 -3
- data/Rakefile +61 -14
- data/bin/nanoc +7 -199
- data/lib/nanoc/base/asset.rb +113 -0
- data/lib/nanoc/base/asset_defaults.rb +21 -0
- data/lib/nanoc/base/asset_rep.rb +277 -0
- data/lib/nanoc/base/binary_filter.rb +44 -0
- data/lib/nanoc/base/code.rb +41 -0
- data/lib/nanoc/base/compiler.rb +46 -34
- data/lib/nanoc/base/core_ext/hash.rb +51 -7
- data/lib/nanoc/base/core_ext/string.rb +8 -0
- data/lib/nanoc/base/data_source.rb +253 -20
- data/lib/nanoc/base/defaults.rb +30 -0
- data/lib/nanoc/base/enhancements.rb +9 -84
- data/lib/nanoc/base/filter.rb +109 -6
- data/lib/nanoc/base/layout.rb +91 -0
- data/lib/nanoc/base/notification_center.rb +66 -0
- data/lib/nanoc/base/page.rb +94 -121
- data/lib/nanoc/base/page_defaults.rb +20 -0
- data/lib/nanoc/base/page_rep.rb +318 -0
- data/lib/nanoc/base/plugin.rb +57 -9
- data/lib/nanoc/base/proxies/asset_proxy.rb +29 -0
- data/lib/nanoc/base/proxies/asset_rep_proxy.rb +26 -0
- data/lib/nanoc/base/proxies/layout_proxy.rb +25 -0
- data/lib/nanoc/base/proxies/page_proxy.rb +35 -0
- data/lib/nanoc/base/proxies/page_rep_proxy.rb +28 -0
- data/lib/nanoc/base/proxy.rb +37 -0
- data/lib/nanoc/base/router.rb +72 -0
- data/lib/nanoc/base/site.rb +220 -91
- data/lib/nanoc/base/template.rb +64 -0
- data/lib/nanoc/binary_filters/image_science_thumbnail.rb +28 -0
- data/lib/nanoc/cli/base.rb +219 -0
- data/lib/nanoc/cli/cli.rb +16 -0
- data/lib/nanoc/cli/command.rb +105 -0
- data/lib/nanoc/cli/commands/autocompile.rb +80 -0
- data/lib/nanoc/cli/commands/compile.rb +273 -0
- data/lib/nanoc/cli/commands/create_layout.rb +85 -0
- data/lib/nanoc/cli/commands/create_page.rb +85 -0
- data/lib/nanoc/cli/commands/create_site.rb +327 -0
- data/lib/nanoc/cli/commands/create_template.rb +76 -0
- data/lib/nanoc/cli/commands/help.rb +69 -0
- data/lib/nanoc/cli/commands/info.rb +114 -0
- data/lib/nanoc/cli/commands/switch.rb +141 -0
- data/lib/nanoc/cli/commands/update.rb +91 -0
- data/lib/nanoc/cli/ext.rb +37 -0
- data/lib/nanoc/cli/logger.rb +66 -0
- data/lib/nanoc/cli/option_parser.rb +168 -0
- data/lib/nanoc/cli.rb +1 -0
- data/lib/nanoc/data_sources/filesystem.rb +650 -215
- data/lib/nanoc/data_sources/filesystem_combined.rb +495 -0
- data/lib/nanoc/extra/auto_compiler.rb +265 -0
- data/lib/nanoc/extra/context.rb +22 -0
- data/lib/nanoc/extra/core_ext/hash.rb +54 -0
- data/lib/nanoc/extra/core_ext/time.rb +13 -0
- data/lib/nanoc/extra/file_proxy.rb +29 -0
- data/lib/nanoc/extra/vcs.rb +48 -0
- data/lib/nanoc/extra/vcses/bazaar.rb +21 -0
- data/lib/nanoc/extra/vcses/dummy.rb +20 -0
- data/lib/nanoc/extra/vcses/git.rb +21 -0
- data/lib/nanoc/extra/vcses/mercurial.rb +21 -0
- data/lib/nanoc/extra/vcses/subversion.rb +21 -0
- data/lib/nanoc/filters/bluecloth.rb +13 -0
- data/lib/nanoc/filters/erb.rb +6 -22
- data/lib/nanoc/filters/erubis.rb +14 -0
- data/lib/nanoc/filters/haml.rb +11 -6
- data/lib/nanoc/filters/markaby.rb +5 -5
- data/lib/nanoc/filters/maruku.rb +14 -0
- data/lib/nanoc/filters/old.rb +19 -0
- data/lib/nanoc/filters/rdiscount.rb +13 -0
- data/lib/nanoc/filters/rdoc.rb +5 -4
- data/lib/nanoc/filters/redcloth.rb +14 -0
- data/lib/nanoc/filters/rubypants.rb +14 -0
- data/lib/nanoc/filters/sass.rb +13 -0
- data/lib/nanoc/helpers/blogging.rb +170 -0
- data/lib/nanoc/helpers/capturing.rb +59 -0
- data/lib/nanoc/helpers/html_escape.rb +23 -0
- data/lib/nanoc/helpers/link_to.rb +69 -0
- data/lib/nanoc/helpers/render.rb +47 -0
- data/lib/nanoc/helpers/tagging.rb +52 -0
- data/lib/nanoc/helpers/xml_sitemap.rb +58 -0
- data/lib/nanoc/routers/default.rb +54 -0
- data/lib/nanoc/routers/no_dirs.rb +66 -0
- data/lib/nanoc/routers/versioned.rb +79 -0
- data/lib/nanoc.rb +83 -12
- metadata +144 -47
- data/lib/nanoc/base/auto_compiler.rb +0 -124
- data/lib/nanoc/base/layout_processor.rb +0 -33
- data/lib/nanoc/base/page_proxy.rb +0 -31
- data/lib/nanoc/base/plugin_manager.rb +0 -33
- data/lib/nanoc/data_sources/database.rb +0 -259
- data/lib/nanoc/data_sources/trivial.rb +0 -145
- data/lib/nanoc/filters/markdown.rb +0 -13
- data/lib/nanoc/filters/smartypants.rb +0 -13
- data/lib/nanoc/filters/textile.rb +0 -13
- data/lib/nanoc/layout_processors/erb.rb +0 -35
- data/lib/nanoc/layout_processors/haml.rb +0 -18
- data/lib/nanoc/layout_processors/markaby.rb +0 -16
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module Nanoc::CLI
|
|
2
|
+
|
|
3
|
+
# Nanoc::CLI::Command represents a command that can be executed on the
|
|
4
|
+
# commandline. It is an abstract superclass for all commands.
|
|
5
|
+
class Command
|
|
6
|
+
|
|
7
|
+
attr_accessor :base
|
|
8
|
+
|
|
9
|
+
# Returns a string containing the name of thi command. Subclasses must
|
|
10
|
+
# implement this method.
|
|
11
|
+
def name
|
|
12
|
+
raise NotImplementedError.new("Command subclasses should override #name")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns an array of strings containing the aliases for this command.
|
|
16
|
+
# Subclasses must implement this method.
|
|
17
|
+
def aliases
|
|
18
|
+
raise NotImplementedError.new("Command subclasses should override #aliases")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns a string containing this command's short description, which
|
|
22
|
+
# should not be longer than 50 characters. Subclasses must implement this
|
|
23
|
+
# method.
|
|
24
|
+
def short_desc
|
|
25
|
+
raise NotImplementedError.new("Command subclasses should override #short_desc")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Returns a string containing this command's complete description, which
|
|
29
|
+
# should explain what this command does and how it works in detail.
|
|
30
|
+
# Subclasses must implement this method.
|
|
31
|
+
def long_desc
|
|
32
|
+
raise NotImplementedError.new("Command subclasses should override #long_desc")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns a string containing this command's usage. Subclasses must
|
|
36
|
+
# implement this method.
|
|
37
|
+
def usage
|
|
38
|
+
raise NotImplementedError.new("Command subclasses should override #usage")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Returns an array containing this command's option definitions. See the
|
|
42
|
+
# documentation for Nanoc::CLI::OptionParser for details on what option
|
|
43
|
+
# definitions look like. Subclasses may implement this method if the
|
|
44
|
+
# command has options.
|
|
45
|
+
def option_definitions
|
|
46
|
+
[]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Executes the command. Subclasses must implement this method
|
|
50
|
+
# (obviously... what's the point of a command that can't be run?).
|
|
51
|
+
#
|
|
52
|
+
# +options+:: A hash containing the parsed commandline options. For
|
|
53
|
+
# example, '--foo=bar' will be converted into { :foo => 'bar'
|
|
54
|
+
# }. See the Nanoc::CLI::OptionParser documentation for
|
|
55
|
+
# details.
|
|
56
|
+
#
|
|
57
|
+
# +arguments+:: An array of strings representing the commandline arguments
|
|
58
|
+
# given to this command.
|
|
59
|
+
def run(options, arguments)
|
|
60
|
+
raise NotImplementedError.new("Command subclasses should override #run")
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Returns the help text for this command.
|
|
64
|
+
def help
|
|
65
|
+
text = ''
|
|
66
|
+
|
|
67
|
+
# Append usage
|
|
68
|
+
text << usage + "\n"
|
|
69
|
+
|
|
70
|
+
# Append aliases
|
|
71
|
+
unless aliases.empty?
|
|
72
|
+
text << "\n"
|
|
73
|
+
text << "aliases: #{aliases.join(' ')}\n"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Append short description
|
|
77
|
+
text << "\n"
|
|
78
|
+
text << short_desc + "\n"
|
|
79
|
+
|
|
80
|
+
# Append long description
|
|
81
|
+
text << "\n"
|
|
82
|
+
text << long_desc.wrap_and_indent(78, 4) + "\n"
|
|
83
|
+
|
|
84
|
+
# Append options
|
|
85
|
+
unless option_definitions.empty?
|
|
86
|
+
text << "\n"
|
|
87
|
+
text << "options:\n"
|
|
88
|
+
text << "\n"
|
|
89
|
+
option_definitions.sort { |x,y| x[:long] <=> y[:long] }.each do |opt_def|
|
|
90
|
+
text << sprintf(" -%1s --%-10s %s\n", opt_def[:short], opt_def[:long], opt_def[:desc])
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Return text
|
|
95
|
+
text
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Compares this command's name to the other given command's name.
|
|
99
|
+
def <=>(other)
|
|
100
|
+
self.name <=> other.name
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
module Nanoc::CLI
|
|
2
|
+
|
|
3
|
+
class AutocompileCommand < 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 = Nanoc::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 ' + Nanoc::Extra::AutoCompiler::HANDLER_NAMES[0].to_s + ').'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def usage
|
|
29
|
+
"nanoc 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 = Nanoc::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 Nanoc::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,273 @@
|
|
|
1
|
+
module Nanoc::CLI
|
|
2
|
+
|
|
3
|
+
class CompileCommand < 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
|
+
"nanoc 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
|
+
]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def run(options, arguments)
|
|
39
|
+
# Make sure we are in a nanoc site directory
|
|
40
|
+
@base.require_site
|
|
41
|
+
|
|
42
|
+
# Find object with given path
|
|
43
|
+
if arguments.size == 0
|
|
44
|
+
objs = nil
|
|
45
|
+
else
|
|
46
|
+
objs = arguments.map do |path|
|
|
47
|
+
# Find object
|
|
48
|
+
path = path.cleaned_path
|
|
49
|
+
obj = @base.site.pages.find { |page| page.path == path }
|
|
50
|
+
obj = @base.site.assets.find { |asset| asset.path == path } if obj.nil?
|
|
51
|
+
|
|
52
|
+
# Ensure object
|
|
53
|
+
if obj.nil?
|
|
54
|
+
$stderr.puts "Unknown page or asset: #{path}"
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
obj
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Compile site
|
|
63
|
+
begin
|
|
64
|
+
# Give feedback
|
|
65
|
+
puts "Compiling #{objs.nil? ? 'site' : 'objects'}..."
|
|
66
|
+
|
|
67
|
+
# Initialize profiling stuff
|
|
68
|
+
time_before = Time.now
|
|
69
|
+
@filter_times ||= {}
|
|
70
|
+
@times_stack ||= []
|
|
71
|
+
setup_notifications
|
|
72
|
+
|
|
73
|
+
# Compile
|
|
74
|
+
@base.site.compiler.run(
|
|
75
|
+
objs,
|
|
76
|
+
:even_when_not_outdated => options.has_key?(:all)
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Find reps
|
|
80
|
+
page_reps = @base.site.pages.map { |p| p.reps }.flatten
|
|
81
|
+
asset_reps = @base.site.assets.map { |a| a.reps }.flatten
|
|
82
|
+
reps = page_reps + asset_reps
|
|
83
|
+
|
|
84
|
+
# Show skipped reps
|
|
85
|
+
reps.select { |r| !r.compiled? }.each do |rep|
|
|
86
|
+
duration = @rep_times[rep.disk_path]
|
|
87
|
+
Nanoc::CLI::Logger.instance.file(:low, :skip, rep.disk_path, duration)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Give general feedback
|
|
91
|
+
puts
|
|
92
|
+
puts "No objects were modified." unless reps.any? { |r| r.modified? }
|
|
93
|
+
puts "#{objs.nil? ? 'Site' : 'Object'} compiled in #{format('%.2f', Time.now - time_before)}s."
|
|
94
|
+
|
|
95
|
+
if options.has_key?(:verbose)
|
|
96
|
+
print_state_feedback(reps)
|
|
97
|
+
print_profiling_feedback(reps)
|
|
98
|
+
end
|
|
99
|
+
rescue Interrupt => e
|
|
100
|
+
exit
|
|
101
|
+
rescue Exception => e
|
|
102
|
+
print_error(e)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def setup_notifications
|
|
109
|
+
Nanoc::NotificationCenter.on(:compilation_started) do |rep|
|
|
110
|
+
rep_compilation_started(rep)
|
|
111
|
+
end
|
|
112
|
+
Nanoc::NotificationCenter.on(:compilation_ended) do |rep|
|
|
113
|
+
rep_compilation_ended(rep)
|
|
114
|
+
end
|
|
115
|
+
Nanoc::NotificationCenter.on(:filtering_started) do |rep, filter_name|
|
|
116
|
+
rep_filtering_started(rep, filter_name)
|
|
117
|
+
end
|
|
118
|
+
Nanoc::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
|
|
119
|
+
rep_filtering_ended(rep, filter_name)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def print_state_feedback(reps)
|
|
124
|
+
# Categorise reps
|
|
125
|
+
rest = reps
|
|
126
|
+
created, rest = *rest.partition { |r| r.created? }
|
|
127
|
+
modified, rest = *rest.partition { |r| r.modified? }
|
|
128
|
+
skipped, rest = *rest.partition { |r| !r.compiled? }
|
|
129
|
+
identical = rest
|
|
130
|
+
|
|
131
|
+
# Print
|
|
132
|
+
puts
|
|
133
|
+
puts format(' %4d created', created.size)
|
|
134
|
+
puts format(' %4d modified', modified.size)
|
|
135
|
+
puts format(' %4d skipped', skipped.size)
|
|
136
|
+
puts format(' %4d identical', identical.size)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def print_profiling_feedback(reps)
|
|
140
|
+
# Get max filter length
|
|
141
|
+
max_filter_name_length = @filter_times.keys.map { |k| k.to_s.size }.max
|
|
142
|
+
return if max_filter_name_length.nil?
|
|
143
|
+
|
|
144
|
+
# Print warning if necessary
|
|
145
|
+
if reps.any? { |r| !r.compiled? }
|
|
146
|
+
$stderr.puts
|
|
147
|
+
$stderr.puts "Warning: profiling information may not be accurate because " +
|
|
148
|
+
"some objects were not compiled."
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Print header
|
|
152
|
+
puts
|
|
153
|
+
puts ' ' * max_filter_name_length + ' | count min avg max tot'
|
|
154
|
+
puts '-' * max_filter_name_length + '-+-----------------------------------'
|
|
155
|
+
|
|
156
|
+
@filter_times.to_a.sort_by { |r| r[1] }.each do |row|
|
|
157
|
+
# Extract data
|
|
158
|
+
filter_name, samples = *row
|
|
159
|
+
|
|
160
|
+
# Calculate stats
|
|
161
|
+
count = samples.size
|
|
162
|
+
min = samples.min
|
|
163
|
+
tot = samples.inject { |memo, i| memo + i}
|
|
164
|
+
avg = tot/count
|
|
165
|
+
max = samples.max
|
|
166
|
+
|
|
167
|
+
# Format stats
|
|
168
|
+
count = format('%4d', count)
|
|
169
|
+
min = format('%4.2f', min)
|
|
170
|
+
avg = format('%4.2f', avg)
|
|
171
|
+
max = format('%4.2f', max)
|
|
172
|
+
tot = format('%5.2f', tot)
|
|
173
|
+
|
|
174
|
+
# Output stats
|
|
175
|
+
filter_name = format("%#{max_filter_name_length}s", filter_name)
|
|
176
|
+
puts "#{filter_name} | #{count} #{min}s #{avg}s #{max}s #{tot}s"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def print_error(error)
|
|
181
|
+
# Get rep
|
|
182
|
+
rep = @base.site.compiler.stack.select { |i| i.is_a?(Nanoc::PageRep) || i.is_a?(Nanoc::AssetRep) }[-1]
|
|
183
|
+
rep_name = rep.nil? ? 'the site' : "#{rep.is_a?(Nanoc::PageRep) ? rep.page.path : rep.asset.path} (rep #{rep.name})"
|
|
184
|
+
|
|
185
|
+
# Build message
|
|
186
|
+
case error
|
|
187
|
+
when Nanoc::Errors::UnknownLayoutError
|
|
188
|
+
message = "Unknown layout: #{error.message}"
|
|
189
|
+
when Nanoc::Errors::UnknownFilterError
|
|
190
|
+
message = "Unknown filter: #{error.message}"
|
|
191
|
+
when Nanoc::Errors::CannotDetermineFilterError
|
|
192
|
+
message = "Cannot determine filter for layout: #{error.message}"
|
|
193
|
+
when Nanoc::Errors::RecursiveCompilationError
|
|
194
|
+
message = "Recursive call to page content."
|
|
195
|
+
when Nanoc::Errors::NoLongerSupportedError
|
|
196
|
+
message = "No longer supported: #{error.message}"
|
|
197
|
+
else
|
|
198
|
+
message = "Error: #{error.message}"
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Print message
|
|
202
|
+
$stderr.puts
|
|
203
|
+
$stderr.puts "ERROR: An exception occured while compiling #{rep_name}."
|
|
204
|
+
$stderr.puts
|
|
205
|
+
$stderr.puts "If you think this is a bug in nanoc, please do report it at " +
|
|
206
|
+
"<http://nanoc.stoneship.org/trac/newticket> -- thanks!"
|
|
207
|
+
$stderr.puts
|
|
208
|
+
$stderr.puts 'Message:'
|
|
209
|
+
$stderr.puts ' ' + message
|
|
210
|
+
$stderr.puts
|
|
211
|
+
$stderr.puts 'Compilation stack:'
|
|
212
|
+
@base.site.compiler.stack.reverse.each do |item|
|
|
213
|
+
if item.is_a?(Nanoc::PageRep) # page rep
|
|
214
|
+
$stderr.puts " - [page] #{item.page.path} (rep #{item.name})"
|
|
215
|
+
elsif item.is_a?(Nanoc::AssetRep) # asset rep
|
|
216
|
+
$stderr.puts " - [asset] #{item.asset.path} (rep #{item.name})"
|
|
217
|
+
else # layout
|
|
218
|
+
$stderr.puts " - [layout] #{item.path}"
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
$stderr.puts
|
|
222
|
+
$stderr.puts 'Backtrace:'
|
|
223
|
+
$stderr.puts error.backtrace.map { |t| ' - ' + t }.join("\n")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def rep_compilation_started(rep)
|
|
227
|
+
# Profile compilation
|
|
228
|
+
@rep_times ||= {}
|
|
229
|
+
@rep_times[rep.disk_path] = Time.now
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def rep_compilation_ended(rep)
|
|
233
|
+
# Profile compilation
|
|
234
|
+
@rep_times ||= {}
|
|
235
|
+
@rep_times[rep.disk_path] = Time.now - @rep_times[rep.disk_path]
|
|
236
|
+
|
|
237
|
+
# Skip if not outputted
|
|
238
|
+
return if rep.attribute_named(:skip_output)
|
|
239
|
+
|
|
240
|
+
# Get action and level
|
|
241
|
+
action, level = *if rep.created?
|
|
242
|
+
[ :create, :high ]
|
|
243
|
+
elsif rep.modified?
|
|
244
|
+
[ :update, :high ]
|
|
245
|
+
elsif !rep.compiled?
|
|
246
|
+
[ nil, nil ]
|
|
247
|
+
else
|
|
248
|
+
[ :identical, :low ]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Log
|
|
252
|
+
unless action.nil?
|
|
253
|
+
duration = @rep_times[rep.disk_path]
|
|
254
|
+
Nanoc::CLI::Logger.instance.file(level, action, rep.disk_path, duration)
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def rep_filtering_started(rep, filter_name)
|
|
259
|
+
@times_stack.push(Time.now)
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def rep_filtering_ended(rep, filter_name)
|
|
263
|
+
# Get last time
|
|
264
|
+
time_start = @times_stack.pop
|
|
265
|
+
|
|
266
|
+
# Update times
|
|
267
|
+
@filter_times[filter_name.to_sym] ||= []
|
|
268
|
+
@filter_times[filter_name.to_sym] << Time.now - time_start
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Nanoc::CLI
|
|
2
|
+
|
|
3
|
+
class CreateLayoutCommand < Command # :nodoc:
|
|
4
|
+
|
|
5
|
+
def name
|
|
6
|
+
'create_layout'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def aliases
|
|
10
|
+
[ 'cl' ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def short_desc
|
|
14
|
+
'create a layout'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def long_desc
|
|
18
|
+
'Create a new layout in the current site.'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def usage
|
|
22
|
+
"nanoc create_layout [path]"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def option_definitions
|
|
26
|
+
[
|
|
27
|
+
# --vcs
|
|
28
|
+
{
|
|
29
|
+
:long => 'vcs', :short => 'c', :argument => :required,
|
|
30
|
+
:desc => 'select the VCS to use'
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def run(options, arguments)
|
|
36
|
+
# Check arguments
|
|
37
|
+
if arguments.length != 1
|
|
38
|
+
$stderr.puts "usage: #{usage}"
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Extract arguments
|
|
43
|
+
path = arguments[0].cleaned_path
|
|
44
|
+
|
|
45
|
+
# Make sure we are in a nanoc site directory
|
|
46
|
+
@base.require_site
|
|
47
|
+
|
|
48
|
+
# Set VCS if possible
|
|
49
|
+
@base.set_vcs(options[:vcs])
|
|
50
|
+
|
|
51
|
+
# Check whether layout is unique
|
|
52
|
+
if !@base.site.layouts.find { |l| l.path == path }.nil?
|
|
53
|
+
$stderr.puts "A layout already exists at #{path}. Please pick a unique name " +
|
|
54
|
+
"for the layout you are creating."
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Setup notifications
|
|
59
|
+
Nanoc::NotificationCenter.on(:file_created) do |file_path|
|
|
60
|
+
Nanoc::CLI::Logger.instance.file(:high, :create, file_path)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Create layout
|
|
64
|
+
layout = Nanoc::Layout.new(
|
|
65
|
+
"<html>\n" +
|
|
66
|
+
" <head>\n" +
|
|
67
|
+
" <title><%= @page.title %></title>\n" +
|
|
68
|
+
" </head>\n" +
|
|
69
|
+
" <body>\n" +
|
|
70
|
+
" <p>Hi, I'm a new layout. Please customize me!</p>\n" +
|
|
71
|
+
"<%= @page.content %>\n" +
|
|
72
|
+
" </body>\n" +
|
|
73
|
+
"</html>\n",
|
|
74
|
+
{ :filter => 'erb' },
|
|
75
|
+
path
|
|
76
|
+
)
|
|
77
|
+
layout.site = @base.site
|
|
78
|
+
layout.save
|
|
79
|
+
|
|
80
|
+
puts "A layout has been created at #{path}."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module Nanoc::CLI
|
|
2
|
+
|
|
3
|
+
class CreatePageCommand < Command # :nodoc:
|
|
4
|
+
|
|
5
|
+
def name
|
|
6
|
+
'create_page'
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def aliases
|
|
10
|
+
[ 'cp' ]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def short_desc
|
|
14
|
+
'create a page'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def long_desc
|
|
18
|
+
'Create a new page in the current site. The template that will be ' +
|
|
19
|
+
'used for generating the page will be \'default\', unless otherwise ' +
|
|
20
|
+
'specified.'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def usage
|
|
24
|
+
"nanoc create_page [options] [path]"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def option_definitions
|
|
28
|
+
[
|
|
29
|
+
# --vcs
|
|
30
|
+
{
|
|
31
|
+
:long => 'vcs', :short => 'c', :argument => :required,
|
|
32
|
+
:desc => 'select the VCS to use'
|
|
33
|
+
},
|
|
34
|
+
# --template
|
|
35
|
+
{
|
|
36
|
+
:long => 'template', :short => 't', :argument => :required,
|
|
37
|
+
:desc => 'specify the template for the new page'
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def run(options, arguments)
|
|
43
|
+
# Check arguments
|
|
44
|
+
if arguments.length != 1
|
|
45
|
+
$stderr.puts "usage: #{usage}"
|
|
46
|
+
exit 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Extract arguments and options
|
|
50
|
+
path = arguments[0].cleaned_path
|
|
51
|
+
template_name = options[:template] || 'default'
|
|
52
|
+
|
|
53
|
+
# Make sure we are in a nanoc site directory
|
|
54
|
+
@base.require_site
|
|
55
|
+
|
|
56
|
+
# Set VCS if possible
|
|
57
|
+
@base.set_vcs(options[:vcs])
|
|
58
|
+
|
|
59
|
+
# Find template
|
|
60
|
+
template = @base.site.templates.find { |t| t.name == template_name }
|
|
61
|
+
if template.nil?
|
|
62
|
+
$stderr.puts "A template named '#{template_name}' was not found; aborting."
|
|
63
|
+
exit 1
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Setup notifications
|
|
67
|
+
Nanoc::NotificationCenter.on(:file_created) do |file_path|
|
|
68
|
+
Nanoc::CLI::Logger.instance.file(:high, :create, file_path)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Create page
|
|
72
|
+
page = Nanoc::Page.new(
|
|
73
|
+
template.page_content,
|
|
74
|
+
template.page_attributes,
|
|
75
|
+
path
|
|
76
|
+
)
|
|
77
|
+
page.site = @base.site
|
|
78
|
+
page.save
|
|
79
|
+
|
|
80
|
+
puts "A page has been created at #{path}."
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|