softwaremill / macwire

Zero-cost, compile-time, type-safe dependency injection library.
https://softwaremill.com/open-source/
Apache License 2.0
1.28k stars 76 forks source link

Wire by class name #5

Closed yudao closed 10 years ago

yudao commented 10 years ago

Hi!

Is it possible to wire a class/object with his name for pure IoC?

Thanks a lot.

adamw commented 10 years ago

I think it's the only way :) But you probably mean something different - maybe you could give an example?

yudao commented 10 years ago

I misspoke :)

Here an example:

val configClass:String = "DatabaseAccess"

class DatabaseAccess()
class SecurityFilter()
class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)
class UserStatusReader(userFinder: UserFinder)

trait UserModule {
    import com.softwaremill.macwire.MacwireMacros._

    lazy val theDatabaseAccess   = wire[configClass]
    lazy val theSecurityFilter   = wire[SecurityFilter]
    lazy val theUserFinder       = wire[UserFinder]
    lazy val theUserStatusReader = wire[UserStatusReader]
}

In fact, I want to configure a class from a config file (yaml, json, properties, ...) and inject it in an another class/object.

My goal is to let a developer to create his own class extended by my trait and instantiate his class in my system (like a plugin for example).

Thanks @adamw

adamw commented 10 years ago

Ah I understand :) Currently that's not possible - wire[] does the wiring at compile time - so it obviously can't know what the actual class in configClass will be.

However, that would be a very good extension. There's already a way to create an InstanceLookup map - for web framework integration - so this could be used to do runtime-wiring as well.

I'll try to add this to the next release.

yudao commented 10 years ago

@adamw Thanks a lot, it will be very great for my open source project :+1:

yudao commented 10 years ago

Hi @adamw,

I don't know where are you with this plugin, but I've tested a way that is working.

trait DatabaseConnector
class MysqlDatabaseConnector extends DatabaseConnector
class MyApp {
  val test: String = "MysqlDatabaseConnector"
  val databaseConnector = Class.forName(test).newInstance().asInstanceOf[AnyRef]
}

class Test {
    def run {
        import MacwireMacros._
        val instanceMap = valsByClass(new MyApp)
        val instanceLookup = InstanceLookup(instanceMap)
        println(instanceLookup.lookup(classOf[DatabaseConnector]))
        // => List(MysqlDatabaseConnector@6e820a0c)
    }
}

But I prefer to ask you if it's a good way and if will working with more complex classes/objects?

Thanks a lot.

adamw commented 10 years ago

Definitely, that's the way you could do it now (and the way the Play integration is done). What I hope to do here (one day) is make the API a bit nicer, but the basic idea will remain the same.

yudao commented 10 years ago

Thx a lot @adamw I'll do it this way until you change your API.