nanoc 4.0.0a1 → 4.0.0a2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +9 -4
- data/NEWS.md +13 -0
- data/lib/nanoc/base.rb +2 -0
- data/lib/nanoc/base/compilation/compiler.rb +0 -1
- data/lib/nanoc/base/compilation/compiler_dsl.rb +21 -7
- data/lib/nanoc/base/compilation/item_rep_proxy.rb +10 -1
- data/lib/nanoc/base/compilation/rule.rb +10 -12
- data/lib/nanoc/base/compilation/rules_collection.rb +2 -2
- data/lib/nanoc/base/pattern.rb +63 -0
- data/lib/nanoc/base/source_data/data_source.rb +33 -18
- data/lib/nanoc/base/source_data/identifier.rb +65 -3
- data/lib/nanoc/base/source_data/item.rb +1 -1
- data/lib/nanoc/base/source_data/item_array.rb +17 -2
- data/lib/nanoc/base/source_data/layout.rb +1 -1
- data/lib/nanoc/base/source_data/site.rb +4 -3
- data/lib/nanoc/base/views/config.rb +22 -0
- data/lib/nanoc/base/views/item.rb +76 -0
- data/lib/nanoc/base/views/item_collection.rb +46 -4
- data/lib/nanoc/base/views/item_rep.rb +23 -0
- data/lib/nanoc/base/views/layout.rb +4 -0
- data/lib/nanoc/base/views/layout_collection.rb +7 -1
- data/lib/nanoc/base/views/mutable_config.rb +5 -0
- data/lib/nanoc/base/views/mutable_item.rb +15 -0
- data/lib/nanoc/base/views/mutable_item_collection.rb +25 -0
- data/lib/nanoc/base/views/mutable_layout.rb +5 -0
- data/lib/nanoc/base/views/mutable_layout_collection.rb +20 -2
- data/lib/nanoc/cli/cleaning_stream.rb +15 -0
- data/lib/nanoc/cli/commands/create-site.rb +17 -35
- data/lib/nanoc/cli/commands/shell.rb +7 -4
- data/lib/nanoc/data_sources.rb +0 -1
- data/lib/nanoc/data_sources/filesystem.rb +75 -76
- data/lib/nanoc/data_sources/filesystem_unified.rb +4 -27
- data/lib/nanoc/data_sources/filesystem_verbose.rb +4 -21
- data/lib/nanoc/version.rb +1 -1
- data/nanoc.gemspec +1 -1
- data/test/base/test_compiler.rb +35 -15
- data/test/base/test_compiler_dsl.rb +32 -30
- data/test/base/test_data_source.rb +2 -2
- data/test/base/test_item_array.rb +10 -1
- data/test/base/test_rule.rb +2 -2
- data/test/base/test_site.rb +32 -0
- data/test/cli/commands/test_create_site.rb +7 -1
- data/test/cli/commands/test_prune.rb +2 -2
- data/test/data_sources/test_filesystem.rb +29 -9
- data/test/data_sources/test_filesystem_unified.rb +48 -68
- data/test/helper.rb +1 -0
- data/test/helpers/test_breadcrumbs.rb +4 -4
- data/test/test_gem.rb +0 -1
- metadata +4 -5
- data/lib/nanoc/data_sources/static.rb +0 -62
- data/test/data_sources/test_static.rb +0 -93
@@ -7,12 +7,37 @@ module Nanoc
|
|
7
7
|
Nanoc::MutableItemView
|
8
8
|
end
|
9
9
|
|
10
|
+
# Creates a new item and adds it to the site’s collection of items.
|
11
|
+
#
|
12
|
+
# @param [String] content The uncompiled item content (if it is a textual
|
13
|
+
# item) or the path to the filename containing the content (if it is a
|
14
|
+
# binary item).
|
15
|
+
#
|
16
|
+
# @param [Hash] attributes A hash containing this item's attributes.
|
17
|
+
#
|
18
|
+
# @param [Nanoc::Identifier, String] identifier This item's identifier.
|
19
|
+
#
|
20
|
+
# @param [Hash] params Extra parameters.
|
21
|
+
#
|
22
|
+
# @option params [Symbol, nil] :binary (true) Whether or not this item is
|
23
|
+
# binary
|
24
|
+
#
|
25
|
+
# @return [self]
|
10
26
|
def create(content, attributes, identifier, params = {})
|
11
27
|
@items << Nanoc::Int::Item.new(content, attributes, identifier, params)
|
28
|
+
self
|
12
29
|
end
|
13
30
|
|
31
|
+
# Deletes every item for which the block evaluates to true.
|
32
|
+
#
|
33
|
+
# @yieldparam [Nanoc::ItemView] item
|
34
|
+
#
|
35
|
+
# @yieldreturn [Boolean]
|
36
|
+
#
|
37
|
+
# @return [self]
|
14
38
|
def delete_if(&block)
|
15
39
|
@items.delete_if(&block)
|
40
|
+
self
|
16
41
|
end
|
17
42
|
end
|
18
43
|
end
|
@@ -7,12 +7,30 @@ module Nanoc
|
|
7
7
|
Nanoc::MutableLayoutView
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# Creates a new layout and adds it to the site’s collection of layouts.
|
11
|
+
#
|
12
|
+
# @param [String] content The layout content.
|
13
|
+
#
|
14
|
+
# @param [Hash] attributes A hash containing this layout's attributes.
|
15
|
+
#
|
16
|
+
# @param [Nanoc::Identifier, String] identifier This layout's identifier.
|
17
|
+
#
|
18
|
+
# @return [self]
|
19
|
+
def create(content, attributes, identifier)
|
20
|
+
@layouts << Nanoc::Int::Layout.new(content, attributes, identifier)
|
21
|
+
self
|
12
22
|
end
|
13
23
|
|
24
|
+
# Deletes every layout for which the block evaluates to true.
|
25
|
+
#
|
26
|
+
# @yieldparam [Nanoc::LayoutView] layout
|
27
|
+
#
|
28
|
+
# @yieldreturn [Boolean]
|
29
|
+
#
|
30
|
+
# @return [self]
|
14
31
|
def delete_if(&block)
|
15
32
|
@layouts.delete_if(&block)
|
33
|
+
self
|
16
34
|
end
|
17
35
|
end
|
18
36
|
end
|
@@ -119,6 +119,21 @@ module Nanoc::CLI
|
|
119
119
|
@stream.winsize = (arg)
|
120
120
|
end
|
121
121
|
|
122
|
+
# @see IO.sync
|
123
|
+
def sync
|
124
|
+
@stream.sync
|
125
|
+
end
|
126
|
+
|
127
|
+
# @see IO.sync=
|
128
|
+
def sync=(arg)
|
129
|
+
@stream.sync = arg
|
130
|
+
end
|
131
|
+
|
132
|
+
# @see IO.sync=
|
133
|
+
def external_encoding
|
134
|
+
@stream.external_encoding
|
135
|
+
end
|
136
|
+
|
122
137
|
protected
|
123
138
|
|
124
139
|
def _nanoc_clean(s)
|
@@ -19,6 +19,11 @@ module Nanoc::CLI::Commands
|
|
19
19
|
end
|
20
20
|
|
21
21
|
DEFAULT_CONFIG = <<EOS unless defined? DEFAULT_CONFIG
|
22
|
+
# The syntax to use for patterns in the Rules file. Can be either `"glob"`
|
23
|
+
# (default) or `null`. The former will enable glob patterns, which behave like
|
24
|
+
# Ruby’s File.fnmatch. The latter will enable nanoc 3.x-style patterns.
|
25
|
+
pattern_syntax: glob
|
26
|
+
|
22
27
|
# A list of file extensions that nanoc will consider to be textual rather than
|
23
28
|
# binary. If an item with an extension not in this list is found, the file
|
24
29
|
# will be considered as binary.
|
@@ -83,6 +88,8 @@ data_sources:
|
|
83
88
|
# UTF-8 (which they should be!), change this.
|
84
89
|
encoding: utf-8
|
85
90
|
|
91
|
+
identifier_style: full
|
92
|
+
|
86
93
|
# Configuration for the “check” command, which run unit tests on the site.
|
87
94
|
checks:
|
88
95
|
# Configuration for the “internal_links” checker, which checks whether all
|
@@ -98,44 +105,19 @@ EOS
|
|
98
105
|
DEFAULT_RULES = <<EOS unless defined? DEFAULT_RULES
|
99
106
|
#!/usr/bin/env ruby
|
100
107
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# identifiers--not for paths. Therefore, you can’t match on extension.
|
105
|
-
#
|
106
|
-
# * The order of rules is important: for each item, only the first matching
|
107
|
-
# rule is applied.
|
108
|
-
#
|
109
|
-
# * Item identifiers start and end with a slash (e.g. “/about/” for the file
|
110
|
-
# “content/about.html”). To select all children, grandchildren, … of an
|
111
|
-
# item, use the pattern “/about/*/”; “/about/*” will also select the parent,
|
112
|
-
# because “*” matches zero or more characters.
|
113
|
-
|
114
|
-
compile '*' do
|
115
|
-
if item[:extension] == 'css'
|
116
|
-
# don’t filter stylesheets
|
117
|
-
elsif item.binary?
|
118
|
-
# don’t filter binary items
|
119
|
-
else
|
120
|
-
filter :erb
|
121
|
-
layout 'default'
|
122
|
-
end
|
108
|
+
compile '/**/*.html' do
|
109
|
+
filter :erb
|
110
|
+
layout '/default.*'
|
123
111
|
end
|
124
112
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# Write item with identifier /foo/ to /foo.ext
|
131
|
-
item.identifier.chop + (item[:extension] ? '.' + item[:extension] : '')
|
132
|
-
else
|
133
|
-
# Write item with identifier /foo/ to /foo/index.html
|
134
|
-
item.identifier + 'index.html'
|
135
|
-
end
|
113
|
+
compile '/**/*' do
|
114
|
+
end
|
115
|
+
|
116
|
+
route '/**/*' do
|
117
|
+
item.identifier.to_s
|
136
118
|
end
|
137
119
|
|
138
|
-
layout '
|
120
|
+
layout '/**/*', :erb
|
139
121
|
EOS
|
140
122
|
|
141
123
|
DEFAULT_ITEM = <<EOS unless defined? DEFAULT_ITEM
|
@@ -261,7 +243,7 @@ EOS
|
|
261
243
|
<head>
|
262
244
|
<meta charset="utf-8">
|
263
245
|
<title>A Brand New nanoc Site - <%= @item[:title] %></title>
|
264
|
-
<link rel="stylesheet" href="<%= @items['/stylesheet
|
246
|
+
<link rel="stylesheet" href="<%= @items['/stylesheet.*'].path %>">
|
265
247
|
|
266
248
|
<!-- you don't need to keep this, but it's cool for stats! -->
|
267
249
|
<meta name="generator" content="nanoc <%= Nanoc::VERSION %>">
|
@@ -20,11 +20,14 @@ module Nanoc::CLI::Commands
|
|
20
20
|
protected
|
21
21
|
|
22
22
|
def env
|
23
|
+
self.class.env_for_site(site)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.env_for(site)
|
23
27
|
{
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
config: site.config
|
28
|
+
items: Nanoc::ItemCollectionView.new(site.items),
|
29
|
+
layouts: Nanoc::LayoutCollectionView.new(site.layouts),
|
30
|
+
config: Nanoc::ConfigView.new(site.config),
|
28
31
|
}
|
29
32
|
end
|
30
33
|
end
|
data/lib/nanoc/data_sources.rb
CHANGED
@@ -5,7 +5,6 @@ module Nanoc::DataSources
|
|
5
5
|
autoload 'Filesystem', 'nanoc/data_sources/filesystem'
|
6
6
|
autoload 'FilesystemUnified', 'nanoc/data_sources/filesystem_unified'
|
7
7
|
autoload 'FilesystemVerbose', 'nanoc/data_sources/filesystem_verbose'
|
8
|
-
autoload 'Static', 'nanoc/data_sources/static'
|
9
8
|
|
10
9
|
Nanoc::DataSource.register '::Nanoc::DataSources::FilesystemVerbose', :filesystem_verbose
|
11
10
|
Nanoc::DataSource.register '::Nanoc::DataSources::FilesystemUnified', :filesystem_unified
|
@@ -33,15 +33,6 @@ module Nanoc::DataSources
|
|
33
33
|
|
34
34
|
protected
|
35
35
|
|
36
|
-
# Creates a new object (item or layout) on disk in dir_name according to
|
37
|
-
# the given identifier. The file will have its attributes taken from the
|
38
|
-
# attributes hash argument and its content from the content argument.
|
39
|
-
def create_object(_dir_name, _content, _attributes, _identifier, _params = {})
|
40
|
-
raise NotImplementedError.new(
|
41
|
-
"#{self.class} does not implement ##{name}"
|
42
|
-
)
|
43
|
-
end
|
44
|
-
|
45
36
|
# Creates instances of klass corresponding to the files in dir_name. The
|
46
37
|
# kind attribute indicates the kind of object that is being loaded and is
|
47
38
|
# used solely for debugging purposes.
|
@@ -54,96 +45,104 @@ module Nanoc::DataSources
|
|
54
45
|
#
|
55
46
|
# @see Nanoc::DataSources::Filesystem#load_objects
|
56
47
|
def load_objects(dir_name, kind, klass)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
48
|
+
res = []
|
49
|
+
|
50
|
+
all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
|
51
|
+
content_exts.each do |content_ext|
|
52
|
+
# Get filenames
|
53
|
+
meta_filename = filename_for(base_filename, meta_ext)
|
54
|
+
content_filename = filename_for(base_filename, content_ext)
|
55
|
+
|
56
|
+
# Read content and metadata
|
57
|
+
is_binary = content_filename && !@site.config[:text_extensions].include?(File.extname(content_filename)[1..-1])
|
58
|
+
if is_binary && klass == Nanoc::Int::Item
|
59
|
+
meta = (meta_filename && YAML.load_file(meta_filename)) || {}
|
60
|
+
content_or_filename = content_filename
|
61
|
+
elsif is_binary && klass == Nanoc::Int::Layout
|
62
|
+
raise "The layout file '#{content_filename}' is a binary file, but layouts can only be textual"
|
63
|
+
else
|
64
|
+
meta, content_or_filename = parse(content_filename, meta_filename, kind)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Get attributes
|
68
|
+
attributes = {
|
69
|
+
filename: content_filename,
|
70
|
+
content_filename: content_filename,
|
71
|
+
meta_filename: meta_filename,
|
72
|
+
extension: content_filename ? ext_of(content_filename)[1..-1] : nil,
|
73
|
+
}.merge(meta)
|
74
|
+
|
75
|
+
# Get identifier
|
76
|
+
if meta_filename
|
77
|
+
identifier = identifier_for_filename(meta_filename[dir_name.length..-1])
|
78
|
+
elsif content_filename
|
79
|
+
identifier = identifier_for_filename(content_filename[dir_name.length..-1])
|
80
|
+
else
|
81
|
+
raise 'meta_filename and content_filename are both nil'
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get modification times
|
85
|
+
meta_mtime = meta_filename ? File.stat(meta_filename).mtime : nil
|
86
|
+
content_mtime = content_filename ? File.stat(content_filename).mtime : nil
|
87
|
+
if meta_mtime && content_mtime
|
88
|
+
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
89
|
+
elsif meta_mtime
|
90
|
+
mtime = meta_mtime
|
91
|
+
elsif content_mtime
|
92
|
+
mtime = content_mtime
|
93
|
+
else
|
94
|
+
raise 'meta_mtime and content_mtime are both nil'
|
95
|
+
end
|
96
|
+
|
97
|
+
# Create layout object
|
98
|
+
res << klass.new(
|
99
|
+
content_or_filename, attributes, identifier,
|
100
|
+
binary: is_binary, mtime: mtime
|
101
|
+
)
|
88
102
|
end
|
89
|
-
|
90
|
-
# Get modification times
|
91
|
-
meta_mtime = meta_filename ? File.stat(meta_filename).mtime : nil
|
92
|
-
content_mtime = content_filename ? File.stat(content_filename).mtime : nil
|
93
|
-
if meta_mtime && content_mtime
|
94
|
-
mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
|
95
|
-
elsif meta_mtime
|
96
|
-
mtime = meta_mtime
|
97
|
-
elsif content_mtime
|
98
|
-
mtime = content_mtime
|
99
|
-
else
|
100
|
-
raise 'meta_mtime and content_mtime are both nil'
|
101
|
-
end
|
102
|
-
|
103
|
-
# Create layout object
|
104
|
-
klass.new(
|
105
|
-
content_or_filename, attributes, identifier,
|
106
|
-
binary: is_binary, mtime: mtime
|
107
|
-
)
|
108
103
|
end
|
104
|
+
|
105
|
+
res
|
109
106
|
end
|
110
107
|
|
111
|
-
#
|
112
|
-
# in which the keys are the file's dirname + basenames, and the values a
|
113
|
-
# pair consisting of the metafile extension and the content file
|
114
|
-
# extension. The meta file extension or the content file extension can be
|
115
|
-
# nil, but not both. Backup files are ignored. For example:
|
108
|
+
# e.g.
|
116
109
|
#
|
117
110
|
# {
|
118
|
-
# 'content/foo' => [ 'yaml', 'html' ],
|
119
|
-
# 'content/bar' => [ 'yaml', nil
|
120
|
-
# 'content/qux' => [ nil, 'html'
|
111
|
+
# 'content/foo' => [ 'yaml', ['html', 'md'] ],
|
112
|
+
# 'content/bar' => [ 'yaml', [nil] ],
|
113
|
+
# 'content/qux' => [ nil, ['html'] ]
|
121
114
|
# }
|
122
115
|
def all_split_files_in(dir_name)
|
123
|
-
|
116
|
+
by_basename =
|
124
117
|
all_files_in(dir_name)
|
125
118
|
.reject { |fn| fn =~ /(~|\.orig|\.rej|\.bak)$/ }
|
126
119
|
.group_by { |fn| basename_of(fn) }
|
127
120
|
|
128
|
-
|
121
|
+
all = {}
|
122
|
+
|
123
|
+
by_basename.each_pair do |basename, filenames|
|
129
124
|
# Divide
|
130
125
|
meta_filenames = filenames.select { |fn| ext_of(fn) == '.yaml' }
|
131
126
|
content_filenames = filenames.select { |fn| ext_of(fn) != '.yaml' }
|
132
127
|
|
133
128
|
# Check number of files per type
|
134
129
|
unless [0, 1].include?(meta_filenames.size)
|
135
|
-
raise "Found #{meta_filenames.size} meta files for #{
|
130
|
+
raise "Found #{meta_filenames.size} meta files for #{basename}; expected 0 or 1"
|
136
131
|
end
|
137
|
-
unless [
|
138
|
-
|
132
|
+
unless config[:identifier_style] == 'full'
|
133
|
+
unless [0, 1].include?(content_filenames.size)
|
134
|
+
raise "Found #{content_filenames.size} content files for #{basename}; expected 0 or 1"
|
135
|
+
end
|
139
136
|
end
|
140
137
|
|
141
|
-
|
142
|
-
|
143
|
-
|
138
|
+
all[basename] = []
|
139
|
+
all[basename][0] =
|
140
|
+
meta_filenames[0] ? 'yaml' : nil
|
141
|
+
all[basename][1] =
|
142
|
+
content_filenames.any? ? content_filenames.map { |fn| ext_of(fn)[1..-1] || '' } : [nil]
|
144
143
|
end
|
145
144
|
|
146
|
-
|
145
|
+
all
|
147
146
|
end
|
148
147
|
|
149
148
|
# Returns all files in the given directory and directories below it.
|
@@ -73,33 +73,6 @@ module Nanoc::DataSources
|
|
73
73
|
|
74
74
|
private
|
75
75
|
|
76
|
-
# See {Nanoc::DataSources::Filesystem#create_object}.
|
77
|
-
def create_object(dir_name, content, attributes, identifier, params = {})
|
78
|
-
# Check for periods
|
79
|
-
if (@config.nil? || !@config[:allow_periods_in_identifiers]) && identifier.include?('.')
|
80
|
-
raise "Attempted to create an object in #{dir_name} with identifier #{identifier} containing a period, but allow_periods_in_identifiers is not enabled in the site configuration. (Enabling allow_periods_in_identifiers may cause the site to break, though.)"
|
81
|
-
end
|
82
|
-
|
83
|
-
# Determine path
|
84
|
-
ext = params[:extension] || '.html'
|
85
|
-
path = dir_name + (identifier == '/' ? '/index.html' : identifier[0..-2] + ext)
|
86
|
-
parent_path = File.dirname(path)
|
87
|
-
|
88
|
-
# Notify
|
89
|
-
Nanoc::Int::NotificationCenter.post(:file_created, path)
|
90
|
-
|
91
|
-
# Write item
|
92
|
-
FileUtils.mkdir_p(parent_path)
|
93
|
-
File.open(path, 'w') do |io|
|
94
|
-
meta = attributes.__nanoc_stringify_keys_recursively
|
95
|
-
unless meta == {}
|
96
|
-
io.write(YAML.dump(meta).strip + "\n")
|
97
|
-
io.write("---\n")
|
98
|
-
end
|
99
|
-
io.write(content)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
76
|
# See {Nanoc::DataSources::Filesystem#filename_for}.
|
104
77
|
def filename_for(base_filename, ext)
|
105
78
|
if ext.nil?
|
@@ -114,6 +87,10 @@ module Nanoc::DataSources
|
|
114
87
|
# Returns the identifier derived from the given filename, first stripping
|
115
88
|
# the given directory name off the filename.
|
116
89
|
def identifier_for_filename(filename)
|
90
|
+
if config[:identifier_style] == 'full'
|
91
|
+
return Nanoc::Identifier.new(filename, style: :full)
|
92
|
+
end
|
93
|
+
|
117
94
|
if filename =~ /(^|\/)index(\.[^\/]+)?$/
|
118
95
|
regex = @config && @config[:allow_periods_in_identifiers] ? /\/?(index)?(\.[^\/\.]+)?$/ : /\/?index(\.[^\/]+)?$/
|
119
96
|
else
|