# Path: templar/linker.py
# def link(source_path):
#     """Links the content found at source_path and represents a Block that represents the content."""
#     if not os.path.isfile(source_path):
#         raise SourceNotFound(source_path)
#     with open(source_path, 'r') as f:
#         content = f.read()
#     block_map = BlockMap()  # The map will be populated with the following function call.
#     all_block = convert_lines_to_block(
#             content.splitlines(), block_map, LinkStack(source_path), source_path)
#     return all_block, block_map.get_variables()
# 
# Path: templar/linker.py
# def get_block_dict(top_level_block):
#     """Returns a dictionary of block names (str) to block contents (str) for all child blocks, as
#     well as the original block itself.
# 
#     The block_dict argument is only used for recursive calls and should not 
#     """
#     block_stack = [top_level_block]
#     block_dict = {}
#     while block_stack:
#         block = block_stack.pop()
#         block_dict[block.name] = str(block)
#         for segment in block.segments:
#             if isinstance(segment, Block):
#                 block_stack.append(segment)
#     return block_dict
# 
# Path: templar/linker.py
# class Block(object):
#     def __init__(self, source_path, name, segments):
#         self.source_path = source_path
#         self.name = name
#         self.segments = segments
#         self._str = None  # Cache the str representation of this block.
# 
#     def apply_rule(self, rule):
#         self._str = None  # Clear str cache.
#         for i, segment in enumerate(self.segments):
#             assert isinstance(segment, str) or isinstance(segment, Block)
#             if isinstance(segment, str):
#                 self.segments[i] = rule.apply(segment)
#             else:
#                 segment.apply_rule(rule)  # Recursively apply rule onto nested blocks.
# 
#     def __str__(self):
#         if self._str is None:
#             self._str = '\n'.join(str(segment) for segment in self.segments)
#         return self._str
# 
# Path: templar/linker.py
# class SourceNotFound(TemplarError):
#     def __init__(self, source_path):
#         super().__init__('Could not find source file: ' + source_path)
# 
# Path: templar/linker.py
# class InvalidBlockName(TemplarError):
#     pass
# 
# Path: templar/linker.py
# class IncludeNonExistentBlock(TemplarError):
#     pass
# 
# Path: templar/linker.py
# class CyclicalIncludeError(TemplarError):
#     def __init__(self, link_stack, last_file):
#         super().__init__(' -> '.join(link_stack + [last_file]))


from templar.linker import link
from templar.linker import get_block_dict
from templar.linker import Block
from templar.linker import SourceNotFound
from templar.linker import InvalidBlockName
from templar.linker import IncludeNonExistentBlock
from templar.linker import CyclicalIncludeError
import unittest
import mock
                'Some text',
                '~ author: bar ',
                'More text')
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)
        block_all = Block('docA.md', 'all', [self.join_lines(
            'Some text',
            'More text')])
        self.assertBlockEqual(block_all, block)

    def testVariables_includeVariablesFromOtherFiles(self):
        mock_open = self.mock_open({
            'docA.md': self.join_lines(
                '~ title: foo ',
                '<include docB.md>'),
            'docB.md': '~ author: bar '
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)

# Path: templar/linker.py
# def link(source_path):
#     """Links the content found at source_path and represents a Block that represents the content."""
#     if not os.path.isfile(source_path):
#         raise SourceNotFound(source_path)
#     with open(source_path, 'r') as f:
#         content = f.read()
#     block_map = BlockMap()  # The map will be populated with the following function call.
#     all_block = convert_lines_to_block(
#             content.splitlines(), block_map, LinkStack(source_path), source_path)
#     return all_block, block_map.get_variables()
# 
# Path: templar/linker.py
# def get_block_dict(top_level_block):
#     """Returns a dictionary of block names (str) to block contents (str) for all child blocks, as
#     well as the original block itself.
# 
#     The block_dict argument is only used for recursive calls and should not 
#     """
#     block_stack = [top_level_block]
#     block_dict = {}
#     while block_stack:
#         block = block_stack.pop()
#         block_dict[block.name] = str(block)
#         for segment in block.segments:
#             if isinstance(segment, Block):
#                 block_stack.append(segment)
#     return block_dict
# 
# Path: templar/linker.py
# class Block(object):
#     def __init__(self, source_path, name, segments):
#         self.source_path = source_path
#         self.name = name
#         self.segments = segments
#         self._str = None  # Cache the str representation of this block.
# 
#     def apply_rule(self, rule):
#         self._str = None  # Clear str cache.
#         for i, segment in enumerate(self.segments):
#             assert isinstance(segment, str) or isinstance(segment, Block)
#             if isinstance(segment, str):
#                 self.segments[i] = rule.apply(segment)
#             else:
#                 segment.apply_rule(rule)  # Recursively apply rule onto nested blocks.
# 
#     def __str__(self):
#         if self._str is None:
#             self._str = '\n'.join(str(segment) for segment in self.segments)
#         return self._str
# 
# Path: templar/linker.py
# class SourceNotFound(TemplarError):
#     def __init__(self, source_path):
#         super().__init__('Could not find source file: ' + source_path)
# 
# Path: templar/linker.py
# class InvalidBlockName(TemplarError):
#     pass
# 
# Path: templar/linker.py
# class IncludeNonExistentBlock(TemplarError):
#     pass
# 
# Path: templar/linker.py
# class CyclicalIncludeError(TemplarError):
#     def __init__(self, link_stack, last_file):
#         super().__init__(' -> '.join(link_stack + [last_file]))


# Path: tests/linker_test.py
from templar.linker import link
from templar.linker import get_block_dict
from templar.linker import Block
from templar.linker import SourceNotFound
from templar.linker import InvalidBlockName
from templar.linker import IncludeNonExistentBlock
from templar.linker import CyclicalIncludeError
import unittest
import mock

                'Some text',
                '~ author: bar ',
                'More text')
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)
        block_all = Block('docA.md', 'all', [self.join_lines(
            'Some text',
            'More text')])
        self.assertBlockEqual(block_all, block)

    def testVariables_includeVariablesFromOtherFiles(self):
        mock_open = self.mock_open({
            'docA.md': self.join_lines(
                '~ title: foo ',
                '<include docB.md>'),
            'docB.md': '~ author: bar '
        })
        with mock_open:
            block, variables = link('docA.md')

        self.assertEqual({
            'title': 'foo',
            'author': 'bar',
        }, variables)

        block_all = Block('docA.md', 'all', [])