
org.scala_tools.subcut.inject.Injectable.scala Maven / Gradle / Ivy
The newest version!
package org.scala_tools.subcut.inject
/**
* The trait that provides dependency injection features for a class or object. To use this trait,
* Mix it in to the class or object definition, and then define the abstract bindingModule which holds
* the bindings to be used. There are several ways to provide these binding modules: simple val override
* in the class, mixing in a trait that defines the bindingModule (see BoundToModule), a constructor parameter
* or, perhaps most flexibly, an implicit constructor parameter in a curried parameter list. This last option
* can provide flexible and mostly invisible bindings all the way down an object instance creation chain.
*/
trait Injectable {
val bindingModule: BindingModule
/**
* Inject an instance for the given trait based on the class type required. If there is no binding, this
* method will throw a BindingException. This form is for straight trait injection without an identifying name.
* @return an instance configured by the binding module to use for the given trait.
*/
def inject[T <: Any](implicit m: scala.reflect.Manifest[T]): T =
bindingModule.inject(m.erasure.asInstanceOf[Class[T]], None)
/**
* Inject an instance for the given trait based on the class type required and an ID symbol. If there is no
* matching binding, this method will throw a BindingException. The Symbol provided will be converted to a string
* prior to the lookup, so the symbol is interchangeable with the string version of the same ID, in other words
* 'maxPoolSize and "maxPoolSize" are considered equivalent by the lookup mechanism.
* @param symbol the identifying name to look up for the binding, e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait and ID
*/
def inject[T <: Any](symbol: Symbol)(implicit m: scala.reflect.Manifest[T]): T =
bindingModule.inject(m.erasure.asInstanceOf[Class[T]], Some(symbol.name))
/**
* Inject an instance for the given trait based on the class type required and an ID string. If there is no
* matching binding, this method will throw a BindingException. The string ID is interchangeable with the
* symbol version of the same ID, in other words 'maxPoolSize and "maxPoolSize" are considered equivalent by the
* lookup mechanism.
* @param symbol the identifying name to look up for the binding, e.g. "maxPoolSize"
* @return an instance configured by the binding module to use for the given trait and ID
*/
def inject[T <: Any](name: String)(implicit m: scala.reflect.Manifest[T]): T =
bindingModule.inject(m.erasure.asInstanceOf[Class[T]], Some(name))
/**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject does not need a provided ID symbol or string.
* @param the implToUse from the call site. If it is null, the binding provider will fill it in instead
* @return an instance configured by the binding module to use for the given trait
*/
def injectIfMissing[T <: Any](implToUse: Option[T])(implicit m: scala.reflect.Manifest[T]): T =
if (implToUse != None) implToUse.get
else inject[T]
/**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject takes a symbol ID to use to match the binding.
* @param implToUse from the call site. If it is null, the binding provider will fill it in instead
* @param name binding ID symbol to use - e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait
*/
def injectIfMissing[T <: Any](implToUse: Option[T], name: String)(implicit m: scala.reflect.Manifest[T]): T =
if (implToUse != None) implToUse.get
else inject[T](name)
/**
* Inject an instance for the given trait based on the class type only if there is no instance already provided.
* If no instance is provided (i.e. the existing impl passed in is null) and no binding is available to match, a
* BindingException will be thrown. If an existing impl is provided (not null), then the binding will not be
* used and does not need to be present. This form of the inject takes a string ID to use to match the binding.
* @param implToUse from the call site. If it is null, the binding provider will fill it in instead
* @param name binding ID string to use - e.g. 'maxPoolSize
* @return an instance configured by the binding module to use for the given trait
*/
def injectIfMissing[T <: Any](implToUse: Option[T], symbol: Symbol)(implicit m: scala.reflect.Manifest[T]): T =
if (implToUse != None) implToUse.get
else inject[T](symbol)
/**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes only a trait to match and no ID name.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
def injectIfBound[T <: Any](fn: => T)(implicit m: scala.reflect.Manifest[T]): T = {
bindingModule.injectOptional(m.erasure.asInstanceOf[Class[Any]], None) match {
case None => // must then have a valid impltouse
val implToUse = fn
if (implToUse == null)
throw new IllegalStateException("No binding for %s, so provided impl function may not result in null".format(m.erasure.toString))
implToUse
case Some(instance) => instance.asInstanceOf[T]
}
}
/**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes a symbol ID to use in the binding definition lookup, e.g. 'maxPoolSize.
* @param name symbol ID to be used to identify the matching binding definition.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
def injectIfBound[T <: Any](name: String)(fn: => T)(implicit m: scala.reflect.Manifest[T]): T = {
bindingModule.injectOptional(m.erasure.asInstanceOf[Class[Any]], Some(name)) match {
case None => // must then have a valid impltouse
val implToUse = fn
if (implToUse == null)
throw new IllegalStateException("No binding for %s named %s, so provided impl function may not result in null".format(m.erasure.toString, name))
implToUse
case Some(instance) => instance.asInstanceOf[T]
}
}
/**
* Inject an instance if a binding for that type is defined. If it is not defined, the function provided will
* be used instead to create an instance to be used. This is arguably the most useful and efficient form of
* injection usage, as the typical configuration can be provided at the call site and developers can easily
* see what the "usual" instance is. An alternative binding will only be used if it is defined, e.g. for testing.
* This form of the injector takes a string ID to use in the binding definition lookup, e.g. "maxPoolSize".
* @param name string ID to be used to identify the matching binding definition.
* @param fn a function to be used to return an instance, if there is no binding defined for the desired trait.
* @return an instance that subclasses the trait, either from the binding definitions, or using the provided
* function if no matching binding is defined.
*/
def injectIfBound[T <: Any](symbol: Symbol)(fn: => T)(implicit m: scala.reflect.Manifest[T]): T =
injectIfBound(symbol.name)(fn)
}
/**
* AutoInjectable is identical to Injectable except that if you are using the compiler plugin, the implicit
* bindingModule parameter will be automatically filled in for you. It is provided as a convenient boilerplate buster
* but is distinct from Injectable because sometimes you want to mark something as Injectable but keep the injection
* abstract until later.
*/
trait AutoInjectable extends Injectable
/**
* A trait that can be used to provide the cake-like ability to mix in a trait upon instance creation
* rather than using the implicit parameter. This is less flexible but may still be desired by some
* developers. To use it, simply create a new trait that extends this one, and provide the definition of
* bindingModule to point to a suitable BindingModule. You can then mix this trait in to any other class or
* new composition to provide the injector bindings.
*/
trait BoundToModule {
val bindingModule: BindingModule
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy