public function getRelated($source, $relationName, $query=null, $meta=null)
    {
        if (!$source) { return; }

        $sourceIsArray = false;

        $test = $source;
        if (is_array($test) || $test instanceof \Traversable) {
            $sourceIsArray = true;
            $test = $test[0];
        }

        if (!$meta) {
            if ($query instanceof Meta) {
                list ($meta, $query) = [$query, null];
            } else {
                $meta = $this->mapper->getMeta(get_class($test));
            }
        }
        else {
            $meta = $meta instanceof Meta ? $meta : $this->mapper->getMeta($meta);
        }

        if (!isset($meta->relations[$relationName])) {
            throw new Exception("Unknown relation $relationName on {$meta->class}");
        }
        
        $relation = $meta->relations[$relationName];
        $relator = $this->getRelator($relation);
        
        if ($query) {
            // need to support both Query\Criteria and Query\Select
            // this is a cheeky hack - the API doesn't declare support for
            // Select in Relators because it carries promises of things like 
            // 'fields' and whatnot that we'll never be able to satisfy. 
            // That whole hierarchy needs to be cleaned
            // up into a bunch of traits so we can have RelatorCriteria or something.
            $query = $query instanceof Query\Criteria ? $query : Query\Select::fromParamArgs([$query]);
            $stack = $query->stack;
        }
        else {
            $stack = [];
        }

        $stack[$meta->class] = true;
        if ($sourceIsArray) {
            return $relator->getRelatedForList($meta, $source, $relation, $query ?: null, $stack);
        } else {
            return $relator->getRelated($meta, $source, $relation, $query ?: null, $stack);
        }
    }