nanoc3 3.0.0
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/NEWS.rdoc +262 -0
- data/README.rdoc +80 -0
- data/Rakefile +11 -0
- data/bin/nanoc3 +16 -0
- data/lib/nanoc3/base/code_snippet.rb +42 -0
- data/lib/nanoc3/base/compiler.rb +225 -0
- data/lib/nanoc3/base/compiler_dsl.rb +110 -0
- data/lib/nanoc3/base/core_ext/array.rb +21 -0
- data/lib/nanoc3/base/core_ext/hash.rb +23 -0
- data/lib/nanoc3/base/core_ext/string.rb +14 -0
- data/lib/nanoc3/base/core_ext.rb +5 -0
- data/lib/nanoc3/base/data_source.rb +197 -0
- data/lib/nanoc3/base/dependency_tracker.rb +291 -0
- data/lib/nanoc3/base/errors.rb +95 -0
- data/lib/nanoc3/base/filter.rb +60 -0
- data/lib/nanoc3/base/item.rb +87 -0
- data/lib/nanoc3/base/item_rep.rb +236 -0
- data/lib/nanoc3/base/layout.rb +53 -0
- data/lib/nanoc3/base/notification_center.rb +68 -0
- data/lib/nanoc3/base/plugin.rb +88 -0
- data/lib/nanoc3/base/preprocessor_context.rb +37 -0
- data/lib/nanoc3/base/rule.rb +37 -0
- data/lib/nanoc3/base/rule_context.rb +68 -0
- data/lib/nanoc3/base/site.rb +334 -0
- data/lib/nanoc3/base.rb +25 -0
- data/lib/nanoc3/cli/base.rb +151 -0
- data/lib/nanoc3/cli/commands/autocompile.rb +89 -0
- data/lib/nanoc3/cli/commands/compile.rb +279 -0
- data/lib/nanoc3/cli/commands/create_item.rb +79 -0
- data/lib/nanoc3/cli/commands/create_layout.rb +94 -0
- data/lib/nanoc3/cli/commands/create_site.rb +320 -0
- data/lib/nanoc3/cli/commands/help.rb +71 -0
- data/lib/nanoc3/cli/commands/info.rb +114 -0
- data/lib/nanoc3/cli/commands/update.rb +96 -0
- data/lib/nanoc3/cli/commands.rb +13 -0
- data/lib/nanoc3/cli/logger.rb +73 -0
- data/lib/nanoc3/cli.rb +16 -0
- data/lib/nanoc3/data_sources/delicious.rb +66 -0
- data/lib/nanoc3/data_sources/filesystem.rb +231 -0
- data/lib/nanoc3/data_sources/filesystem_combined.rb +202 -0
- data/lib/nanoc3/data_sources/filesystem_common.rb +22 -0
- data/lib/nanoc3/data_sources/filesystem_compact.rb +232 -0
- data/lib/nanoc3/data_sources/last_fm.rb +103 -0
- data/lib/nanoc3/data_sources/twitter.rb +53 -0
- data/lib/nanoc3/data_sources.rb +20 -0
- data/lib/nanoc3/extra/auto_compiler.rb +97 -0
- data/lib/nanoc3/extra/chick.rb +119 -0
- data/lib/nanoc3/extra/context.rb +24 -0
- data/lib/nanoc3/extra/core_ext/time.rb +19 -0
- data/lib/nanoc3/extra/core_ext.rb +3 -0
- data/lib/nanoc3/extra/deployers/rsync.rb +64 -0
- data/lib/nanoc3/extra/deployers.rb +12 -0
- data/lib/nanoc3/extra/file_proxy.rb +31 -0
- data/lib/nanoc3/extra/validators/links.rb +0 -0
- data/lib/nanoc3/extra/validators/w3c.rb +71 -0
- data/lib/nanoc3/extra/validators.rb +12 -0
- data/lib/nanoc3/extra/vcs.rb +65 -0
- data/lib/nanoc3/extra/vcses/bazaar.rb +21 -0
- data/lib/nanoc3/extra/vcses/dummy.rb +20 -0
- data/lib/nanoc3/extra/vcses/git.rb +21 -0
- data/lib/nanoc3/extra/vcses/mercurial.rb +21 -0
- data/lib/nanoc3/extra/vcses/subversion.rb +21 -0
- data/lib/nanoc3/extra/vcses.rb +17 -0
- data/lib/nanoc3/extra.rb +16 -0
- data/lib/nanoc3/filters/bluecloth.rb +13 -0
- data/lib/nanoc3/filters/coderay.rb +17 -0
- data/lib/nanoc3/filters/erb.rb +19 -0
- data/lib/nanoc3/filters/erubis.rb +17 -0
- data/lib/nanoc3/filters/haml.rb +20 -0
- data/lib/nanoc3/filters/less.rb +13 -0
- data/lib/nanoc3/filters/markaby.rb +14 -0
- data/lib/nanoc3/filters/maruku.rb +14 -0
- data/lib/nanoc3/filters/rainpress.rb +13 -0
- data/lib/nanoc3/filters/rdiscount.rb +13 -0
- data/lib/nanoc3/filters/rdoc.rb +23 -0
- data/lib/nanoc3/filters/redcloth.rb +14 -0
- data/lib/nanoc3/filters/relativize_paths.rb +32 -0
- data/lib/nanoc3/filters/rubypants.rb +14 -0
- data/lib/nanoc3/filters/sass.rb +17 -0
- data/lib/nanoc3/filters.rb +37 -0
- data/lib/nanoc3/helpers/blogging.rb +226 -0
- data/lib/nanoc3/helpers/breadcrumbs.rb +25 -0
- data/lib/nanoc3/helpers/capturing.rb +71 -0
- data/lib/nanoc3/helpers/filtering.rb +46 -0
- data/lib/nanoc3/helpers/html_escape.rb +22 -0
- data/lib/nanoc3/helpers/link_to.rb +120 -0
- data/lib/nanoc3/helpers/rendering.rb +76 -0
- data/lib/nanoc3/helpers/tagging.rb +58 -0
- data/lib/nanoc3/helpers/text.rb +40 -0
- data/lib/nanoc3/helpers/xml_sitemap.rb +69 -0
- data/lib/nanoc3/helpers.rb +16 -0
- data/lib/nanoc3/package.rb +106 -0
- data/lib/nanoc3/tasks/clean.rake +16 -0
- data/lib/nanoc3/tasks/clean.rb +33 -0
- data/lib/nanoc3/tasks/deploy/rsync.rake +11 -0
- data/lib/nanoc3/tasks/validate.rake +35 -0
- data/lib/nanoc3/tasks.rb +9 -0
- data/lib/nanoc3.rb +19 -0
- data/vendor/cri/ChangeLog +0 -0
- data/vendor/cri/LICENSE +19 -0
- data/vendor/cri/NEWS +0 -0
- data/vendor/cri/README +4 -0
- data/vendor/cri/Rakefile +25 -0
- data/vendor/cri/lib/cri/base.rb +153 -0
- data/vendor/cri/lib/cri/command.rb +105 -0
- data/vendor/cri/lib/cri/core_ext/string.rb +41 -0
- data/vendor/cri/lib/cri/core_ext.rb +8 -0
- data/vendor/cri/lib/cri/option_parser.rb +186 -0
- data/vendor/cri/lib/cri.rb +12 -0
- data/vendor/cri/test/test_base.rb +6 -0
- data/vendor/cri/test/test_command.rb +6 -0
- data/vendor/cri/test/test_core_ext.rb +21 -0
- data/vendor/cri/test/test_option_parser.rb +279 -0
- metadata +225 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc3::DataSources
|
|
4
|
+
|
|
5
|
+
# Nanoc3::DataSources::Delicious provides for a delicious.com bookmarks from
|
|
6
|
+
# a single user as items (Nanoc3::Item instances).
|
|
7
|
+
#
|
|
8
|
+
# The configuration must have a "username" attribute containing the username
|
|
9
|
+
# of the account from which to fetch the bookmarks.
|
|
10
|
+
#
|
|
11
|
+
# The items returned by this data source will be mounted at {root}/{id},
|
|
12
|
+
# where +id+ is a sequence number that is not necessarily unique for this
|
|
13
|
+
# bookmark (because delicious.com unfortunately does not provide unique IDs
|
|
14
|
+
# for each bookmark).
|
|
15
|
+
#
|
|
16
|
+
# The items returned by this data source will have the following attributes:
|
|
17
|
+
#
|
|
18
|
+
# +:url+:: The URL the bookmark leads to.
|
|
19
|
+
#
|
|
20
|
+
# +:description+:: The description (title, usually) of the bookmark.
|
|
21
|
+
#
|
|
22
|
+
# +:tags+:: An array of tags (strings) for this bookmark.
|
|
23
|
+
#
|
|
24
|
+
# +:date+:: The timestamp when this bookmark was created (a Time object).
|
|
25
|
+
#
|
|
26
|
+
# +:note+:: The personal note for this bookmark (can be nil).
|
|
27
|
+
#
|
|
28
|
+
# +:author+:: The username of the person storing the bookmark.
|
|
29
|
+
class Delicious < Nanoc3::DataSource
|
|
30
|
+
|
|
31
|
+
def items
|
|
32
|
+
@items ||= begin
|
|
33
|
+
require 'json'
|
|
34
|
+
require 'time'
|
|
35
|
+
|
|
36
|
+
# Get data
|
|
37
|
+
@http_client ||= Nanoc3::Extra::CHiCk::Client.new
|
|
38
|
+
status, headers, data = *@http_client.get("http://feeds.delicious.com/v2/json/#{self.config[:username]}")
|
|
39
|
+
|
|
40
|
+
# Parse as JSON
|
|
41
|
+
raw_items = JSON.parse(data)
|
|
42
|
+
|
|
43
|
+
# Convert to items
|
|
44
|
+
raw_items.enum_with_index.map do |raw_item, i|
|
|
45
|
+
# Get data
|
|
46
|
+
content = raw_item['n']
|
|
47
|
+
attributes = {
|
|
48
|
+
:url => raw_item['u'],
|
|
49
|
+
:description => raw_item['d'],
|
|
50
|
+
:tags => raw_item['t'],
|
|
51
|
+
:date => Time.parse(raw_item['dt']),
|
|
52
|
+
:note => raw_item['n'],
|
|
53
|
+
:author => raw_item['a']
|
|
54
|
+
}
|
|
55
|
+
identifier = "/#{i}/"
|
|
56
|
+
mtime = nil
|
|
57
|
+
|
|
58
|
+
# Build item
|
|
59
|
+
Nanoc3::Item.new(content, attributes, identifier, mtime)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc3::DataSources
|
|
4
|
+
|
|
5
|
+
# The filesystem data source is the default data source for a new nanoc
|
|
6
|
+
# site. It stores all data as files on the hard disk.
|
|
7
|
+
#
|
|
8
|
+
# None of the methods are documented in this file. See Nanoc3::DataSource
|
|
9
|
+
# for documentation on the overridden methods instead.
|
|
10
|
+
#
|
|
11
|
+
# = Items
|
|
12
|
+
#
|
|
13
|
+
# The filesystem data source stores its items in nested directories. Each
|
|
14
|
+
# directory represents a single item. The root directory is the 'content'
|
|
15
|
+
# directory.
|
|
16
|
+
#
|
|
17
|
+
# Every directory has a content file and a meta file. The content file
|
|
18
|
+
# contains the actual item content, while the meta file contains the item's
|
|
19
|
+
# metadata, formatted as YAML.
|
|
20
|
+
#
|
|
21
|
+
# Both content files and meta files are named after its parent directory
|
|
22
|
+
# (i.e. item). For example, a item named 'foo' will have a directory named
|
|
23
|
+
# 'foo', with e.g. a 'foo.markdown' content file and a 'foo.yaml' meta file.
|
|
24
|
+
#
|
|
25
|
+
# Content file extensions are not used for determining the filter that
|
|
26
|
+
# should be run; the meta file defines the list of filters. The meta file
|
|
27
|
+
# extension must always be 'yaml', though.
|
|
28
|
+
#
|
|
29
|
+
# Content files can also have the 'index' basename. Similarly, meta files
|
|
30
|
+
# can have the 'meta' basename. For example, a parent directory named 'foo'
|
|
31
|
+
# can have an 'index.txt' content file and a 'meta.yaml' meta file. This is
|
|
32
|
+
# to preserve backward compatibility.
|
|
33
|
+
#
|
|
34
|
+
# = Layouts
|
|
35
|
+
#
|
|
36
|
+
# Layouts are stored as directories in the 'layouts' directory. Each layout
|
|
37
|
+
# contains a content file and a meta file. The content file contain the
|
|
38
|
+
# actual layout, and the meta file describes how the item should be handled
|
|
39
|
+
# (contains the filter that should be used).
|
|
40
|
+
#
|
|
41
|
+
# For backward compatibility, a layout can also be a single file in the
|
|
42
|
+
# 'layouts' directory. Such a layout cannot have any metadata; the filter
|
|
43
|
+
# used for this layout is determined from the file extension.
|
|
44
|
+
#
|
|
45
|
+
# = Code Snippets
|
|
46
|
+
#
|
|
47
|
+
# Code snippets are stored in '.rb' files in the 'lib' directory. Code
|
|
48
|
+
# snippets can reside in sub-directories.
|
|
49
|
+
class Filesystem < Nanoc3::DataSource
|
|
50
|
+
|
|
51
|
+
include Nanoc3::DataSources::FilesystemCommon
|
|
52
|
+
|
|
53
|
+
########## VCSes ##########
|
|
54
|
+
|
|
55
|
+
attr_accessor :vcs
|
|
56
|
+
|
|
57
|
+
def vcs
|
|
58
|
+
@vcs ||= Nanoc3::Extra::VCSes::Dummy.new
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
########## Preparation ##########
|
|
62
|
+
|
|
63
|
+
def up
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def down
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def setup
|
|
70
|
+
# Create directories
|
|
71
|
+
%w( content layouts lib ).each do |dir|
|
|
72
|
+
FileUtils.mkdir_p(dir)
|
|
73
|
+
vcs.add(dir)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
########## Loading data ##########
|
|
78
|
+
|
|
79
|
+
def items
|
|
80
|
+
meta_filenames('content').map do |meta_filename|
|
|
81
|
+
# Read metadata
|
|
82
|
+
meta = YAML.load_file(meta_filename) || {}
|
|
83
|
+
|
|
84
|
+
# Get content
|
|
85
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
|
86
|
+
content = File.read(content_filename)
|
|
87
|
+
|
|
88
|
+
# Get attributes
|
|
89
|
+
attributes = meta.merge(:file => Nanoc3::Extra::FileProxy.new(content_filename))
|
|
90
|
+
|
|
91
|
+
# Get identifier
|
|
92
|
+
identifier = meta_filename.sub(/^content/, '').sub(/[^\/]+\.yaml$/, '')
|
|
93
|
+
|
|
94
|
+
# Get modification times
|
|
95
|
+
meta_mtime = File.stat(meta_filename).mtime
|
|
96
|
+
content_mtime = File.stat(content_filename).mtime
|
|
97
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
|
98
|
+
|
|
99
|
+
# Create item object
|
|
100
|
+
Nanoc3::Item.new(content, attributes, identifier, mtime)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def layouts
|
|
105
|
+
meta_filenames('layouts').map do |meta_filename|
|
|
106
|
+
# Get content
|
|
107
|
+
content_filename = content_filename_for_dir(File.dirname(meta_filename))
|
|
108
|
+
content = File.read(content_filename)
|
|
109
|
+
|
|
110
|
+
# Get attributes
|
|
111
|
+
attributes = YAML.load_file(meta_filename) || {}
|
|
112
|
+
|
|
113
|
+
# Get identifier
|
|
114
|
+
identifier = meta_filename.sub(/^layouts\//, '').sub(/\/[^\/]+\.yaml$/, '')
|
|
115
|
+
|
|
116
|
+
# Get modification times
|
|
117
|
+
meta_mtime = File.stat(meta_filename).mtime
|
|
118
|
+
content_mtime = File.stat(content_filename).mtime
|
|
119
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
|
120
|
+
|
|
121
|
+
# Create layout object
|
|
122
|
+
Nanoc3::Layout.new(content, attributes, identifier, mtime)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
########## Creating data ##########
|
|
127
|
+
|
|
128
|
+
# Creates a new item with the given content, attributes and identifier.
|
|
129
|
+
def create_item(content, attributes, identifier)
|
|
130
|
+
# Determine base path
|
|
131
|
+
last_component = identifier.split('/')[-1] || 'content'
|
|
132
|
+
base_path = 'content' + identifier + last_component
|
|
133
|
+
|
|
134
|
+
# Get filenames
|
|
135
|
+
dir_path = 'content' + identifier
|
|
136
|
+
meta_filename = 'content' + identifier + last_component + '.yaml'
|
|
137
|
+
content_filename = 'content' + identifier + last_component + '.html'
|
|
138
|
+
|
|
139
|
+
# Notify
|
|
140
|
+
Nanoc3::NotificationCenter.post(:file_created, meta_filename)
|
|
141
|
+
Nanoc3::NotificationCenter.post(:file_created, content_filename)
|
|
142
|
+
|
|
143
|
+
# Create files
|
|
144
|
+
FileUtils.mkdir_p(dir_path)
|
|
145
|
+
File.open(meta_filename, 'w') { |io| io.write(YAML.dump(attributes.stringify_keys)) }
|
|
146
|
+
File.open(content_filename, 'w') { |io| io.write(content) }
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Creates a new layout with the given content, attributes and identifier.
|
|
150
|
+
def create_layout(content, attributes, identifier)
|
|
151
|
+
# Determine base path
|
|
152
|
+
last_component = identifier.split('/')[-1]
|
|
153
|
+
base_path = 'layouts' + identifier + last_component
|
|
154
|
+
|
|
155
|
+
# Get filenames
|
|
156
|
+
dir_path = 'layouts' + identifier
|
|
157
|
+
meta_filename = 'layouts' + identifier + last_component + '.yaml'
|
|
158
|
+
content_filename = 'layouts' + identifier + last_component + '.html'
|
|
159
|
+
|
|
160
|
+
# Notify
|
|
161
|
+
Nanoc3::NotificationCenter.post(:file_created, meta_filename)
|
|
162
|
+
Nanoc3::NotificationCenter.post(:file_created, content_filename)
|
|
163
|
+
|
|
164
|
+
# Create files
|
|
165
|
+
FileUtils.mkdir_p(dir_path)
|
|
166
|
+
File.open(meta_filename, 'w') { |io| io.write(YAML.dump(attributes.stringify_keys)) }
|
|
167
|
+
File.open(content_filename, 'w') { |io| io.write(content) }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
private
|
|
171
|
+
|
|
172
|
+
########## Custom functions ##########
|
|
173
|
+
|
|
174
|
+
# Returns the list of all meta files in the given base directory as well
|
|
175
|
+
# as its subdirectories.
|
|
176
|
+
def meta_filenames(base)
|
|
177
|
+
# Find all possible meta file names
|
|
178
|
+
filenames = Dir[base + '/**/*.yaml']
|
|
179
|
+
|
|
180
|
+
# Filter out invalid meta files
|
|
181
|
+
good_filenames = []
|
|
182
|
+
bad_filenames = []
|
|
183
|
+
filenames.each do |filename|
|
|
184
|
+
if filename =~ /meta\.yaml$/ or filename =~ /([^\/]+)\/\1\.yaml$/
|
|
185
|
+
good_filenames << filename
|
|
186
|
+
else
|
|
187
|
+
bad_filenames << filename
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Warn about bad filenames
|
|
192
|
+
unless bad_filenames.empty?
|
|
193
|
+
raise RuntimeError.new(
|
|
194
|
+
"The following files appear to be meta files, " +
|
|
195
|
+
"but have an invalid name:\n - " +
|
|
196
|
+
bad_filenames.join("\n - ")
|
|
197
|
+
)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
good_filenames
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Returns the filename of the content file in the given directory,
|
|
204
|
+
# ignoring any unwanted files (files that end with '~', '.orig', '.rej' or
|
|
205
|
+
# '.bak')
|
|
206
|
+
def content_filename_for_dir(dir)
|
|
207
|
+
# Find all files
|
|
208
|
+
filename_glob_1 = dir.sub(/([^\/]+)$/, '\1/\1.*')
|
|
209
|
+
filename_glob_2 = dir.sub(/([^\/]+)$/, '\1/index.*')
|
|
210
|
+
filenames = (Dir[filename_glob_1] + Dir[filename_glob_2]).uniq
|
|
211
|
+
|
|
212
|
+
# Reject meta files
|
|
213
|
+
filenames.reject! { |f| f =~ /\.yaml$/ }
|
|
214
|
+
|
|
215
|
+
# Reject backups
|
|
216
|
+
filenames.reject! { |f| f =~ /(~|\.orig|\.rej|\.bak)$/ }
|
|
217
|
+
|
|
218
|
+
# Make sure there is only one content file
|
|
219
|
+
if filenames.size != 1
|
|
220
|
+
raise RuntimeError.new(
|
|
221
|
+
"Expected 1 content file in #{dir} but found #{filenames.size}"
|
|
222
|
+
)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Return content filename
|
|
226
|
+
filenames.first
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc3::DataSources
|
|
4
|
+
|
|
5
|
+
# = Items
|
|
6
|
+
#
|
|
7
|
+
# The filesystem data source stores its items in nested directories. A item
|
|
8
|
+
# is represented by a single file. The root directory is the 'content'
|
|
9
|
+
# directory.
|
|
10
|
+
#
|
|
11
|
+
# The metadata for a item is embedded into the file itself. It is stored at
|
|
12
|
+
# the top of the file, between '---' (three dashes) separators. For example:
|
|
13
|
+
#
|
|
14
|
+
# ---
|
|
15
|
+
# title: "Moo!"
|
|
16
|
+
# ---
|
|
17
|
+
# h1. Hello!
|
|
18
|
+
#
|
|
19
|
+
# The identifier of a item is determined as follows. A file with an
|
|
20
|
+
# 'index.*' filename, such as 'index.txt', will have the filesystem path
|
|
21
|
+
# with the 'index.*' part stripped as a identifier. For example,
|
|
22
|
+
# 'foo/bar/index.html' will have '/foo/bar/' as identifier.
|
|
23
|
+
#
|
|
24
|
+
# A file with a filename not starting with 'index.', such as 'foo.html',
|
|
25
|
+
# will have an identifier ending in 'foo/'. For example, 'foo/bar.html' will
|
|
26
|
+
# have '/foo/bar/' as identifier.
|
|
27
|
+
#
|
|
28
|
+
# Note that it is possible for two different, separate files to have the
|
|
29
|
+
# same identifier. It is therefore recommended to avoid such situations.
|
|
30
|
+
#
|
|
31
|
+
# Some more examples:
|
|
32
|
+
#
|
|
33
|
+
# content/index.html --> /
|
|
34
|
+
# content/foo.html --> /foo/
|
|
35
|
+
# content/foo/index.html --> /foo/
|
|
36
|
+
# content/foo/bar.html --> /foo/bar/
|
|
37
|
+
# content/foo/bar/index.html --> /foo/bar/
|
|
38
|
+
#
|
|
39
|
+
# File extensions are ignored by nanoc. The file extension does not
|
|
40
|
+
# determine the filters to run on it; the metadata in the file defines the
|
|
41
|
+
# list of filters.
|
|
42
|
+
#
|
|
43
|
+
# = Layouts
|
|
44
|
+
#
|
|
45
|
+
# Layouts are stored as files in the 'layouts' directory. Similar to items,
|
|
46
|
+
# each layout consists of a metadata part and a content part, separated by
|
|
47
|
+
# '---' (three dashes).
|
|
48
|
+
#
|
|
49
|
+
# = Code Snippets
|
|
50
|
+
#
|
|
51
|
+
# Code snippets are stored in '.rb' files in the 'lib' directory. Code
|
|
52
|
+
# snippets can reside in sub-directories.
|
|
53
|
+
class FilesystemCombined < Nanoc3::DataSource
|
|
54
|
+
|
|
55
|
+
include Nanoc3::DataSources::FilesystemCommon
|
|
56
|
+
|
|
57
|
+
########## VCSes ##########
|
|
58
|
+
|
|
59
|
+
attr_accessor :vcs
|
|
60
|
+
|
|
61
|
+
def vcs
|
|
62
|
+
@vcs ||= Nanoc3::Extra::VCSes::Dummy.new
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
########## Preparation ##########
|
|
66
|
+
|
|
67
|
+
def up
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def down
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def setup
|
|
74
|
+
# Create directories
|
|
75
|
+
%w( content layouts lib ).each do |dir|
|
|
76
|
+
FileUtils.mkdir_p(dir)
|
|
77
|
+
vcs.add(dir)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
########## Loading data ##########
|
|
82
|
+
|
|
83
|
+
def items
|
|
84
|
+
files('content', true).map do |filename|
|
|
85
|
+
# Read and parse data
|
|
86
|
+
meta, content = *parse_file(filename, 'item')
|
|
87
|
+
|
|
88
|
+
# Get attributes
|
|
89
|
+
attributes = meta.merge(:file => Nanoc3::Extra::FileProxy.new(filename))
|
|
90
|
+
|
|
91
|
+
# Get actual identifier
|
|
92
|
+
if filename =~ /\/index\.[^\/]+$/
|
|
93
|
+
identifier = filename.sub(/^content/, '').sub(/index\.[^\/]+$/, '') + '/'
|
|
94
|
+
else
|
|
95
|
+
identifier = filename.sub(/^content/, '').sub(/\.[^\/]+$/, '') + '/'
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Get mtime
|
|
99
|
+
mtime = File.stat(filename).mtime
|
|
100
|
+
|
|
101
|
+
# Build item
|
|
102
|
+
Nanoc3::Item.new(content, attributes, identifier, mtime)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def layouts
|
|
107
|
+
files('layouts', true).map do |filename|
|
|
108
|
+
# Read and parse data
|
|
109
|
+
meta, content = *parse_file(filename, 'layout')
|
|
110
|
+
|
|
111
|
+
# Get actual identifier
|
|
112
|
+
if filename =~ /\/index\.[^\/]+$/
|
|
113
|
+
identifier = filename.sub(/^layouts/, '').sub(/index\.[^\/]+$/, '') + '/'
|
|
114
|
+
else
|
|
115
|
+
identifier = filename.sub(/^layouts/, '').sub(/\.[^\/]+$/, '') + '/'
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Get mtime
|
|
119
|
+
mtime = File.stat(filename).mtime
|
|
120
|
+
|
|
121
|
+
# Build layout
|
|
122
|
+
Nanoc3::Layout.new(content, meta, identifier, mtime)
|
|
123
|
+
end.compact
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
########## Creating data ##########
|
|
127
|
+
|
|
128
|
+
# Creates a new item with the given content, attributes and identifier.
|
|
129
|
+
def create_item(content, attributes, identifier)
|
|
130
|
+
# Determine path
|
|
131
|
+
if identifier == '/'
|
|
132
|
+
path = 'content/index.html'
|
|
133
|
+
else
|
|
134
|
+
path = 'content' + identifier[0..-2] + '.html'
|
|
135
|
+
end
|
|
136
|
+
parent_path = File.dirname(path)
|
|
137
|
+
|
|
138
|
+
# Notify
|
|
139
|
+
Nanoc3::NotificationCenter.post(:file_created, path)
|
|
140
|
+
|
|
141
|
+
# Write item
|
|
142
|
+
FileUtils.mkdir_p(parent_path)
|
|
143
|
+
File.open(path, 'w') do |io|
|
|
144
|
+
io.write(YAML.dump(attributes.stringify_keys) + "\n")
|
|
145
|
+
io.write("---\n")
|
|
146
|
+
io.write(content)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Creates a new layout with the given content, attributes and identifier.
|
|
151
|
+
def create_layout(content, attributes, identifier)
|
|
152
|
+
# Determine path
|
|
153
|
+
path = 'layouts' + identifier[0..-2] + '.html'
|
|
154
|
+
parent_path = File.dirname(path)
|
|
155
|
+
|
|
156
|
+
# Notify
|
|
157
|
+
Nanoc3::NotificationCenter.post(:file_created, path)
|
|
158
|
+
|
|
159
|
+
# Write layout
|
|
160
|
+
FileUtils.mkdir_p(parent_path)
|
|
161
|
+
File.open(path, 'w') do |io|
|
|
162
|
+
io.write(YAML.dump(attributes.stringify_keys) + "\n")
|
|
163
|
+
io.write("---\n")
|
|
164
|
+
io.write(content)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
private
|
|
169
|
+
|
|
170
|
+
# Returns a list of all files in +dir+, ignoring any unwanted files (files
|
|
171
|
+
# that end with '~', '.orig', '.rej' or '.bak').
|
|
172
|
+
#
|
|
173
|
+
# +recursively+:: When +true+, finds files in +dir+ as well as its
|
|
174
|
+
# subdirectories; when +false+, only searches +dir+
|
|
175
|
+
# itself.
|
|
176
|
+
def files(dir, recursively)
|
|
177
|
+
glob = File.join([dir] + (recursively ? [ '**', '*' ] : [ '*' ]))
|
|
178
|
+
Dir[glob].reject { |f| File.directory?(f) or f =~ /(~|\.orig|\.rej|\.bak)$/ }
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Parses the file named +filename+ and returns an array with its first
|
|
182
|
+
# element a hash with the file's metadata, and with its second element the
|
|
183
|
+
# file content itself.
|
|
184
|
+
def parse_file(filename, kind)
|
|
185
|
+
# Split file
|
|
186
|
+
pieces = File.read(filename).split(/^(-{5}|-{3})/).compact
|
|
187
|
+
if pieces.size < 3
|
|
188
|
+
raise RuntimeError.new(
|
|
189
|
+
"The file '#{filename}' does not seem to be a nanoc #{kind}"
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Parse
|
|
194
|
+
meta = YAML.load(pieces[2]) || {}
|
|
195
|
+
content = pieces[4..-1].join.strip
|
|
196
|
+
|
|
197
|
+
[ meta, content ]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Nanoc3::DataSources
|
|
4
|
+
|
|
5
|
+
# The Nanoc3::DataSources::FilesystemCommon module provides code
|
|
6
|
+
# snippet-loading and rule-loading methods that are used by both the
|
|
7
|
+
# filesystem and the filesystem_combined data sources.
|
|
8
|
+
module FilesystemCommon
|
|
9
|
+
|
|
10
|
+
def code_snippets
|
|
11
|
+
Dir['lib/**/*.rb'].sort.map do |filename|
|
|
12
|
+
Nanoc3::CodeSnippet.new(
|
|
13
|
+
File.read(filename),
|
|
14
|
+
filename.sub(/^lib\//, ''),
|
|
15
|
+
File.stat(filename).mtime
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|