Creating generic update function for Slick 3.1.1 - scala

I have this fucntion:
def updateInvoiceAdminStatusField(id: Int, newAdminStatus: AdminStatus): Future[Int] = {
db.run {
val adminStatus: Query[Rep[AdminStatus], AdminStatus, Seq] = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => invoice.status)
adminStatus.update(newAdminStatus)
}
}
I thought of making it generic:
def updateInvoiceField[T](id: Int, fieldExtractor: (Invoices) => Rep[T], newValue: T): Future[Int] = {
db.run {
val adminStatus = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => {
fieldExtractor(invoice)
})
adminStatus.update(newValue)
}
}
But this does not compile. Can somebody assist?

It's nearly good. With small changes like below it should work:
// I added below T: ColumnType
def updateInvoiceField[T: ColumnType](id: Int, fieldExtractor: (Invoices) => Rep[T], newValue: T): Future[Int] = {
db.run {
val adminStatus = InvoicesTable.filter(invoice => invoice.id === id).map(invoice => {
fieldExtractor(invoice)
})
adminStatus.update(newValue)
}
}
Notice I added this : ColumnType which basically mean you need to have proper implicit in scope - specifically the one that would convert T => ColumnType[T]. That's simply because otherwise T could be anything and Slick wouldn't be able to figure out how to convert it.
So for things like String, Int etc. you obviously have proper conversations already in place (imported from api in profile/driver). For you custom types you would need to use MappedColumnType to have proper conversion supplied. Example below (typesafe ID):
implicit def columnType[T]: BaseColumnType[Id[T]] =
MappedColumnType.base[Id[T], Long](toLong, fromLong)
private def fromLong[T](dbId: Long): Id[T] = Id(dbId)
private def toLong[T](id: Id[T]): Long = id.value

Related

How to propagate context via Kleisli?

Just trying to propagate my tracing context inside Kleisli as it was done originally in the next tutorial.
object TraceLogger {
def log(msg: String): Kleisli[IO, UUID, Unit] = Kleisli { traceId => IO(println(s"[$traceId] $msg")) }
}
trait ServiceStub {
def request(arg: String): Kleisli[IO, UUID, _]
}
trait ClientStub {
def get(arg: String): Kleisli[IO, UUID, _]
}
case class FirstServiceExample(clientStub: ClientStub) extends ServiceStub {
override def request(arg: String): Kleisli[IO, UUID, _] = Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)
requestComputation(context)
}
}
case class FirstClientExample(service: FirstServiceExample) {
def request(): IO[_] = {
val traceId = UUID.randomUUID()
service.request("root!").run(traceId)
}
}
And now I need to run execution:
val exampleClientStub = new ClientStub() {
override def get(arg: String): Kleisli[IO, UUID, _] = Kleisli.ask
}
val exampleClientService = FirstServiceExample(exampleClientStub)
FirstClientExample(exampleClientService).request().unsafeRunSync()
But, unfortunately, I don't see any logs here. Would you kindly help me to find an issue?
TraceLogger.log(arg) This returns an IO which is just a description of computation; it is doing nothing.
And since you just leave that value alone it is equivalent to just having a 1 in the middle of your code, it is simply discarded.
You need to chain your IOs together to create new IOs that represent "do this and then do that", that is basically what the flatMap method does.
Kleisli { (context: UUID) =>
val requestComputation = clientStub.get("calling second service!")
TraceLogger.log(arg)(context) >> // >> is equivalent to flatMap(_ => )
requestComputation(context)
}
(There is probably a better way to write this, I am not used to Kliesli)
Fabio's series on "Programas as Values" may be very useful: https://systemfw.org/archive.html

Scala: generic function taking function with not known arity as argument

I have some functions like these ones (I don't know the arity, I just know that the return is of type Future[T]):
def a(a: Int): Future[String] = { Future("a") }
def b(b: Long): Future[Double] = { Future(1.0) }
I'd like to write a generic function WithLoading that could be used like this:
def a(a: Int): Future[String] = WithLoading { Future("a") }
def b(b: Long, c: Int): Future[Double] = WithLoading { Future(1.0) }
and would do the same result as:
def a(a: Int): Future[String] = { var loading = true; Future("a").map (_ => loading = false) }
def b(b: Long): Future[Double] = { var loading = true; Future(1.0).map (_ => loading = false) }
Is it is possible to do this? And if the answer is yes, could you please give me some advice?
Seems that WithLoading doesn't need to know about arity at all.
def withLoading[T](action: => Future[T]): Future[T] = {
doSmthWithLoading
val triggeredAction = action
val result = triggeredAction.onComplete(r => {
cleanupSmth
})
result
}

Is there something like Map.keySet for a partial function in scala?

More specifically, I have:
case class Key (key: String)
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
def useIt (key: Key, value: String) = {
val meth = myMap(key)
meth(value)
}
def report = {
for (key <- myMap.keySet) // how to do this
println("I support "+key)
}
}
I use it like this:
class concrete extends abstr {
var one: Boolean
def method1(v: String): Unit = ???
def method2(v: String): Unit = ???
def map1: MethodMap = {
case Key("AAA") => method1
}
def map2: MethodMap = {
case Key("AAA") => method2
}
override def myMap: MethodMap = if (one) map1 else map2
}
Of course, this is somewhat simplified, but the report function is necessary.
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the following override def myMap: MethodMap = if (one) map1 else map2.
Any suggestion to refactor my code to support everything is also appreciated.
No. PartialFunction can be defined (and often is) on infinite sets. E.g. what do you expect report to return in these situations:
class concrete2 extends abstr {
def myMap = { case Key(_) => ??? }
}
or
class concrete2 extends abstr {
def myMap = { case Key(key) if key.length > 3 => ??? }
}
? If you have a finite list of values you are interested in, you can do
abstract class abstr {
type MethodMap = PartialFunction[Key, String => Unit]
def myMap: MethodMap // abstract
val keys: Seq[Key] = ...
def report = {
for (key <- keys if myMap.isDefined(key))
println("I support "+key)
}
}
Some history: I first had it implemented using Map but then I changed it to PartialFunction in order to support the last line in second part.
Why? This would work just as well with Map.
In your solution, is there any way to define the domain of the partial function to be the finite set keys
def f: MethodMap = { case key if keys.contains(key) => ... }
Of course, the domain isn't part of the type.

Scala transformations

I am really new in Scala and I would like to ask a simple question.
I have a function that returns Future[Option[T]
def findOne(query: JsObject)(implicit reader: Reads[T]): Future[Option[T]] = {
Logger.debug(s"Finding one: [collection=$collectionName, query=$query]")
collection.find(query).one[T]
}
And I have to return Future[Option[PasswordInfo]].
I tried:
def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = {
val result = find(Json.obj("loginInfo.providerID" -> loginInfo.providerID, "loginInfo.providerKey" -> loginInfo.providerKey))
result.onSuccess{
case something => Future.successful(Some(something).getOrElse(None))
}
}
My class:
case class PersistentPasswordInfo(
loginInfo: LoginInfo,
authInfo: PasswordInfo
) extends TemporalModel {
override var created: Option[DateTime] = _
override var updated: Option[DateTime] = _
override var _id: Option[BSONObjectID] = _
}
you need to map over the Future to get the Option, then map over the Option to get the PersistedPasswordInfo
def find(loginInfo: LoginInfo): Future[Option[PasswordInfo]] = {
val result = find(Json.obj("loginInfo.providerID" -> loginInfo.providerID, "loginInfo.providerKey" -> loginInfo.providerKey))
result.map(opt => opt.map(ppi => ppi.authInfo))
}
Future and Option are both Functors, they implement a method map with something like the following signature (assuming the type arg to the containing Future is A):
def map[B](f: A => B): Future[B]
so the code above changes Future by applying a function Option[PersistentPasswordInfo] => Option[PasswordInfo]
that function is created by applying map to on the contained Option with a function PersistentPasswordInfo => PasswordInfo

Writing custom object JSON as a simple value in Scala (JSON Reads/Writes)

I have a custom data type in Scala:
case class GPID(value: Int) {
// ... other stuff ...
implicit val writesGPID = new Writes[GPID] {
def writes(g: GPID): JsValue = {
Json.obj(
"GPID" -> g.value
)
}
}
implicit val reads: Reads[GPID] = (
(__ \ "GPID").read[Int]
).map(GPID(_))
}
As you can see, it has a reads/writes method, but this result in output like this:
"id":{"GPID":1000}
But, we just want it to serialize/deserialize like a regular Int:
"id":1000
I've been trying to figure out how to rewrite the reads/writes but am not having much luck... Any advice would be appreciated!
Thank you.
I added some validation, amend to your needs.
object GPID {
def unapply(in: Int): Option[GPID] = Option(in).filter(_ > 0).map(apply)
implicit val reads = new Reads[GPID] {
def reads(in: JsValue) =
Option(in)
.flatMap {
case JsNumber(value) => Some(value)
case _ => None
}
.flatMap(num => unapply(num.toInt))
.map(JsSuccess(_, __))
.getOrElse(JsError(__, "validate.error.expected.GPID"))
}
implicit val writes = new Writes[GPID] {
def writes(g: GPID) = JsNumber(g.value)
}
}