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.
- data/ChangeLog +3 -0
- data/LICENSE +19 -0
- data/README +75 -0
- data/Rakefile +76 -0
- data/bin/nanoc2 +26 -0
- data/lib/nanoc2.rb +73 -0
- data/lib/nanoc2/base.rb +26 -0
- data/lib/nanoc2/base/asset.rb +117 -0
- data/lib/nanoc2/base/asset_defaults.rb +21 -0
- data/lib/nanoc2/base/asset_rep.rb +282 -0
- data/lib/nanoc2/base/binary_filter.rb +44 -0
- data/lib/nanoc2/base/code.rb +41 -0
- data/lib/nanoc2/base/compiler.rb +67 -0
- data/lib/nanoc2/base/core_ext.rb +2 -0
- data/lib/nanoc2/base/core_ext/hash.rb +78 -0
- data/lib/nanoc2/base/core_ext/string.rb +8 -0
- data/lib/nanoc2/base/data_source.rb +286 -0
- data/lib/nanoc2/base/defaults.rb +30 -0
- data/lib/nanoc2/base/filter.rb +93 -0
- data/lib/nanoc2/base/layout.rb +91 -0
- data/lib/nanoc2/base/notification_center.rb +66 -0
- data/lib/nanoc2/base/page.rb +132 -0
- data/lib/nanoc2/base/page_defaults.rb +20 -0
- data/lib/nanoc2/base/page_rep.rb +324 -0
- data/lib/nanoc2/base/plugin.rb +71 -0
- data/lib/nanoc2/base/proxies.rb +5 -0
- data/lib/nanoc2/base/proxies/asset_proxy.rb +29 -0
- data/lib/nanoc2/base/proxies/asset_rep_proxy.rb +26 -0
- data/lib/nanoc2/base/proxies/layout_proxy.rb +25 -0
- data/lib/nanoc2/base/proxies/page_proxy.rb +35 -0
- data/lib/nanoc2/base/proxies/page_rep_proxy.rb +28 -0
- data/lib/nanoc2/base/proxy.rb +37 -0
- data/lib/nanoc2/base/router.rb +72 -0
- data/lib/nanoc2/base/site.rb +274 -0
- data/lib/nanoc2/base/template.rb +64 -0
- data/lib/nanoc2/binary_filters.rb +1 -0
- data/lib/nanoc2/binary_filters/image_science_thumbnail.rb +28 -0
- data/lib/nanoc2/cli.rb +9 -0
- data/lib/nanoc2/cli/base.rb +132 -0
- data/lib/nanoc2/cli/commands.rb +10 -0
- data/lib/nanoc2/cli/commands/autocompile.rb +80 -0
- data/lib/nanoc2/cli/commands/compile.rb +312 -0
- data/lib/nanoc2/cli/commands/create_layout.rb +85 -0
- data/lib/nanoc2/cli/commands/create_page.rb +85 -0
- data/lib/nanoc2/cli/commands/create_site.rb +323 -0
- data/lib/nanoc2/cli/commands/create_template.rb +76 -0
- data/lib/nanoc2/cli/commands/help.rb +69 -0
- data/lib/nanoc2/cli/commands/info.rb +125 -0
- data/lib/nanoc2/cli/commands/switch.rb +141 -0
- data/lib/nanoc2/cli/commands/update.rb +91 -0
- data/lib/nanoc2/cli/logger.rb +72 -0
- data/lib/nanoc2/data_sources.rb +2 -0
- data/lib/nanoc2/data_sources/filesystem.rb +707 -0
- data/lib/nanoc2/data_sources/filesystem_combined.rb +495 -0
- data/lib/nanoc2/extra.rb +6 -0
- data/lib/nanoc2/extra/auto_compiler.rb +285 -0
- data/lib/nanoc2/extra/context.rb +22 -0
- data/lib/nanoc2/extra/core_ext.rb +2 -0
- data/lib/nanoc2/extra/core_ext/hash.rb +54 -0
- data/lib/nanoc2/extra/core_ext/time.rb +13 -0
- data/lib/nanoc2/extra/file_proxy.rb +29 -0
- data/lib/nanoc2/extra/vcs.rb +48 -0
- data/lib/nanoc2/extra/vcses.rb +5 -0
- data/lib/nanoc2/extra/vcses/bazaar.rb +21 -0
- data/lib/nanoc2/extra/vcses/dummy.rb +20 -0
- data/lib/nanoc2/extra/vcses/git.rb +21 -0
- data/lib/nanoc2/extra/vcses/mercurial.rb +21 -0
- data/lib/nanoc2/extra/vcses/subversion.rb +21 -0
- data/lib/nanoc2/filters.rb +16 -0
- data/lib/nanoc2/filters/bluecloth.rb +13 -0
- data/lib/nanoc2/filters/erb.rb +19 -0
- data/lib/nanoc2/filters/erubis.rb +14 -0
- data/lib/nanoc2/filters/haml.rb +21 -0
- data/lib/nanoc2/filters/markaby.rb +14 -0
- data/lib/nanoc2/filters/maruku.rb +14 -0
- data/lib/nanoc2/filters/old.rb +19 -0
- data/lib/nanoc2/filters/rainpress.rb +13 -0
- data/lib/nanoc2/filters/rdiscount.rb +13 -0
- data/lib/nanoc2/filters/rdoc.rb +23 -0
- data/lib/nanoc2/filters/redcloth.rb +14 -0
- data/lib/nanoc2/filters/relativize_paths.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_css.rb +16 -0
- data/lib/nanoc2/filters/relativize_paths_in_html.rb +16 -0
- data/lib/nanoc2/filters/rubypants.rb +14 -0
- data/lib/nanoc2/filters/sass.rb +18 -0
- data/lib/nanoc2/helpers.rb +9 -0
- data/lib/nanoc2/helpers/blogging.rb +217 -0
- data/lib/nanoc2/helpers/capturing.rb +63 -0
- data/lib/nanoc2/helpers/filtering.rb +54 -0
- data/lib/nanoc2/helpers/html_escape.rb +25 -0
- data/lib/nanoc2/helpers/link_to.rb +113 -0
- data/lib/nanoc2/helpers/render.rb +49 -0
- data/lib/nanoc2/helpers/tagging.rb +56 -0
- data/lib/nanoc2/helpers/text.rb +38 -0
- data/lib/nanoc2/helpers/xml_sitemap.rb +63 -0
- data/lib/nanoc2/routers.rb +3 -0
- data/lib/nanoc2/routers/default.rb +54 -0
- data/lib/nanoc2/routers/no_dirs.rb +66 -0
- data/lib/nanoc2/routers/versioned.rb +79 -0
- metadata +185 -0
data/lib/nanoc2/extra.rb
ADDED
@@ -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>—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,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
|