nanoc 2.0.4 → 2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. data/ChangeLog +31 -1
  2. data/LICENSE +1 -1
  3. data/README +63 -3
  4. data/Rakefile +59 -12
  5. data/bin/nanoc +7 -199
  6. data/lib/nanoc.rb +83 -12
  7. data/lib/nanoc/base/asset.rb +113 -0
  8. data/lib/nanoc/base/asset_defaults.rb +21 -0
  9. data/lib/nanoc/base/asset_rep.rb +277 -0
  10. data/lib/nanoc/base/binary_filter.rb +44 -0
  11. data/lib/nanoc/base/code.rb +41 -0
  12. data/lib/nanoc/base/compiler.rb +46 -34
  13. data/lib/nanoc/base/core_ext/hash.rb +51 -7
  14. data/lib/nanoc/base/core_ext/string.rb +8 -0
  15. data/lib/nanoc/base/data_source.rb +253 -20
  16. data/lib/nanoc/base/defaults.rb +30 -0
  17. data/lib/nanoc/base/enhancements.rb +9 -84
  18. data/lib/nanoc/base/filter.rb +109 -6
  19. data/lib/nanoc/base/layout.rb +91 -0
  20. data/lib/nanoc/base/notification_center.rb +66 -0
  21. data/lib/nanoc/base/page.rb +94 -126
  22. data/lib/nanoc/base/page_defaults.rb +20 -0
  23. data/lib/nanoc/base/page_rep.rb +318 -0
  24. data/lib/nanoc/base/plugin.rb +57 -9
  25. data/lib/nanoc/base/proxies/asset_proxy.rb +29 -0
  26. data/lib/nanoc/base/proxies/asset_rep_proxy.rb +26 -0
  27. data/lib/nanoc/base/proxies/layout_proxy.rb +25 -0
  28. data/lib/nanoc/base/proxies/page_proxy.rb +35 -0
  29. data/lib/nanoc/base/proxies/page_rep_proxy.rb +28 -0
  30. data/lib/nanoc/base/proxy.rb +37 -0
  31. data/lib/nanoc/base/router.rb +72 -0
  32. data/lib/nanoc/base/site.rb +219 -88
  33. data/lib/nanoc/base/template.rb +64 -0
  34. data/lib/nanoc/binary_filters/image_science_thumbnail.rb +28 -0
  35. data/lib/nanoc/cli.rb +1 -0
  36. data/lib/nanoc/cli/base.rb +219 -0
  37. data/lib/nanoc/cli/cli.rb +16 -0
  38. data/lib/nanoc/cli/command.rb +105 -0
  39. data/lib/nanoc/cli/commands/autocompile.rb +80 -0
  40. data/lib/nanoc/cli/commands/compile.rb +273 -0
  41. data/lib/nanoc/cli/commands/create_layout.rb +85 -0
  42. data/lib/nanoc/cli/commands/create_page.rb +85 -0
  43. data/lib/nanoc/cli/commands/create_site.rb +327 -0
  44. data/lib/nanoc/cli/commands/create_template.rb +76 -0
  45. data/lib/nanoc/cli/commands/help.rb +69 -0
  46. data/lib/nanoc/cli/commands/info.rb +114 -0
  47. data/lib/nanoc/cli/commands/switch.rb +141 -0
  48. data/lib/nanoc/cli/commands/update.rb +91 -0
  49. data/lib/nanoc/cli/ext.rb +37 -0
  50. data/lib/nanoc/cli/logger.rb +66 -0
  51. data/lib/nanoc/cli/option_parser.rb +168 -0
  52. data/lib/nanoc/data_sources/filesystem.rb +645 -224
  53. data/lib/nanoc/data_sources/filesystem_combined.rb +495 -0
  54. data/lib/nanoc/extra/auto_compiler.rb +265 -0
  55. data/lib/nanoc/extra/context.rb +22 -0
  56. data/lib/nanoc/extra/core_ext/hash.rb +54 -0
  57. data/lib/nanoc/extra/core_ext/time.rb +13 -0
  58. data/lib/nanoc/extra/file_proxy.rb +29 -0
  59. data/lib/nanoc/extra/vcs.rb +48 -0
  60. data/lib/nanoc/extra/vcses/bazaar.rb +21 -0
  61. data/lib/nanoc/extra/vcses/dummy.rb +20 -0
  62. data/lib/nanoc/extra/vcses/git.rb +21 -0
  63. data/lib/nanoc/extra/vcses/mercurial.rb +21 -0
  64. data/lib/nanoc/extra/vcses/subversion.rb +21 -0
  65. data/lib/nanoc/filters/bluecloth.rb +13 -0
  66. data/lib/nanoc/filters/erb.rb +6 -22
  67. data/lib/nanoc/filters/erubis.rb +14 -0
  68. data/lib/nanoc/filters/haml.rb +7 -23
  69. data/lib/nanoc/filters/markaby.rb +5 -5
  70. data/lib/nanoc/filters/maruku.rb +14 -0
  71. data/lib/nanoc/filters/old.rb +19 -0
  72. data/lib/nanoc/filters/rdiscount.rb +13 -0
  73. data/lib/nanoc/filters/rdoc.rb +5 -4
  74. data/lib/nanoc/filters/redcloth.rb +14 -0
  75. data/lib/nanoc/filters/rubypants.rb +14 -0
  76. data/lib/nanoc/filters/sass.rb +13 -0
  77. data/lib/nanoc/helpers/blogging.rb +170 -0
  78. data/lib/nanoc/helpers/capturing.rb +59 -0
  79. data/lib/nanoc/helpers/html_escape.rb +23 -0
  80. data/lib/nanoc/helpers/link_to.rb +69 -0
  81. data/lib/nanoc/helpers/render.rb +47 -0
  82. data/lib/nanoc/helpers/tagging.rb +52 -0
  83. data/lib/nanoc/helpers/xml_sitemap.rb +58 -0
  84. data/lib/nanoc/routers/default.rb +54 -0
  85. data/lib/nanoc/routers/no_dirs.rb +66 -0
  86. data/lib/nanoc/routers/versioned.rb +79 -0
  87. metadata +112 -22
  88. data/lib/nanoc/base/auto_compiler.rb +0 -132
  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 -38
  99. data/lib/nanoc/layout_processors/markaby.rb +0 -16
@@ -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
@@ -0,0 +1,327 @@
1
+ module Nanoc::CLI
2
+
3
+ class CreateSiteCommand < Command # :nodoc:
4
+
5
+ DEFAULT_PAGE = <<EOS
6
+ <h1>A Brand New nanoc Site</h1>
7
+ <p>You&#8217;ve just created a new nanoc site. The page you are looking at right now is the home page for your site (and it&#8217;s probably the only page).</p>
8
+ <p>To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:</p>
9
+ <ul>
10
+ <li><strong>Change this page&#8217;s content</strong> by editing &#8220;content.txt&#8221; file in the &#8220;content&#8221; directory. This is the actual page content, and therefore doesn&#8217;t include the header, sidebar or style information (those are part of the layout).</li>
11
+ <li><strong>Change the layout</strong>, which is the &#8220;default.txt&#8221; file in the &#8220;layouts/default&#8221; directory, and create something unique (and hopefully less bland).</li>
12
+ </ul>
13
+ <p>If you need any help with customizing your nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!</p>
14
+ EOS
15
+
16
+ DEFAULT_LAYOUT = <<EOS
17
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
18
+ <html>
19
+ <head>
20
+ <title>A Brand New nanoc Site - <%= @page.title %></title>
21
+ <style type="text/css" media="screen">
22
+ * {
23
+ margin: 0;
24
+ padding: 0;
25
+
26
+ font-family: Georgia, Palatino, Times, 'Times New Roman', sans-serif;
27
+ }
28
+
29
+ body {
30
+ background: #fff;
31
+ }
32
+
33
+ a {
34
+ text-decoration: none;
35
+ }
36
+
37
+ a:link,
38
+ a:visited {
39
+ color: #f30;
40
+ }
41
+
42
+ a:hover {
43
+ color: #f90;
44
+ }
45
+
46
+ #main {
47
+ position: absolute;
48
+
49
+ top: 20px;
50
+ left: 280px;
51
+
52
+ width: 500px;
53
+ }
54
+
55
+ #main h1 {
56
+ font-size: 40px;
57
+ font-weight: normal;
58
+
59
+ line-height: 40px;
60
+
61
+ padding: 20px 0 20px 0;
62
+
63
+ letter-spacing: -1px;
64
+ }
65
+
66
+ #main p {
67
+ margin: 0 0 20px 0;
68
+
69
+ font-size: 15px;
70
+
71
+ line-height: 20px;
72
+ }
73
+
74
+ #main ul {
75
+ padding: 0 0 0 20px;
76
+ }
77
+
78
+ #main li {
79
+ margin: 0 0 20px 0;
80
+
81
+ list-style-type: square;
82
+
83
+ font-size: 15px;
84
+
85
+ line-height: 20px;
86
+ }
87
+
88
+ #sidebar {
89
+ position: absolute;
90
+
91
+ top: 40px;
92
+ left: 20px;
93
+ width: 200px;
94
+
95
+ padding: 20px 20px 0 0;
96
+
97
+ border-right: 1px solid #ccc;
98
+
99
+ text-align: right;
100
+ }
101
+
102
+ #sidebar h2 {
103
+ text-transform: uppercase;
104
+
105
+ font-size: 13px;
106
+
107
+ color: #333;
108
+
109
+ letter-spacing: 1px;
110
+
111
+ line-height: 20px;
112
+ }
113
+
114
+ #sidebar ul {
115
+ list-style-type: none;
116
+
117
+ margin: 20px 0;
118
+ }
119
+
120
+ #sidebar li {
121
+ font-size: 14px;
122
+
123
+ line-height: 20px;
124
+ }
125
+ </style>
126
+ </head>
127
+ <body>
128
+ <div id="main">
129
+ <%= @page.content %>
130
+ </div>
131
+ <div id="sidebar">
132
+ <h2>Documentation</h2>
133
+ <ul>
134
+ <li><a href="http://nanoc.stoneship.org/help/tutorial/">Tutorial</a></li>
135
+ <li><a href="http://nanoc.stoneship.org/help/manual/">Manual</a></li>
136
+ </ul>
137
+ <h2>Community</h2>
138
+ <ul>
139
+ <li><a href="http://groups.google.com/group/nanoc/">Discussion Group</a></li>
140
+ <li><a href="http://groups.google.com/group/nanoc-es/">Spanish Discussion Group</a></li>
141
+ <li><a href="http://nanoc.stoneship.org/trac/">Wiki</a></li>
142
+ </ul>
143
+ </div>
144
+ </body>
145
+ </html>
146
+ EOS
147
+
148
+ def name
149
+ 'create_site'
150
+ end
151
+
152
+ def aliases
153
+ [ 'cs' ]
154
+ end
155
+
156
+ def short_desc
157
+ 'create a site'
158
+ end
159
+
160
+ def long_desc
161
+ 'Create a new site at the given path. The site will use the ' +
162
+ 'filesystem data source (but this can be changed later on). It will ' +
163
+ 'also include a few stub rakefiles to make adding new tasks easier.'
164
+ end
165
+
166
+ def usage
167
+ "nanoc create_site [path]"
168
+ end
169
+
170
+ def option_definitions
171
+ [
172
+ # --datasource
173
+ {
174
+ :long => 'datasource', :short => 'd', :argument => :required,
175
+ :desc => 'specify the data source for the new site'
176
+ }
177
+ ]
178
+ end
179
+
180
+ def run(options, arguments)
181
+ # Check arguments
182
+ if arguments.length != 1
183
+ $stderr.puts "usage: #{usage}"
184
+ exit 1
185
+ end
186
+
187
+ # Extract arguments and options
188
+ path = arguments[0]
189
+ data_source = options[:datasource] || 'filesystem'
190
+
191
+ # Check whether site exists
192
+ if File.exist?(path)
193
+ $stderr.puts "A site at '#{path}' already exists."
194
+ exit 1
195
+ end
196
+
197
+ # Check whether data source exists
198
+ if Nanoc::DataSource.named(data_source).nil?
199
+ $stderr.puts "Unrecognised data source: #{data_source}"
200
+ exit 1
201
+ end
202
+
203
+ # Setup notifications
204
+ Nanoc::NotificationCenter.on(:file_created) do |file_path|
205
+ Nanoc::CLI::Logger.instance.file(:high, :create, file_path)
206
+ end
207
+
208
+ # Build entire site
209
+ FileUtils.mkdir_p(path)
210
+ begin
211
+ FileUtils.cd(File.join(path))
212
+
213
+ site_create_minimal(data_source)
214
+ site_setup
215
+ site_populate
216
+ ensure
217
+ FileUtils.cd(File.join(path.map { |n| '..' }))
218
+ end
219
+
220
+ puts "Created a blank nanoc site at '#{path}'. Enjoy!"
221
+ end
222
+
223
+ protected
224
+
225
+ # Creates a configuration file and a output directory for this site, as
226
+ # well as a rakefile and a 'tasks' directory because raking is fun.
227
+ def site_create_minimal(data_source)
228
+ # Create output
229
+ FileUtils.mkdir_p('output')
230
+
231
+ # Create config
232
+ File.open('config.yaml', 'w') do |io|
233
+ io.write "output_dir: \"output\"\n"
234
+ io.write "data_source: \"#{data_source}\"\n"
235
+ io.write "router: \"default\"\n"
236
+ end
237
+ Nanoc::NotificationCenter.post(:file_created, 'config.yaml')
238
+
239
+ # Create rakefile
240
+ File.open('Rakefile', 'w') do |io|
241
+ io.write "Dir['tasks/**/*.rake'].sort.each { |rakefile| load rakefile }\n"
242
+ io.write "\n"
243
+ io.write "task :default do\n"
244
+ io.write " puts 'This is an example rake task.'\n"
245
+ io.write "end\n"
246
+ end
247
+ Nanoc::NotificationCenter.post(:file_created, 'Rakefile')
248
+
249
+ # Create tasks
250
+ FileUtils.mkdir_p('tasks')
251
+ File.open('tasks/default.rake', 'w') do |io|
252
+ io.write "task :example do\n"
253
+ io.write " puts 'This is an example rake task in tasks/default.rake.'\n"
254
+ io.write "end\n"
255
+ end
256
+ Nanoc::NotificationCenter.post(:file_created, 'tasks/default.rake')
257
+ end
258
+
259
+ # Sets up the site's data source, i.e. creates the bare essentials for
260
+ # this data source to work.
261
+ def site_setup
262
+ # Get site
263
+ site = Nanoc::Site.new(YAML.load_file('config.yaml'))
264
+
265
+ # Set up data source
266
+ site.data_source.loading do
267
+ site.data_source.setup
268
+ end
269
+ end
270
+
271
+ # Populates the site with some initial data, such as a root page, a
272
+ # default layout, a default template, and so on.
273
+ def site_populate
274
+ # Get site
275
+ site = Nanoc::Site.new(YAML.load_file('config.yaml'))
276
+
277
+ # Create page
278
+ page = Nanoc::Page.new(
279
+ DEFAULT_PAGE,
280
+ { :title => 'Home' },
281
+ '/'
282
+ )
283
+ page.site = site
284
+ page.save
285
+
286
+ # Fill asset defaults
287
+ Nanoc::Asset::DEFAULTS.each_pair do |key, value|
288
+ site.asset_defaults.attributes[key] = value
289
+ end
290
+ site.asset_defaults.save
291
+
292
+ # Fill page defaults
293
+ Nanoc::Page::DEFAULTS.each_pair do |key, value|
294
+ site.page_defaults.attributes[key] = value
295
+ end
296
+ site.page_defaults.save
297
+
298
+ # Create layout
299
+ layout = Nanoc::Layout.new(
300
+ DEFAULT_LAYOUT,
301
+ { :filter => 'erb' },
302
+ '/default/'
303
+ )
304
+ layout.site = site
305
+ layout.save
306
+
307
+ # Create template
308
+ template = Nanoc::Template.new(
309
+ "Hi, I'm a new page!\n",
310
+ { :title => "A New Page" },
311
+ 'default'
312
+ )
313
+ template.site = site
314
+ template.save
315
+
316
+ # Fill code
317
+ code = Nanoc::Code.new(
318
+ "\# All files in the 'lib' directory will be loaded\n" +
319
+ "\# before nanoc starts compiling.\n"
320
+ )
321
+ code.site = site
322
+ code.save
323
+ end
324
+
325
+ end
326
+
327
+ end