Skip to content

Commit

Permalink
Raise when meta-file cannot be unambiguously associated with content …
Browse files Browse the repository at this point in the history
…file
  • Loading branch information
denisdefreyne committed Oct 13, 2018
1 parent 2efe0fd commit 30adf86
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 20 deletions.
6 changes: 6 additions & 0 deletions nanoc/lib/nanoc/base/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,12 @@ def initialize(filter_name)
end
end

class AmbiguousMetadataAssociation < Generic
def initialize(content_filenames, meta_filename)
super("There are multiple content files (#{content_filenames.join(', ')}) that could match the file containing metadata (#{meta_filename}).")
end
end

class InternalInconsistency < Generic
end
end
Expand Down
62 changes: 42 additions & 20 deletions nanoc/lib/nanoc/data_sources/filesystem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,30 +176,49 @@ def load_objects(dir_name, klass)

return [] if dir_name.nil?

all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
content_exts.each do |content_ext|
meta_filename = filename_for(base_filename, meta_ext)
content_filename = filename_for(base_filename, content_ext)

proto_doc = read_proto_document(content_filename, meta_filename, klass)

content = content_for(proto_doc, content_filename)
attributes = attributes_for(proto_doc, content_filename, meta_filename)
identifier = identifier_for(content_filename, meta_filename, dir_name)

res << klass.new(
content,
attributes,
identifier,
content_checksum_data: content_checksum_data_for(proto_doc),
attributes_checksum_data: attributes_checksum_data_for(proto_doc, content_filename, meta_filename),
)
end
each_content_meta_pair_in(dir_name) do |content_filename, meta_filename|
proto_doc = read_proto_document(content_filename, meta_filename, klass)

content = content_for(proto_doc, content_filename)
attributes = attributes_for(proto_doc, content_filename, meta_filename)
identifier = identifier_for(content_filename, meta_filename, dir_name)

res << klass.new(
content,
attributes,
identifier,
content_checksum_data: content_checksum_data_for(proto_doc),
attributes_checksum_data: attributes_checksum_data_for(proto_doc, content_filename, meta_filename),
)
end

res
end

# Enumerates each pair of content file and meta file. If there is ambiguity, it will raise an error.
def each_content_meta_pair_in(dir_name)
all_split_files_in(dir_name).each do |base_filename, (meta_ext, content_exts)|
meta_filename = filename_for(base_filename, meta_ext)
content_filenames = content_exts.map { |e| filename_for(base_filename, e) }

have_possible_ambiguity = meta_filename && content_filenames.size > 1
if have_possible_ambiguity && content_filenames.count { |fn| !parser.has_frontmatter?(fn) } != 1
raise Nanoc::Int::Errors::AmbiguousMetadataAssociation.new(content_filenames, meta_filename)
end

content_filenames.each do |content_filename|
real_meta_filename =
if have_possible_ambiguity && parser.has_frontmatter?(content_filename)
nil
else
meta_filename
end

yield(content_filename, real_meta_filename)
end
end
end

def content_checksum_data_for(proto_doc)
data = proto_doc.content_checksum_data
data ? Digest::SHA1.digest(data) : nil
Expand Down Expand Up @@ -379,8 +398,11 @@ def allow_periods_in_identifiers?
end
end

def parser
@parser ||= Parser.new(config: @config)
end

def parse(content_filename, meta_filename)
parser = Parser.new(config: @config)
parser.call(content_filename, meta_filename)
end
end
Expand Down
5 changes: 5 additions & 0 deletions nanoc/lib/nanoc/data_sources/filesystem/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ def parse_metadata(data, filename)
meta
end

def has_frontmatter?(filename)
data = Tools.read_file(filename, config: @config)
/\A#{SEPARATOR}\s*$/.match?(data)
end

def verify_meta(meta, filename)
return if meta.is_a?(Hash)

Expand Down
111 changes: 111 additions & 0 deletions nanoc/spec/nanoc/data_sources/filesystem_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,117 @@
expect(subject[0].content_checksum_data).to be_nil
end
end

context 'two content files (no inline metadata) with one meta file' do
let(:params) { { identifier_type: 'full' } }

before do
FileUtils.mkdir_p('foo')
File.write('foo/a.txt', 'hi')
File.write('foo/a.md', 'ho')
File.write('foo/a.yaml', 'title: Aaah')
end

it 'errors' do
expect { subject }
.to raise_error(
Nanoc::Int::Errors::AmbiguousMetadataAssociation,
'There are multiple content files (foo/a.txt, foo/a.md) that could match the file containing metadata (foo/a.yaml).',
)
end
end

context 'two content files (one has inline metadata) with one meta file' do
let(:params) { { identifier_type: 'full' } }

before do
FileUtils.mkdir_p('foo')
File.write('foo/a.txt', "---\ntitle: Hi\n---\n\nhi")
File.write('foo/a.md', 'ho')
File.write('foo/a.yaml', 'title: Aaah')
end

it 'assigns metadata to the file that doesn’t have any yet' do
expect(subject.size).to eq(2)

items = subject.sort_by { |i| i.identifier.to_s }

expect(items[0].content).to be_a(Nanoc::Int::TextualContent)
expect(items[0].identifier).to eq(Nanoc::Identifier.new('/a.md', type: :full))
expect(items[0].attributes[:title]).to eq('Aaah')

expect(items[1].content).to be_a(Nanoc::Int::TextualContent)
expect(items[1].identifier).to eq(Nanoc::Identifier.new('/a.txt', type: :full))
expect(items[1].attributes[:title]).to eq('Hi')
end
end

context 'two content files (both have inline metadata) with one meta file' do
let(:params) { { identifier_type: 'full' } }

before do
FileUtils.mkdir_p('foo')
File.write('foo/a.txt', "---\ntitle: Hi\n---\n\nhi")
File.write('foo/a.md', "---\ntitle: Ho\n---\n\nho")
File.write('foo/a.yaml', 'title: Aaah')
end

it 'errors' do
expect { subject }
.to raise_error(
Nanoc::Int::Errors::AmbiguousMetadataAssociation,
'There are multiple content files (foo/a.txt, foo/a.md) that could match the file containing metadata (foo/a.yaml).',
)
end
end

context 'two content files (both have inline metadata) with no meta file' do
let(:params) { { identifier_type: 'full' } }

before do
FileUtils.mkdir_p('foo')
File.write('foo/a.txt', "---\ntitle: Hi\n---\n\nhi")
File.write('foo/a.md', "---\ntitle: Ho\n---\n\nho")
end

it 'uses inline metadata' do
expect(subject.size).to eq(2)

items = subject.sort_by { |i| i.identifier.to_s }

expect(items[0].content).to be_a(Nanoc::Int::TextualContent)
expect(items[0].identifier).to eq(Nanoc::Identifier.new('/a.md', type: :full))
expect(items[0].attributes[:title]).to eq('Ho')

expect(items[1].content).to be_a(Nanoc::Int::TextualContent)
expect(items[1].identifier).to eq(Nanoc::Identifier.new('/a.txt', type: :full))
expect(items[1].attributes[:title]).to eq('Hi')
end
end

context 'two content files (neither have inline metadata) with no meta file' do
let(:params) { { identifier_type: 'full' } }

before do
FileUtils.mkdir_p('foo')
File.write('foo/a.txt', "hi")
File.write('foo/a.md', "ho")
end

it 'uses no metadata' do
expect(subject.size).to eq(2)

items = subject.sort_by { |i| i.identifier.to_s }

expect(items[0].content).to be_a(Nanoc::Int::TextualContent)
expect(items[0].identifier).to eq(Nanoc::Identifier.new('/a.md', type: :full))
expect(items[0].attributes[:title]).to be_nil

expect(items[1].content).to be_a(Nanoc::Int::TextualContent)
expect(items[1].identifier).to eq(Nanoc::Identifier.new('/a.txt', type: :full))
expect(items[1].attributes[:title]).to be_nil
end
end
end
end

Expand Down

0 comments on commit 30adf86

Please sign in to comment.