Create a one to one relation to a given target :class:`Resource`.

    Args:

        dest_class(Resource): The *target* class for the relationship

    Keyword Args:

        type(RelationType): The relationship approach to use.
        reverse(to_may or to_one): An *optional* reverse relationship.
        reverse_type(RelationType): The reverse relationship approach.
        resource_classes(Resource): The kinds of Resources to expect
            in the relationship

    Returns:

        A builder function which, given a source class creates a
        one-to-one relationship with the target

    A one to one relationship means that you can get the associated
    target object from the object on which the ``to_one`` was declared.

    .. code-block:: python

        @to_one(Organization)
        def User(Resource):
            pass

    Declares that a User is associated with *one* Organization. The
    decorator automatically adds a method to fetch the associated
    organization:

    .. code-block:: python

        org = user.organization()

def to_one(dest_class, type=RelationType.DIRECT, resource_classes=None,
           reverse=None, reverse_type=RelationType.DIRECT,
           writable=False):
    """Create a one to one relation to a given target :class:`Resource`.

    Args:

        dest_class(Resource): The *target* class for the relationship

    Keyword Args:

        type(RelationType): The relationship approach to use.
        reverse(to_may or to_one): An *optional* reverse relationship.
        reverse_type(RelationType): The reverse relationship approach.
        resource_classes(Resource): The kinds of Resources to expect
            in the relationship

    Returns:

        A builder function which, given a source class creates a
        one-to-one relationship with the target

    A one to one relationship means that you can get the associated
    target object from the object on which the ``to_one`` was declared.

    .. code-block:: python

        @to_one(Organization)
        def User(Resource):
            pass

    Declares that a User is associated with *one* Organization. The
    decorator automatically adds a method to fetch the associated
    organization:

    .. code-block:: python

        org = user.organization()

    """
    def method_builder(cls):
        dest_resource_type = dest_class._resource_type()
        dest_method_name = dest_resource_type.replace('-', '_')
        doc_variables = {
            'from_class': cls.__name__,
            'to_class': dest_class.__name__,
            'to_name': dest_method_name
        }

        fetch_method_doc = """Fetch the {2} associated with this :class:`{0}`.

        Returns:

          {1}: The :class:`{1}` of this :class:`{0}`
        """.format(cls.__name__, dest_class.__name__, dest_method_name)

        def _fetch_relationship_included(self):
            session = self._session
            include = self._include
            if include is None or dest_class not in include:
                # You requested an included relationship that was
                # not originally included
                error = "{} was not included".format(dest_class.__name__)
                raise AttributeError(error)
            included = self._included.get(dest_resource_type)
            if len(included) == 0:
                return None
            mk_one = dest_class._mk_one(session,
                                        resource_classes=resource_classes)
            return mk_one({
                'data': included[0]
            })

        def fetch_relationship_direct(self, use_included=False):
            if use_included:
                return _fetch_relationship_included(self)
            session = self._session
            id = None if self.is_singleton() else self.id
            url = session._build_url(self._resource_path(), id,
                                     dest_resource_type)
            process = dest_class._mk_one(session,
                                         resource_classes=resource_classes)
            return session.get(url, CB.json(200, process))

        def fetch_relationship_include(self, use_included=False):
            if use_included:
                return _fetch_relationship_included(self)
            session = self._session
            id = None if self.is_singleton() else self.id
            url = session._build_url(self._resource_path(), id)
            params = build_request_include([dest_class], None)

            def _process(json):
                included = json.get('included')
                if len(included) == 0:
                    return None

                mk_one = dest_class._mk_one(session,
                                            resource_classes=resource_classes)
                return mk_one({
                    'data': included[0]
                })
            return session.get(url, CB.json(200, _process),
                               params=params)

        if type == RelationType.DIRECT:
            fetch_relationship = fetch_relationship_direct
        elif type == RelationType.INCLUDE:
            fetch_relationship = fetch_relationship_include
        else:  # pragma: no cover
            raise ValueError("Invalid RelationType: {}".format(type))

        fetch_relationship.__doc__ = fetch_method_doc

        def update_method(self, resource):
            """Set the {to_name} for this :class:`{from_class}`.

            Args:

              resource: The :class:`{to_class}` to set

            Returns:

                True if successful
            """
            session, url, json = _build_relatonship(self, dest_resource_type,
                                                    resource)
            return session.patch(url, CB.boolean(200), json=json)

        methods = [(dest_method_name, fetch_relationship)]
        if writable:
            methods.extend([
                ('update_{}'.format(dest_method_name), update_method)
            ])
        for name, method in methods:
            method.__doc__ = method.__doc__.format(**doc_variables)
            setattr(cls, name, method)

        if reverse is not None:
            reverse(cls, type=reverse_type)(dest_class)

        return cls

    return method_builder