Skip to content

Commit 820de9c

Browse files
committedJun 4, 2019
Functions passed as arguments in DistanceApi
1 parent 69673b8 commit 820de9c

File tree

5 files changed

+50
-25
lines changed

5 files changed

+50
-25
lines changed
 

‎caches/no-cache/src/main/scala/com/guizmaii/distances/caches/NoCache.scala

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.guizmaii.distances.caches
22

33
import cats.effect.Async
44
import com.guizmaii.distances.Cache
5+
import com.guizmaii.distances.Cache.CachingF
56
import io.circe.{Decoder, Encoder, Json}
67
import scalacache.{Cache => InnerCache}
78

@@ -12,10 +13,10 @@ object NoCache {
1213
override private[distances] final val innerCache: InnerCache[Json] = null
1314

1415
@inline
15-
override def cachingF[V](keyParts: Any*)(f: => F[V])(
16-
implicit decoder: Decoder[V],
17-
encoder: Encoder[V]
18-
): F[V] = f
16+
override def cachingF[V]: CachingF[F, V] =
17+
new CachingF[F, V] {
18+
override def apply(keys: Any*)(f: F[V])(implicit decoder: Decoder[V], encoder: Encoder[V]): F[V] = f
19+
}
1920
}
2021

2122
}

‎core/src/main/scala/com/guizmaii/distances/Cache.scala

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.guizmaii.distances
22

33
import cats.effect.Async
4+
import com.guizmaii.distances.Cache.CachingF
45
import io.circe.{Decoder, Encoder, Json}
56
import scalacache.{Cache => InnerCache}
67

@@ -14,9 +15,19 @@ abstract class Cache[F[_]: Async](ttl: Option[Duration]) {
1415
private[distances] val innerCache: InnerCache[Json]
1516

1617
// TODO Jules: An optimization is possible when there's a cache miss because in that case the deserialization is useless.
17-
def cachingF[V](keyParts: Any*)(f: => F[V])(implicit decoder: Decoder[V], encoder: Encoder[V]): F[V] =
18-
innerCache
19-
.cachingF(keyParts: _*)(ttl)(f.map(encoder.apply))
20-
.flatMap(json => Async[F].fromEither(decoder.decodeJson(json)))
18+
def cachingF[V]: CachingF[F, V] =
19+
new CachingF[F, V] {
20+
override def apply(keys: Any*)(f: F[V])(implicit decoder: Decoder[V], encoder: Encoder[V]): F[V] =
21+
innerCache
22+
.cachingF(keys: _*)(ttl)(f.map(encoder.apply))
23+
.flatMap(json => Async[F].fromEither(decoder.decodeJson(json)))
24+
}
25+
}
26+
27+
object Cache {
28+
trait Function2I2[F[_], V, I1, I2] {
29+
def apply(keys: Any*)(f: F[V])(implicit i1: I1, i2: I2): F[V]
30+
}
2131

32+
type CachingF[F[_], V] = Function2I2[F, V, Decoder[V], Encoder[V]]
2233
}

‎core/src/main/scala/com/guizmaii/distances/DistanceApi.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import java.time.Instant
55
import cats.effect.Async
66
import cats.kernel.Semigroup
77
import cats.temp.par.Par
8+
import com.guizmaii.distances.Cache.CachingF
9+
import com.guizmaii.distances.DistanceProvider.DistanceF
810
import com.guizmaii.distances.Types._
911

1012
import scala.collection.breakOut
1113

12-
class DistanceApi[F[_]: Async: Par](distanceProvider: DistanceProvider[F], cache: Cache[F]) {
14+
class DistanceApi[F[_]: Async: Par](distanceF: DistanceF[F], cachingF: CachingF[F, Distance]) {
1315

1416
import DistanceApi._
1517
import cats.implicits._
@@ -71,19 +73,17 @@ class DistanceApi[F[_]: Async: Par](distanceProvider: DistanceProvider[F], cache
7173
): F[List[((TravelMode, LatLong, LatLong), Distance)]] = {
7274
modes
7375
.parTraverse { mode =>
74-
cache
75-
.cachingF(mode, origin, destination, maybeDepartureTime) {
76-
distanceProvider.distance(mode, origin, destination, maybeDepartureTime)
77-
}
78-
.map((mode, origin, destination) -> _)
76+
cachingF(mode, origin, destination, maybeDepartureTime) {
77+
distanceF(mode, origin, destination, maybeDepartureTime)
78+
}.map((mode, origin, destination) -> _)
7979
}
8080
}
8181

8282
}
8383

8484
object DistanceApi {
85-
final def apply[F[_]: Async: Par](provider: DistanceProvider[F], cacheProvider: Cache[F]): DistanceApi[F] =
86-
new DistanceApi(provider, cacheProvider)
85+
final def apply[F[_]: Async: Par](distanceF: DistanceF[F], cachingF: CachingF[F, Distance]): DistanceApi[F] =
86+
new DistanceApi(distanceF, cachingF)
8787

8888
private[DistanceApi] final val directedPathSemiGroup: Semigroup[DirectedPath] =
8989
new Semigroup[DirectedPath] {

‎core/src/main/scala/com/guizmaii/distances/DistanceProvider.scala

+4
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,7 @@ abstract class DistanceProvider[F[_]: Async] {
1414
maybeDepartureTime: Option[Instant] = None
1515
): F[Distance]
1616
}
17+
18+
object DistanceProvider {
19+
type DistanceF[F[_]] = (TravelMode, LatLong, LatLong, Option[Instant]) => F[Distance]
20+
}

‎tests/src/test/scala/com/guizmaii/distances/DistanceApiSpec.scala

+18-9
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ package com.guizmaii.distances
22

33
import cats.effect.internals.IOContextShift
44
import cats.effect.{Concurrent, ContextShift, IO}
5+
import com.guizmaii.distances.Cache.CachingF
6+
import com.guizmaii.distances.DistanceProvider.DistanceF
57
import com.guizmaii.distances.Types.TravelMode.{Bicycling, Driving}
6-
import com.guizmaii.distances.Types.{Distance, LatLong, PostalCode, _}
8+
import com.guizmaii.distances.Types._
79
import com.guizmaii.distances.caches.CaffeineCache
810
import com.guizmaii.distances.providers.google.{GoogleDistanceProvider, GoogleGeoApiContext, GoogleGeoProvider}
911
import monix.eval.Task
@@ -29,9 +31,11 @@ class DistanceApiSpec extends WordSpec with Matchers with ScalaFutures with Befo
2931
"#distance" should {
3032
"if origin == destination" should {
3133
"not call the provider and return immmediatly Distance.zero" in {
32-
val distanceApi: DistanceApi[IO] = DistanceApi[IO](distanceProviderStub[IO], CaffeineCache(Some(1 days)))
33-
val latLong = LatLong(0.0, 0.0)
34-
val expectedResult = Map((Driving, Distance.zero), (Bicycling, Distance.zero))
34+
val cachingF: CachingF[IO, Distance] = CaffeineCache[IO](Some(1 days)).cachingF[Distance]
35+
val distanceF: DistanceF[IO] = distanceProviderStub[IO].distance
36+
val distanceApi: DistanceApi[IO] = DistanceApi[IO](distanceF, cachingF)
37+
val latLong = LatLong(0.0, 0.0)
38+
val expectedResult = Map((Driving, Distance.zero), (Bicycling, Distance.zero))
3539
distanceApi.distance(latLong, latLong, Driving :: Bicycling :: Nil).unsafeRunSync() shouldBe expectedResult
3640
}
3741
}
@@ -40,9 +44,11 @@ class DistanceApiSpec extends WordSpec with Matchers with ScalaFutures with Befo
4044
"#distanceFromPostalCodes" should {
4145
"if origin == destination" should {
4246
"not call the provider and return immmediatly Distance.zero" in {
43-
val distanceApi: DistanceApi[IO] = DistanceApi[IO](distanceProviderStub[IO], CaffeineCache(Some(1 days)))
44-
val postalCode = PostalCode("59000")
45-
val expectedResult = Map((Driving, Distance.zero), (Bicycling, Distance.zero))
47+
val cachingF: CachingF[IO, Distance] = CaffeineCache[IO](Some(1 days)).cachingF[Distance]
48+
val distanceF: DistanceF[IO] = distanceProviderStub[IO].distance
49+
val distanceApi: DistanceApi[IO] = DistanceApi[IO](distanceF, cachingF)
50+
val postalCode = PostalCode("59000")
51+
val expectedResult = Map((Driving, Distance.zero), (Bicycling, Distance.zero))
4652
distanceApi
4753
.distanceFromPostalCodes(geocoderStub)(postalCode, postalCode, Driving :: Bicycling :: Nil)
4854
.unsafeRunSync() shouldBe expectedResult
@@ -53,8 +59,11 @@ class DistanceApiSpec extends WordSpec with Matchers with ScalaFutures with Befo
5359
"#distances" should {
5460
"pass the same test suite than GoogleDistanceProvider" should {
5561
def passTests[F[+ _]: Concurrent: Par](runSync: F[Any] => Any): Unit = {
56-
val geocoder: GeoProvider[F] = GoogleGeoProvider[F](geoContext)
57-
val distanceApi: DistanceApi[F] = DistanceApi[F](GoogleDistanceProvider[F](geoContext), CaffeineCache(Some(1 days)))
62+
val geocoder: GeoProvider[F] = GoogleGeoProvider[F](geoContext)
63+
val cachingF: CachingF[F, Distance] = CaffeineCache[F](Some(1 days)).cachingF[Distance]
64+
val distanceF: DistanceF[F] = GoogleDistanceProvider[F](geoContext).distance
65+
val distanceApi: DistanceApi[F] = DistanceApi[F](distanceF, cachingF)
66+
5867
"says that Paris 02 is nearest to Paris 01 than Paris 18" in {
5968
val paris01 = runSync(geocoder.geocode(PostalCode("75001"))).asInstanceOf[LatLong]
6069
val paris02 = runSync(geocoder.geocode(PostalCode("75002"))).asInstanceOf[LatLong]

0 commit comments

Comments
 (0)
Please sign in to comment.