Path = {} function Path:new(p) local path = { segments = p:split('/') } setmetatable(path, self) self.__index = self return path end function Path:name() return self.segments[#self.segments] end function Path:stem() return self.segments[#self.segments]:match('[^.]+') end function Path:parent() local path = { segments = { table.unpack(self.segments, 1, #self.segments - 1) } } setmetatable(path, self) self.__index = self return path end function Path:__tostring() return table.concat(self.segments, "/") end function Path:__div(next) return Path:new(self:__tostring() .. "/" .. next) end return { tests = { function() local path = Path:new("some/path/here") assert.equals("here", path:name()) end, function() local path = Path:new("some/path/here.txt") assert.equals("here", path:stem()) end, function() local path = Path:new("some/path/here.txt") assert.equals("some/path", path:parent():__tostring()) end, function() local path = Path:new("some/path/here.txt") assert.equals("some/path/here.txt", path:__tostring()) end, function() local path = Path:new("some/path/here") path = path / 'next.txt' assert.equals("some/path/here/next.txt", path:__tostring()) end } }