docs: def resource_factory(self, name, schema, resource_cls=None):
          """
          Registers a new resource with a given schema. The schema must not have any unresolved references
          (such as `{"$ref": "#"}` for self-references, or otherwise). A subclass of :class:`Resource`
          may be provided to add specific functionality to the resulting :class:`Resource`.
  
          :param str name:
          :param dict schema:
          :param Resource resource_cls: a subclass of :class:`Resource` or None
          :return: The new :class:`Resource`.
          """
          cls = type(str(upper_camel_case(name)), (resource_cls or Resource, collections.MutableMapping), {
              '__doc__': schema.get('description', '')
          })
  
          cls._schema = schema
          cls._client = self
          cls._links = links = {}
  
          for link_schema in schema['links']:
              link = Link(self,
                          rel=link_schema['rel'],
                          href=link_schema['href'],
                          method=link_schema['method'],
                          schema=link_schema.get('schema', None),
                          target_schema=link_schema.get('targetSchema', None))
  
              # Set Resource._self, etc. for the special methods as they are managed by the Resource class
              if link.rel in ('self', 'instances', 'create', 'update', 'destroy'):
                  setattr(cls, '_{}'.format(link.rel), link)
              links[link.rel] = link
  
              if link.rel != 'update':  # 'update' is a special case because of MutableMapping.update()
                  setattr(cls, snake_case(link.rel), link)
  
          # TODO routes (instance & non-instance)
  
          for property_name, property_schema in schema.get('properties', {}).items():
              # skip $uri and $id as these are already implemented in Resource and overriding them causes unnecessary
              # fetches.
              if property_name.startswith('$'):
                  continue
  
              if property_schema.get('readOnly', False):
                  # TODO better error message. Raises AttributeError("can't set attribute")
                  setattr(cls,
                          property_name,
                          property(fget=partial((lambda name, obj: getitem(obj, name)), property_name),
                                   doc=property_schema.get('description', None)))
              else:
                  setattr(cls,
                          property_name,
                          property(fget=partial((lambda name, obj: getitem(obj, name)), property_name),
                                   fset=partial((lambda name, obj, value: setitem(obj, name, value)), property_name),
                                   fdel=partial((lambda name, obj: delitem(obj, name)), property_name),
                                   doc=property_schema.get('description', None)))
  
          root = None
          if 'instances' in links:
              root = cls._instances.href
          elif 'self' in links:
              root = cls._self.href[:cls._self.href.rfind('/')]
          else:
              root = self._root_path + '/' + name.replace('_', '-')
  
          self._resources[root] = cls
          return cls