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.
Files changed (99) hide show
  1. data/ChangeLog +54 -0
  2. data/LICENSE +1 -1
  3. data/README +63 -3
  4. data/Rakefile +61 -14
  5. data/bin/nanoc +7 -199
  6. data/lib/nanoc/base/asset.rb +113 -0
  7. data/lib/nanoc/base/asset_defaults.rb +21 -0
  8. data/lib/nanoc/base/asset_rep.rb +277 -0
  9. data/lib/nanoc/base/binary_filter.rb +44 -0
  10. data/lib/nanoc/base/code.rb +41 -0
  11. data/lib/nanoc/base/compiler.rb +46 -34
  12. data/lib/nanoc/base/core_ext/hash.rb +51 -7
  13. data/lib/nanoc/base/core_ext/string.rb +8 -0
  14. data/lib/nanoc/base/data_source.rb +253 -20
  15. data/lib/nanoc/base/defaults.rb +30 -0
  16. data/lib/nanoc/base/enhancements.rb +9 -84
  17. data/lib/nanoc/base/filter.rb +109 -6
  18. data/lib/nanoc/base/layout.rb +91 -0
  19. data/lib/nanoc/base/notification_center.rb +66 -0
  20. data/lib/nanoc/base/page.rb +94 -121
  21. data/lib/nanoc/base/page_defaults.rb +20 -0
  22. data/lib/nanoc/base/page_rep.rb +318 -0
  23. data/lib/nanoc/base/plugin.rb +57 -9
  24. data/lib/nanoc/base/proxies/asset_proxy.rb +29 -0
  25. data/lib/nanoc/base/proxies/asset_rep_proxy.rb +26 -0
  26. data/lib/nanoc/base/proxies/layout_proxy.rb +25 -0
  27. data/lib/nanoc/base/proxies/page_proxy.rb +35 -0
  28. data/lib/nanoc/base/proxies/page_rep_proxy.rb +28 -0
  29. data/lib/nanoc/base/proxy.rb +37 -0
  30. data/lib/nanoc/base/router.rb +72 -0
  31. data/lib/nanoc/base/site.rb +220 -91
  32. data/lib/nanoc/base/template.rb +64 -0
  33. data/lib/nanoc/binary_filters/image_science_thumbnail.rb +28 -0
  34. data/lib/nanoc/cli/base.rb +219 -0
  35. data/lib/nanoc/cli/cli.rb +16 -0
  36. data/lib/nanoc/cli/command.rb +105 -0
  37. data/lib/nanoc/cli/commands/autocompile.rb +80 -0
  38. data/lib/nanoc/cli/commands/compile.rb +273 -0
  39. data/lib/nanoc/cli/commands/create_layout.rb +85 -0
  40. data/lib/nanoc/cli/commands/create_page.rb +85 -0
  41. data/lib/nanoc/cli/commands/create_site.rb +327 -0
  42. data/lib/nanoc/cli/commands/create_template.rb +76 -0
  43. data/lib/nanoc/cli/commands/help.rb +69 -0
  44. data/lib/nanoc/cli/commands/info.rb +114 -0
  45. data/lib/nanoc/cli/commands/switch.rb +141 -0
  46. data/lib/nanoc/cli/commands/update.rb +91 -0
  47. data/lib/nanoc/cli/ext.rb +37 -0
  48. data/lib/nanoc/cli/logger.rb +66 -0
  49. data/lib/nanoc/cli/option_parser.rb +168 -0
  50. data/lib/nanoc/cli.rb +1 -0
  51. data/lib/nanoc/data_sources/filesystem.rb +650 -215
  52. data/lib/nanoc/data_sources/filesystem_combined.rb +495 -0
  53. data/lib/nanoc/extra/auto_compiler.rb +265 -0
  54. data/lib/nanoc/extra/context.rb +22 -0
  55. data/lib/nanoc/extra/core_ext/hash.rb +54 -0
  56. data/lib/nanoc/extra/core_ext/time.rb +13 -0
  57. data/lib/nanoc/extra/file_proxy.rb +29 -0
  58. data/lib/nanoc/extra/vcs.rb +48 -0
  59. data/lib/nanoc/extra/vcses/bazaar.rb +21 -0
  60. data/lib/nanoc/extra/vcses/dummy.rb +20 -0
  61. data/lib/nanoc/extra/vcses/git.rb +21 -0
  62. data/lib/nanoc/extra/vcses/mercurial.rb +21 -0
  63. data/lib/nanoc/extra/vcses/subversion.rb +21 -0
  64. data/lib/nanoc/filters/bluecloth.rb +13 -0
  65. data/lib/nanoc/filters/erb.rb +6 -22
  66. data/lib/nanoc/filters/erubis.rb +14 -0
  67. data/lib/nanoc/filters/haml.rb +11 -6
  68. data/lib/nanoc/filters/markaby.rb +5 -5
  69. data/lib/nanoc/filters/maruku.rb +14 -0
  70. data/lib/nanoc/filters/old.rb +19 -0
  71. data/lib/nanoc/filters/rdiscount.rb +13 -0
  72. data/lib/nanoc/filters/rdoc.rb +5 -4
  73. data/lib/nanoc/filters/redcloth.rb +14 -0
  74. data/lib/nanoc/filters/rubypants.rb +14 -0
  75. data/lib/nanoc/filters/sass.rb +13 -0
  76. data/lib/nanoc/helpers/blogging.rb +170 -0
  77. data/lib/nanoc/helpers/capturing.rb +59 -0
  78. data/lib/nanoc/helpers/html_escape.rb +23 -0
  79. data/lib/nanoc/helpers/link_to.rb +69 -0
  80. data/lib/nanoc/helpers/render.rb +47 -0
  81. data/lib/nanoc/helpers/tagging.rb +52 -0
  82. data/lib/nanoc/helpers/xml_sitemap.rb +58 -0
  83. data/lib/nanoc/routers/default.rb +54 -0
  84. data/lib/nanoc/routers/no_dirs.rb +66 -0
  85. data/lib/nanoc/routers/versioned.rb +79 -0
  86. data/lib/nanoc.rb +83 -12
  87. metadata +144 -47
  88. data/lib/nanoc/base/auto_compiler.rb +0 -124
  89. data/lib/nanoc/base/layout_processor.rb +0 -33
  90. data/lib/nanoc/base/page_proxy.rb +0 -31
  91. data/lib/nanoc/base/plugin_manager.rb +0 -33
  92. data/lib/nanoc/data_sources/database.rb +0 -259
  93. data/lib/nanoc/data_sources/trivial.rb +0 -145
  94. data/lib/nanoc/filters/markdown.rb +0 -13
  95. data/lib/nanoc/filters/smartypants.rb +0 -13
  96. data/lib/nanoc/filters/textile.rb +0 -13
  97. data/lib/nanoc/layout_processors/erb.rb +0 -35
  98. data/lib/nanoc/layout_processors/haml.rb +0 -18
  99. 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