Skip to content

Commit d1dacba

Browse files
committed
more docs
1 parent 622cba2 commit d1dacba

File tree

3 files changed

+125
-7
lines changed

3 files changed

+125
-7
lines changed

shared/src/main/scala/com/github/rssh/appcontext/AppContext.scala

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,61 @@ package com.github.rssh.appcontext
22

33
import scala.quoted.*
44

5+
/**
6+
* Annotation to mark class as a key for component cache.
7+
* Useful for cases when we have multiple implementation of some generic component.
8+
* i.e.
9+
* <code>
10+
* @appContextCacheClass[UserRepository]
11+
* class UserRepositoryTestImpl(using AppContextProviders[...]) extends UserRepository {
12+
* ...
13+
* }
14+
* </code>
15+
* @tparam T
16+
*/
517
class appContextCacheClass[T] extends scala.annotation.StaticAnnotation
618

719

8-
20+
/**
21+
* Application context is a way, to resolve components dependencies.
22+
* Each component should provide an AppContextProvider, and
23+
* AppContext[Component] will instantiate this components with dependencies.
24+
*/
925
object AppContext {
1026

11-
27+
/**
28+
* Get component from application context.
29+
* For this component should have an implicit AppContextProvider.
30+
* @see AppContextProvider
31+
* @tparam T - component to instantiate
32+
* @return
33+
*/
1234
def apply[T](using AppContextProvider[T]): T =
1335
summon[AppContextProvider[T]].get
1436

37+
/**
38+
* Type for component cache.
39+
* By convention, if component depends from cache then it's AppContextProvider should check
40+
* if this component is already in cache and instantiate new (and update cache) only if it's not found.
41+
*/
1542
opaque type Cache = AppContextCacheMap[String, Any]
1643

44+
/**
45+
* Type for cache key.
46+
* By convention, usually this is a type.show.
47+
*/
1748
opaque type CacheKey[T] = String
1849

50+
/**
51+
* Create empty cache.
52+
* @return empty cache
53+
*/
1954
def newCache: Cache = AppContextCacheMap.empty
2055

56+
2157
inline def cacheKey[T] = ${ cacheKeyImpl[T] }
2258

23-
def cacheKeyImpl[T:Type](using Quotes): Expr[CacheKey[T]] = {
59+
private def cacheKeyImpl[T:Type](using Quotes): Expr[CacheKey[T]] = {
2460
import quotes.reflect.*
2561
val annotationClass = TypeRepr.of[appContextCacheClass[?]]
2662
//val keyNameExpr = TypeRepr.of[T].classSymbol.get.annotations.find(_.tpe <:< annotationClass) match
@@ -45,16 +81,44 @@ object AppContext {
4581
def customCacheKey[T](key: String): CacheKey[T] = key
4682

4783
extension (c: Cache)
48-
84+
85+
/**
86+
* Get component from cache.
87+
* @tparam T - component type
88+
* @return Some(component) or None if component is not found.
89+
*/
4990
inline def get[T]: Option[T] =
5091
c.get(cacheKey[T]).asInstanceOf[Option[T]]
51-
92+
93+
/**
94+
* Get component from cache or create new one and update cache.
95+
* @param value - function to create new component
96+
* @tparam T - component type
97+
* @return just created or cached component
98+
*/
5299
inline def getOrCreate[T](value: => T): T =
53100
c.getOrElseUpdate(cacheKey[T], value).asInstanceOf[T]
54-
101+
102+
/**
103+
* Put component to cache, replacing old if needed
104+
* @param value
105+
* @tparam T
106+
*/
55107
inline def put[T](value: T): Unit =
56108
c.put(cacheKey[T], value)
109+
110+
/**
111+
* Modify component in cache.
112+
* @param f - function to remap
113+
* @tparam T - component type
114+
*/
115+
inline def modify[T](f: Option[T] => Option[T]): Unit =
116+
c.updateWith(cacheKey[T])(v => f(v.asInstanceOf[Option[T]]))
57117

118+
/**
119+
* Remove component from cache.
120+
* @tparam T - component type
121+
*/
58122
inline def remove[T]: Unit =
59123
c.remove(cacheKey[T])
60124

shared/src/main/scala/com/github/rssh/appcontext/AppContextProvider.scala

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,42 @@ package com.github.rssh.appcontext
44
import scala.compiletime.*
55
import scala.quoted.*
66

7+
8+
/**
9+
* AppContextProvider is a type class that provides a value of type T
10+
*
11+
* When we have AppContextProvider for a type T, then we can resolve T
12+
* for dependency injection, using AppContext[T] syntax.
13+
* <code>
14+
* AppContext[T]
15+
* </code>
16+
*/
717
trait AppContextProvider[T] {
818
def get: T
9-
1019
}
1120

1221

1322
object AppContextProvider {
1423

24+
/**
25+
* Create AppContextProvider for a value of type T
26+
*
27+
* @param value the value of type T
28+
* @return AppContextProvider for the value
29+
*/
1530
def of[T](value: T): AppContextProvider[T] = new AppContextProvider[T]:
1631
override def get: T = value
1732

33+
/**
34+
* Create default AppContextProvider for a implicit value of type T.
35+
*/
1836
given ofGiven[T](using T): AppContextProvider[T] with {
1937
def get: T = summon[T]
2038
}
2139

40+
/**
41+
* Create AppContextProvider for T from a set of AppContextProviders[Xs], where Xs is a tuple of types containing T.
42+
*/
2243
given fromProviders[Xs <: NonEmptyTuple, X, N <: Int](using AppContextProvidersSearch[Xs], TupleIndex.OfSubtype[Xs, X, N]): AppContextProvider[X] =
2344
summon[AppContextProvidersSearch[Xs]].getProvider[X, N]
2445

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.github.rssh.appcontext
2+
3+
class AppContextCacheTest extends munit.FunSuite {
4+
5+
trait MyTrait {
6+
def name: String
7+
}
8+
9+
class OtherClass
10+
11+
test("AppContextCache") {
12+
val cache = AppContext.newCache
13+
cache.put(1)
14+
cache.put[String]("AAAA")
15+
val myTrait = cache.getOrCreate(new MyTrait {def name="myTrait"})
16+
cache.put("QQQ")
17+
18+
19+
assertEquals(cache.get[Int], Some(1))
20+
assertEquals(cache.get[String], Some("AAAA"))
21+
assertEquals(cache.get[java.lang.String], Some("QQQ"))
22+
assertEquals(cache.get[MyTrait].map(_.name), Some("myTrait") )
23+
assertEquals(cache.get[OtherClass], None)
24+
25+
cache.modify[Int]{
26+
case Some(x) => Some(x+1)
27+
case None => Some(0)
28+
}
29+
assertEquals(cache.get[Int], Some(2))
30+
31+
}
32+
33+
}

0 commit comments

Comments
 (0)