Extract a class that represents artifacts file path

This commit is contained in:
Grzegorz Bizon 2017-09-05 12:17:56 +02:00
parent dfb8fcbb65
commit c53f319f88
3 changed files with 236 additions and 121 deletions

View file

@ -1,5 +1,7 @@
module Gitlab
module Ci::Build::Artifacts
module Ci
module Build
module Artifacts
class Metadata
##
# Class that represents an entry (path and metadata) to a file or
@ -11,26 +13,18 @@ module Gitlab
# This class is working only with UTF-8 encoded paths.
#
class Entry
attr_reader :path, :entries
attr_reader :entries
attr_accessor :name
def initialize(path, entries)
@path = path.dup.force_encoding('UTF-8')
@entries = entries
if path.include?("\0")
raise ArgumentError, 'Path contains zero byte character!'
end
unless path.valid_encoding?
raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
end
@path = Artifacts::Path.new(path)
end
delegate :empty?, to: :children
def directory?
blank_node? || @path.end_with?('/')
blank_node? || @path.directory?
end
def file?
@ -49,7 +43,7 @@ module Gitlab
def parent
return nil unless has_parent?
self.class.new(@path.chomp(basename), @entries)
self.class.new(@path.to_s.chomp(basename), @entries)
end
def basename
@ -57,14 +51,14 @@ module Gitlab
end
def name
@name || @path.split('/').last.to_s
@name || @path.name
end
def children
return [] unless directory?
return @children if @children
child_pattern = %r{^#{Regexp.escape(@path)}[^/]+/?$}
child_pattern = %r{^#{Regexp.escape(@path.to_s)}[^/]+/?$}
@children = select_entries { |path| path =~ child_pattern }
end
@ -84,38 +78,42 @@ module Gitlab
end
def metadata
@entries[@path] || {}
@entries[@path.to_s] || {}
end
def nodes
@path.count('/') + (file? ? 1 : 0)
@path.nodes + (file? ? 1 : 0)
end
def blank_node?
@path.empty? # "" is considered to be './'
@path.to_s.empty? # "" is considered to be './'
end
def exists?
blank_node? || @entries.include?(@path)
blank_node? || @entries.include?(@path.to_s)
end
def total_size
descendant_pattern = %r{^#{Regexp.escape(@path)}}
descendant_pattern = %r{^#{Regexp.escape(@path.to_s)}}
entries.sum do |path, entry|
(entry[:size] if path =~ descendant_pattern).to_i
end
end
def path
@path.to_s
end
def to_s
@path
@path.to_s
end
def ==(other)
@path == other.path && @entries == other.entries
path == other.path && @entries == other.entries
end
def inspect
"#{self.class.name}: #{@path}"
"#{self.class.name}: #{self.to_s}"
end
private
@ -128,3 +126,5 @@ module Gitlab
end
end
end
end
end

View file

@ -0,0 +1,51 @@
module Gitlab
module Ci
module Build
module Artifacts
class Path
def initialize(path)
@path = path.dup.force_encoding('UTF-8')
end
def valid?
nonzero? && utf8?
end
def directory?
@path.end_with?('/')
end
def name
@path.split('/').last.to_s
end
def nodes
@path.count('/')
end
def to_s
@path.tap do |path|
unless nonzero?
raise ArgumentError, 'Path contains zero byte character!'
end
unless utf8?
raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
end
end
end
private
def nonzero?
@path.exclude?("\0")
end
def utf8?
@path.valid_encoding?
end
end
end
end
end
end

View file

@ -0,0 +1,64 @@
require 'spec_helper'
describe Gitlab::Ci::Build::Artifacts::Path do
describe '#valid?' do
context 'when path contains a zero character' do
it 'is not valid' do
expect(described_class.new("something/\255")).not_to be_valid
end
end
context 'when path is not utf8 string' do
it 'is not valid' do
expect(described_class.new("something/\0")).not_to be_valid
end
end
context 'when path is valid' do
it 'is valid' do
expect(described_class.new("some/file/path")).to be_valid
end
end
end
describe '#directory?' do
context 'when path ends with a directory indicator' do
it 'is a directory' do
expect(described_class.new("some/file/dir/")).to be_directory
end
end
context 'when path does not end with a directory indicator' do
it 'is not a directory' do
expect(described_class.new("some/file")).not_to be_directory
end
end
end
describe '#name' do
it 'returns a base name' do
expect(described_class.new("some/file").name).to eq 'file'
end
end
describe '#nodes' do
it 'returns number of path nodes' do
expect(described_class.new("some/dir/file").nodes).to eq 2
end
end
describe '#to_s' do
context 'when path is valid' do
it 'returns a string representation of a path' do
expect(described_class.new('some/path').to_s).to eq 'some/path'
end
end
context 'when path is invalid' do
it 'raises an error' do
expect { described_class.new("invalid/\0").to_s }
.to raise_error ArgumentError
end
end
end
end