Last active
April 25, 2018 09:26
-
-
Save timvw/dd8f7c14e18bd31ccc061dfdc25c915e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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