add relative fetcher

This helps reduce any folder structures, weather on disk or in archives, to their relative root paths; i.e. ignore all file-prefixes that are given and go directly to the underlying files, relative to the common folders that contain it
This commit is contained in:
Dominik Richter 2016-02-21 17:31:43 +01:00 committed by Stephan Renatus
parent f023d02bbb
commit c9d1272f49
3 changed files with 113 additions and 2 deletions

View file

@ -27,6 +27,66 @@ module Inspec
def read(_file)
fail "Fetcher #{self} does not implement `read(...)`. This is required."
end
def relative_target
RelFetcher.new(self)
end
end
class RelFetcher
attr_reader :files
def initialize(fetcher)
@parent = fetcher
@prefix = get_prefix(fetcher.files)
@files = fetcher.files.find_all { |x| x.start_with? @prefix }
.map { |x| x[@prefix.length..-1] }
end
def read(file)
@parent.read(@prefix + file)
end
private
def get_prefix(files)
return '' if files.empty?
sorted = files.sort_by(&:length)
get_folder_prefix(sorted)
end
def get_folder_prefix(files, first_iteration = true)
return get_files_prefix(files) if files.length == 1
pre = files[0] + File::SEPARATOR
rest = files[1..-1]
if rest.all? { |i| i.start_with? pre }
return get_folder_prefix(rest, false)
end
return get_files_prefix(files) if first_iteration
files
end
def get_files_prefix(files)
return '' if files.empty?
file = files[0]
bn = File.basename(file)
# no more prefixes
return '' if bn == file
i = file.rindex(bn)
prefix = file[0..i-1]
rest = files.find_all { |f| !f.start_with?(prefix) }
return prefix if rest.empty?
nu_prefix = get_prefix(rest)
return nu_prefix if prefix.start_with? nu_prefix
# edge case: completely different prefixes; retry prefix detection
a = File.dirname(prefix + 'a')
b = File.dirname(nu_prefix + 'b')
get_prefix([a, b])
end
end
end
end

View file

@ -10,3 +10,52 @@ describe Inspec::Fetcher do
res.must_be_kind_of Fetchers::Local
end
end
describe Inspec::Plugins::RelFetcher do
def fetcher
src_fetcher.expects(:files).returns(in_files).at_least_once
Inspec::Plugins::RelFetcher.new(src_fetcher)
end
let(:src_fetcher) { mock() }
IN_AND_OUT = {
[] => [],
%w{file} => %w{file},
# don't prefix just by filename
%w{file file_a} => %w{file file_a},
%w{path/file path/file_a} => %w{file file_a},
%w{path/to/file} => %w{file},
%w{/path/to/file} => %w{file},
%w{alice bob} => %w{alice bob},
# mixed paths
%w{x/a y/b} => %w{x/a y/b},
%w{/x/a /y/b} => %w{x/a y/b},
%w{z/x/a z/y/b} => %w{x/a y/b},
%w{/z/x/a /z/y/b} => %w{x/a y/b},
# mixed with relative path
%w{a path/to/b} => %w{a path/to/b},
%w{path/to/b a} => %w{path/to/b a},
%w{path/to/b path/a} => %w{to/b a},
%w{path/to/b path/a c} => %w{path/to/b path/a c},
# mixed with absolute paths
%w{/path/to/b /a} => %w{path/to/b a},
%w{/path/to/b /path/a} => %w{to/b a},
%w{/path/to/b /path/a /c} => %w{path/to/b path/a c},
# mixing absolute and relative paths
%w{path/a /path/b} => %w{path/a /path/b},
%w{/path/a path/b} => %w{/path/a path/b},
# extract folder structure buildup
%w{/a /a/b /a/b/c} => %w{c},
%w{/a /a/b /a/b/c/d/e} => %w{e},
}.each do |ins, outs|
describe 'empty profile' do
let(:in_files) { ins }
it 'also has no files' do
fetcher.files.must_equal outs
end
end
end
end

View file

@ -32,7 +32,8 @@ describe Fetchers::Url do
end
it 'must contain all files' do
_(res.files).must_equal ["inspec.yml", "libraries", "libraries/testlib.rb", "controls", "controls/filesystem_spec.rb"]
_(res.files).must_equal %w{inspec.yml libraries libraries/testlib.rb
controls controls/filesystem_spec.rb}
end
it 'must not read if the file isnt included' do
@ -60,7 +61,8 @@ describe Fetchers::Url do
end
it 'must contain all files' do
_(res.files).must_equal ["inspec.yml", "controls", "controls/filesystem_spec.rb"]
_(res.files).must_equal %w{inspec.yml libraries libraries/testlib.rb
controls controls/filesystem_spec.rb}
end
it 'must not read if the file isnt included' do