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
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Nanoc2::CLI
|
4
|
+
|
5
|
+
# Nanoc2::CLI::Logger is a singleton class responsible for generating
|
6
|
+
# feedback in the terminal.
|
7
|
+
class Logger
|
8
|
+
|
9
|
+
ACTION_COLORS = {
|
10
|
+
:create => "\e[1m" + "\e[32m", # bold + green
|
11
|
+
:update => "\e[1m" + "\e[33m", # bold + yellow
|
12
|
+
:identical => "\e[1m", # bold
|
13
|
+
:skip => "\e[1m", # bold
|
14
|
+
:'not written' => "\e[1m" # bold
|
15
|
+
}
|
16
|
+
|
17
|
+
include Singleton
|
18
|
+
|
19
|
+
# The log level, which can be :high, :low or :off (which will log all
|
20
|
+
# messages, only high-priority messages, or no messages at all,
|
21
|
+
# respectively).
|
22
|
+
attr_accessor :level
|
23
|
+
|
24
|
+
# Whether to use color in log messages or not
|
25
|
+
attr_accessor :color
|
26
|
+
alias_method :color?, :color
|
27
|
+
|
28
|
+
def initialize # :nodoc:
|
29
|
+
@level = :high
|
30
|
+
@color = true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Logs a file-related action.
|
34
|
+
#
|
35
|
+
# +level+:: The importance of this action. Can be :high or :low.
|
36
|
+
#
|
37
|
+
# +action+:: The kind of file action. Can be :create, :update or
|
38
|
+
# :identical.
|
39
|
+
#
|
40
|
+
# +path+:: The path to the file the action was performed on.
|
41
|
+
def file(level, action, path, duration=nil)
|
42
|
+
log(
|
43
|
+
level,
|
44
|
+
'%s%12s%s %s%s' % [
|
45
|
+
color? ? ACTION_COLORS[action.to_sym] : '',
|
46
|
+
action,
|
47
|
+
color? ? "\e[0m" : '',
|
48
|
+
duration.nil? ? '' : "[%2.2fs] " % [ duration ],
|
49
|
+
path
|
50
|
+
]
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Logs a message.
|
55
|
+
#
|
56
|
+
# +level+:: The importance of this message. Can be :high or :low.
|
57
|
+
#
|
58
|
+
# +s+:: The message to be logged.
|
59
|
+
#
|
60
|
+
# +io+:: The IO instance to which the message will be written. Defaults to
|
61
|
+
# standard output.
|
62
|
+
def log(level, s, io=$stdout)
|
63
|
+
# Don't log when logging is disabled
|
64
|
+
return if @level == :off
|
65
|
+
|
66
|
+
# Log when level permits it
|
67
|
+
io.puts(s) if (@level == :low or @level == level)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,707 @@
|
|
1
|
+
module Nanoc2::DataSources
|
2
|
+
|
3
|
+
# The filesystem data source is the default data source for a new nanoc
|
4
|
+
# site. It stores all data as files on the hard disk.
|
5
|
+
#
|
6
|
+
# None of the methods are documented in this file. See Nanoc2::DataSource for
|
7
|
+
# documentation on the overridden methods instead.
|
8
|
+
#
|
9
|
+
# = Pages
|
10
|
+
#
|
11
|
+
# The filesystem data source stores its pages in nested directories. Each
|
12
|
+
# directory represents a single page. The root directory is the 'content'
|
13
|
+
# directory.
|
14
|
+
#
|
15
|
+
# Every directory has a content file and a meta file. The content file
|
16
|
+
# contains the actual page content, while the meta file contains the page's
|
17
|
+
# metadata, formatted as YAML.
|
18
|
+
#
|
19
|
+
# Both content files and meta files are named after its parent directory
|
20
|
+
# (i.e. page). For example, a page named 'foo' will have a directorynamed
|
21
|
+
# 'foo', with e.g. a 'foo.markdown' content file and a 'foo.yaml' meta file.
|
22
|
+
#
|
23
|
+
# Content file extensions are not used for determining the filter that
|
24
|
+
# should be run; the meta file defines the list of filters. The meta file
|
25
|
+
# extension must always be 'yaml', though.
|
26
|
+
#
|
27
|
+
# Content files can also have the 'index' basename. Similarly, meta files
|
28
|
+
# can have the 'meta' basename. For example, a parent directory named 'foo'
|
29
|
+
# can have an 'index.txt' content file and a 'meta.yaml' meta file. This is
|
30
|
+
# to preserve backward compatibility.
|
31
|
+
#
|
32
|
+
# = Page defaults
|
33
|
+
#
|
34
|
+
# The page defaults are loaded from a YAML-formatted file named
|
35
|
+
# 'page_defaults.yaml' at the top level of the nanoc site directory. For
|
36
|
+
# backward compatibility, the file can also be named 'meta.yaml'.
|
37
|
+
#
|
38
|
+
# = Assets
|
39
|
+
#
|
40
|
+
# Assets are stored in the 'assets' directory (surprise!). The structure is
|
41
|
+
# very similar to the structure of the 'content' directory, so see the Pages
|
42
|
+
# section for details on how this directory is structured.
|
43
|
+
#
|
44
|
+
# = Asset defaults
|
45
|
+
#
|
46
|
+
# The asset defaults are stored similar to the way page defaults are stored,
|
47
|
+
# except that the asset defaults file is named 'asset_defaults.yaml'
|
48
|
+
# instead.
|
49
|
+
#
|
50
|
+
# = Layouts
|
51
|
+
#
|
52
|
+
# Layouts are stored as directories in the 'layouts' directory. Each layout
|
53
|
+
# contains a content file and a meta file. The content file contain the
|
54
|
+
# actual layout, and the meta file describes how the page should be handled
|
55
|
+
# (contains the filter that should be used).
|
56
|
+
#
|
57
|
+
# For backward compatibility, a layout can also be a single file in the
|
58
|
+
# 'layouts' directory. Such a layout cannot have any metadata; the filter
|
59
|
+
# used for this layout is determined from the file extension.
|
60
|
+
#
|
61
|
+
# = Templates
|
62
|
+
#
|
63
|
+
# Templates are located in the 'templates' directroy. Every template is a
|
64
|
+
# directory consisting of a content file and a meta file, both named after
|
65
|
+
# the template. This is very similar to the way pages are stored, except
|
66
|
+
# that templates cannot be nested.
|
67
|
+
#
|
68
|
+
# = Code
|
69
|
+
#
|
70
|
+
# Code is stored in '.rb' files in the 'lib' directory. Code can reside in
|
71
|
+
# sub-directories.
|
72
|
+
class Filesystem < Nanoc2::DataSource
|
73
|
+
|
74
|
+
PAGE_DEFAULTS_FILENAME = 'page_defaults.yaml'
|
75
|
+
PAGE_DEFAULTS_FILENAME_OLD = 'meta.yaml'
|
76
|
+
ASSET_DEFAULTS_FILENAME = 'asset_defaults.yaml'
|
77
|
+
|
78
|
+
########## Attributes ##########
|
79
|
+
|
80
|
+
identifier :filesystem
|
81
|
+
|
82
|
+
########## VCSes ##########
|
83
|
+
|
84
|
+
attr_accessor :vcs
|
85
|
+
|
86
|
+
def vcs
|
87
|
+
@vcs ||= Nanoc2::Extra::VCSes::Dummy.new
|
88
|
+
end
|
89
|
+
|
90
|
+
########## Preparation ##########
|
91
|
+
|
92
|
+
def up # :nodoc:
|
93
|
+
end
|
94
|
+
|
95
|
+
def down # :nodoc:
|
96
|
+
end
|
97
|
+
|
98
|
+
def setup # :nodoc:
|
99
|
+
# Create directories
|
100
|
+
%w( assets content templates layouts lib ).each do |dir|
|
101
|
+
FileUtils.mkdir_p(dir)
|
102
|
+
vcs.add(dir)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def destroy # :nodoc:
|
107
|
+
# Remove files
|
108
|
+
vcs.remove(ASSET_DEFAULTS_FILENAME) if File.file?(ASSET_DEFAULTS_FILENAME)
|
109
|
+
vcs.remove(PAGE_DEFAULTS_FILENAME) if File.file?(PAGE_DEFAULTS_FILENAME)
|
110
|
+
vcs.remove(PAGE_DEFAULTS_FILENAME_OLD) if File.file?(PAGE_DEFAULTS_FILENAME_OLD)
|
111
|
+
|
112
|
+
# Remove directories
|
113
|
+
vcs.remove('assets')
|
114
|
+
vcs.remove('content')
|
115
|
+
vcs.remove('templates')
|
116
|
+
vcs.remove('layouts')
|
117
|
+
vcs.remove('lib')
|
118
|
+
end
|
119
|
+
|
120
|
+
def update # :nodoc:
|
121
|
+
update_page_defaults
|
122
|
+
update_pages
|
123
|
+
update_templates
|
124
|
+
end
|
125
|
+
|
126
|
+
########## Pages ##########
|
127
|
+
|
128
|
+
def pages # :nodoc:
|
129
|
+
meta_filenames('content').map do |meta_filename|
|
130
|
+
# Read metadata
|
131
|
+
meta = YAML.load_file(meta_filename) || {}
|
132
|
+
|
133
|
+
if meta['is_draft']
|
134
|
+
# Skip drafts
|
135
|
+
nil
|
136
|
+
else
|
137
|
+
# Get content
|
138
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
139
|
+
content = File.read(content_filename)
|
140
|
+
|
141
|
+
# Get attributes
|
142
|
+
attributes = meta.merge(:file => Nanoc2::Extra::FileProxy.new(content_filename))
|
143
|
+
|
144
|
+
# Get path
|
145
|
+
path = meta_filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')
|
146
|
+
|
147
|
+
# Get modification times
|
148
|
+
meta_mtime = File.stat(meta_filename).mtime
|
149
|
+
content_mtime = File.stat(content_filename).mtime
|
150
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
151
|
+
|
152
|
+
# Create page object
|
153
|
+
Nanoc2::Page.new(content, attributes, path, mtime)
|
154
|
+
end
|
155
|
+
end.compact
|
156
|
+
end
|
157
|
+
|
158
|
+
def save_page(page) # :nodoc:
|
159
|
+
# Determine possible meta file paths
|
160
|
+
last_component = page.path.split('/')[-1]
|
161
|
+
meta_filename_worst = 'content' + page.path + 'index.yaml'
|
162
|
+
meta_filename_best = 'content' + page.path + (last_component || 'content') + '.yaml'
|
163
|
+
|
164
|
+
# Get existing path
|
165
|
+
existing_path = nil
|
166
|
+
existing_path = meta_filename_best if File.file?(meta_filename_best)
|
167
|
+
existing_path = meta_filename_worst if File.file?(meta_filename_worst)
|
168
|
+
|
169
|
+
if existing_path.nil?
|
170
|
+
# Get filenames
|
171
|
+
dir_path = 'content' + page.path
|
172
|
+
meta_filename = meta_filename_best
|
173
|
+
content_filename = 'content' + page.path + (last_component || 'content') + '.html'
|
174
|
+
|
175
|
+
# Notify
|
176
|
+
Nanoc2::NotificationCenter.post(:file_created, meta_filename)
|
177
|
+
Nanoc2::NotificationCenter.post(:file_created, content_filename)
|
178
|
+
|
179
|
+
# Create directories if necessary
|
180
|
+
FileUtils.mkdir_p(dir_path)
|
181
|
+
else
|
182
|
+
# Get filenames
|
183
|
+
meta_filename = existing_path
|
184
|
+
content_filename = content_filename_for_dir(File.dirname(existing_path))
|
185
|
+
|
186
|
+
# Notify
|
187
|
+
Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
|
188
|
+
Nanoc2::NotificationCenter.post(:file_updated, content_filename)
|
189
|
+
end
|
190
|
+
|
191
|
+
# Write files
|
192
|
+
File.open(meta_filename, 'w') { |io| io.write(page.attributes.to_split_yaml) }
|
193
|
+
File.open(content_filename, 'w') { |io| io.write(page.content) }
|
194
|
+
|
195
|
+
# Add to working copy if possible
|
196
|
+
if existing_path.nil?
|
197
|
+
vcs.add(meta_filename)
|
198
|
+
vcs.add(content_filename)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def move_page(page, new_path) # :nodoc:
|
203
|
+
# TODO implement
|
204
|
+
end
|
205
|
+
|
206
|
+
def delete_page(page) # :nodoc:
|
207
|
+
# TODO implement
|
208
|
+
end
|
209
|
+
|
210
|
+
########## Assets ##########
|
211
|
+
|
212
|
+
def assets # :nodoc:
|
213
|
+
meta_filenames('assets').map do |meta_filename|
|
214
|
+
# Read metadata
|
215
|
+
meta = YAML.load_file(meta_filename) || {}
|
216
|
+
|
217
|
+
# Get content file
|
218
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
219
|
+
content_file = Nanoc2::Extra::FileProxy.new(content_filename)
|
220
|
+
|
221
|
+
# Get attributes
|
222
|
+
attributes = { 'extension' => File.extname(content_filename)[1..-1] }.merge(meta)
|
223
|
+
|
224
|
+
# Get path
|
225
|
+
path = meta_filename.sub(/^assets/, '').sub(/[^\/]+\.yaml$/, '')
|
226
|
+
|
227
|
+
# Get modification times
|
228
|
+
meta_mtime = File.stat(meta_filename).mtime
|
229
|
+
content_mtime = File.stat(content_filename).mtime
|
230
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
231
|
+
|
232
|
+
# Create asset object
|
233
|
+
Nanoc2::Asset.new(content_file, attributes, path, mtime)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def save_asset(asset) # :nodoc:
|
238
|
+
# Determine meta file path
|
239
|
+
last_component = asset.path.split('/')[-1]
|
240
|
+
meta_filename = 'assets' + asset.path + last_component + '.yaml'
|
241
|
+
|
242
|
+
# Get existing path
|
243
|
+
existing_path = nil
|
244
|
+
existing_path = meta_filename_best if File.file?(meta_filename_best)
|
245
|
+
existing_path = meta_filename_worst if File.file?(meta_filename_worst)
|
246
|
+
|
247
|
+
if meta_filename.nil?
|
248
|
+
# Get filenames
|
249
|
+
dir_path = 'assets' + asset.path
|
250
|
+
content_filename = 'assets' + asset.path + last_component + '.dat'
|
251
|
+
|
252
|
+
# Notify
|
253
|
+
Nanoc2::NotificationCenter.post(:file_created, meta_filename)
|
254
|
+
Nanoc2::NotificationCenter.post(:file_created, content_filename)
|
255
|
+
|
256
|
+
# Create directories if necessary
|
257
|
+
FileUtils.mkdir_p(dir_path)
|
258
|
+
else
|
259
|
+
# Get filenames
|
260
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
261
|
+
|
262
|
+
# Notify
|
263
|
+
Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
|
264
|
+
Nanoc2::NotificationCenter.post(:file_updated, content_filename)
|
265
|
+
end
|
266
|
+
|
267
|
+
# Write files
|
268
|
+
File.open(meta_filename, 'w') { |io| io.write(asset.attributes.to_split_yaml) }
|
269
|
+
File.open(content_filename, 'w') { }
|
270
|
+
|
271
|
+
# Add to working copy if possible
|
272
|
+
if meta_filename.nil?
|
273
|
+
vcs.add(meta_filename)
|
274
|
+
vcs.add(content_filename)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def move_asset(asset, new_path) # :nodoc:
|
279
|
+
# TODO implement
|
280
|
+
end
|
281
|
+
|
282
|
+
def delete_asset(asset) # :nodoc:
|
283
|
+
# TODO implement
|
284
|
+
end
|
285
|
+
|
286
|
+
########## Page Defaults ##########
|
287
|
+
|
288
|
+
def page_defaults # :nodoc:
|
289
|
+
# Get attributes
|
290
|
+
filename = File.file?(PAGE_DEFAULTS_FILENAME) ? PAGE_DEFAULTS_FILENAME : PAGE_DEFAULTS_FILENAME_OLD
|
291
|
+
attributes = YAML.load_file(filename) || {}
|
292
|
+
|
293
|
+
# Get mtime
|
294
|
+
mtime = File.stat(filename).mtime
|
295
|
+
|
296
|
+
# Build page defaults
|
297
|
+
Nanoc2::PageDefaults.new(attributes, mtime)
|
298
|
+
end
|
299
|
+
|
300
|
+
def save_page_defaults(page_defaults) # :nodoc:
|
301
|
+
# Notify
|
302
|
+
if File.file?(PAGE_DEFAULTS_FILENAME)
|
303
|
+
filename = PAGE_DEFAULTS_FILENAME
|
304
|
+
created = false
|
305
|
+
Nanoc2::NotificationCenter.post(:file_updated, filename)
|
306
|
+
elsif File.file?(PAGE_DEFAULTS_FILENAME_OLD)
|
307
|
+
filename = PAGE_DEFAULTS_FILENAME_OLD
|
308
|
+
created = false
|
309
|
+
Nanoc2::NotificationCenter.post(:file_updated, filename)
|
310
|
+
else
|
311
|
+
filename = PAGE_DEFAULTS_FILENAME
|
312
|
+
created = true
|
313
|
+
Nanoc2::NotificationCenter.post(:file_created, filename)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Write
|
317
|
+
File.open(filename, 'w') do |io|
|
318
|
+
io.write(page_defaults.attributes.to_split_yaml)
|
319
|
+
end
|
320
|
+
|
321
|
+
# Add to working copy if possible
|
322
|
+
vcs.add(filename) if created
|
323
|
+
end
|
324
|
+
|
325
|
+
########## Asset Defaults ##########
|
326
|
+
|
327
|
+
def asset_defaults # :nodoc:
|
328
|
+
if File.file?(ASSET_DEFAULTS_FILENAME)
|
329
|
+
# Get attributes
|
330
|
+
attributes = YAML.load_file(ASSET_DEFAULTS_FILENAME) || {}
|
331
|
+
|
332
|
+
# Get mtime
|
333
|
+
mtime = File.stat(ASSET_DEFAULTS_FILENAME).mtime
|
334
|
+
|
335
|
+
# Build asset defaults
|
336
|
+
Nanoc2::AssetDefaults.new(attributes, mtime)
|
337
|
+
else
|
338
|
+
Nanoc2::AssetDefaults.new({})
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def save_asset_defaults(asset_defaults) # :nodoc:
|
343
|
+
# Notify
|
344
|
+
if File.file?(ASSET_DEFAULTS_FILENAME)
|
345
|
+
Nanoc2::NotificationCenter.post(:file_updated, ASSET_DEFAULTS_FILENAME)
|
346
|
+
created = false
|
347
|
+
else
|
348
|
+
Nanoc2::NotificationCenter.post(:file_created, ASSET_DEFAULTS_FILENAME)
|
349
|
+
created = true
|
350
|
+
end
|
351
|
+
|
352
|
+
# Write
|
353
|
+
File.open(ASSET_DEFAULTS_FILENAME, 'w') do |io|
|
354
|
+
io.write(asset_defaults.attributes.to_split_yaml)
|
355
|
+
end
|
356
|
+
|
357
|
+
# Add to working copy if possible
|
358
|
+
vcs.add(ASSET_DEFAULTS_FILENAME) if created
|
359
|
+
end
|
360
|
+
|
361
|
+
########## Layouts ##########
|
362
|
+
|
363
|
+
def layouts # :nodoc:
|
364
|
+
# Determine what layout directory structure is being used
|
365
|
+
is_old_school = (Dir['layouts/*'].select { |f| File.file?(f) }.size > 0)
|
366
|
+
|
367
|
+
if is_old_school
|
368
|
+
# Warn about deprecation
|
369
|
+
warn(
|
370
|
+
'DEPRECATION WARNING: nanoc 2.1 changes the way layouts are ' +
|
371
|
+
'stored. Future versions will not support these outdated sites. ' +
|
372
|
+
'To update your site, issue \'nanoc update\'.'
|
373
|
+
)
|
374
|
+
|
375
|
+
Dir[File.join('layouts', '*')].reject { |f| f =~ /~$/ }.map do |filename|
|
376
|
+
# Get content
|
377
|
+
content = File.read(filename)
|
378
|
+
|
379
|
+
# Get attributes
|
380
|
+
attributes = { :extension => File.extname(filename)}
|
381
|
+
|
382
|
+
# Get path
|
383
|
+
path = File.basename(filename, attributes[:extension])
|
384
|
+
|
385
|
+
# Get modification time
|
386
|
+
mtime = File.stat(filename).mtime
|
387
|
+
|
388
|
+
# Create layout object
|
389
|
+
Nanoc2::Layout.new(content, attributes, path, mtime)
|
390
|
+
end
|
391
|
+
else
|
392
|
+
meta_filenames('layouts').map do |meta_filename|
|
393
|
+
# Get content
|
394
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
395
|
+
content = File.read(content_filename)
|
396
|
+
|
397
|
+
# Get attributes
|
398
|
+
attributes = YAML.load_file(meta_filename) || {}
|
399
|
+
|
400
|
+
# Get path
|
401
|
+
path = meta_filename.sub(/^layouts\//, '').sub(/\/[^\/]+\.yaml$/, '')
|
402
|
+
|
403
|
+
# Get modification times
|
404
|
+
meta_mtime = File.stat(meta_filename).mtime
|
405
|
+
content_mtime = File.stat(content_filename).mtime
|
406
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
407
|
+
|
408
|
+
# Create layout object
|
409
|
+
Nanoc2::Layout.new(content, attributes, path, mtime)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
414
|
+
def save_layout(layout) # :nodoc:
|
415
|
+
# Determine what layout directory structure is being used
|
416
|
+
is_old_school = (Dir['layouts/*'].select { |f| File.file?(f) }.size > 0)
|
417
|
+
error_outdated if is_old_school
|
418
|
+
|
419
|
+
# Get paths
|
420
|
+
last_component = layout.path.split('/')[-1]
|
421
|
+
dir_path = 'layouts' + layout.path
|
422
|
+
meta_filename = dir_path + last_component + '.yaml'
|
423
|
+
content_filename = Dir[dir_path + last_component + '.*'][0]
|
424
|
+
|
425
|
+
if File.file?(meta_filename)
|
426
|
+
created = false
|
427
|
+
|
428
|
+
# Notify
|
429
|
+
Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
|
430
|
+
Nanoc2::NotificationCenter.post(:file_updated, content_filename)
|
431
|
+
else
|
432
|
+
created = true
|
433
|
+
|
434
|
+
# Create dir
|
435
|
+
FileUtils.mkdir_p(dir_path)
|
436
|
+
|
437
|
+
# Get content filename
|
438
|
+
content_filename = dir_path + last_component + '.html'
|
439
|
+
|
440
|
+
# Notify
|
441
|
+
Nanoc2::NotificationCenter.post(:file_created, meta_filename)
|
442
|
+
Nanoc2::NotificationCenter.post(:file_created, content_filename)
|
443
|
+
end
|
444
|
+
|
445
|
+
# Write files
|
446
|
+
File.open(meta_filename, 'w') { |io| io.write(layout.attributes.to_split_yaml) }
|
447
|
+
File.open(content_filename, 'w') { |io| io.write(layout.content) }
|
448
|
+
|
449
|
+
# Add to working copy if possible
|
450
|
+
if created
|
451
|
+
vcs.add(meta_filename)
|
452
|
+
vcs.add(content_filename)
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def move_layout(layout, new_path) # :nodoc:
|
457
|
+
# TODO implement
|
458
|
+
end
|
459
|
+
|
460
|
+
def delete_layout(layout) # :nodoc:
|
461
|
+
# TODO implement
|
462
|
+
end
|
463
|
+
|
464
|
+
########## Templates ##########
|
465
|
+
|
466
|
+
def templates # :nodoc:
|
467
|
+
meta_filenames('templates').map do |meta_filename|
|
468
|
+
# Get name
|
469
|
+
name = meta_filename.sub(/^templates\/(.*)\/[^\/]+\.yaml$/, '\1')
|
470
|
+
|
471
|
+
# Get content
|
472
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
473
|
+
content = File.read(content_filename)
|
474
|
+
|
475
|
+
# Get attributes
|
476
|
+
attributes = YAML.load_file(meta_filename) || {}
|
477
|
+
|
478
|
+
# Build template
|
479
|
+
Nanoc2::Template.new(content, attributes, name)
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
def save_template(template) # :nodoc:
|
484
|
+
# Determine possible meta file paths
|
485
|
+
meta_filename_worst = 'templates/' + template.name + '/index.yaml'
|
486
|
+
meta_filename_best = 'templates/' + template.name + '/' + template.name + '.yaml'
|
487
|
+
|
488
|
+
# Get existing path
|
489
|
+
existing_path = nil
|
490
|
+
existing_path = meta_filename_best if File.file?(meta_filename_best)
|
491
|
+
existing_path = meta_filename_worst if File.file?(meta_filename_worst)
|
492
|
+
|
493
|
+
if existing_path.nil?
|
494
|
+
# Get filenames
|
495
|
+
dir_path = 'templates/' + template.name
|
496
|
+
meta_filename = meta_filename_best
|
497
|
+
content_filename = 'templates/' + template.name + '/' + template.name + '.html'
|
498
|
+
|
499
|
+
# Notify
|
500
|
+
Nanoc2::NotificationCenter.post(:file_created, meta_filename)
|
501
|
+
Nanoc2::NotificationCenter.post(:file_created, content_filename)
|
502
|
+
|
503
|
+
# Create directories if necessary
|
504
|
+
FileUtils.mkdir_p(dir_path)
|
505
|
+
else
|
506
|
+
# Get filenames
|
507
|
+
meta_filename = existing_path
|
508
|
+
content_filename = content_filename_for_dir(File.dirname(existing_path))
|
509
|
+
|
510
|
+
# Notify
|
511
|
+
Nanoc2::NotificationCenter.post(:file_updated, meta_filename)
|
512
|
+
Nanoc2::NotificationCenter.post(:file_updated, content_filename)
|
513
|
+
end
|
514
|
+
|
515
|
+
# Write files
|
516
|
+
File.open(meta_filename, 'w') { |io| io.write(template.page_attributes.to_split_yaml) }
|
517
|
+
File.open(content_filename, 'w') { |io| io.write(template.page_content) }
|
518
|
+
|
519
|
+
# Add to working copy if possible
|
520
|
+
if existing_path.nil?
|
521
|
+
vcs.add(meta_filename)
|
522
|
+
vcs.add(content_filename)
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def move_template(template, new_name) # :nodoc:
|
527
|
+
# TODO implement
|
528
|
+
end
|
529
|
+
|
530
|
+
def delete_template(template) # :nodoc:
|
531
|
+
# TODO implement
|
532
|
+
end
|
533
|
+
|
534
|
+
########## Code ##########
|
535
|
+
|
536
|
+
def code # :nodoc:
|
537
|
+
# Get files
|
538
|
+
filenames = Dir['lib/**/*.rb'].sort
|
539
|
+
|
540
|
+
# Get data
|
541
|
+
data = filenames.map { |filename| File.read(filename) + "\n" }.join('')
|
542
|
+
|
543
|
+
# Get modification time
|
544
|
+
mtimes = filenames.map { |filename| File.stat(filename).mtime }
|
545
|
+
mtime = mtimes.inject { |memo, mtime| memo > mtime ? mtime : memo }
|
546
|
+
|
547
|
+
# Build code
|
548
|
+
Nanoc2::Code.new(data, mtime)
|
549
|
+
end
|
550
|
+
|
551
|
+
def save_code(code) # :nodoc:
|
552
|
+
# Check whether code existed
|
553
|
+
existed = File.file?('lib/default.rb')
|
554
|
+
|
555
|
+
# Remove all existing code files
|
556
|
+
Dir['lib/**/*.rb'].each do |file|
|
557
|
+
vcs.remove(file) unless file == 'lib/default.rb'
|
558
|
+
end
|
559
|
+
|
560
|
+
# Notify
|
561
|
+
if existed
|
562
|
+
Nanoc2::NotificationCenter.post(:file_updated, 'lib/default.rb')
|
563
|
+
else
|
564
|
+
Nanoc2::NotificationCenter.post(:file_created, 'lib/default.rb')
|
565
|
+
end
|
566
|
+
|
567
|
+
# Write new code
|
568
|
+
File.open('lib/default.rb', 'w') do |io|
|
569
|
+
io.write(code.data)
|
570
|
+
end
|
571
|
+
|
572
|
+
# Add to working copy if possible
|
573
|
+
vcs.add('lib/default.rb') unless existed
|
574
|
+
end
|
575
|
+
|
576
|
+
private
|
577
|
+
|
578
|
+
########## Custom functions ##########
|
579
|
+
|
580
|
+
# Returns the list of all meta files in the given base directory as well
|
581
|
+
# as its subdirectories.
|
582
|
+
def meta_filenames(base)
|
583
|
+
# Find all possible meta file names
|
584
|
+
filenames = Dir[base + '/**/*.yaml']
|
585
|
+
|
586
|
+
# Filter out invalid meta files
|
587
|
+
good_filenames = []
|
588
|
+
bad_filenames = []
|
589
|
+
filenames.each do |filename|
|
590
|
+
if filename =~ /meta\.yaml$/ or filename =~ /([^\/]+)\/\1\.yaml$/
|
591
|
+
good_filenames << filename
|
592
|
+
else
|
593
|
+
bad_filenames << filename
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
# Warn about bad filenames
|
598
|
+
unless bad_filenames.empty?
|
599
|
+
raise RuntimeError.new(
|
600
|
+
"The following files appear to be meta files, " +
|
601
|
+
"but have an invalid name:\n - " +
|
602
|
+
bad_filenames.join("\n - ")
|
603
|
+
)
|
604
|
+
end
|
605
|
+
|
606
|
+
good_filenames
|
607
|
+
end
|
608
|
+
|
609
|
+
# Returns the filename of the content file in the given directory,
|
610
|
+
# ignoring any unwanted files (files that end with '~', '.orig', '.rej' or
|
611
|
+
# '.bak')
|
612
|
+
def content_filename_for_dir(dir)
|
613
|
+
# Find all files
|
614
|
+
filename_glob_1 = dir.sub(/([^\/]+)$/, '\1/\1.*')
|
615
|
+
filename_glob_2 = dir.sub(/([^\/]+)$/, '\1/index.*')
|
616
|
+
filenames = (Dir[filename_glob_1] + Dir[filename_glob_2]).uniq
|
617
|
+
|
618
|
+
# Reject meta files
|
619
|
+
filenames.reject! { |f| f =~ /\.yaml$/ }
|
620
|
+
|
621
|
+
# Reject backups
|
622
|
+
filenames.reject! { |f| f =~ /(~|\.orig|\.rej|\.bak)$/ }
|
623
|
+
|
624
|
+
# Make sure there is only one content file
|
625
|
+
if filenames.size != 1
|
626
|
+
raise RuntimeError.new(
|
627
|
+
"Expected 1 content file in #{dir} but found #{filenames.size}"
|
628
|
+
)
|
629
|
+
end
|
630
|
+
|
631
|
+
# Return content filename
|
632
|
+
filenames.first
|
633
|
+
end
|
634
|
+
|
635
|
+
# Raises an "outdated data format" error.
|
636
|
+
def error_outdated
|
637
|
+
raise RuntimeError.new(
|
638
|
+
'This site\'s data is stored in an old format and must be updated. ' +
|
639
|
+
'To do so, issue the \'nanoc update\' command. For help on ' +
|
640
|
+
'updating a site\'s data, issue \'nanoc help update\'.'
|
641
|
+
)
|
642
|
+
end
|
643
|
+
|
644
|
+
# Updated outdated page defaults (renames page defaults file)
|
645
|
+
def update_page_defaults
|
646
|
+
return unless File.file?(PAGE_DEFAULTS_FILENAME_OLD)
|
647
|
+
|
648
|
+
vcs.move(PAGE_DEFAULTS_FILENAME_OLD, PAGE_DEFAULTS_FILENAME)
|
649
|
+
end
|
650
|
+
|
651
|
+
# Updates outdated pages (both content and meta file names).
|
652
|
+
def update_pages
|
653
|
+
# Update content files
|
654
|
+
# content/foo/bar/baz/index.ext -> content/foo/bar/baz/baz.ext
|
655
|
+
Dir['content/**/index.*'].select { |f| File.file?(f) }.each do |old_filename|
|
656
|
+
# Determine new name
|
657
|
+
if old_filename =~ /^content\/index\./
|
658
|
+
new_filename = old_filename.sub(/^content\/index\./, 'content/content.')
|
659
|
+
else
|
660
|
+
new_filename = old_filename.sub(/([^\/]+)\/index\.([^\/]+)$/, '\1/\1.\2')
|
661
|
+
end
|
662
|
+
|
663
|
+
# Move
|
664
|
+
vcs.move(old_filename, new_filename)
|
665
|
+
end
|
666
|
+
|
667
|
+
# Update meta files
|
668
|
+
# content/foo/bar/baz/meta.yaml -> content/foo/bar/baz/baz.yaml
|
669
|
+
Dir['content/**/meta.yaml'].select { |f| File.file?(f) }.each do |old_filename|
|
670
|
+
# Determine new name
|
671
|
+
if old_filename == 'content/meta.yaml'
|
672
|
+
new_filename = 'content/content.yaml'
|
673
|
+
else
|
674
|
+
new_filename = old_filename.sub(/([^\/]+)\/meta.yaml$/, '\1/\1.yaml')
|
675
|
+
end
|
676
|
+
|
677
|
+
# Move
|
678
|
+
vcs.move(old_filename, new_filename)
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
# Updates outdated templates (both content and meta file names).
|
683
|
+
def update_templates
|
684
|
+
# Update content files
|
685
|
+
# templates/foo/index.ext -> templates/foo/foo.ext
|
686
|
+
Dir['templates/**/index.*'].select { |f| File.file?(f) }.each do |old_filename|
|
687
|
+
# Determine new name
|
688
|
+
new_filename = old_filename.sub(/([^\/]+)\/index\.([^\/]+)$/, '\1/\1.\2')
|
689
|
+
|
690
|
+
# Move
|
691
|
+
vcs.move(old_filename, new_filename)
|
692
|
+
end
|
693
|
+
|
694
|
+
# Update meta files
|
695
|
+
# templates/foo/meta.yaml -> templates/foo/foo.yaml
|
696
|
+
Dir['templates/**/meta.yaml'].select { |f| File.file?(f) }.each do |old_filename|
|
697
|
+
# Determine new name
|
698
|
+
new_filename = old_filename.sub(/([^\/]+)\/meta.yaml$/, '\1/\1.yaml')
|
699
|
+
|
700
|
+
# Move
|
701
|
+
vcs.move(old_filename, new_filename)
|
702
|
+
end
|
703
|
+
end
|
704
|
+
|
705
|
+
end
|
706
|
+
|
707
|
+
end
|