Предоставлено неявное средство чтения / записи для класса case not found

Я пытаюсь написать хорошую общую службу сохранения с одной реализацией для mongo с использованием Reactive Mongo, и я изо всех сил пытаюсь предоставить неявный читатель / писатель для моих дочерних классов записи. Вот код.

Сначала базовый класс записи (каждая сохраняемая запись должна реализовывать его.

abstract class Record {
  val _id: BSONObjectID = BSONObjectID.generate()
}

Второй класс дочернего случая записи (очень простой) с его писателем / читателем (оба возможных способа сделать это, макросы против пользовательского в комментарии)

case class TestRecord() extends Record {}
  object TestRecord {
//    implicit object TestRecordWriter extends BSONDocumentWriter[TestRecord] {
//      def write(testRecord: TestRecord): BSONDocument = BSONDocument()
//    }
//
//    implicit object TestRecordReader extends BSONDocumentReader[TestRecord] {
//      def read(doc: BSONDocument): TestRecord = TestRecord()
//    }

    implicit def reader = Macros.reader[TestRecord]
    implicit def writer = Macros.writer[TestRecord]
  }

А потом сама услуга

class MongoPersistenceService[R <: Record] @Inject()()
                                                    (implicit ec: ExecutionContext, tag: ClassTag[R]) {

  val collectionName: String = tag.runtimeClass.getSimpleName

  def db: Future[DefaultDB] = MongoConnectionWrapper.getMongoConnection("mongodb://127.0.0.1", "27017")
                              .flatMap(_.database("testDb"))

  def collection: Future[BSONCollection] = db.map(_.collection(collectionName))

  def persist(record: R): Future[Unit] = {
    collection.flatMap(_.insert(record)).map(_ => {})
  }

  def read(id: BSONObjectID): Future[R] = {
    for {
      coll <- collection
      record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]
    } yield record
  }
}

И вот мой провальный тест:

import scala.concurrent.ExecutionContext.Implicits.global

class MongoPersistenceServiceSpec extends WordSpec with Matchers with BeforeAndAfter {
  val persistenceService = new MongoPersistenceService[TestRecord]()

    "persist" when {
      "called" should {
        "succeeds when passing a not persisted record" in {
          val testRecord = TestRecord()

          persistenceService.persist(testRecord)

          val persistedRecord = Await.result(persistenceService.read(testRecord._id), Duration(1000, "millis"))
        assert(persistedRecord.eq(testRecord))
      }
    }
  }
}

Компилятор жалуется на следующие сообщения:

Error:(33, 32) could not find implicit value for parameter writer: reactivemongo.bson.BSONDocumentWriter[R]
  collection.flatMap(_.insert(record)).map(_ => {})

Error:(33, 32) not enough arguments for method insert: (implicit writer: reactivemongo.bson.BSONDocumentWriter[R], implicit ec: scala.concurrent.ExecutionContext)scala.concurrent.Future[reactivemongo.api.commands.WriteResult].
  Unspecified value parameters writer, ec.
    collection.flatMap(_.insert(record)).map(_ => {})

Error:(39, 57) could not find implicit value for parameter reader: reactivemongo.bson.BSONDocumentReader[R]
  record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]

Error:(39, 57) not enough arguments for method one: (implicit reader: reactivemongo.bson.BSONDocumentReader[R], implicit ec: scala.concurrent.ExecutionContext)scala.concurrent.Future[Option[R]].
  Unspecified value parameters reader, ec.
    record <- coll.find(BSONDocument("_id" -> id)).one[R].mapTo[R]

Кто-нибудь знает, что мне может не хватать? Я все еще новичок в scala, так что помогите мне найти здесь проблему.

Я уже безуспешно пытался написать собственный BSONWriter / BSONReader вместо предоставленного здесь BSONDocumentWriter / BSONDocumentReader.


person Jeep87c    schedule 12.07.2017    source источник


Ответы (2)


Короче говоря, Scala не ищет имплицитов повсюду в вашем коде. Их необходимо включить в область видимости либо посредством импорта, передав их в качестве параметров, либо, как я предлагаю здесь, через контекстную привязку:

class MongoPersistenceService[R <: Record : BSONDocumentReader : BSONDocumentWriter]

Это своего рода сокращение для (а) требования, чтобы неявный BSONDocumentReader[R] (и писатель) мог быть найден в то время, когда класс конструируется с конкретным R, и (б) включение этих имплицитов в область видимости в реализации класса, чтобы они могут быть неявно переданы другим методам, таким как insert.

Для выполнения этого нового требования (а) вам, возможно, придется import TestRecord._ в вашем тесте.

person Joe K    schedule 12.07.2017
comment
Спасибо за ответ, сегодня я узнал еще кое-что о scala! - person Jeep87c; 12.07.2017

Ваша persist функция не имеет доступа к тому факту, что вы определили эти неявные функции. Подпись должна быть примерно такой:

def persist(record: R)(implicit def writer: BSONDocumentWriter[R]): Future[Unit]

И везде, где вы вызываете persist и передаете TestRecord, убедитесь, что неявные функции находятся в области видимости.

person Tyler    schedule 12.07.2017
comment
Спасибо за Ваш ответ. Это также действительный ответ (пробовал и работал как другой), но я лично предпочитаю первый, так как я пишу его один раз и только один раз при объявлении класса, а не для каждого метода в классе. Но оба работают. - person Jeep87c; 12.07.2017