Kotlin recommended way of unregistering a listener with a SAM - listener

So i have an interactor which performs an insert operation with Realm and then notifies that the insertion is completed with a RealChangeListener. It is something like this:
fun insertCar(item: Car) {
realm.doInTransaction {
val car = Car(...)
val copy = copyToRealm(car)
copy.addChangeListener(...)
}
}
I can do it this way:
fun insertCar(item: Car, listener: RealmChangeListener<Car>) {
realm.doInTransaction {
val car = Car(...)
val copy = copyToRealm(car)
copy.addChangeListener(listener)
}
}
And access like this:
realmInteractor.insertCar(item, RealmChangeListener {
// do something here
})
But then i have no way of removing this listener
realmInteractor.insertCar(item, RealmChangeListener {
// do something here
it.removeChangeListener(this)
})
this will point to the class its located not the actual listener
I can also do this:
fun insertCar(item: Car, doAfterChange (Car) -> Unit) {
realm.doInTransaction {
val car = Car(...)
val copy = copyToRealm(car)
copy.addChangeListener(RealmChangeListener{
doAfterChange()
})
}
}
But then i have a SAM inside another SAM (too overkill imo)
I can do it like this:
fun insertCar(item: Car, listener: RealmChangeListener<Car>) {
realm.doInTransaction {
val car = Car(...)
val copy = copyToRealm(car)
copy.addChangeListener(listener)
}
}
realmInteractor.insertCar(item, object : RealmChangeListener<Car> {
override fun onChange(element: Car?) {
...
element?.removeChangeListener(this)
}
})
Which works but it is too verbose.
So how do you deal with this and what is considered the best approach?

You can create a generic method to perform a run-once listener. Something along the lines of:
fun <T> createInRealm(objectFactory: () -> T, changeListener: (T) -> Unit) {
realm.doInTransaction {
val obj = objectFactory()
val copy = copyToRealm(obj)
copy.addChangeListener(object : RealmChangeListener<T> {
override fun onChange(element: T) {
changeListener()
element.removeChangeListener(this)
}
}
}
}

Related

How to call function indirectly in Kotlin

Assume I have a mutableMap:
val MM = mutableMapOf()
Now I define a function as a method for it:
MM["testF"] = fun () {
println("WOW")
}
Now I want to call it in another place:
val MMTF = MM["testF"] as Function<*>
MMTF() <-- NOT WORKING
Any help will be appreciated.
This code will print bar
fun main() {
val map = mutableMapOf<String, () -> Any>()
map["foo"] = {
println("bar")
}
run(map["foo"]!!)
}

How to use output from one generator in another generator in Kotest?

Using an example from Clojure's test.check let generator, generate a non-empty list of strings, give that list to another generator to pick a string from, then create a map that contains the string list and the selected string. In Clojure, it looks as follows:
(gen/let [list-of-strings (gen/not-empty (gen/list gen/string))
a-string (gen/element list-of-strings)] ;; use the generated list-of-strings above
{:all-strings list-of-strings
:selected a-string})
Taking io.kotest.property.arbitrary.bind for inspiration, I've tried implementing it as follows, but it doesn't work (Kotlin compiler spitted out "Type inference failed"):
fun <A, B, T: Any> let(genA: Gen<A>, genB: (A) -> Gen<B>, bindFn: (A, B) -> T): Arb<T> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next()
val iterB = genB(a.value).generate(rs).iterator()
val b = iterB.next()
bindFn(a.value, b.value)
}
}
}
Turns out dropping bindFn parameter solves the problem, but the solution looks a little ugly as it needs to return a Pair:
fun <A, B> let(genA: Gen<A>, genBFn: (A) -> Gen<B>): Arb<Pair<A, B>> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next().value
// could combine the following to one line, but split for clarity
val genB = genBFn(a)
val iterB = genB.generate(rs).iterator()
Pair(a, iterB.next().value)
}
}
}
Then with the above, using it looks as follows:
class StringTest : StringSpec({
"element is in list" {
val letGen = let(
Arb.list(Arb.string(), range=1..100), // genA
{ xs -> Arb.element(xs) } // genBFn
)
forAll(letGen) { (xs, x) ->
x in xs
}
}
})
Inspire from above solution and wrote a shorter one
fun <A, B> Gen<A>.then(genFn: (A) -> Gen<B>): Arb<Pair<A, B>> =
arbitrary { rs ->
val first = this.generate(rs).first().value
val second = genFn(first).generate(rs).first().value
Pair(first, second)
}
class StringTest : StringSpec({
"element is in list" {
val dependArb =
Arb.list(Arb.string(), range=1..100).then { Arb.element(it) } // genBFn
forAll(dependArb) { (xs, x) ->
x in xs
}
}
})

Android - How to trigger standalone navigation applications with multiple way points via intent?

Today I'm using Android Intent in the following format to trigger navigation from my application on standalone navigation applications:
Action : "android.intent.action.VIEW"
URI : "google.navigation:q=48.605086,2.367014/48.607231,2.356997"
Component Name of the navigation app : For example Google Maps "com.google.android.apps.maps/com.google.android.maps.MapsActivity"
For example:
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
mapIntent.setPackage("com.google.android.apps.maps");
startActivity(mapIntent);
from : https://developers.google.com/maps/documentation/urls/android-intents
I want to trigger navigation with multiple way points, Is it possible on TomTom Go Mobile, Google Maps, Waze, Here WeGo and Sygic via Intent ?
Can I trigger navigation on the application above and start driving automatically? Without user interaction ?
I tried to trigger the above intent via ADB and do some tweaking by adding "," , ";", "and". Nothing worked.
In order to open the navigation mode in the HERE WeGo app you can use the following function
private fun navigateToDestination(destination: GeoCoordinate) {
try {
val intent = Intent().apply {
action = "com.here.maps.DIRECTIONS"
addCategory(Intent.CATEGORY_DEFAULT)
data = Uri.parse("here.directions://v1.0/mylocation/${destination.latitude},${destination.longitude}")
}
intent.resolveActivity(packageManager)?.let {
startActivity(intent)
}
} catch (t: Throwable) {
Timber.e(t)
}
}
Sygic:
private fun navigateToDestination(destination: GeoCoordinate) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("com.sygic.aura://coordinate|${destination.longitude}|${destination.latitude}|drive"))
intent.resolveActivity(packageManager)?.let {
startActivity(intent)
}
} catch (t: Throwable) {
Timber.e(t)
}
}
Waze:
private fun navigateToDestination(destination: GeoCoordinate) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("waze://?ll=${destination.latitude}, ${destination.longitude}&navigate=yes"))
intent.resolveActivity(packageManager)?.let {
startActivity(intent)
}
} catch (t: Throwable) {
Timber.e(t)
}
}
You can also resolve the installed apps that can be used for navigation and let the user decide which one s/he wants to use:
private fun navigateToDestination(destination: GeoCoordinate) {
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("google.navigation:q=${destination.latitude}, ${destination.longitude}"))
val resolvedPackages = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL)
if (resolvedPackages.isNotEmpty()) {
val packageNames = resolvedPackages.map { it.activityInfo.packageName }
val targetIntents = packageNames.map { packageManager.getLaunchIntentForPackage(it) }
val intentChooser = Intent.createChooser(Intent(), "Choose a navigation app")
intentChooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toTypedArray())
startActivity(intentChooser)
}
} catch (t: Throwable) {
Timber.e(t)
}
}

Do one webservice request only from play framework

I'm new to the play framework generally and how to use it with Scala. I want to build a proxy for big Json objects. I achieved so far that the json is stored in a cache and if it is not there, requested from a webservice.
However when two requests are coming in, targeting the same end point (webservice and path are identicall) only one call should be performed and the other request should wait for the result of the first call. At the moment it is performing a call to the service with every request.
This is my controller:
#Singleton
class CmsProxyController #Inject()(val cmsService: CmsProxyService) extends Controller {
implicit def ec : ExecutionContext = play.api.libs.concurrent.Execution.defaultContext
def header(path: String) = Action.async { context =>
cmsService.head(path) map { title =>
Ok(Json.obj("title" -> title))
}
}
def teaser(path: String) = Action.async { context =>
cmsService.teaser(path) map { res =>
Ok(res).as(ContentTypes.JSON)
}
}
}
This is the service:
trait CmsProxyService {
def head(path: String): Future[String]
def teaser(path: String): Future[String]
}
#Singleton
class DefaultCmsProxyService #Inject()(cache: CacheApi, cmsCaller: CmsCaller) extends CmsProxyService {
private val BASE = "http://foo.com"
private val CMS = "bar/rest/"
private val log = Logger("application")
override def head(path: String) = {
query(url(path), "$.payload[0].title")
}
override def teaser(path: String) = {
query(url(path), "$.payload[0].content.teaserText")
}
private def url(path: String) = s"${BASE}/${CMS}/${path}"
private def query(url: String, jsonPath: String): Future[String] = {
val key = s"${url}?${jsonPath}"
val payload = findInCache(key)
if (payload.isDefined) {
log.debug("found payload in cache")
Future.successful(payload.get)
} else {
val queried = parse(fetch(url)) map { json =>
JSONPath.query(jsonPath, json).as[String]
}
queried.onComplete(value => saveInCache(key, value.get))
queried
}
}
private def parse(fetched: Future[String]): Future[JsValue] = {
fetched map { jsonString =>
Json.parse(jsonString)
}
}
//retrieve the requested value from the cache or from ws
private def fetch(url: String): Future[String] = {
val body = findInCache(url)
if (body.isDefined) {
log.debug("found body in cache")
Future.successful(body.get)
} else {
cmsCaller.call(url)
}
}
private def findInCache(key: String): Option[String] = cache.get(key)
private def saveInCache(key: String, value: String, duration: FiniteDuration = 5.minutes) = cache.set(key, value, 5.minutes)
}
And finally the call to the webservice:
trait CmsCaller {
def call(url: String): Future[String]
}
#Singleton
class DefaultCmsCaller #Inject()(wsClient: WSClient) extends CmsCaller {
import scala.concurrent.ExecutionContext.Implicits.global
//keep those futures which are currently requested
private val calls: Map[String, Future[String]] = TrieMap()
private val log = Logger("application")
override def call(url: String): Future[String] = {
if(calls.contains(url)) {
Future.successful("ok")
}else {
val f = doCall(url)
calls put(url, f)
f
}
}
//do the final call
private def doCall(url: String): Future[String] = {
val request = ws(url)
val response = request.get()
val mapped = mapResponse(response)
mapped.onComplete(_ => cmsCalls.remove(url))
mapped
}
private def ws(url: String): WSRequest = wsClient.url(url)
//currently executed with every request
private def mapResponse(f: Future[WSResponse]): Future[String] = {
f.onComplete(_ => log.debug("call completed"))
f map {res =>
val status = res.status
log.debug(s"ws called, response status: ${status}")
if (status == 200) {
res.body
} else {
""
}
}
}
}
My question is: How can only one call to the webservice beeing executed? Even if there are several requests to the same target. I don't want to block it, the other request (not sure if I use the right word here) shall just be informed that there is already a webservice call on the way.
The request to head and teaser, see controller, shall perform only one call to the webservice.
Simple answer using Scala lazy keyword
def requestPayload(): String = ??? //do something
#Singleton
class SimpleCache #Inject() () {
lazy val result: Future[String] = requestPayload()
}
//Usage
#Singleton
class SomeController #Inject() (simpleCache: SimpleCache) {
def action = Action { req =>
simpleCache.result.map { result =>
Ok("success")
}
}
}
First request will trigger the rest call and all the other requests will use the cached result. Use map and flatMap to chain the requests.
Complicated answer using Actors
Use Actor to queue requests and Cache the result of the first successful request json result. All the other requests will read the result of the first request.
case class Request(value: String)
class RequestManager extends Actor {
var mayBeResult: Option[String] = None
var reqs = List.empty[(ActorRef, Request)]
def receive = {
case req: Request =>
context become firstReq
self ! req
}
def firstReq = {
case req: Request =>
process(req).onSuccess { value =>
mayBeResult = Some(value)
context become done
self ! "clear_pending_reqs"
}
context become processing
}
def processing = {
case req: Request =>
//queue requests
reqs = reqs ++ List(sender -> req)
}
def done = {
case "clear_pending_reqs" =>
reqs.foreach { case (sender, _) =>
//send value to the sender
sender ! value.
}
}
}
handle the case where the first request fails. In the above code block if the first request fails then actor will never go to the done state.
I solved my problem with a synchronization of the cache in the service. I'm not sure if this an elegant solution, but it works for me.
trait SyncCmsProxyService {
def head(path: String): String
def teaser(path: String): String
}
#Singleton
class DefaultSyncCmsProxyService #Inject()(implicit cache: CacheApi, wsClient: WSClient) extends SyncCmsProxyService with UrlBuilder with CacheAccessor{
private val log = Logger("application")
override def head(path: String) = {
log.debug("looking for head ...")
query(url(path), "$.payload[0].title")
}
override def teaser(path: String) = {
log.debug("looking for teaser ...")
query(url(path), "$.payload[0].content.teaserText")
}
private def query(url: String, jsonPath: String) = {
val key = s"${url}?${jsonPath}"
val payload = findInCache(key)
if (payload.isDefined) {
payload.get
}else{
val json = Json.parse(body(url))
val queried = JSONPath.query(jsonPath, json).as[String]
saveInCache(key, queried)
}
}
private def body(url: String) = {
cache.synchronized {
val body = findInCache(url)
if (body.isDefined) {
log.debug("found body in cache")
body.get
} else {
saveInCache(url, doCall(url))
}
}
}
private def doCall(url : String): String = {
import scala.concurrent.ExecutionContext.Implicits.global
log.debug("calling...")
val req = wsClient.url(url).get()
val f = req map { res =>
val status = res.status
log.debug(s"endpoint called! response status: ${status}")
if (status == 200) {
res.body
} else {
""
}
}
Await.result(f, 15.seconds)
}
}
Note that I omitted the traits UrlBuilder and CacheAccessor here because they are trivial.

What should a Play framework implicit val Writes[T] look like for super type?

What do I put instead of ??? so the code will type check? Or is there something else I should be doing? I'm using Play to generate JSON for classes B, C, D that all extend A (Layer), but the code that tries to build the JSON only knows it has an A, not which subtype B, C or D.
class Layer
object Layer {
implicit val layerWrites = new Writes[Layer] {
def writes(x: Layer) = x match {
case a: CloudLayer => ???
case b: VerticalVisibility => ???
case c: SkyClear => ???
}
}
}
case class CloudLayer(coverage: String, base: Int) extends Layer
case class VerticalVisibility(height: Int) extends Layer
case class SkyClear() extends Layer
object CloudLayer {
implicit val cloudLayerWrites = new Writes[CloudLayer] {
def writes(x: CloudLayer) = Json.obj(
"layerType" -> "cloudLayer",
"coverage" -> x.cloudCoverage,
"base" -> x.base * 100
)
}
}
object VerticalVisibility {
implicit val verticalVisibilityWrites = new Writes[VerticalVisibility] {
def writes(x: VerticalVisibility) = Json.obj(
"layerType" -> "verticalVisibility",
"height" -> x.height * 100
)
}
}
object SkyClear {
implicit val skyClearWrites = new Writes[SkyClear] {
def writes(x: SkyClear) = Json.obj( "layerType" -> "skyClear" )
}
}
The easiest solution would be just to remove the implicit modifiers from the instances in the subclasses and then refer to them explicitly:
object Layer {
implicit val layerWrites = new Writes[Layer] {
def writes(x: Layer) = x match {
case a: CloudLayer => CloudLayer.cloudLayerWrites.writes(a)
case b: VerticalVisibility =>
VerticalVisibility.verticalVisibilityWrites.writes(b)
case c: SkyClear => SkyClear.skyClearWrites.writes(c)
}
}
}
You could also just scrap the individual instances and move their contents into the pattern match.
If you're feeling adventurous, Julien Richard-Foy has a pretty neat enhanced version of the Json.writes, etc. macros that works on sealed type hierarchies.