jam
jam copied to clipboard
Incredibly simple DI Scala library.
Jam 
Jam is an incredibly simple DI Scala library.
Essential differences from macwire:
- is simpler, faster, and more predictable
- supports Scala 3, Scala JS, Scala Native
- can inject arguments recursively
- searches candidates in
this
Quick start
Latest stable jam dependency:
"com.github.yakivy" %% "jam-core" % "0.1.0"
Usage example:
class DatabaseAccess()
class SecurityFilter(databaseAccess: DatabaseAccess)
class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)
class UserStatusReader(userFinder: UserFinder)
trait UserModule {
val singletonDatabaseAccess = jam.brewRec[DatabaseAccess]
val userStatusReader = jam.brewRec[UserStatusReader]
}
Macro output:
trait UserModule {
val singletonDatabaseAccess = new DatabaseAccess()
val userStatusReader = new UserStatusReader(
new UserFinder(
singletonDatabaseAccess,
new SecurityFilter(singletonDatabaseAccess)
)
)
}
Brew types
jam.brew- injects constructor arguments if they are provided inthis, otherwise throws an errorjam.brewRec- injects constructor arguments if they are provided inthisor recursively brews themjam.brewWith- injects lambda arguments if they are provided inthis, otherwise throws an error, especially useful when the constructor cannot be resolved automatically:
class PasswordValidator(databaseAccess: DatabaseAccess, salt: String)
object PasswordValidator {
def create(databaseAccess: DatabaseAccess): PasswordValidator =
new PasswordValidator(databaseAccess, "salt")
}
trait PasswordValidatorModule extends UserModule {
val passwordValidator = jam.brewWith(PasswordValidator.create _)
}
jam.brewFrom- injects constructor arguments if they are provided inselfargument, otherwise throws an error:
class QuotaChecker(databaseAccess: DatabaseAccess)
trait QuotaCheckerModule {
object ResolvedUserModule extends UserModule
val quotaChecker = jam.brewFrom[QuotaChecker](ResolvedUserModule)
}
Implementation details
- injection candidates are being searched in
thisinstance, so to provide an instance for future injection, you need to make it a member ofthis. Examples:
trait A {
val a = new A
...brewing //val a will be used
}
val container = new {
val a = new A
...brewing //val a will be used
}
trait A {
def b(): String = {
val a = new A
...brewing //val a will be ignored
}
}
trait A {
val a1 = new A
{
val a2 = new A
...brewing //val a1 will be used
}
}
valmember works like a singleton provider (instance will be reused for all injections inthisscore),defmember works like a prototype provider (one method call per each injection)- library injects only non-implicit constructor arguments; implicits will be resolved by the compiler
- jam is intended to be minimal; features like scopes or object lifecycles should be implemented manually
Changelog
0.1.0:
- fix import ambiguity:
jam.tree.brewwas renamed tojam.brewRec - fix method overload ambiguity:
jam.brew(f)was renamed tojam.brewWith(f) - allow passing an instance to brew from (instead of
this):jam.brewFrom - various refactorings and cleanups