diff --git a/lib/bagit/bag.rb b/lib/bagit/bag.rb index 84cd529..28ee257 100644 --- a/lib/bagit/bag.rb +++ b/lib/bagit/bag.rb @@ -8,9 +8,28 @@ require "bagit/valid" module BagIt + class FileFinder + def self.find(dir) + raise NotImplementedError + end + end + + class StandardFileFinder < FileFinder + def self.find(dir) + Dir[File.join(dir, "**", "*")].select { |f| File.file? f } + end + end + + class StandardWithHiddenFileFinder < FileFinder + def self.find(dir) + Dir.glob(File.join(dir, "**", "*"), File::FNM_DOTMATCH).select { |f| File.file? f } + end + end + # Represents the state of a bag on a filesystem class Bag attr_reader :bag_dir + attr_reader :detect_hidden include Validity # Validity functionality include Info # bagit & bag info functionality @@ -18,8 +37,11 @@ class Bag include Fetch # fetch related functionality # Make a new Bag based at path - def initialize(path, info = {}, _create = false) + def initialize(path, info = {}, _create = false, detect_hidden = false) @bag_dir = path + @detect_hidden = detect_hidden + @file_finder = @detect_hidden ? StandardWithHiddenFileFinder : StandardFileFinder + # make the dir structure if it doesn't exist FileUtils.mkdir bag_dir unless File.directory? bag_dir FileUtils.mkdir data_dir unless File.directory? data_dir @@ -37,7 +59,7 @@ def data_dir # Return the paths to each bag file relative to bag_dir def bag_files - Dir[File.join(data_dir, "**", "*")].select { |f| File.file? f } + @file_finder.find(data_dir) end # Return the paths to each tag file relative to bag_dir diff --git a/lib/bagit/valid.rb b/lib/bagit/valid.rb index 2b036c4..760e7c0 100644 --- a/lib/bagit/valid.rb +++ b/lib/bagit/valid.rb @@ -33,7 +33,11 @@ def complete? empty_manifests.each do |file| logger.error("#{file} is manifested but not present".red) - errors.add :completeness, "#{file} is manifested but not present" + error_message = "#{file} is manifested but not present" + if !detect_hidden && file.start_with?(File.join("data", ".")) + error_message += "; consider turning on hidden file detection" + end + errors.add :completeness, error_message end tag_empty_manifests.each do |file| logger.error("#{file} is a manifested tag but not present".red) diff --git a/spec/bagit_spec.rb b/spec/bagit_spec.rb index 80569be..74d384d 100644 --- a/spec/bagit_spec.rb +++ b/spec/bagit_spec.rb @@ -165,4 +165,31 @@ end end end + + describe "bag with hidden files" do + before do + @sandbox = Sandbox.new + + # make the bag + @bag_path = File.join @sandbox.to_s, "the_bag" + @bag = described_class.new @bag_path, {}, false, true + + # add some files + @bag.add_file(".keep") { |io| io.puts "" } + @bag.add_file("test.txt") { |io| io.puts "testing testing" } + end + + after do + @sandbox.cleanup! + end + + describe "#bag_files" do + it "returns an array including non-hidden and hidden files" do + files = @bag.bag_files.map { |f| f.sub(File.join(@bag_path, "data", ""), "") } + expect(files).to be_a_kind_of(Array) + expect(files).not_to be_empty + expect(files).to eq([".keep", "test.txt"]) + end + end + end end diff --git a/spec/validation_spec.rb b/spec/validation_spec.rb index b286b8a..aa0bf67 100644 --- a/spec/validation_spec.rb +++ b/spec/validation_spec.rb @@ -44,13 +44,17 @@ it "is invalid if there are files that are in the manifest but not in the bag" do # add a file and then remove it through the back door - @bag.add_file("file-k") { |io| io.puts "time to go" } + file_name = "file-k" + @bag.add_file(file_name) { |io| io.puts "time to go" } @bag.manifest! - FileUtils.rm File.join(@bag.bag_dir, "data", "file-k") + FileUtils.rm File.join(@bag.bag_dir, "data", file_name) @bag.validate_only("true_for/completeness") expect(@bag.errors.on(:completeness)).not_to be_empty + expect(@bag.errors.on(:completeness)).to include( + "#{File.join("data", file_name)} is manifested but not present" + ) expect(@bag).not_to be_valid end @@ -139,4 +143,63 @@ end end end + + describe "a bag with unmanifested hidden files" do + before do + @sandbox = Sandbox.new + + # make a bag with hidden files not manifested + @source_bag_path = File.join @sandbox.to_s, "the_bag" + @source_bag = described_class.new @source_bag_path, {}, false, false + @source_bag.add_file(".keep") { |io| io.puts "" } + @source_bag.add_file("test.txt") { |io| io.puts "testing testing" } + @source_bag.manifest! + end + + after do + @sandbox.cleanup! + end + + it "fails validation when hidden file detection is on" do + @aware_bag = described_class.new @source_bag_path, {}, false, true + expect(@aware_bag).to_not be_valid + expect(@aware_bag.errors.on(:completeness)).not_to be_empty + end + + it "passes validation when hidden file detection is off" do + @unaware_bag = described_class.new @source_bag_path, {}, false, false + expect(@unaware_bag).to be_valid + end + end + + describe "a bag with manifested hidden files" do + before do + @sandbox = Sandbox.new + + # make a bag with hidden files manifested + @source_bag_path = File.join @sandbox.to_s, "the_bag" + @source_bag = described_class.new @source_bag_path, {}, false, true + @source_bag.add_file(".keep") { |io| io.puts "" } + @source_bag.add_file("test.txt") { |io| io.puts "testing testing" } + @source_bag.manifest! + end + + after do + @sandbox.cleanup! + end + + it "passes validation when hidden file detection is on" do + @aware_bag = described_class.new @source_bag_path, {}, false, true + expect(@aware_bag).to be_valid + end + + it "fails validation when hidden file detection is off, with suggested fix offered" do + @unaware_bag = described_class.new @source_bag_path, {}, false, false + expect(@unaware_bag).to_not be_valid + expect(@unaware_bag.errors.on(:completeness)).not_to be_empty + expect(@unaware_bag.errors.on(:completeness)).to include( + "data/.keep is manifested but not present; consider turning on hidden file detection" + ) + end + end end