mardi 18 décembre 2018

Is it possible to call a generically-typed function by passing in a class specified in a map?

Asking here because I'm pulling out my hair trying to figure out what exactly it is I need to do here.

I'm writing a batch-processing endpoint that attempts to convert the body of a request to a specific Scala case class before executing the logic within the endpoint itself.

This is as far as I currently got. First, I have a function executeWithType that takes a generic type, a controller method, and the request body and executes the controller method after converting the request body to the provided type. The request param is available in a scope outside this function.

def executeWithType[A](action: () => Action[A], batchRequest: BatchRequest): Future[Result] = {
    action()(request.map(_ => batchRequest.body.map(_.as[A]).get))
}

Then, I have some code that checks what endpoint to call and what type to cast to depending on what's in the BatchRequest itself.

val res: Future[Result] = (batchRequest.method, batchRequest.endpoint) match {
    case ("POST", "/endpoint1") => {
        executeWithType[EndpointOneType](controller.endpointOne _, batchRequest)
    }
    case ("POST", "/endpoint2") => {
        executeWithType[EndpointTwoType](controller.endpointTwo _, batchRequest)
    }
    case _ => Future.successful(NotFound)
}

The above works perfectly fine - however, I want to avoid this sort of tuple-matching with individual cases if possible, and specify a Map that does this instead. In my ideal world, the end result of the code block immediately above would look like this:

val actions = Map(
    Seq("POST", "/endpoint1") -> (controller.endpointOne _, EndpointOneType),
    Seq("POST", "/endpoint2") -> (controller.endpointTwo _, EndpointTwoType)
)

val res = actions.get(Seq(batchRequest.method, batchRequest.endpoint)) match {
    case Some(action) => {
         executeWithType[action._2](action._1, batchRequest)
    }
    case _ => Future.successful(NotFound)
}

Is this possible at all? I've been trying to fight with it but my understanding of reflection in Scala is really weak, so I'm not sure exactly how I'd go about doing this. I've tried a bunch of classOf and typeTag and Class[_] stuff but I'm basically swinging in the dark. Hoping someone more knowledgeable than I am could help me out.

The big things are:

  1. What needs to go in the second space of the tuple in the value of the Map? How do you pass a Class variable?
  2. How do we use that class-as-a-variable to call a generically typed method?




Aucun commentaire:

Enregistrer un commentaire