nanoc2 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. data/ChangeLog +3 -0
  2. data/LICENSE +19 -0
  3. data/README +75 -0
  4. data/Rakefile +76 -0
  5. data/bin/nanoc2 +26 -0
  6. data/lib/nanoc2.rb +73 -0
  7. data/lib/nanoc2/base.rb +26 -0
  8. data/lib/nanoc2/base/asset.rb +117 -0
  9. data/lib/nanoc2/base/asset_defaults.rb +21 -0
  10. data/lib/nanoc2/base/asset_rep.rb +282 -0
  11. data/lib/nanoc2/base/binary_filter.rb +44 -0
  12. data/lib/nanoc2/base/code.rb +41 -0
  13. data/lib/nanoc2/base/compiler.rb +67 -0
  14. data/lib/nanoc2/base/core_ext.rb +2 -0
  15. data/lib/nanoc2/base/core_ext/hash.rb +78 -0
  16. data/lib/nanoc2/base/core_ext/string.rb +8 -0
  17. data/lib/nanoc2/base/data_source.rb +286 -0
  18. data/lib/nanoc2/base/defaults.rb +30 -0
  19. data/lib/nanoc2/base/filter.rb +93 -0
  20. data/lib/nanoc2/base/layout.rb +91 -0
  21. data/lib/nanoc2/base/notification_center.rb +66 -0
  22. data/lib/nanoc2/base/page.rb +132 -0
  23. data/lib/nanoc2/base/page_defaults.rb +20 -0
  24. data/lib/nanoc2/base/page_rep.rb +324 -0
  25. data/lib/nanoc2/base/plugin.rb +71 -0
  26. data/lib/nanoc2/base/proxies.rb +5 -0
  27. data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
  28. data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
  29. data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
  30. data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
  31. data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
  32. data/lib/nanoc2/base/proxy.rb +37 -0
  33. data/lib/nanoc2/base/router.rb +72 -0
  34. data/lib/nanoc2/base/site.rb +274 -0
  35. data/lib/nanoc2/base/template.rb +64 -0
  36. data/lib/nanoc2/binary_filters.rb +1 -0
  37. data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
  38. data/lib/nanoc2/cli.rb +9 -0
  39. data/lib/nanoc2/cli/base.rb +132 -0
  40. data/lib/nanoc2/cli/commands.rb +10 -0
  41. data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
  42. data/lib/nanoc2/cli/commands/compile.rb +312 -0
  43. data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
  44. data/lib/nanoc2/cli/commands/create_page.rb +85 -0
  45. data/lib/nanoc2/cli/commands/create_site.rb +323 -0
  46. data/lib/nanoc2/cli/commands/create_template.rb +76 -0
  47. data/lib/nanoc2/cli/commands/help.rb +69 -0
  48. data/lib/nanoc2/cli/commands/info.rb +125 -0
  49. data/lib/nanoc2/cli/commands/switch.rb +141 -0
  50. data/lib/nanoc2/cli/commands/update.rb +91 -0
  51. data/lib/nanoc2/cli/logger.rb +72 -0
  52. data/lib/nanoc2/data_sources.rb +2 -0
  53. data/lib/nanoc2/data_sources/filesystem.rb +707 -0
  54. data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
  55. data/lib/nanoc2/extra.rb +6 -0
  56. data/lib/nanoc2/extra/auto_compiler.rb +285 -0
  57. data/lib/nanoc2/extra/context.rb +22 -0
  58. data/lib/nanoc2/extra/core_ext.rb +2 -0
  59. data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
  60. data/lib/nanoc2/extra/core_ext/time.rb +13 -0
  61. data/lib/nanoc2/extra/file_proxy.rb +29 -0
  62. data/lib/nanoc2/extra/vcs.rb +48 -0
  63. data/lib/nanoc2/extra/vcses.rb +5 -0
  64. data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
  65. data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
  66. data/lib/nanoc2/extra/vcses/git.rb +21 -0
  67. data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
  68. data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
  69. data/lib/nanoc2/filters.rb +16 -0
  70. data/lib/nanoc2/filters/bluecloth.rb +13 -0
  71. data/lib/nanoc2/filters/erb.rb +19 -0
  72. data/lib/nanoc2/filters/erubis.rb +14 -0
  73. data/lib/nanoc2/filters/haml.rb +21 -0
  74. data/lib/nanoc2/filters/markaby.rb +14 -0
  75. data/lib/nanoc2/filters/maruku.rb +14 -0
  76. data/lib/nanoc2/filters/old.rb +19 -0
  77. data/lib/nanoc2/filters/rainpress.rb +13 -0
  78. data/lib/nanoc2/filters/rdiscount.rb +13 -0
  79. data/lib/nanoc2/filters/rdoc.rb +23 -0
  80. data/lib/nanoc2/filters/redcloth.rb +14 -0
  81. data/lib/nanoc2/filters/relativize_paths.rb +16 -0
  82. data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
  83. data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
  84. data/lib/nanoc2/filters/rubypants.rb +14 -0
  85. data/lib/nanoc2/filters/sass.rb +18 -0
  86. data/lib/nanoc2/helpers.rb +9 -0
  87. data/lib/nanoc2/helpers/blogging.rb +217 -0
  88. data/lib/nanoc2/helpers/capturing.rb +63 -0
  89. data/lib/nanoc2/helpers/filtering.rb +54 -0
  90. data/lib/nanoc2/helpers/html_escape.rb +25 -0
  91. data/lib/nanoc2/helpers/link_to.rb +113 -0
  92. data/lib/nanoc2/helpers/render.rb +49 -0
  93. data/lib/nanoc2/helpers/tagging.rb +56 -0
  94. data/lib/nanoc2/helpers/text.rb +38 -0
  95. data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
  96. data/lib/nanoc2/routers.rb +3 -0
  97. data/lib/nanoc2/routers/default.rb +54 -0
  98. data/lib/nanoc2/routers/no_dirs.rb +66 -0
  99. data/lib/nanoc2/routers/versioned.rb +79 -0
  100. metadata +185 -0
@@ -0,0 +1,6 @@
1
+ require 'nanoc2/extra/auto_compiler'
2
+ require 'nanoc2/extra/context'
3
+ require 'nanoc2/extra/core_ext'
4
+ require 'nanoc2/extra/file_proxy'
5
+ require 'nanoc2/extra/vcs'
6
+ require 'nanoc2/extra/vcses'
@@ -0,0 +1,285 @@
1
+ require 'webrick'
2
+
3
+ module Nanoc2::Extra
4
+
5
+ # Nanoc2::Extra::AutoCompiler is a web server that will automatically compile
6
+ # pages as they are requested. It also serves static files such as
7
+ # stylesheets and images.
8
+ class AutoCompiler
9
+
10
+ # Error that is raised when the autocompiler is started if the specified
11
+ # handler cannot be found.
12
+ class UnknownHandlerError < Nanoc2::Error ; end
13
+
14
+ HANDLER_NAMES = [ :thin, :mongrel, :webrick, :ebb, :cgi, :fastcgi, :lsws, :scgi ]
15
+
16
+ ERROR_404 = <<END
17
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
18
+ <html>
19
+ <head>
20
+ <title>404 File Not Found</title>
21
+ <style type="text/css">
22
+ body { padding: 10px; border: 10px solid #f00; margin: 10px; font-family: Helvetica, Arial, sans-serif; }
23
+ </style>
24
+ </head>
25
+ <body>
26
+ <h1>404 File Not Found</h1>
27
+ <p>The file you requested, <i><%=h path %></i>, was not found on this server.</p>
28
+ </body>
29
+ </html>
30
+ END
31
+
32
+ ERROR_500 = <<END
33
+ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
34
+ <html>
35
+ <head>
36
+ <title>500 Server Error</title>
37
+ <style type="text/css">
38
+ body { padding: 10px; border: 10px solid #f00; margin: 10px; font-family: Helvetica, Arial, sans-serif; }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <h1>500 Server Error</h1>
43
+ <p>An error occurred while compiling the page you requested, <i><%=h path %></i>.</p>
44
+ <p>If you think this is a bug in nanoc, please do <a href="http://nanoc.stoneship.org/trac/newticket">report it</a>&mdash;thanks!</p>
45
+ <p>Message:</p>
46
+ <blockquote><p><%=h message %></p></blockquote>
47
+ <p>Page compilation stack:</p>
48
+ <ol>
49
+ <% @site.compiler.stack.reverse.each do |item| %>
50
+ <% if item.is_a?(Nanoc2::PageRep) # page rep %>
51
+ <li><strong>Page</strong> <%= item.page.path %> (rep <%= item.name %>)</li>
52
+ <% else # layout %>
53
+ <li><strong>Layout</strong> <%= item.path %></li>
54
+ <% end %>
55
+ <% end %>
56
+ </ol>
57
+ <p>Backtrace:</p>
58
+ <ol>
59
+ <% exception.backtrace.each do |line| %>
60
+ <li><%= line %></li>
61
+ <% end %>
62
+ </ol>
63
+ </body>
64
+ </html>
65
+ END
66
+
67
+ # Creates a new autocompiler for the given site.
68
+ def initialize(site, include_outdated=false)
69
+ # Set site
70
+ @site = site
71
+
72
+ # Set options
73
+ @include_outdated = include_outdated
74
+
75
+ # Create mutex to prevent parallel requests
76
+ @mutex = Mutex.new
77
+ end
78
+
79
+ # Starts the server on the given port.
80
+ #
81
+ # +port+:: The port the autocompiler web server should be started on. Can
82
+ # be nil; in this case the server will be started on port 3000.
83
+ #
84
+ # +handler_name+:: A symbol containing the name of the handler to use. See
85
+ # HANDLER_NAMES for a list of supported handlers. Can be
86
+ # set to nil; in this case the best handler will be
87
+ # picked.
88
+ def start(port, handler_name)
89
+ require 'mime/types'
90
+ require 'rack'
91
+
92
+ # Determine handler
93
+ if handler_name.nil?
94
+ handler = preferred_handler
95
+ else
96
+ handler = handler_named(handler_name.to_sym)
97
+ raise UnknownHandlerError.new(handler_name) if handler.nil?
98
+ end
99
+
100
+ # Build Rack app
101
+ app = lambda do |env|
102
+ begin
103
+ handle_request(env['PATH_INFO'])
104
+ rescue Exception => exception
105
+ return serve_500(nil, exception)
106
+ end
107
+ end
108
+
109
+ # Run Rack app
110
+ port ||= 3000
111
+ handler.run(app, :Port => port, :port => port) do |server|
112
+ trap(:INT) { server.stop }
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def preferred_handler
119
+ return @preferred_handler unless @preferred_handler.nil?
120
+
121
+ HANDLER_NAMES.each do |handler_name|
122
+ # Get handler
123
+ @preferred_handler = handler_named(handler_name)
124
+
125
+ # Make sure we have one
126
+ break unless @preferred_handler.nil?
127
+ end
128
+
129
+ @preferred_handler
130
+ end
131
+
132
+ def handler_named(handler_name)
133
+ # Build list of handlers
134
+ @handlers ||= {
135
+ :cgi => {
136
+ :proc => lambda { Rack::Handler::CGI }
137
+ },
138
+ :fastcgi => { # FIXME buggy
139
+ :proc => lambda { Rack::Handler::FastCGI }
140
+ },
141
+ :lsws => { # FIXME test
142
+ :proc => lambda { Rack::Handler::LSWS }
143
+ },
144
+ :mongrel => {
145
+ :proc => lambda { Rack::Handler::Mongrel }
146
+ },
147
+ :scgi => { # FIXME buggy
148
+ :proc => lambda { Rack::Handler::SCGI }
149
+ },
150
+ :webrick => {
151
+ :proc => lambda { Rack::Handler::WEBrick }
152
+ },
153
+ :thin => {
154
+ :proc => lambda { Rack::Handler::Thin },
155
+ :requires => [ 'thin' ]
156
+ },
157
+ :ebb => {
158
+ :proc => lambda { Rack::Handler::Ebb },
159
+ :requires => [ 'ebb' ]
160
+ }
161
+ }
162
+
163
+ begin
164
+ # Lookup handler
165
+ handler = @handlers[handler_name]
166
+
167
+ # Load requirements
168
+ (handler[:requires] || []).each { |r| require r }
169
+
170
+ # Get handler class
171
+ handler[:proc].call
172
+ rescue NameError, LoadError
173
+ nil
174
+ end
175
+ end
176
+
177
+ def handle_request(path)
178
+ @mutex.synchronize do
179
+ # Reload site data
180
+ @site.load_data(true)
181
+
182
+ # Get file path
183
+ file_path = @site.config[:output_dir] + path
184
+
185
+ # Find rep
186
+ objs = @site.pages + @site.assets
187
+ reps = objs.map { |o| o.reps }.flatten
188
+ rep = reps.find { |r| r.web_path == path }
189
+
190
+ if rep.nil?
191
+ # Get list of possible filenames
192
+ if file_path =~ /\/$/
193
+ all_file_paths = @site.config[:index_filenames].map { |f| file_path + f }
194
+ else
195
+ all_file_paths = [ file_path ]
196
+ end
197
+ good_file_path = all_file_paths.find { |f| File.file?(f) }
198
+
199
+ # Serve file
200
+ if good_file_path
201
+ serve_file(good_file_path)
202
+ else
203
+ serve_404(path)
204
+ end
205
+ else
206
+ # Serve rep
207
+ serve_rep(rep)
208
+ end
209
+ end
210
+ end
211
+
212
+ def h(s)
213
+ ERB::Util.html_escape(s)
214
+ end
215
+
216
+ def mime_type_of(path, fallback)
217
+ mime_type = MIME::Types.of(path).first
218
+ mime_type = mime_type.nil? ? fallback : mime_type.simplified
219
+ end
220
+
221
+ def serve_404(path)
222
+ # Build response
223
+ [
224
+ 404,
225
+ { 'Content-Type' => 'text/html' },
226
+ [ ERB.new(ERROR_404).result(binding) ]
227
+ ]
228
+ end
229
+
230
+ def serve_500(path, exception)
231
+ # Build message
232
+ case exception
233
+ when Nanoc2::Errors::UnknownLayoutError
234
+ message = "Unknown layout: #{exception.message}"
235
+ when Nanoc2::Errors::UnknownFilterError
236
+ message = "Unknown filter: #{exception.message}"
237
+ when Nanoc2::Errors::CannotDetermineFilterError
238
+ message = "Cannot determine filter for layout: #{exception.message}"
239
+ when Nanoc2::Errors::RecursiveCompilationError
240
+ message = "Recursive call to page content. Page stack:"
241
+ when Nanoc2::Errors::NoLongerSupportedError
242
+ message = "No longer supported: #{exception.message}"
243
+ else
244
+ message = "Unknown error: #{exception.message}"
245
+ end
246
+
247
+ # Build response
248
+ [
249
+ 500,
250
+ { 'Content-Type' => 'text/html' },
251
+ [ ERB.new(ERROR_500).result(binding) ]
252
+ ]
253
+ end
254
+
255
+ def serve_file(path)
256
+ # Build response
257
+ [
258
+ 200,
259
+ { 'Content-Type' => mime_type_of(path, 'application/octet-stream') },
260
+ [ File.open(path, 'rb') { |io| io.read } ]
261
+ ]
262
+ end
263
+
264
+ def serve_rep(rep)
265
+ # Recompile rep
266
+ begin
267
+ @site.compiler.run(
268
+ [ rep.respond_to?(:page) ? rep.page : rep.asset ],
269
+ :even_when_not_outdated => @include_outdated
270
+ )
271
+ rescue Exception => exception
272
+ return serve_500(rep.web_path, exception)
273
+ end
274
+
275
+ # Build response
276
+ [
277
+ 200,
278
+ { 'Content-Type' => mime_type_of(rep.disk_path, 'text/html') },
279
+ [ File.read(rep.disk_path) ]
280
+ ]
281
+ end
282
+
283
+ end
284
+
285
+ end
@@ -0,0 +1,22 @@
1
+ module Nanoc2::Extra
2
+
3
+ # Nanoc2::Extra::Context provides a context and a Binding for use in various
4
+ # filters, such as the ERB and Haml one.
5
+ class Context
6
+
7
+ # Creates a new context based off the contents of the hash. Each pair in
8
+ # the hash will be converted to an instance variable. For example, passing
9
+ # the hash { :foo => 'bar' } will cause @foo to have the value "bar".
10
+ def initialize(hash)
11
+ hash.each_pair do |key, value|
12
+ instance_variable_set('@' + key.to_s, value)
13
+ end
14
+ end
15
+
16
+ # Returns a binding for this context.
17
+ def get_binding
18
+ binding
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,2 @@
1
+ require 'nanoc2/extra/core_ext/hash'
2
+ require 'nanoc2/extra/core_ext/time'
@@ -0,0 +1,54 @@
1
+ class Hash
2
+
3
+ # Converts this hash into YAML format, splitting the YAML output into a
4
+ # 'builtin' and a 'custom' section. A key that is present in
5
+ # Nanoc2::Page::DEFAULTS will be considered a 'default' key; all other keys
6
+ # will be put in the 'Custom' section.
7
+ #
8
+ # For example, the hash:
9
+ #
10
+ # {
11
+ # :title => 'My Cool Page',
12
+ # :filters_pre => [ 'foo', 'bar' ]
13
+ # }
14
+ #
15
+ # will be converted into:
16
+ #
17
+ # # Built-in
18
+ # filters_pre: [ 'foo', 'bar' ]
19
+ #
20
+ # # Custom
21
+ # title: 'My Cool Page'
22
+ #
23
+ # as +filters_pre+ is considered a 'default' key while +title+ is not.
24
+ def to_split_yaml
25
+ # Skip irrelevant keys
26
+ hash = self.reject { |k,v| k == :file }
27
+
28
+ # Split keys
29
+ hashes = { :builtin => {}, :custom => {} }
30
+ hash.each_pair do |key, value|
31
+ kind = Nanoc2::Page::DEFAULTS.include?(key) || Nanoc2::Asset::DEFAULTS.include?(key) ? :builtin : :custom
32
+ hashes[kind][key] = value
33
+ end
34
+
35
+ # Dump and clean hashes
36
+ dumps = { :builtin => '', :custom => '' }
37
+ [ :builtin, :custom ].each do |kind|
38
+ if hashes[kind].keys.empty?
39
+ dumps[kind] = "\n"
40
+ else
41
+ raw_dump = YAML.dump(hashes[kind].stringify_keys)
42
+ dumps[kind] = raw_dump.split('---')[1].gsub("\n\n", "\n")
43
+ end
44
+ end
45
+
46
+ # Built composite YAML file
47
+ '# Built-in' +
48
+ dumps[:builtin] +
49
+ "\n" +
50
+ '# Custom' +
51
+ dumps[:custom]
52
+ end
53
+
54
+ end
@@ -0,0 +1,13 @@
1
+ class Time
2
+
3
+ # Returns a string with the time in an ISO-8601 date format.
4
+ def to_iso8601_date
5
+ self.strftime("%Y-%m-%d")
6
+ end
7
+
8
+ # Returns a string with the time in an ISO-8601 time format.
9
+ def to_iso8601_time
10
+ self.gmtime.strftime("%Y-%m-%dT%H:%M:%SZ")
11
+ end
12
+
13
+ end
@@ -0,0 +1,29 @@
1
+ module Nanoc2::Extra
2
+
3
+ # A FileProxy is a proxy for a File object. It is used to prevent a File
4
+ # object from being created until it is actually necessary.
5
+ #
6
+ # For example, a site with a few thousand pages would fail to compile
7
+ # because the massive amount of file descriptors necessary, but the file
8
+ # proxy will make sure the File object is not created until it is used.
9
+ class FileProxy
10
+
11
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ || m.to_s == 'object_id' }
12
+
13
+ # Creates a new file proxy for the given path. This is similar to
14
+ # creating a File object with the same path, except that the File object
15
+ # will not be created until it is accessed.
16
+ def initialize(path)
17
+ @path = path
18
+ end
19
+
20
+ # Makes sure all method calls are relayed to a File object, which will
21
+ # be created right before the method call takes place and destroyed
22
+ # right after.
23
+ def method_missing(sym, *args, &block)
24
+ File.new(@path).__send__(sym, *args, &block)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,48 @@
1
+ module Nanoc2::Extra
2
+
3
+ # Nanoc2::Extra::VCS is a very simple representation of a version control
4
+ # system that abstracts the add, remove and move operations. It does not
5
+ # commit. This class is primarily used by data sources that store data as
6
+ # flat files on the disk.
7
+ #
8
+ # This is the abstract superclass for all VCSes. Subclasses should implement
9
+ # the indicated methods.
10
+ class VCS < Nanoc2::Plugin
11
+
12
+ # Adds the file with the given filename to the working copy.
13
+ #
14
+ # Subclasses must implement this method.
15
+ def add(filename)
16
+ not_implemented('add')
17
+ end
18
+
19
+ # Removes the file with the given filename from the working copy. When
20
+ # this method is executed, the file should no longer be present on the
21
+ # disk.
22
+ #
23
+ # Subclasses must implement this method.
24
+ def remove(filename)
25
+ not_implemented('remove')
26
+ end
27
+
28
+ # Moves the file with the given filename to a new location. When this
29
+ # method is executed, the original file should no longer be present on the
30
+ # disk.
31
+ #
32
+ # Subclasses must implement this method.
33
+ def move(src, dst)
34
+ not_implemented('move')
35
+ end
36
+
37
+ private
38
+
39
+ def not_implemented(name)
40
+ raise NotImplementedError.new(
41
+ "#{self.class} does not override ##{name}, which is required for " +
42
+ "this data source to be used."
43
+ )
44
+ end
45
+
46
+ end
47
+
48
+ end