nanoc3 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. data/ChangeLog +3 -0
  2. data/LICENSE +19 -0
  3. data/NEWS.rdoc +262 -0
  4. data/README.rdoc +80 -0
  5. data/Rakefile +11 -0
  6. data/bin/nanoc3 +16 -0
  7. data/lib/nanoc3/base/code_snippet.rb +42 -0
  8. data/lib/nanoc3/base/compiler.rb +225 -0
  9. data/lib/nanoc3/base/compiler_dsl.rb +110 -0
  10. data/lib/nanoc3/base/core_ext/array.rb +21 -0
  11. data/lib/nanoc3/base/core_ext/hash.rb +23 -0
  12. data/lib/nanoc3/base/core_ext/string.rb +14 -0
  13. data/lib/nanoc3/base/core_ext.rb +5 -0
  14. data/lib/nanoc3/base/data_source.rb +197 -0
  15. data/lib/nanoc3/base/dependency_tracker.rb +291 -0
  16. data/lib/nanoc3/base/errors.rb +95 -0
  17. data/lib/nanoc3/base/filter.rb +60 -0
  18. data/lib/nanoc3/base/item.rb +87 -0
  19. data/lib/nanoc3/base/item_rep.rb +236 -0
  20. data/lib/nanoc3/base/layout.rb +53 -0
  21. data/lib/nanoc3/base/notification_center.rb +68 -0
  22. data/lib/nanoc3/base/plugin.rb +88 -0
  23. data/lib/nanoc3/base/preprocessor_context.rb +37 -0
  24. data/lib/nanoc3/base/rule.rb +37 -0
  25. data/lib/nanoc3/base/rule_context.rb +68 -0
  26. data/lib/nanoc3/base/site.rb +334 -0
  27. data/lib/nanoc3/base.rb +25 -0
  28. data/lib/nanoc3/cli/base.rb +151 -0
  29. data/lib/nanoc3/cli/commands/autocompile.rb +89 -0
  30. data/lib/nanoc3/cli/commands/compile.rb +279 -0
  31. data/lib/nanoc3/cli/commands/create_item.rb +79 -0
  32. data/lib/nanoc3/cli/commands/create_layout.rb +94 -0
  33. data/lib/nanoc3/cli/commands/create_site.rb +320 -0
  34. data/lib/nanoc3/cli/commands/help.rb +71 -0
  35. data/lib/nanoc3/cli/commands/info.rb +114 -0
  36. data/lib/nanoc3/cli/commands/update.rb +96 -0
  37. data/lib/nanoc3/cli/commands.rb +13 -0
  38. data/lib/nanoc3/cli/logger.rb +73 -0
  39. data/lib/nanoc3/cli.rb +16 -0
  40. data/lib/nanoc3/data_sources/delicious.rb +66 -0
  41. data/lib/nanoc3/data_sources/filesystem.rb +231 -0
  42. data/lib/nanoc3/data_sources/filesystem_combined.rb +202 -0
  43. data/lib/nanoc3/data_sources/filesystem_common.rb +22 -0
  44. data/lib/nanoc3/data_sources/filesystem_compact.rb +232 -0
  45. data/lib/nanoc3/data_sources/last_fm.rb +103 -0
  46. data/lib/nanoc3/data_sources/twitter.rb +53 -0
  47. data/lib/nanoc3/data_sources.rb +20 -0
  48. data/lib/nanoc3/extra/auto_compiler.rb +97 -0
  49. data/lib/nanoc3/extra/chick.rb +119 -0
  50. data/lib/nanoc3/extra/context.rb +24 -0
  51. data/lib/nanoc3/extra/core_ext/time.rb +19 -0
  52. data/lib/nanoc3/extra/core_ext.rb +3 -0
  53. data/lib/nanoc3/extra/deployers/rsync.rb +64 -0
  54. data/lib/nanoc3/extra/deployers.rb +12 -0
  55. data/lib/nanoc3/extra/file_proxy.rb +31 -0
  56. data/lib/nanoc3/extra/validators/links.rb +0 -0
  57. data/lib/nanoc3/extra/validators/w3c.rb +71 -0
  58. data/lib/nanoc3/extra/validators.rb +12 -0
  59. data/lib/nanoc3/extra/vcs.rb +65 -0
  60. data/lib/nanoc3/extra/vcses/bazaar.rb +21 -0
  61. data/lib/nanoc3/extra/vcses/dummy.rb +20 -0
  62. data/lib/nanoc3/extra/vcses/git.rb +21 -0
  63. data/lib/nanoc3/extra/vcses/mercurial.rb +21 -0
  64. data/lib/nanoc3/extra/vcses/subversion.rb +21 -0
  65. data/lib/nanoc3/extra/vcses.rb +17 -0
  66. data/lib/nanoc3/extra.rb +16 -0
  67. data/lib/nanoc3/filters/bluecloth.rb +13 -0
  68. data/lib/nanoc3/filters/coderay.rb +17 -0
  69. data/lib/nanoc3/filters/erb.rb +19 -0
  70. data/lib/nanoc3/filters/erubis.rb +17 -0
  71. data/lib/nanoc3/filters/haml.rb +20 -0
  72. data/lib/nanoc3/filters/less.rb +13 -0
  73. data/lib/nanoc3/filters/markaby.rb +14 -0
  74. data/lib/nanoc3/filters/maruku.rb +14 -0
  75. data/lib/nanoc3/filters/rainpress.rb +13 -0
  76. data/lib/nanoc3/filters/rdiscount.rb +13 -0
  77. data/lib/nanoc3/filters/rdoc.rb +23 -0
  78. data/lib/nanoc3/filters/redcloth.rb +14 -0
  79. data/lib/nanoc3/filters/relativize_paths.rb +32 -0
  80. data/lib/nanoc3/filters/rubypants.rb +14 -0
  81. data/lib/nanoc3/filters/sass.rb +17 -0
  82. data/lib/nanoc3/filters.rb +37 -0
  83. data/lib/nanoc3/helpers/blogging.rb +226 -0
  84. data/lib/nanoc3/helpers/breadcrumbs.rb +25 -0
  85. data/lib/nanoc3/helpers/capturing.rb +71 -0
  86. data/lib/nanoc3/helpers/filtering.rb +46 -0
  87. data/lib/nanoc3/helpers/html_escape.rb +22 -0
  88. data/lib/nanoc3/helpers/link_to.rb +120 -0
  89. data/lib/nanoc3/helpers/rendering.rb +76 -0
  90. data/lib/nanoc3/helpers/tagging.rb +58 -0
  91. data/lib/nanoc3/helpers/text.rb +40 -0
  92. data/lib/nanoc3/helpers/xml_sitemap.rb +69 -0
  93. data/lib/nanoc3/helpers.rb +16 -0
  94. data/lib/nanoc3/package.rb +106 -0
  95. data/lib/nanoc3/tasks/clean.rake +16 -0
  96. data/lib/nanoc3/tasks/clean.rb +33 -0
  97. data/lib/nanoc3/tasks/deploy/rsync.rake +11 -0
  98. data/lib/nanoc3/tasks/validate.rake +35 -0
  99. data/lib/nanoc3/tasks.rb +9 -0
  100. data/lib/nanoc3.rb +19 -0
  101. data/vendor/cri/ChangeLog +0 -0
  102. data/vendor/cri/LICENSE +19 -0
  103. data/vendor/cri/NEWS +0 -0
  104. data/vendor/cri/README +4 -0
  105. data/vendor/cri/Rakefile +25 -0
  106. data/vendor/cri/lib/cri/base.rb +153 -0
  107. data/vendor/cri/lib/cri/command.rb +105 -0
  108. data/vendor/cri/lib/cri/core_ext/string.rb +41 -0
  109. data/vendor/cri/lib/cri/core_ext.rb +8 -0
  110. data/vendor/cri/lib/cri/option_parser.rb +186 -0
  111. data/vendor/cri/lib/cri.rb +12 -0
  112. data/vendor/cri/test/test_base.rb +6 -0
  113. data/vendor/cri/test/test_command.rb +6 -0
  114. data/vendor/cri/test/test_core_ext.rb +21 -0
  115. data/vendor/cri/test/test_option_parser.rb +279 -0
  116. metadata +225 -0
@@ -0,0 +1,89 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::CLI::Commands
4
+
5
+ class Autocompile < Cri::Command
6
+
7
+ def name
8
+ 'autocompile'
9
+ end
10
+
11
+ def aliases
12
+ [ 'aco' ]
13
+ end
14
+
15
+ def short_desc
16
+ 'start the autocompiler'
17
+ end
18
+
19
+ def long_desc
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
+ end
24
+
25
+ def usage
26
+ "nanoc3 autocompile [options]"
27
+ end
28
+
29
+ def option_definitions
30
+ [
31
+ # --port
32
+ {
33
+ :long => 'port', :short => 'p', :argument => :required,
34
+ :desc => 'specify a port number for the autocompiler'
35
+ },
36
+ # --server
37
+ {
38
+ :long => 'server', :short => 's', :argument => :required,
39
+ :desc => 'specify the server to use (webrick/mongrel)'
40
+ },
41
+ # --host
42
+ {
43
+ :long => 'host', :short => 'o', :argument => :required,
44
+ :desc => 'specify the host to listen on (default: 0.0.0.0)'
45
+ },
46
+ # --port
47
+ {
48
+ :long => 'port', :short => 'p', :argument => :required,
49
+ :desc => 'specify the port to listen on (default: 3000)'
50
+ }
51
+ ]
52
+ end
53
+
54
+ def run(options, arguments)
55
+ require 'rack'
56
+
57
+ # Make sure we are in a nanoc site directory
58
+ @base.require_site
59
+
60
+ # Set options
61
+ options_for_rack = {
62
+ :Port => (options[:port] || 3000).to_i,
63
+ :Host => (options[:host] || '0.0.0.0')
64
+ }
65
+
66
+ # Guess which handler we should use
67
+ unless handler = Rack::Handler.get(options[:server])
68
+ begin
69
+ handler = Rack::Handler::Mongrel
70
+ rescue LoadError => e
71
+ handler = Rack::Handler::WEBrick
72
+ end
73
+ end
74
+
75
+ # Build app
76
+ autocompiler = Nanoc3::Extra::AutoCompiler.new('.')
77
+ app = Rack::Builder.new do
78
+ use Rack::CommonLogger, $stderr
79
+ use Rack::ShowExceptions
80
+ run autocompiler
81
+ end.to_app
82
+
83
+ # Run autocompiler
84
+ handler.run(app, options_for_rack)
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -0,0 +1,279 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::CLI::Commands
4
+
5
+ class Compile < Cri::Command
6
+
7
+ def name
8
+ 'compile'
9
+ end
10
+
11
+ def aliases
12
+ []
13
+ end
14
+
15
+ def short_desc
16
+ 'compile items of this site'
17
+ end
18
+
19
+ def long_desc
20
+ 'Compile all items of the current site. If an identifier is given, ' +
21
+ 'only the item with the given identifier will be compiled. ' +
22
+ "\n\n" +
23
+ 'By default, only item that are outdated will be compiled. This can ' +
24
+ 'speed up the compilation process quite a bit, but items that include ' +
25
+ 'content from other items may have to be recompiled manually. In ' +
26
+ 'order to compile items even when they are outdated, use the --force option.'
27
+ end
28
+
29
+ def usage
30
+ "nanoc3 compile [options] [identifier]"
31
+ end
32
+
33
+ def option_definitions
34
+ [
35
+ # --all
36
+ {
37
+ :long => 'all', :short => 'a', :argument => :forbidden,
38
+ :desc => 'alias for --force (DEPRECATED)'
39
+ },
40
+ # --force
41
+ {
42
+ :long => 'force', :short => 'f', :argument => :forbidden,
43
+ :desc => 'compile items even when they are not outdated'
44
+ }
45
+ ]
46
+ end
47
+
48
+ def run(options, arguments)
49
+ # Make sure we are in a nanoc site directory
50
+ puts "Loading site data..."
51
+ @base.require_site
52
+ @base.site.load_data
53
+
54
+ # Check presence of --all option
55
+ if options.has_key?(:all)
56
+ $stderr.puts "Warning: the --all option is deprecated; please use --force instead."
57
+ end
58
+
59
+ # Find item(s) to compile
60
+ if arguments.size == 0
61
+ item = nil
62
+ elsif arguments.size == 1
63
+ # Find item
64
+ identifier = arguments[0].cleaned_identifier
65
+ item = @base.site.items.find { |item| item.identifier == identifier }
66
+
67
+ # Ensure item
68
+ if item.nil?
69
+ $stderr.puts "Unknown item: #{identifier}"
70
+ exit 1
71
+ end
72
+ end
73
+
74
+ # Give feedback
75
+ puts "Compiling #{item.nil? ? 'site' : 'item'}..."
76
+
77
+ # Initialize profiling stuff
78
+ time_before = Time.now
79
+ @filter_times ||= {}
80
+ @times_stack ||= []
81
+ setup_notifications
82
+
83
+ # Compile
84
+ @base.site.compiler.run(
85
+ item,
86
+ :force => options.has_key?(:all) || options.has_key?(:force)
87
+ )
88
+
89
+ # Find reps
90
+ reps = @base.site.items.map { |i| i.reps }.flatten
91
+
92
+ # Show skipped reps
93
+ reps.select { |r| !r.compiled? }.each do |rep|
94
+ next if rep.raw_path.nil?
95
+ duration = @rep_times[rep.raw_path]
96
+ Nanoc3::CLI::Logger.instance.file(:low, :skip, rep.raw_path, duration)
97
+ end
98
+
99
+ # Give general feedback
100
+ puts
101
+ puts "No items were modified." unless reps.any? { |r| r.modified? }
102
+ puts "#{item.nil? ? 'Site' : 'Item'} compiled in #{format('%.2f', Time.now - time_before)}s."
103
+
104
+ if options.has_key?(:verbose)
105
+ print_state_feedback(reps)
106
+ print_profiling_feedback(reps)
107
+ end
108
+ rescue Interrupt => e
109
+ exit(1)
110
+ rescue StandardError, ScriptError => e
111
+ print_error(e)
112
+ exit(1)
113
+ end
114
+
115
+ private
116
+
117
+ def setup_notifications
118
+ Nanoc3::NotificationCenter.on(:compilation_started) do |rep|
119
+ rep_compilation_started(rep)
120
+ end
121
+ Nanoc3::NotificationCenter.on(:compilation_ended) do |rep|
122
+ rep_compilation_ended(rep)
123
+ end
124
+ Nanoc3::NotificationCenter.on(:filtering_started) do |rep, filter_name|
125
+ rep_filtering_started(rep, filter_name)
126
+ end
127
+ Nanoc3::NotificationCenter.on(:filtering_ended) do |rep, filter_name|
128
+ rep_filtering_ended(rep, filter_name)
129
+ end
130
+ end
131
+
132
+ def print_state_feedback(reps)
133
+ # Categorise reps
134
+ rest = reps
135
+ created, rest = *rest.partition { |r| r.created? }
136
+ modified, rest = *rest.partition { |r| r.modified? }
137
+ skipped, rest = *rest.partition { |r| !r.compiled? }
138
+ not_written, rest = *rest.partition { |r| r.compiled? && !r.written? }
139
+ identical = rest
140
+
141
+ # Print
142
+ puts
143
+ puts format(' %4d created', created.size)
144
+ puts format(' %4d modified', modified.size)
145
+ puts format(' %4d skipped', skipped.size)
146
+ puts format(' %4d not written', not_written.size)
147
+ puts format(' %4d identical', identical.size)
148
+ end
149
+
150
+ def print_profiling_feedback(reps)
151
+ # Get max filter length
152
+ max_filter_name_length = @filter_times.keys.map { |k| k.to_s.size }.max
153
+ return if max_filter_name_length.nil?
154
+
155
+ # Print warning if necessary
156
+ if reps.any? { |r| !r.compiled? }
157
+ $stderr.puts
158
+ $stderr.puts "Warning: profiling information may not be accurate because " +
159
+ "some items were not compiled."
160
+ end
161
+
162
+ # Print header
163
+ puts
164
+ puts ' ' * max_filter_name_length + ' | count min avg max tot'
165
+ puts '-' * max_filter_name_length + '-+-----------------------------------'
166
+
167
+ @filter_times.to_a.sort_by { |r| r[1] }.each do |row|
168
+ # Extract data
169
+ filter_name, samples = *row
170
+
171
+ # Calculate stats
172
+ count = samples.size
173
+ min = samples.min
174
+ tot = samples.inject { |memo, i| memo + i}
175
+ avg = tot/count
176
+ max = samples.max
177
+
178
+ # Format stats
179
+ count = format('%4d', count)
180
+ min = format('%4.2f', min)
181
+ avg = format('%4.2f', avg)
182
+ max = format('%4.2f', max)
183
+ tot = format('%5.2f', tot)
184
+
185
+ # Output stats
186
+ filter_name = format("%#{max_filter_name_length}s", filter_name)
187
+ puts "#{filter_name} | #{count} #{min}s #{avg}s #{max}s #{tot}s"
188
+ end
189
+ end
190
+
191
+ def print_error(error)
192
+ $stderr.puts
193
+
194
+ # Header
195
+ $stderr.puts '+--- /!\ ERROR /!\ -------------------------------------------+'
196
+ $stderr.puts '| An exception occured while compiling the site. If you think |'
197
+ $stderr.puts '| this is a bug in nanoc, please do report it at |'
198
+ $stderr.puts '| <http://projects.stoneship.org/trac/nanoc/newticket> -- |'
199
+ $stderr.puts '| thanks in advance! |'
200
+ $stderr.puts '+-------------------------------------------------------------+'
201
+
202
+ # Exception
203
+ $stderr.puts
204
+ $stderr.puts '=== MESSAGE:'
205
+ $stderr.puts
206
+ $stderr.puts "#{error.class}: #{error.message}"
207
+
208
+ # Compilation stack
209
+ $stderr.puts
210
+ $stderr.puts '=== COMPILATION STACK:'
211
+ $stderr.puts
212
+ if ((@base.site && @base.site.compiler.stack) || []).empty?
213
+ $stderr.puts " (empty)"
214
+ else
215
+ @base.site.compiler.stack.reverse.each do |obj|
216
+ if obj.is_a?(Nanoc3::ItemRep)
217
+ $stderr.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
218
+ else # layout
219
+ $stderr.puts " - [layout] #{obj.identifier}"
220
+ end
221
+ end
222
+ end
223
+
224
+ # Backtrace
225
+ require 'enumerator'
226
+ $stderr.puts
227
+ $stderr.puts '=== BACKTRACE:'
228
+ $stderr.puts
229
+ $stderr.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
230
+ end
231
+
232
+ def rep_compilation_started(rep)
233
+ # Profile compilation
234
+ @rep_times ||= {}
235
+ @rep_times[rep.raw_path] = Time.now
236
+ end
237
+
238
+ def rep_compilation_ended(rep)
239
+ # Profile compilation
240
+ @rep_times ||= {}
241
+ @rep_times[rep.raw_path] = Time.now - @rep_times[rep.raw_path]
242
+
243
+ # Skip if not outputted
244
+ return unless rep.written?
245
+
246
+ # Get action and level
247
+ action, level = *if rep.created?
248
+ [ :create, :high ]
249
+ elsif rep.modified?
250
+ [ :update, :high ]
251
+ elsif !rep.compiled?
252
+ [ nil, nil ]
253
+ else
254
+ [ :identical, :low ]
255
+ end
256
+
257
+ # Log
258
+ unless action.nil?
259
+ duration = @rep_times[rep.raw_path]
260
+ Nanoc3::CLI::Logger.instance.file(level, action, rep.raw_path, duration)
261
+ end
262
+ end
263
+
264
+ def rep_filtering_started(rep, filter_name)
265
+ @times_stack.push(Time.now)
266
+ end
267
+
268
+ def rep_filtering_ended(rep, filter_name)
269
+ # Get last time
270
+ time_start = @times_stack.pop
271
+
272
+ # Update times
273
+ @filter_times[filter_name.to_sym] ||= []
274
+ @filter_times[filter_name.to_sym] << Time.now - time_start
275
+ end
276
+
277
+ end
278
+
279
+ end
@@ -0,0 +1,79 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::CLI::Commands
4
+
5
+ class CreateItem < Cri::Command
6
+
7
+ def name
8
+ 'create_item'
9
+ end
10
+
11
+ def aliases
12
+ [ 'ci' ]
13
+ end
14
+
15
+ def short_desc
16
+ 'create a item'
17
+ end
18
+
19
+ def long_desc
20
+ 'Create a new item in the current site. The first data source in the site configuration will be used.'
21
+ end
22
+
23
+ def usage
24
+ "nanoc3 create_item [options] [identifier]"
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
+ ]
35
+ end
36
+
37
+ def run(options, arguments)
38
+ # Check arguments
39
+ if arguments.length != 1
40
+ $stderr.puts "usage: #{usage}"
41
+ exit 1
42
+ end
43
+
44
+ # Extract arguments and options
45
+ identifier = arguments[0].cleaned_identifier
46
+
47
+ # Make sure we are in a nanoc site directory
48
+ @base.require_site
49
+ @base.site.load_data
50
+
51
+ # Set VCS if possible
52
+ @base.set_vcs(options[:vcs])
53
+
54
+ # Check whether item is unique
55
+ if !@base.site.items.find { |i| i.identifier == identifier }.nil?
56
+ $stderr.puts "An item already exists at #{identifier}. Please " +
57
+ "pick a unique name for the item you are creating."
58
+ exit 1
59
+ end
60
+
61
+ # Setup notifications
62
+ Nanoc3::NotificationCenter.on(:file_created) do |file_path|
63
+ Nanoc3::CLI::Logger.instance.file(:high, :create, file_path)
64
+ end
65
+
66
+ # Create item
67
+ data_source = @base.site.data_sources[0]
68
+ data_source.create_item(
69
+ "Hi, I'm a new item!\n",
70
+ { :title => "A New Item" },
71
+ identifier
72
+ )
73
+
74
+ puts "An item has been created at #{identifier}."
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,94 @@
1
+ # encoding: utf-8
2
+
3
+ module Nanoc3::CLI::Commands
4
+
5
+ class CreateLayout < Cri::Command
6
+
7
+ def name
8
+ 'create_layout'
9
+ end
10
+
11
+ def aliases
12
+ [ 'cl' ]
13
+ end
14
+
15
+ def short_desc
16
+ 'create a layout'
17
+ end
18
+
19
+ def long_desc
20
+ 'Create a new layout in the current site. The first data source in the site configuration will be used.'
21
+ end
22
+
23
+ def usage
24
+ "nanoc3 create_layout [identifier]"
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
+ ]
35
+ end
36
+
37
+ def run(options, arguments)
38
+ # Check arguments
39
+ if arguments.length != 1
40
+ $stderr.puts "usage: #{usage}"
41
+ exit 1
42
+ end
43
+
44
+ # Extract arguments
45
+ identifier = arguments[0].cleaned_identifier
46
+
47
+ # Make sure we are in a nanoc site directory
48
+ @base.require_site
49
+ @base.site.load_data
50
+
51
+ # Set VCS if possible
52
+ @base.set_vcs(options[:vcs])
53
+
54
+ # Check whether layout is unique
55
+ if !@base.site.layouts.find { |l| l.identifier == identifier }.nil?
56
+ $stderr.puts "A layout already exists at #{identifier}. Please " +
57
+ "pick a unique name for the layout you are creating."
58
+ exit 1
59
+ end
60
+
61
+ # Check whether layout is not at /
62
+ if identifier == '/'
63
+ $stderr.puts "There cannot be a layout with the identifier '/'; " +
64
+ "please pick a different identifier for this layout."
65
+ exit 1
66
+ end
67
+
68
+ # Setup notifications
69
+ Nanoc3::NotificationCenter.on(:file_created) do |file_path|
70
+ Nanoc3::CLI::Logger.instance.file(:high, :create, file_path)
71
+ end
72
+
73
+ # Create layout
74
+ data_source = @base.site.data_sources[0]
75
+ data_source.create_layout(
76
+ "<html>\n" +
77
+ " <head>\n" +
78
+ " <title><%= @item.title %></title>\n" +
79
+ " </head>\n" +
80
+ " <body>\n" +
81
+ " <p>Hi, I'm a new layout. Please customize me!</p>\n" +
82
+ "<%= yield %>\n" +
83
+ " </body>\n" +
84
+ "</html>\n",
85
+ {},
86
+ identifier
87
+ )
88
+
89
+ puts "A layout has been created at #{identifier}."
90
+ end
91
+
92
+ end
93
+
94
+ end