public static function map(RecordInterface $source, $destination, array $rules)
    {
        if (!is_object($destination)) {
            throw new InvalidArgumentException('Destination must be an object');
        }

        $data = $source->getProperties();

        foreach ($data as $key => $value) {
            // convert to camelcase if underscore is in name
            if (strpos($key, '_') !== false) {
                $key = implode('', array_map('ucfirst', explode('_', $key)));
            }

            $method = null;
            if (isset($rules[$key])) {
                if (is_string($rules[$key])) {
                    $method = 'set' . ucfirst($rules[$key]);
                } elseif ($rules[$key] instanceof Rule) {
                    $method = 'set' . ucfirst($rules[$key]->getName());
                    $value  = $rules[$key]->getValue($value, $data);
                }
            } else {
                $method = 'set' . ucfirst($key);
            }

            if (is_callable(array($destination, $method))) {
                $destination->$method($value);
            }
        }
    }