-
Notifications
You must be signed in to change notification settings - Fork 28
Added support for config file properties injection #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a8fdec5
2f43811
19c29f0
0f639d1
d3a7bdd
48c32c8
0796358
58e9119
8102d1f
4a4e592
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| resolvers += "sbt-idea-repo" at "http://mpeltonen.github.com/maven/" | ||
| resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/" | ||
|
|
||
| addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.2.0") | ||
| addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0") | ||
|
|
||
| resolvers += Classpaths.typesafeResolver | ||
|
|
||
| addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.0.0") | ||
| addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "2.5.0") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,23 +7,39 @@ package com.escalatesoft.subcut.inject | |
| * Time: 11:39 AM | ||
| */ | ||
| import scala.collection._ | ||
| import com.escalatesoft.subcut.inject.config._ | ||
| import com.escalatesoft.subcut.inject.config.Undefined | ||
| import scala.Some | ||
| import com.escalatesoft.subcut.inject.config.Defined | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as before. |
||
|
|
||
| /** | ||
| * The binding key, used to uniquely identify the desired injection using the class and an optional name. | ||
| */ | ||
| private[inject] case class BindingKey[A](m: Manifest[A], name: Option[String]) | ||
|
|
||
|
|
||
| trait ConfigPropertySourceProvider { | ||
| def configPropertySource: ConfigPropertySource | ||
| } | ||
|
|
||
| trait WithoutConfigPropertySource extends ConfigPropertySourceProvider { | ||
| def configPropertySource: ConfigPropertySource = new ConfigPropertySource { | ||
| def getOptional(propertyName: String): ConfigProperty = throw new IllegalStateException("No ConfigPropertySource provided") | ||
| } | ||
| } | ||
|
|
||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid empty lines change. |
||
| /** | ||
| * The main BindingModule trait. | ||
| * Not intended to be used directly, instead extend NewBindingModule with a function to create the bindings | ||
| * (recommended - the result will be immutable) or a MutableBindingModule (not recommended unless you know what | ||
| * you are doing and take on the thread safety responsibility yourself). | ||
| */ | ||
| trait BindingModule { outer => | ||
| trait BindingModule extends ConfigPropertySourceProvider { outer : ConfigPropertySourceProvider => | ||
|
|
||
| /** Abstract binding map definition */ | ||
| def bindings: immutable.Map[BindingKey[_], Any] | ||
|
|
||
| /** | ||
| * Merge this module with another. The resulting module will include all bindings from both modules, with this | ||
| * module winning if there are common bindings (binding override). If you prefer symbolic operators, | ||
|
|
@@ -44,6 +60,8 @@ trait BindingModule { outer => | |
| case notLmip => | ||
| key -> notLmip | ||
| }}).toMap | ||
|
|
||
| def configPropertySource: ConfigPropertySource = outer.configPropertySource | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -66,6 +84,8 @@ trait BindingModule { outer => | |
| def modifyBindings[A](fn: MutableBindingModule => A): A = { | ||
| val mutableBindings = new MutableBindingModule { | ||
| bindings = outer.bindings | ||
|
|
||
| def configPropertySource: ConfigPropertySource = outer.configPropertySource | ||
| } | ||
| fn(mutableBindings) | ||
| } | ||
|
|
@@ -135,8 +155,33 @@ trait BindingModule { outer => | |
| for ((k, v) <- bindings) yield { k.toString + " -> " + v.toString } | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves a mandatory binding for class T for the giving property in the ConfigPropertySource owned by this module. | ||
| * If there is no ConfigPropertySource the binding will fail (use NewBindingModule.newBindingModuleWithConfig to create | ||
| * a config with ConfigPropertySource). | ||
| * | ||
| * @param propertyName The key to use to retrieve the property from the ConfigPropertySource | ||
| * @return The property value converted to the required type by the implicit property converter | ||
| */ | ||
| def injectPropertyMandatory[T <: Any](propertyName: String)(implicit m: scala.reflect.Manifest[T], propertyConverter: ConfigProperty => T): T = | ||
| propertyConverter( configPropertySource.get(propertyName) ) | ||
|
|
||
| /** | ||
| * Retrieves an optional binding for class T for the giving property in the ConfigPropertySource owned by this module. | ||
| * If there is no ConfigPropertySource the binding will fail (use NewBindingModule.newBindingModuleWithConfig to create | ||
| * a config with ConfigPropertySource). | ||
| * | ||
| * @param propertyName The key to use to retrieve the property from the ConfigPropertySource | ||
| * @return An optional property value converted to the required type by the implicit property converter | ||
| */ | ||
| def injectPropertyOptional[T <: Any](propertyName: String)(implicit m: scala.reflect.Manifest[T], propertyConverter: ConfigProperty => T): Option[T] = | ||
| configPropertySource.getOptional(propertyName) match { | ||
| case property@Defined(_, _) => Some(propertyConverter(property)) | ||
| case Undefined(_) => None | ||
| } | ||
| } | ||
|
|
||
|
|
||
| /** | ||
| * A class to create a new, immutable, binding module. In order to work, the constructor of this class | ||
| * takes a function to evaluate, and passes this on to a bindings method which can be used to resolve | ||
|
|
@@ -159,9 +204,21 @@ trait BindingModule { outer => | |
| * you want. The module will be frozen after creation of the bindings, but is mutable for the | ||
| * time you are defining it with the DSL. | ||
| */ | ||
| class NewBindingModule(fn: MutableBindingModule => Unit) extends BindingModule { | ||
| class NewBindingModule(fn: MutableBindingModule => Unit) extends BindingModule with WithoutConfigPropertySource { | ||
| lazy val bindings = { | ||
| val module = new Object with MutableBindingModule with WithoutConfigPropertySource | ||
| fn(module) | ||
| module.freeze().fixed.bindings | ||
| } | ||
| } | ||
|
|
||
| class NewBindingModuleWithConfig(fn: MutableBindingModule => Unit)(implicit configProvider: ConfigPropertySource) extends BindingModule { | ||
| override val configPropertySource = configProvider | ||
|
|
||
| lazy val bindings = { | ||
| val module = new Object with MutableBindingModule | ||
| val module = new Object with MutableBindingModule { | ||
| def configPropertySource: ConfigPropertySource = configProvider | ||
| } | ||
| fn(module) | ||
| module.freeze().fixed.bindings | ||
| } | ||
|
|
@@ -182,9 +239,19 @@ class NewBindingModule(fn: MutableBindingModule => Unit) extends BindingModule { | |
| * bind [QueryService] toSingle { new SlowInitQueryService } | ||
| * } | ||
| * </pre> | ||
| * | ||
| * Use the newBindingModuleWithConfig method if you need to inject from a ConfigPropertySource. That needs a | ||
| * ConfigPropertySource instance to be implicitly available. A version without any binding is available if you | ||
| * are just injecting properties using the injectProperty method | ||
| */ | ||
| object NewBindingModule { | ||
| def newBindingModule(fn: MutableBindingModule => Unit): BindingModule = new NewBindingModule(fn) | ||
|
|
||
| def newBindingModuleWithConfig(fn: MutableBindingModule => Unit)(implicit configProvider: ConfigPropertySource): BindingModule = | ||
| new NewBindingModuleWithConfig(fn)(configProvider) | ||
|
|
||
| def newBindingModuleWithConfig(implicit configProvider: ConfigPropertySource): BindingModule = | ||
| new NewBindingModuleWithConfig( { mutableBindingModule: MutableBindingModule => } )(configProvider) | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -239,6 +306,7 @@ trait MutableBindingModule extends BindingModule { outer => | |
| def fixed: BindingModule = { | ||
| new BindingModule { | ||
| override val bindings = outer._bindings | ||
| def configPropertySource: ConfigPropertySource = outer.configPropertySource | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -576,6 +644,22 @@ trait MutableBindingModule extends BindingModule { outer => | |
| name = None | ||
| } | ||
|
|
||
| /** | ||
| * Bind to a new instance of the provided class for each injection. The value for this instance is read from | ||
| * the ConfigPropertySource available in the BindingModule and converted using an implicitly available converter from | ||
| * ConfigPropertySource (essentially a wrapper for String) to the target type. | ||
| * Converters for the basic types are already available, you need to provide a new one for special types. | ||
| * | ||
| * @param name | ||
| * @param source | ||
| * @param converter | ||
| * @tparam I | ||
| * @return | ||
| */ | ||
| def toProperty[I <: T](name: String)(implicit source: ConfigPropertySource, converter: ConfigProperty => I) { | ||
| toSingle( converter(source.get(name)) ) | ||
| } | ||
|
|
||
| /** | ||
| * Part of the fluent interface in the DSL, identified by provides a name to attach to the binding key so that, | ||
| * in combination with the trait type being bound, a unique key is formed. This form takes a string name, but | ||
|
|
@@ -622,3 +706,4 @@ trait MutableBindingModule extends BindingModule { outer => | |
| // and a parameterized bind method to kick it all off | ||
| def bind[T <: Any](implicit m: scala.reflect.Manifest[T]) = new Bind[T]() | ||
| } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid empty lines changes. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package com.escalatesoft.subcut.inject.config | ||
|
|
||
| object BasicPropertyConversions { | ||
|
|
||
| implicit def _toString(input: ConfigProperty) : String = input.value | ||
| implicit def _toInt(input: ConfigProperty) : Int = augmentString(input.value).toInt | ||
| implicit def _toLong(input: ConfigProperty) : Long = augmentString(input.value).toLong | ||
| implicit def _toDouble(input: ConfigProperty) : Double = augmentString(input.value).toDouble | ||
| implicit def _toFloat(input: ConfigProperty) : Float = augmentString(input.value).toFloat | ||
| implicit def _toByte(input: ConfigProperty) : Byte = augmentString(input.value).toByte | ||
| implicit def _toShort(input: ConfigProperty) : Short = augmentString(input.value).toShort | ||
| } | ||
|
|
||
| import BasicPropertyConversions._ | ||
| trait BasicPropertyConversions { | ||
|
|
||
| implicit def toString(input: ConfigProperty) : String = _toString(input) | ||
| implicit def toInt(input: ConfigProperty) : Int = _toInt(input) | ||
| implicit def toLong(input: ConfigProperty) : Long = _toLong(input) | ||
| implicit def toDouble(input: ConfigProperty) : Double = _toDouble(input) | ||
| implicit def toFloat(input: ConfigProperty) : Float = _toFloat(input) | ||
| implicit def toByte(input: ConfigProperty) : Byte = _toByte(input) | ||
| implicit def toShort(input: ConfigProperty) : Short = _toShort(input) | ||
|
|
||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| package com.escalatesoft.subcut.inject.config | ||
|
|
||
| sealed trait ConfigProperty { | ||
| def name: String | ||
| def value: String | ||
|
|
||
| def valueAs[T](implicit converter: ConfigProperty => T) : Option[T] | ||
| } | ||
|
|
||
| case class Defined(name: String, value: String) extends ConfigProperty { | ||
| def valueAs[T](implicit converter: ConfigProperty => T) : Option[T] = Some( converter(this) ) | ||
| } | ||
|
|
||
| case class Undefined(name: String) extends ConfigProperty { | ||
| def value = throw new IllegalArgumentException(s"Undefined value for property $name") | ||
|
|
||
| def valueAs[T](implicit converter: ConfigProperty => T) : Option[T] = None | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| package com.escalatesoft.subcut.inject.config | ||
|
|
||
| trait ConfigPropertySource { | ||
| def get(propertyName: String) : ConfigProperty = getOptional(propertyName) match { | ||
| case property@Defined(_, value) => property | ||
| case Undefined(_) => throw new IllegalArgumentException(s"Missing property $propertyName in config") | ||
| } | ||
|
|
||
| def getOptional(propertyName: String) : ConfigProperty | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is the "import .....config.Undefined" really needed ? (as you imported the entire package before)