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
- is able to 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 inthis
or recursively brews themjam.brewWith
- injects lambda arguments if they are provided inthis
, otherwise throws an error, especially useful when constructor cannot be resolved automatically:
class PasswordValidator(databaseAccess: DatabaseAccess, salt: String)
object PasswordValidator {
def create(databaseAccess: DatabaseAccess): PasswordValidator =
new PasswordValidator(atabaseAccess, "salt")
}
trait PasswordValidatorModule extends UserModule {
val passwordValidator = jam.brewWith(PasswordValidator.create _)
}
jam.brewFrom
- injects constructor arguments if they are provided inself
argument, otherwise throws an error:
class QuotaChecker(databaseAccess: DatabaseAccess)
trait QuotaCheckerModule {
object ResolvedUserModule extends UserModule
val quotaChecker = jam.brewFrom[QuotaChecker](ResolvedUserModule)
}
Implementation details
- injection candidates is being searched in
this
instance, 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
}
}
val
member works like singleton provider (instance will be reused for all injections inthis
score),def
member works like prototype provider (one method call per each injection)- library injects only non implicit constructor arguments, implicits will be resolved by 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.brew
was 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