Skip to content

Instantly share code, notes, and snippets.

@timvw
Last active April 25, 2018 09:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save timvw/dd8f7c14e18bd31ccc061dfdc25c915e to your computer and use it in GitHub Desktop.
Save timvw/dd8f7c14e18bd31ccc061dfdc25c915e to your computer and use it in GitHub Desktop.
package be.icteam.playground
import be.icteam.playground.App.ParseError
import cats._
import cats.instances._
import cats.implicits._
import cats.syntax.apply._
object App {
def main(args: Array[String]): Unit = {
val inputs = List("Tim;Thomas;Fool", "Tim;Mathias;Bro", "Tim;Mike;Go Away", "Tim;")
inputs.map(prepareForProcessing).foreach(process)
}
/* all related to the parsing */
case class Message(from: String, to: String, body: String)
case class ParseError(input: String, errorMessage: String)
def parse(input: String): Either[ParseError, Message] = input.split(";", -1) match {
case Array(from, to, body) => Message(from, to, body).asRight[ParseError]
case _ => ParseError(input, s"Could not parse the following input: '${input}'").asLeft[Message]
}
/* all related to enrichment/lookup */
case class Recipient(recipientName: String, active: Boolean)
def lookupRecipient(recipientName: String): Option[Recipient] =
if (recipientName.equalsIgnoreCase("Mike"))
None
else
Some(Recipient(recipientName, true))
// in order to reduce latency, i would like to use this method instead
def lookupRecipients(recipientNames: List[String]): Map[String, Option[Recipient]] =
recipientNames.map(x => (x, lookupRecipient(x))).toMap
/* all related to the processing */
sealed trait ProcessError
case class ParseErrorT(pe: ParseError) extends ProcessError
case class LookupError(msg: String) extends ProcessError
def prepareForProcessing(input: String): Either[ProcessError, (Message, Recipient)] = for {
message <- parse(input).leftMap(ParseErrorT(_)) // would be nice if we did not need the leftMap, following does not work? implicit def toX(parseError: ParseError): ParseErrorT = ParseErrorT(parseError)
recipient <- Either.fromOption(lookupRecipient(message.to), LookupError(s"failed to find recipient ${message.to}")) // would be nice we could have a single lookup for all entries...
} yield (message, recipient)
def process(inputType: Either[ProcessError, (Message, Recipient)]): Unit = inputType match {
case Right((Message(from, to, body), Recipient(_, active))) => println(s"we found a valid message from ${from} to ${to}")
case Left(ParseErrorT(pe)) => println(s"we could not parse the input: ${pe.input}")
case Left(LookupError(msg)) => println(msg)
}
}
package be.icteam.playground
import cats._
import cats.instances._
import cats.data._
import cats.implicits._
object App {
def main(args: Array[String]): Unit = {
val inputs = List("Tim;Thomas;Fool", "Tim;Mathias;Bro", "Tim;Mike;Go Away", "Tim;")
inputs.map(prepareForProcessing(buildRecipientLookupMap(inputs))).foreach(process)
}
/* all related to the parsing */
case class Message(from: String, to: String, body: String)
case class ParseError(input: String, errorMessage: String)
def parse(input: String): Either[ParseError, Message] = input.split(";", -1) match {
case Array(from, to, body) => Message(from, to, body).asRight[ParseError]
case _ => ParseError(input, s"Could not parse the following input: '${input}'").asLeft[Message]
}
/* all related to enrichment/lookup */
case class Recipient(recipientName: String, active: Boolean)
def lookupRecipient(recipientName: String): Option[Recipient] =
if (recipientName.equalsIgnoreCase("Mike"))
None
else
Some(Recipient(recipientName, true))
def lookupRecipients(recipientNames: List[String]): Map[String, Recipient] =
recipientNames.map(x => (x, lookupRecipient(x))).filter(x => x._2.isDefined).map(x => (x._1, x._2.get)).toMap
/* all related to the processing */
sealed trait ProcessingError
case class ProcessingParseError(pe: ParseError) extends ProcessingError
case class ProcessingLookupError(msg: String) extends ProcessingError
def parseforProcessing(input: String): Either[ProcessingParseError, Message] =
parse(input).leftMap(ProcessingParseError(_))
def buildRecipientLookupMap(inputs: List[String]): Map[String, Recipient] = {
val recipientNames = inputs.map(parse).foldRight(List.empty[Message])((result, acc) => result.toList ++ acc).map(_.to)
lookupRecipients(recipientNames)
}
def lookupRecipientForProcessing(recipientsByName: Map[String, Recipient], message: Message): Either[ProcessingLookupError, Recipient] =
Either.fromOption(recipientsByName.get(message.to), ProcessingLookupError(s"failed to find recipient ${message.to}"))
def prepareForProcessing(recipientsByName: Map[String, Recipient])(input: String): Either[ProcessingError, (Message, Recipient)] = for {
message <- parseforProcessing(input)
recipient <- lookupRecipientForProcessing(recipientsByName, message)
} yield (message, recipient)
def process(inputType: Either[ProcessingError, (Message, Recipient)]): Unit = inputType match {
case Right((Message(from, to, body), Recipient(_, active))) => println(s"we found a valid message from ${from} to ${to}")
case Left(ProcessingParseError(pe)) => println(s"we could not parse the input: ${pe.input}")
case Left(ProcessingLookupError(msg)) => println(msg)
}
}
package be.icteam.playground
import be.icteam.playground.Lookup.Recipient
import be.icteam.playground.Parser.{Message, ParseError}
import cats.implicits._
object App {
def main(args: Array[String]): Unit = {
val inputs = List("Tim;Thomas;Fool", "Tim;Mathias;Bro", "Tim;Mike;Go Away", "Tim;")
Processor.processInputs(inputs)
}
}
object Parser {
case class Message(from: String, to: String, body: String)
case class ParseError(input: String, errorMessage: String)
def parse(input: String): Either[ParseError, Message] = input.split(";", -1) match {
case Array(from, to, body) => Message(from, to, body).asRight[ParseError]
case _ => ParseError(input, s"Could not parse the following input: '${input}'").asLeft[Message]
}
}
object Lookup {
case class Recipient(recipientName: String, active: Boolean)
def lookupRecipient(recipientName: String): Option[Recipient] =
if (recipientName.equalsIgnoreCase("Mike"))
None
else
Some(Recipient(recipientName, true))
// in case this becomes too expensive, we can memoize it...
def lookupRecipients(recipientNames: List[String]): Map[String, Recipient] =
recipientNames.map(x => (x, lookupRecipient(x))).filter(x => x._2.isDefined).map(x => (x._1, x._2.get)).toMap
}
object Processor {
sealed trait ProcessingError
case class ProcessingParseError(pe: ParseError) extends ProcessingError
case class ProcessingLookupError(msg: String) extends ProcessingError
def processInputs(inputs: List[String]): Unit =
prepareMessagesForProcessing(inputs).foreach(process)
def prepareMessagesForProcessing(inputs: List[String]): List[Either[ProcessingError, (Message, Recipient)]] = {
val parseResults = inputs.map(parseMessage)
val recipientsByName = buildRecipientLookupMap(parseResults)
parseResults.map(enrichWithRecipient(recipientsByName))
}
def parseMessage(input: String): Either[ProcessingParseError, Message] =
Parser.parse(input).leftMap(ProcessingParseError(_))
def buildRecipientLookupMap(parseResults: List[Either[ProcessingParseError, Message]]): Map[String, Recipient] = {
val recipientNames = parseResults.foldRight(List.empty[Message])((result, acc) => result.toList ++ acc).map(_.to)
Lookup.lookupRecipients(recipientNames)
}
def enrichWithRecipient(recipientsByName: Map[String, Recipient])(parseResult: Either[ProcessingParseError, Message]): Either[ProcessingError, (Message, Recipient)] = for {
message <- parseResult
recipient <- lookupRecipientForProcessing(recipientsByName, message)
} yield (message, recipient)
def lookupRecipientForProcessing(recipientsByName: Map[String, Recipient], message: Message): Either[ProcessingLookupError, Recipient] =
Either.fromOption(recipientsByName.get(message.to), ProcessingLookupError(s"failed to find recipient ${message.to}"))
def process(inputType: Either[ProcessingError, (Message, Recipient)]): Unit = inputType match {
case Right((Message(from, to, body), Recipient(_, active))) => println(s"we found a valid message from ${from} to ${to}")
case Left(ProcessingParseError(pe)) => println(s"we could not parse the input: ${pe.input}")
case Left(ProcessingLookupError(msg)) => println(msg)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment