Giter Site home page Giter Site logo

softwaremill / macwire Goto Github PK

View Code? Open in Web Editor NEW
1.3K 60.0 75.0 1.4 MB

Lightweight and Nonintrusive Scala Dependency Injection Library

Home Page: https://softwaremill.com/open-source/

License: Apache License 2.0

Scala 99.06% Java 0.94%
dependency-injection scala macwire

macwire's Introduction

MacWire

Table of Contents

MacWire

Ideas, suggestions, problems, questions Maven Central

MacWire generates new instance creation code of given classes, using values in the enclosing type for constructor parameters, with the help of Scala Macros.

For a general introduction to DI in Scala, take a look at the Guide to DI in Scala, which also features MacWire.

MacWire helps to implement the Dependency Injection (DI) pattern, by removing the need to write the class-wiring code by hand. Instead, it is enough to declare which classes should be wired, and how the instances should be accessed (see Scopes).

Classes to be wired should be organized in "modules", which can be Scala traits, classes or objects. Multiple modules can be combined using inheritance or composition; values from the inherited/nested modules are also used for wiring.

MacWire can be in many cases a replacement for DI containers, offering greater control on when and how classes are instantiated, typesafety and using only language (Scala) mechanisms.

Example usage:

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

trait UserModule {
    import com.softwaremill.macwire._

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

will generate:

trait UserModule {
    lazy val theDatabaseAccess   = new DatabaseAccess()
    lazy val theSecurityFilter   = new SecurityFilter()
    lazy val theUserFinder       = new UserFinder(theDatabaseAccess, theSecurityFilter)
    lazy val theUserStatusReader = new UserStatusReader(theUserFinder)
}

For testing, just extend the base module and override any dependencies with mocks/stubs etc, e.g.:

trait UserModuleForTests extends UserModule {
    override lazy val theDatabaseAccess = mockDatabaseAccess
    override lazy val theSecurityFilter = mockSecurityFilter
}

The core library has no dependencies.

For more motivation behind the project see also these blogs:

How wiring works

For each constructor parameter of the given class, MacWire tries to find a value conforming to the parameter's type in the enclosing method and trait/class/object:

  • first it tries to find a unique value declared as a value in the current block, argument of enclosing methods and anonymous functions.
  • then it tries to find a unique value declared or imported in the enclosing type
  • then it tries to find a unique value in parent types (traits/classes)
  • if the parameter is marked as implicit, it is ignored by MacWire and handled by the normal implicit resolution mechanism

Here value means either a val or a no-parameter def, as long as the return type matches.

A compile-time error occurs if:

  • there are multiple values of a given type declared in the enclosing block/method/function's arguments list, enclosing type or its parents.
  • parameter is marked as implicit and implicit lookup fails to find a value
  • there is no value of a given type

The generated code is then once again type-checked by the Scala compiler.

Factories

A factory is simply a method. The constructor of the wired class can contain parameters both from the factory (method) parameters, and from the enclosing/super type(s).

For example:

class DatabaseAccess()
class TaxDeductionLibrary(databaseAccess: DatabaseAccess)
class TaxCalculator(taxBase: Double, taxDeductionLibrary: TaxDeductionLibrary)

trait TaxModule {
    import com.softwaremill.macwire._

    lazy val theDatabaseAccess      = wire[DatabaseAccess]
    lazy val theTaxDeductionLibrary = wire[TaxDeductionLibrary]
    def taxCalculator(taxBase: Double) = wire[TaxCalculator]
    // or: lazy val taxCalculator = (taxBase: Double) => wire[TaxCalculator]
}

will generate:

trait TaxModule {
    lazy val theDatabaseAccess      = new DatabaseAccess()
    lazy val theTaxDeductionLibrary = new TaxDeductionLibrary(theDatabaseAccess)
    def taxCalculator(taxBase: Double) =
       new TaxCalculator(taxBase, theTaxDeductionLibrary)
}

Factory methods

You can also wire an object using a factory method, instead of a constructor. For that, use wireWith instead of wire. For example:

class A()

class C(a: A, specialValue: Int)
object C {
  def create(a: A) = new C(a, 42)
}

trait MyModule {
  lazy val a = wire[A]
  lazy val c = wireWith(C.create _)
}

lazy val vs. val

It is safer to use lazy vals, as when using val, if a value is forward-referenced, it's value during initialization will be null. With lazy val the correct order of initialization is resolved by Scala.

Recursive wiring

When using wire and a value for a parameter can't be found, an error is reported. wireRec takes a different approach - it tries to recursively create an instance, using normal wiring rules. This allows to explicitly wire only those objects, which are referenced from the code, skipping helper or internal ones.

The previous example becomes:

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

trait UserModule {
    import com.softwaremill.macwire._

    lazy val theUserStatusReader = wireRec[UserStatusReader]
}

and will generate:

trait UserModule {
    lazy val theUserStatusReader = new UserStatusReader(
		new UserFinder(new DatabaseAccess(), new SecurityFilter()))
}

This feature is inspired by @yakivy's work on jam.

Autowire

Warning: autowire is an experimental feature, if you have any feedback regarding its usage, let us know! Future releases might break source/binary compatibility. It is available for Scala 2 only for now.

Dependency: "com.softwaremill.macwire" %% "macrosautocats" % "2.5.9"

In case you need to build an instance from some particular instances and factory methods you can leverage autowire. This feature is intended to integrate with effect-management libraries (currently we support cats-effect).

autowire takes as an argument a list of arguments which may contain:

  • values (e.g. new A())
  • factory methods (e.g. C.create _)
  • factory methods that return cats.effect.Resource or cats.effect.IO (e.g. C.createIO _)
  • cats.effect.Resource (e.g. cats.effect.Resource[IO].pure(new A()))
  • cats.effect.IO (e.g. cats.effect.IO.pure(new A()))

Using the dependencies from the given arguments it creates an instance of the given type. Any missing instances are created using their primary constructor, provided that the dependencies are met. If this is not possible, a compile-time error is reported. In other words, a wireRec is performed, bypassing the instances search phase.

The result of the wiring is always wrapped in cats.effect.Resource. For example:

import cats.effect._

class DatabaseAccess()

class SecurityFilter private (databaseAccess: DatabaseAccess)
object SecurityFilter {
  def apply(databaseAccess: DatabaseAccess): SecurityFilter = new SecurityFilter(databaseAccess)
}

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

object UserModule {
  import com.softwaremill.macwire.autocats._

  val theDatabaseAccess: Resource[IO, DatabaseAccess] = Resource.pure(new DatabaseAccess())

  val theUserStatusReader: Resource[IO, UserStatusReader] = autowire[UserStatusReader](theDatabaseAccess)
}

will generate:

[...]
object UserModule {
  import com.softwaremill.macwire.autocats._

  val theDatabaseAccess: Resource[IO, DatabaseAccess] = Resource.pure(new DatabaseAccess())

  val theUserStatusReader: Resource[IO, UserStatusReader] = UserModule.this.theDatabaseAccess.flatMap(
    da => Resource.pure[IO, UserStatusReader](new UserStatusReader(da, new UserFinder(da, SecurityFilter.apply(da))))
  )
}

Composing modules

Modules (traits or classes containing parts of the object graph) can be combined using inheritance or composition. The inheritance case is straightforward, as wire simply looks for values in parent traits/classes. With composition, you need to tell MacWire that it should look inside the nested modules.

To do that, you can use imports:

class FacebookAccess(userFind: UserFinder)

class UserModule {
  lazy val userFinder = ... // as before
}

class SocialModule(userModule: UserModule) {
  import userModule._

  lazy val facebookAccess = wire[FacebookAccess]
}

Or, if you are using that pattern a lot, you can annotate your modules using @Module, and they will be used when searching for values automatically:

class FacebookAccess(userFind: UserFinder)

@Module
class UserModule { ... } // as before

class SocialModule(userModule: UserModule) {
  lazy val facebookAccess = wire[FacebookAccess]
}

Warning: the @Module annotation is an experimental feature, if you have any feedback regarding its usage, let us know!

Scopes

There are two "built-in" scopes, depending on how the dependency is defined:

  • singleton: lazy val / val
  • dependent - separate instance for each dependency usage: def

MacWire also supports user-defined scopes, which can be used to implement request or session scopes in web applications. The proxy subproject defines a Scope trait, which has two methods:

  • apply, to create a scoped value
  • get, to get or create the current value from the scope

To define a dependency as scoped, we need a scope instance, e.g.:

trait WebModule {
   lazy val loggedInUser = session(new LoggedInUser)

   def session: Scope
}

With abstract scopes as above, it is possible to use no-op scopes for testing (NoOpScope).

There's an implementation of Scope targeted at classical synchronous frameworks, ThreadLocalScope. The apply method of this scope creates a proxy (using javassist); the get method stores the value in a thread local. The proxy should be defined as a val or lazy val.

In a web application, the scopes have to be associated and disassociated with storages. This can be done for example in a servlet filter. To implement a:

  • request scope, we need a new empty storage for every request. The associateWithEmptyStorage is useful here
  • session scope, the storage (a Map) should be stored in the HttpSession. The associate(Map) method is useful here

For example usage see the MacWire+Scalatra example sources.

You can run the example with sbt examples-scalatra/run and going to http://localhost:8080.

Note that the proxy subproject does not depend on MacWire core, and can be used stand-alone with manual wiring or any other frameworks.

Accessing wired instances dynamically

To integrate with some frameworks (e.g. Play 2) or to create instances of classes which names are only known at run-time (e.g. plugins) it is necessary to access the wired instances dynamically. MacWire contains a utility class in the util subproject, Wired, to support such functionality.

An instance of Wired can be obtained using the wiredInModule macro, given an instance of a module containing the wired object graph. Any vals, lazy vals and parameter-less defs (factories) from the module which are references will be available in the Wired instance.

The object graph in the module can be hand-wired, wired using wire, or a result of any computation.

Wired has two basic functionalities: looking up an instance by its class (or trait it implements), and instantiating new objects using the available dependencies. You can also extend Wired with new instances/instance factories.

For example:

// 1. Defining the object graph and the module
trait DatabaseConnector
class MysqlDatabaseConnector extends DatabaseConnector

class MyApp {
    def securityFilter = new SecurityFilter()
    val databaseConnector = new MysqlDatabaseConnector()
}

// 2. Creating a Wired instance
import com.softwaremill.macwire._
val wired = wiredInModule(new MyApp)

// 3. Dynamic lookup of instances
wired.lookup(classOf[SecurityFilter])

// Returns the mysql database connector, even though its type is MysqlDatabaseConnector, which is
// assignable to DatabaseConnector.
wired.lookup(classOf[DatabaseConnector])

// 4. Instantiation using the available dependencies
{
    package com.softwaremill
    class AuthenticationPlugin(databaseConnector: DatabaseConnector)
}

// Creates a new instance of the given class using the dependencies available in MyApp
wired.wireClassInstanceByName("com.softwaremill.AuthenticationPlugin")

Interceptors

MacWire contains an implementation of interceptors, which can be applied to class instances in the modules. Similarly to scopes, the proxy subproject defines an Interceptor trait, which has only one method: apply. When applied to an instance, it should return an instance of the same class, but with the interceptor applied.

There are two implementations of the Interceptor trait provided:

  • NoOpInterceptor: returns the given instance without changes
  • ProxyingInterceptor: proxies the instance, and returns the proxy. A provided function is called with information on the invocation

Interceptors can be abstract in modules. E.g.:

trait BusinessLogicModule {
   lazy val moneyTransferer = transactional(wire[MoneyTransferer])

   def transactional: Interceptor
}

During tests, you can then use the NoOpInterceptor. In production code or integration tests, you can specify a real interceptor, either by extending the ProxyingInterceptor trait, or by passing a function to the ProxyingInterceptor object:

object MyApplication extends BusinessLogicModule {
    lazy val tm = wire[TransactionManager]

    lazy val transactional = ProxyingInterceptor { ctx =>
        try {
            tm.begin()
            val result = ctx.proceed()
            tm.commit()

            result
        } catch {
            case e: Exception => tm.rollback()
        }
    }
}

The ctx is an instance of an InvocationContext, and contains information on the parameters passed to the method, the method itself, and the target object. It also allows to proceed with the invocation with the same or changed parameters.

For more general AOP, e.g. if you want to apply an interceptor to all methods matching a given pointcut expression, you should use AspectJ or an equivalent library. The interceptors that are implemented in MacWire correspond to annotation-based interceptors in Java.

Qualifiers

Sometimes you have multiple objects of the same type that you want to use during wiring. Macwire needs to have some way of telling the instances apart. As with other things, the answer is: types! Even when not using wire, it may be useful to give the instances distinct types, to get compile-time checking.

For that purpose Macwire includes support for tagging via scala-common, which lets you attach tags to instances to qualify them. This is a compile-time only operation, and doesn't affect the runtime. The tags are derived from Miles Sabin's gist.

To bring the tagging into scope, import com.softwaremill.tagging._.

Using tagging has two sides. In the constructor, when declaring a dependency, you need to declare what tag it needs to have. You can do this with the _ @@ _ type constructor, or if you prefer another syntax Tagged[_, _]. The first type parameter is the type of the dependency, the second is a tag.

The tag can be any type, but usually it is just an empty marker trait.

When defining the available instances, you need to specify which instance has which tag. This can be done with the taggedWith[_] method, which returns a tagged instance (A.taggedWith[T]: A @@ T). Tagged instances can be used as regular ones, without any constraints.

The wire macro does not contain any special support for tagging, everything is handled by subtyping. For example:

class Berry()
trait Black
trait Blue

case class Basket(blueberry: Berry @@ Blue, blackberry: Berry @@ Black)

lazy val blueberry = wire[Berry].taggedWith[Blue]
lazy val blackberry = wire[Berry].taggedWith[Black]
lazy val basket = wire[Basket]

Multiple tags can be combined using the andTaggedWith method. E.g. if we had a berry that is both blue and black:

lazy val blackblueberry = wire[Berry].taggedWith[Black].andTaggedWith[Blue]

The resulting value has type Berry @ (Black with Blue) and can be used both as a blackberry and as a blueberry.

Multi Wiring (wireSet)

Using wireSet you can obtain a set of multiple instances of the same type. This is done without constructing the set explicitly. All instances of the same type which are found by MacWire are used to construct the set.

Consider the below example. Let's suppose that you want to create a RockBand(musicians: Set[Musician]) object. It's easy to do so using the wireSet functionality:

trait Musician
class RockBand(musicians: Set[Musician])

trait RockBandModule {
  lazy val singer    = new Musician {}
  lazy val guitarist = new Musician {}
  lazy val drummer   = new Musician {}
  lazy val bassist   = new Musician {}

  lazy val musicians = wireSet[Musician] // all above musicians will be wired together
                                         // musicians has type Set[Musician]

  lazy val rockBand  = wire[RockBand]
}

Limitations

When:

  • referencing wired values within the trait/class/object
  • using multiple modules in the same compilation unit
  • using multiple modules with scopes

due to limitations of the current macros implementation in Scala (for more details see this discussion) to avoid compilation errors it is recommended to add type ascriptions to the dependencies. This is a way of helping the type-checker that is invoked by the macro to figure out the types of the values which can be wired.

For example:

class A()
class B(a: A)

// note the explicit type. Without it wiring would fail with recursive type compile errors
lazy val theA: A = wire[A]
// reference to theA; if for some reason we need explicitly write the constructor call
lazy val theB = new B(theA)

This is an inconvenience, but hopefully will get resolved once post-typer macros are introduced to the language.

Also, wiring will probably not work properly for traits and classes defined inside the containing trait/class, or in super traits/classes.

Note that the type ascription may be a subtype of the wired type. This can be useful if you want to expose e.g. a trait that the wired class extends, instead of the full implementation.

Akka integration

Macwire provides wiring suport for akka through the macrosAkka module. Here you can find example code. The module adds three macros wireAnonymousActor[A], wireActor[A] and wireProps[A] which help create instances of akka.actor.ActorRef and akka.actor.Props.

These macros require an ActoRefFactory (ActorSystem or Actor.context) to be in scope as a dependency. If actor's primary constructor has dependencies - they are required to be in scope as well.

Example usage:

import akka.actor.{Actor, ActorRef, ActorSystem}

class DatabaseAccess()
class SecurityFilter()
class UserFinderActor(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter) extends Actor {
  override def receive: Receive = {
    case m => // ...
  }
}

import com.softwaremill.macwire._
import com.softwaremill.macwire.akkasupport._

val theDatabaseAccess = wire[DatabaseAccess] //1st dependency for UserFinderActor
                                             //it compiles to: val theDatabaseAccess = new DatabaseAccess

val theSecurityFilter = wire[SecurityFilter] //2nd dependency for UserFinderActor
                                             //it compiles to: val theSecurityFilter = new SecurityFilter

val system = ActorSystem("actor-system") //there must be instance of ActoRefFactory in scope

val theUserFinder = wireActor[UserFinderActor]("userFinder")
//this compiles to:
//lazy val theUserFinder = system.actorOf(Props(classOf[UserFinderActor], theDatabaseAccess, theSecurityFilter), "userFinder")

In order to make it working all dependencies created Actor's (UserFinderActor's) primary constructor and instance of the akka.actor.ActorRefFactory must be in scope. In above example this is all true. Dependencies of the UserFinderActor are DatabaseAccess and SecurityFilter and they are in scope. The ActorRefFactory is in scope as well because ActorSystem which is subtype of it is there.

Creating actor within another actor is even simpler than in first example because we don't need to have ActorSystem in scope. The ActorRefFactory is here because Actor.context is subtype of it. Let's see this in action:

class UserStatusReaderActor(theDatabaseAccess: DatabaseAccess) extends Actor {
  val theSecurityFilter = wire[SecurityFilter]

  val userFinder = wireActor[UserFinderActor]("userFinder")
  //this compiles to:
  //val userFinder = context.actorOf(Props(classOf[UserFinderActor], theDatabaseAccess, theSecurityFilter), "userFinder")

  override def receive = ...
}

The difference is that previously macro expanded into system.actorOf(...) and when inside another actor it expanded into context.actorOf(...).

It's possible to create anonymous actors. wireAnonymousActor is for it:

val userFinder = wireAnonymousActor[UserFinderActor]
//this compiles to:
//val userFinder = context.actorOf(Props(classOf[UserFinderActor], theDatabaseAccess, theSecurityFilter))

How about creating akka.actor.Props? It's there and can be achieved by calling wireProps[A]. Wiring only Props can be handy when it's required to setup the Props before passing them to the actorOf(...) method.

Let's say we want to create some actor with router. It can be done as below:

val userFinderProps = wireProps[UserFinderActor] //create Props
  //This compiles to: Props(classOf[UserFinderActor], theDatabaseAccess, theSecurityFilter)
  .withRouter(RoundRobinPool(4)) //change it according requirements
val userFinderActor = system.actorOf(userFinderProps, "userFinder")  //create the actor

How about creating actors which depends on ActorRefs? The simplest way is to pass them as arguments to the constructor. But how to distinguish two actorRefs representing two different actors? They have the same type though.

class DatabaseAccessActor extends Actor { ... }
class SecurityFilterActor extends Actor { ... }
val db: ActorRef = wireActor[DatabaseAccessActor]("db")
val filter: ActorRef = wireActor[SecurityFilterActor]("filter")
class UserFinderActor(databaseAccess: ActorRef, securityFilter: ActorRef) extends Actor {...}
//val userFinder = wireActor[UserFinderActor] wont work here

We can't just call wireActor[UserFinderActor] because it's not obvious which instance of ActorRef is for databaseAccess and which are for securityFilter. They are both of the same type - ActorRef.

The solution for it is to use earlier described qualifiers. In above example solution for wiring may look like this:

sealed trait DatabaseAccess //marker type
class DatabaseAccessActor extends Actor { ... }
sealed trait SecurityFilter //marker type
class SecurityFilterActor extends Actor { ... }

val db: ActorRef @@ DatabaseAccess = wireActor[DatabaseAccessActor]("db").taggedWith[DatabaseAccess]
val filter: ActorRef @@ SecurityFilter = wireActor[SecurityFilterActor]("filter").taggedWith[SecurityFilter]

class UserFinderActor(databaseAccess: ActorRef @@ DatabaseAccess, securityFilter: ActorRef @@ SecurityFilter) extends Actor {...}

val userFinder = wireActor[UserFinderActor]

It is also possible to wire an actor using a factory function. For that, the module provides three additional macros wireAnonymousActorWith, wireActorWith and wirePropsWith. Their usage is similar to wireWith (see Factory methods). For example:

class UserFinderActor(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter) extends Actor { ... }

object UserFinderActor {
  def get(databaseAccess: DatabaseAccess) = new UserFinderActor(databaseAccess, new SimpleSecurityFilter())
}

val theUserFinder = wireActorWith(UserFinderActor.get _)("userFinder")

Installation, using with SBT

The jars are deployed to Sonatype's OSS repository. To use MacWire in your project, add a dependency:

libraryDependencies += "com.softwaremill.macwire" %% "macros" % "2.5.9" % "provided"

libraryDependencies += "com.softwaremill.macwire" %% "macrosakka" % "2.5.9" % "provided"

libraryDependencies += "com.softwaremill.macwire" %% "util" % "2.5.9"

libraryDependencies += "com.softwaremill.macwire" %% "proxy" % "2.5.9"

MacWire is available for Scala 2.12, 2.13, 3 on the JVM and JS.

The macros subproject contains only code which is used at compile-time, hence the provided scope.

The util subproject contains tagging, Wired and the @Module annotation; if you don't use these features, you don't need to include this dependency.

The proxy subproject contains interceptors and scopes, and has a dependency on javassist.

Older 1.x release for Scala 2.10 and 2.11:

libraryDependencies += "com.softwaremill.macwire" %% "macros" % "1.0.7"

libraryDependencies += "com.softwaremill.macwire" %% "runtime" % "1.0.7"

Debugging

To print debugging information on what MacWire does when looking for values, and what code is generated, set the macwire.debug system property. E.g. with SBT, just add a System.setProperty("macwire.debug", "") line to your build file.

Scala.js

Macwire also works with Scala.js. For an example, see here: Macwire+Scala.js example.

Future development - vote!

Take a look at the available issues. If you'd like to see one developed please vote on it. Or maybe you'll attempt to create a pull request?

Migrating from 1.x

  • changed how code is split across modules. You'll need to depend on util to get tagging & Wired, and proxy to get interceptors and scopes
  • tagging moved to a separate package. If you use tagging, you'll need to import com.softwaremill.tagging._
  • removed wireImplicit
  • implicit parameters aren't handled by wire at all (they used to be subject to the same lookup procedure as normal parameters + implicit lookup)

Play 2.4.x

In Play 2.4.x, you can no longer use getControllerInstance in GlobalSettings for injection. Play has a new pattern for injecting controllers. You must extend ApplicationLoader, from there you can mix in your modules.

import controllers.{Application, Assets}
import play.api.ApplicationLoader.Context
import play.api._
import play.api.routing.Router
import router.Routes
import com.softwaremill.macwire._

class AppApplicationLoader extends ApplicationLoader {
  def load(context: Context) = {

    // make sure logging is configured
    Logger.configure(context.environment)

    (new BuiltInComponentsFromContext(context) with AppComponents).application
  }
}

trait AppComponents extends BuiltInComponents with AppModule {
  lazy val assets: Assets = wire[Assets]
  lazy val prefix: String = "/"
  lazy val router: Router = wire[Routes]
}

trait AppModule {
  // Define your dependencies and controllers
  lazy val applicationController = wire[Application]
}

In application.conf, add the reference to the ApplicationLoader.

play.application.loader = "AppApplicationLoader"

For more information and to see the sample project, go to examples/play24

Reference Play docs for more information:

Play 2.5.x

For Play 2.5.x, you must do the same as for Play 2.4.x, except the Logger configuration.

import play.api.LoggerConfigurator
class AppApplicationLoader extends ApplicationLoader {
  def load(context: Context) = {

    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment)
    }
    // ... do the same as for Play 2.4.x
  }
}

Scala3 support

The Scala 3 version is written to be compatible with Scala 2 where possible. Currently there are a few missing features:

For full list of incompatibilities take a look at tests/src/test/resources/test-cases and util-tests/src/test/resources/test-cases .

Commercial Support

We offer commercial support for MacWire and related technologies, as well as development services. Contact us to learn more about our offer!

Copyright

Copyright (C) 2013-2021 SoftwareMill https://softwaremill.com.

macwire's People

Contributors

aaabramov avatar adamw avatar aeons avatar backuitist avatar bitdeli-chef avatar chenrui333 avatar ghik avatar gitter-badger avatar jotomo avatar kaplanbar avatar kciesielski avatar krivachy avatar lolgab avatar mbore avatar mergify[bot] avatar mkljakubowski-ltq avatar mkrzemien avatar mkubala avatar olegych avatar pawelpanasewicz avatar rcirka avatar rcirka12 avatar scala-steward avatar sethtisue avatar szimano avatar taormina avatar tkroman avatar xevix avatar xuwei-k avatar zunder avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

macwire's Issues

handle wiring in methods

Any plans to handle the wire[T] macro in methods ?

for instance this works

@RunWith(classOf[JUnitRunner])
class TranslationSpec extends FlatSpec with Matchers with MockitoSugar {
  val config = TranslationConfig("net.hearthstats.resources.Main", "fr")
  val translation = wire[Translation]

  "A translation" should "handle properly parameters" in {
    translation.t("match.end.vs.named", "a", "b", "win") shouldBe "a vs. b (win)"
  }
}

but this does not

@RunWith(classOf[JUnitRunner])
class TranslationSpec extends FlatSpec with Matchers with MockitoSugar {
 "A translation" should "handle properly parameters" in {
    val config = TranslationConfig("net.hearthstats.resources.Main", "fr")
    val translation = wire[Translation]
    translation.t("match.end.vs.named", "a", "b", "win") shouldBe "a vs. b (win)"
  }
}

Excellent library, I love it and the guide you wrote !

Factories

Hi,

I have been using Injectable factories for a while with google-guice. Hence i'm wondering what support is it in here for that? Otherwise, what alternative route do you usually recommend?

Many thanks,

M

NullPointerException will be thrown if defined custom scope and factories

package my

import com.softwaremill.macwire.Macwire
import com.softwaremill.macwire.scopes.{ProxyingScope, Scope}

import scala.collection.mutable

class Group {
  def helloGroup(): Unit = {
    println("Hello group")
  }
}

object UserServiceFactory {
  type UserService = UserServiceFactory#create
}

class UserServiceFactory(group: Group) {
  case class create() {
    def helloService() = group.helloGroup()
  }
}

class User(userServiceFactory: UserServiceFactory) {
  def helloUser() = userServiceFactory.create().helloService()
}

trait Module extends Macwire {
  lazy val group = projectScope(wire[Group])
  lazy val userServiceFactory = projectScope(wire[UserServiceFactory])
  lazy val user = projectScope(wire[User])
  def projectScope: Scope = new ProjectScope
}

class ProjectScope extends ProxyingScope {
  private val map = mutable.HashMap[String, Any]()
  override def get[T](key: String, createT: => T): T = {
    map.get(key) match {
      case Some(v) => v.asInstanceOf[T]
      case _ => val v = createT
        map.put(key, v)
        v
    }
  }
}

object Main extends App with Module {
  user.helloUser()
}

When run it, it will throw exception:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.softwaremill.macwire.scopes.ProxyingScope$$anon$1.invoke(ProxyingScope.scala:16)
    at my.User_$$_jvst16_0.helloUser(User_$$_jvst16_0.java)
    at my.Main$.delayedEndpoint$my$Main$1(User.scala:48)
    at my.Main$delayedInit$body.apply(User.scala:47)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.App$$anonfun$main$1.apply(App.scala:76)
    at scala.collection.immutable.List.foreach(List.scala:381)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35)
    at scala.App$class.main(App.scala:76)
    at my.Main$.main(User.scala:47)
    at my.Main.main(User.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)
Caused by: java.lang.NullPointerException
    at my.UserServiceFactory$create.helloService(User.scala:20)
    at my.User.helloUser(User.scala:25)
    ... 22 more

One way to fix it, is to add val to parameters of the factories:

class UserServiceFactory(val group: Group) 
class User(val userServiceFactory: UserServiceFactory) 

Any thoughts?

macwire macro's generate "[warn] dead code following this construct" warnings when using the -Ywarn-dead-code compiler flag

When using macwire macro's we noticed a lot of "[warn] dead code following this construct" messages being logged when compiling

It turns out these are coming from the macwire macro.

You can easily reproduce this in the macwire-activator project by adding the following line to build.sbt: scalacOptions ++= Seq("-Ywarn-dead-code")

This results in the following output:

[info] Compiling 16 Scala sources and 1 Java source to /projects/macwire-activator/target/scala-2.11/classes...
[warn] dead code following this construct
[warn] dead code following this construct
[warn] dead code following this construct
[warn] dead code following this construct
[warn] dead code following this construct
[warn] dead code following this construct
[warn] 6 warnings found
[success] Total time: 14 s, completed 29-apr-2015 15:09:39

Play 2.4 example does not compile with Macwire 2.0

I tried to update Play example to use Macwire 2.0, but it fails to compile with the following errors:

[error] /home/guersam/workspace/github/macwire/examples/play24/app/com/softwaremill/play24/AppApplicationLoader.scala:36: Found multiple values of type [slick.driver.H2Driver.api.Database]: [List(db, db)]
[error]   val seed = wire[Seed]
[error]                  ^
[error] /home/guersam/workspace/github/macwire/examples/play24/app/com/softwaremill/play24/AppApplicationLoader.scala:31: Cannot find a value of type: [String]
[error]   lazy val router: Router = wire[Routes] withPrefix "/"
[error]                                 ^
[error] two errors found
[error] (compile:compileIncremental) Compilation failed

It seems that macwire 2.0

  • recognizes overridden (abstract) member as another value
  • does not take secondary constructors into account (even if it's annotated with @javax.inject.Inject())

Can't use multiple tags

When trying to use multiple Tags, the compile throws the error cyclic aliasing or subtyping involving type.

trait NonEmpty
trait Trimmed
case class (name: String @@ NonEmpty @@Trimmed)

@Module annotation retention

More thoughts following #56...

Currently the retention for the @Module annotation has been set to SOURCE, meaning that it will be discarded by the compiler. This allows us to leave the annotation in the macros artifact, but on the other hand makes it impossible, as a library author, to provide annotated modules.
A possible middle ground would be to have 2 annotations, one with SOURCE retention (living in the macros artifact) another with CLASS retention (living in the utils artifact, under a different package). That way, library authors can pick the later if they need to (bringing an extra dependency along the way), whereas application developers can be fine with the SOURCE retention.

http://docs.oracle.com/javase/7/docs/api/java/lang/annotation/RetentionPolicy.html

Support function defs as function values (for factory dependencies)

It would be nice if function defs were automatically converted to function values.

So that with the following class:

class PlatformsPage(store:PlatformStore,platformBackend:(BackendScope[Props,State] => Backend))

You could wire its dependencies with a module like:

trait PlatformsModule {
  lazy val platformsPage = wire[PlatformsPage]
  lazy val platformsStore = wire[PlatformStore]
  def platformsBackend(scope:BackendScope[Props,State]):Backend = new Backend(scope)
}

There are easy alternatives:

lazy val platformsBackend:BackendScope[Props,State] => Backend = new Backend(_)
type BackendFactory = BackendScope[Props,State] => Backend
lazy val platformsBackend:BackendFactory = new Backend(_)

But given that defs are used with MacWire, it seems like it would be nice to support function defs as function value dependencies.

Support parameters in wire[] to override dependencies

(One of the ideas from ScalaDays 2014 - by @matthewfarwell)

The wire[] method could accept parameters, which would be used to wire an instance of the given type; the parameters would take precedence over (override) vals/defs in wider scopes.

Example usage:

class Service(s2: Service2, s3: Service3, config: Map[String, String])

lazy val s2 = wire[Service2]
lazy val s3 = wire[Service3]
lazy val defaultConfig = Map("username" -> "default")

// here the given map would be used instead of the default
lazy val sA = wire[Service](Map("username" -> "admin"))

// here the default config map would be used and an alternative Service2 impl
lazy val sB = wire[Service](new Service2(...))

A parameter which doesn't match any of the constructor parameters of the wired type would result in a compile-time error.

This can be useful for:

  • providing alternative implementations only for a single usage
  • providing configuration values on a per-instance basis

Self-types that are type aliases don't work.

The following does not compile because as of writing, self types that are aliases don't resolve properly into the aliased types (as parents)

trait AProvider {
    def a : A
}

trait BProvider {
    def b : B
}

trait ABProviderImpl extends AProvider with BProvider {
    lazy val a = wire[A]
    lazy val b = wire[B]
}

object ProviderAliasing {
  type AAndB = AProvider with BProvider
}

trait AliasedMultiModule { this: ProviderAliasing.AAndB =>
  lazy val c = wire[C]
}

object TestAliasedMultiple extends AliasedMultiModule with ABProviderImpl

Factories example with two Double args in README does not compile

With Scala 2.11.7, macwire 2.2.2, Scala-IDE, this line from the Factories example:
def taxCalculator(taxBase: Double, taxFreeAmount: Double) = wire[TaxCalculator]
gives the error "Found multiple values of type [Double]"

Is tagging required to make this work ?

Remove wireImplicit

IMO the import feature has made wireImplicit obsolete. It is also IMO not how implicits are supposed to work: implicit values are made for implicit parameters. I'd suggest to remove it as part of the 2.0.0 change.

@Module annotation for automatic import

I'm using the wire-from-import feature quite a lot in this manner:

class ModuleA {
   lazy val serviceA = wire[ServiceA]
   // ...
}

class ModuleB(moduleA: ModuleA) {
   import moduleA._

   lazy val serviceB = wire[ServiceB] // ServiceB depends on ServiceA
}

class ModuleC(moduleA: ModuleA,
              moduleB: ModuleB) {
   import moduleA._
   import moduleB._

  // and so on...
}

As you can see the import statement becomes a bit of a drag...
I'm suggesting the following:

@Module
class ModuleA {
   lazy val serviceA = wire[ServiceA]
   // ...
}

@Module
class ModuleB(moduleA: ModuleA) {  
   lazy val serviceB = wire[ServiceB] // ServiceB depends on ServiceA
}

@Module
class ModuleC(moduleA: ModuleA,
              moduleB: ModuleB) {
  // and so on...
}

By annotating a class @Module we make macwire a little more aggresive in its search for candidates:
it will automatically look at the members of the usual candidates, as if they were imported.
The annotation is compile-time only so that it can live within the macro artefact.

Qualifier support

(One of the ideas from ScalaDays 2014)

Qualifiers are used to distinguish between instances of the same type, and are necessary to be able to support wiring when there's more than one instance of the searched type in the wiring scope.

Currently with MacWire there are two work-arounds for the problem:

  • when qualifiers would have to be used, use manual wiring
  • use descriptive types to differentiate between different values (simple wrapper classes, value classes etc.)

Qualifiers support can be extended in three ways:

  1. support annotation qualifiers, as in CDI, for example. The annotation would have to be present on the usage side (where the dependency should be "injected") and on the declaration side, either on the class or on the val/def where the instance is wired
  2. document and verify that things work as expected the approach using "type qualifiers". E.g. this may take the form of wire[Service1 with RedQualifier], and then at usage-site: class Service2(s1: Service1 with RedQualifiers); and of the simple wrapper-types as well.
  3. #7 may also provide a solution for the problem

I feel that 2. is more Scala-way and that's what should be documented/implemented.

If there's sufficient demand, in fact both 1. and 2. can be documented/implemented independently.

macwire fails to find lazy vals for injection if there is a block comment above them in the publish phase

This is using macwire 0.7.1 and scala 2.11.2.

When we have a code block like this:

/**
  * some comment what this service does.
  */
  lazy val service: Service = MyService

as part of the dependencies, then

  • compiling normally works as expected
  • the publish command, which re-compiles everything, fails to find service in the compilation phase.

The reason is the block comment which seems to interfere with the macros somehow. When the block comment is deleted everything works as expected. Still, this bug is rather annoying when wanting to publish a well-commented library....

publish
[info] Packaging /Users/joe/Documents/git/brokenMacwireParsing/target/scala-2.11/brokenmacwireparsing_2.11-1.0-sources.jar ...
[info] Done packaging.
[info] Packaging /Users/joe/Documents/git/brokenMacwireParsing/target/scala-2.11/brokenmacwireparsing_2.11-1.0.jar ...
[info] Main Scala API documentation to /Users/joe/Documents/git/brokenMacwireParsing/target/scala-2.11/api...
[info] Wrote /Users/joe/Documents/git/brokenMacwireParsing/target/scala-2.11/brokenmacwireparsing_2.11-1.0.pom
[info] Done packaging.
[info] :: delivering :: brokenmacwireparsing#brokenmacwireparsing_2.11;1.0 :: 1.0 :: release :: Fri Oct 10 17:04:11 CEST 2014
[info]  delivering ivy file to /Users/joe/Documents/git/brokenMacwireParsing/target/scala-2.11/ivy-1.0.xml
[debug] Trying to find parameters to create new instance of: [com.example.Hello]
   [debug] Trying to find value [service] of type: [com.example.Service]
      [debug] Looking in the enclosing method
      [debug] Looking in the enclosing class/trait
      [debug] Checking def: [$init$]
      [debug] Checking val: [hello]
      [debug] Looking in parents
      [debug] Checking parent: [AnyRef]
[error] /Users/joe/Documents/git/brokenMacwireParsing/src/main/scala/com/example/Hello.scala:18: Cannot find a value of type: [com.example.Service]
[error]   lazy val hello: Hello = wire[Hello]
[error]  

See the specially created repo reproducing this bug here, including macwire debug output:

https://github.com/jschaul/macwire-bug-demonstration

Compile error on expressing the dependencies via self-types

wire[T] fails to lookup the instance when using self-types (although it works when extending the other trait).

For instance the code block:

class A
class B(a: A)

trait ModuleA {
  lazy val a = wire[A]
}

trait ModuleB {
  self: ModuleA =>
  lazy val b = wire[B]
}

gets the following compile error:

Cannot find a value of type: [~.A]
[error]   lazy val b = wire[B]
[error]                    ^

Parents scope lookup fails

My last changes introduced in parents scope values finder may lead to compilation failure when used with diamond inheritance.

Consider following code:

class Dep
class Service(dep: Dep)
class AnotherService(dep: Dep)

trait A {
  val a = wire[Dep]
}

trait B extends A {
  val service = wire[Service]
}

trait C extends A with B {
  val anotherService = wire[AnotherService]
}

It fails with:

[error] /foo/bar/Baz.scala:14: Found multiple values of type [Dep]: [List(a, a)]
[error]   val anotherService = wire[AnotherService]
[error]                            ^

Problem doesn't occur under 0.7.1

I'll fix this regression ASAP.

Unclear compilation error using valsByClass

I get the following error trying to compile Global.scala for play framework

[error] C:\Trader\app\Global.scala:7: type mismatch;
[error] found : Any
[error] required: AnyRef
[error] val instanceLookup = InstanceLookup(valsByClass(ProdModule))
[error] ^

ProdModule is an object and inherits from other traits. My problem is I cannot understand what causes this error or how to debug it.

Remove wiring from `def` or add `@NotForWiring`

Currently we have this problem:

case class A()
case class B(a: A)

object Test {
    lazy val a: A = wire[A]
    def buildSomeA(i: Int): A = A()
    lazy val b: B = wire[B] // without `val a`, macwire would produce this: `A(buildSomeA)`...
}

Fails with: Found multiple values of type [A]: [List(buildSomeA, a)]

So 2 things:

  • first methods with args are found eligible and they are used without any args (thus producing a compilation error),
  • second we quickly end-up with ambiguities

I have the following use case:

class Stuff(clock: Clock)

trait TestBase {
  lazy val clock: Clock = Time
  def newFrozenClock: Clock
}

class StuffTest extends TestBase {
   @Test def doStuff() { wire[Stuff] } // clock is ambiguous
}

So either we ditch def "support" (I know you've been using it as a "prototype" scope) or we keep it (modulo a little bugfix for parameterized methods) and we introduce another annotation: @NotForWiring. That way things can be disambiguated once for all:

trait TestBase {
  lazy val clock: Clock = Time
  @NotForWiring def newFrozenClock: Clock
}

Support wiring using anonymous function parameters

Right now parameters of the enclosing method are used. It would be great if anonymous function parameters would work as well:

  class A
  class B
  class C(a: A, b: B)

  object module {
    lazy val a = wire[A]
    // doesn't work
    lazy val cf = (b: B) => wire[C]
    // works
    def cf(b: B) = wire[C]
  }

Wire by class name

Hi!

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

Thanks a lot.

Support companion object factory method

Sometimes you want to perform some validation before constructing an object. A good place for that is the method factory on the companion object:

class ParamsNeedValidation private(param1: Param1, param2: Param2)
object ParamsNeedValidation {
  def apply(param1: Param1, param2: Param2) : ParamsNeedValidation = {
     // validate param1 & param2
     new ParamsNeedValidation(param1, param2)
  }
}

When the primary constructor is found to be private, macwire could give the companion object a chance and IF it has a single apply method, try it.
So we would end up with this:

lazy val pnv = wire[ParamsNeedValidation]

Turned into

lazy val pnv = ParamsNeedValidation(param1, param2)

If the companion object has more than one apply method, a compilation error is returned:

ParamsNeedValidation constructor is private and its companion object has more than one apply method:
- apply(param1, param2)
- apply(param3)

If the companion object has no apply method, then we would fail with the usual:

ParamsNeedValidation constructor is private

Release 2.0

This includes the actual release, updating README.md and di-in-scala

Macwire fails to wire dependencies declared as implicit

Consider the following code:

https://gist.github.com/maciej/402d82cceb580a95eab9

It results in the following compile-time error:

Error:(9, 26) could not find implicit value for parameter otherDependency: ServiceDependency
  lazy val service = wire[Service]
                         ^
Error:(9, 26) not enough arguments for constructor Service: (implicit otherDependency: ServiceDependency)Service.
Unspecified value parameter otherDependency.
  lazy val service = wire[Service]
                         ^

Removing the implicit modifier at line 5 results in a successful combination and wiring.

(MacWire 0.7.1, Scala 2.11.2)

wireSet[] method to get all dependencies of the given type

(One of the ideas from ScalaDays 2014 by @PSanjuanSzklarz)

Sometimes it is useful to get a set of all dependencies with a given type.

For example:

trait Service
class Service1(...) extends Service
class Service2(...) extends Service
class Service3(...) extends Service

lazy val s1 = wire[Service1]
lazy val s2 = wire[Service2]
lazy val s3 = wire[Service3]

// has type Set[Service] contains s1, s2, s3
lazy val services = wireSet[Service]

compiler warning with wiredInModule

When using wiredInModule, the compiler emits a warning:

method any2ArrowAssoc in trait DeprecatedPredef is deprecated: Use `ArrowAssoc`
[12:56:21][Step 1/1] [warn]   val instanceLookup = wiredInModule(RuntimeEnvironment)

I use the scala compiler version 2.11.2

release for 2.11.4 ?

Would you please make a new release for scala 2.11.4 ?

I would also appreciate to use the #9 feature, at the moment I had to duplicate this code in my project to use it.

Thanks !

Play 2.4 example with PlaySlick

Hey. Thanks a million for the Play 2.4 example with Slick. Since PlaySlick is recommended by Play itself I want to use that instead.

I actually got it working with PlaySlick by modifying DatabaseModule to create a DatabaseConfig rather than a db directly. My DAO then has a dependency on a DatabaseConfig which it uses to pull the db that it will end up using. Among other things, this allows me to not marry myself to H2, instead importing the appropriate driver from config.

// DaoModule.scala
trait DaoModule {
  def dbConfig: DatabaseConfig[JdbcProfile]

  lazy val myDao = wire[MyDao]
}

// DatabaseModule.scala
trait DatabaseModule extends DBComponents with HikariCPComponents with SlickComponents {
  def dbApi: DBApi
  // NOTE: manual instantiation
  lazy val dbConfig = api.dbConfig[JdbcProfile](DbName("default"))
}

// MyDao.scala
trait MyDaoLike {
  def all: Future[Seq[MyModel]]
}

class MyDao(dbConfig: DatabaseConfig[JdbcProfile]) extends MyDaoLike {
  import dbConfig.driver.api._

  val db = dbConfig.db
  val myModels = TableQuery[MyModelTable]

  def all: Future[Seq[MyModel]] = db.run(myModels.result)
}

My questions are:

  • Does this seem sensical?
  • Is there a way to avoid the manual instantiation of the dbConfig as shown above, instead using MacWire?
  • Should the Play 2.4 example use PlaySlick instead, or perhaps have it be a slight variation on this for demonstration purposes? (I'd be happy to make the changes)

Sorry for the long post, and thanks!

change 0.7-0.8 : default arguments of case class

I just found a minor regression : the default arguments of case class are not taken into account.

I had case class HearthstoneMatch(mode: GameMode = GameMode.UNDETECTED, ... // all with default values)

and now wire[HearthstoneMatch] does not work anymore, I had to replace by new HearthstoneMatch.

wireWith(factory)

An example is better than a 1000 words:

trait CFactory {
   def cFactory(a: A, b: B): C = wire[C]
}

class MyModule extends CFactory {  
  lazy val a: A = wire[A]
  lazy val b: B = wire[B]
  lazy val c: C = wireWith(cFactory)
}

Useful?

MacWire cannot handle implicit dependencies

If class constructor takes implicit arguments, MacWire fails to wire that class instance.
Example:

  implicit val clock: Clock = SystemClock
  // class TaskStepFactory(archiveDao: ArchiveDao)(implicit clock: Clock)
  lazy val taskStepFactory = wire[TaskStepFactory]

Error (with shortened packages):

too many arguments for constructor TaskStepFactory: (archiveDao: p1.ArchiveDao)(implicit clock: p2.Clock)p3.TaskStepFactory

wire can't find the dependencies if it's in a scope

trait Module extends Macwire{
  lazy val projectScope: Scope = new ProjectScope()

  class A()
  class B(a: A)

  lazy val b = projectScope(new B(a))
  lazy val a = projectScope(new A())
}

If I don't use wire, there is no compilation error, but:

  lazy val b = projectScope(wire[B])
  lazy val a = projectScope(wire[A])

Reports compilation error:

Error:(90, 33) Cannot find a value of type: [Module.this.A]
  lazy val b = projectScope(wire[B])
                                ^

Remove wiring implicit parameters from non-implicit values

This is making macwire more complex for, IMO, a dubious feature.
Here is an example of this added complexity:

#include commonClassesWithImplicitDependencies

object ModuleScope {
  implicit val moduleImplicitDependency = new Dependency()

  def service(dep: Dependency) = wire[Service]
  def serviceManual(dep: Dependency) = new Service
}
val dep = new Dependency()
require(ModuleScope.service(dep).dependency eq dep)
require(ModuleScope.serviceManual(dep).dependency eq ModuleScope.moduleImplicitDependency)

You can run this example in your tests, it is successful - macwire favors local params over implicit ones.
As you can see this is counter intuitive.

I would just not take implicit parameters into consideration and let the compiler do its job.

valsByClass creates instance of 'environment' object multiple times.

Here is a test case, which reproduces an issue in 0.5.

build.sbt

scalaVersion := "2.10.3"

name := "macwire test"

libraryDependencies += "com.softwaremill.macwire" %% "macros" % "0.5"

initialize ~= { _ =>
  System.setProperty("macwire.debug", "")
}

Main.scala

package macwire.issue

import com.softwaremill.macwire.InstanceLookup
import macwire.issue.App.FinalApp
import com.softwaremill.macwire.MacwireMacros._

object Main {

  def main(args: Array[String]) {
    val lookup = InstanceLookup(valsByClass(new FinalApp))

    println(lookup.lookupSingleOrThrow(classOf[App.Formatter]).format("TEST FORMATTER"))

    lookup.lookupSingleOrThrow(classOf[App.Printer]).print("TEST PRINTER")
  }

}

object App {

  trait Printer {
    def print(text: String): Unit
  }

  class CLIPrinter(formatter: Formatter) extends Printer {
    println("creating CLIPrinter")

    def print(text: String) = println(formatter.format(text))
  }

  trait Formatter {
    def format(line: String): String
  }

  class LowercaseFormatter extends Formatter {
    println("creating LowercaseFormatter")

    def format(line: String) = line.toLowerCase
  }

  trait PrinterModule extends FormatterModule {
    val printer: Printer
  }

  trait ConcretePrinter extends PrinterModule {
    lazy val printer = wire[CLIPrinter]
  }

  trait FormatterModule {
    val formatter: Formatter
  }

  trait ConcreteFormatter extends FormatterModule {
    lazy val formatter = wire[LowercaseFormatter]
  }

  class FinalApp extends ConcretePrinter with ConcreteFormatter

}

Actual output is:

creating LowercaseFormatter
creating LowercaseFormatter
creating CLIPrinter
test formatter
test printer

Thing is that MacWire creates the following mappings:

[debug] Found a mapping: classOf[macwire.issue.App$$LowercaseFormatter] -> new macwire.issue.App.FinalApp().formatter
[debug] Found a mapping: classOf[macwire.issue.App$$CLIPrinter] -> new macwire.issue.App.FinalApp().printer

Moreover this can go unnoticed until you have some kind of validation that only one instance is being created. In my case I had a class which created a named actor, so Actor System complained about non-unique name of the actor.

Wire from enclosing imports

I'd like to be able to wire imported members:

class CoreService
class CoreModule {
  lazy val service = wire[CoreService]
}

class ExtensionService(core: CoreService)
class ExtensionModule(coreModule: CoreModule) {
  import coreModule._

  lazy val service = wire[ExtensionService]
}

This is currently not possible as import coreModule._ isn't taken into account.

Problems replacing wired class with mock

I'm trying to use Mockito with MacWire and my mocked class is not being called.

Here's my test:

package unit.services

class OrgServiceSpec extends UnitSpec {

  implicit val app: FakeApplication =
    FakeApplication()

  "An org" can "be created via the service" in {

    running(FakeApplication()){

      val mockRepo = mock[OrgRepo]

      val service = new OrgService {

        val orgRepo = mockRepo
      }

      val org = new Org(None, "Test Org")

      when(mockRepo.create(org)).thenReturn(1)

      val id: Int = service.create(org)

     // TODO: mock the repo being used by the service via DI

      // check that
      assert(id > 0)

      verify[OrgRepo](mockRepo).create(org)
    }

  }

And here's my service:

class OrgService {

  def create(org: Org) : Int = {

    // Use a macro to DI the repo class
    lazy val orgRepo = wire[OrgRepo]

    // TODO: validate the org object (scalaz lib)

    // TODO: assign one or more org roles, buyer, seller, reseller, etc...

    // TODO: log this activity somewhere

    val orgId = orgRepo.create(org)

    orgId
  }
}

The error is:

Wanted but not invoked:
orgRepo.create(Org(None,Test Org));
-> at         unit.services.OrgServiceSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply$mcI$sp(OrgServiceSpec.    scala:38)
Actually, there were zero interactions with this mock.

I'm probably doing something stupid - I'm new to Scala - but I couldn't find a working example of how to do this so cribbed from the activator project and this where I ended up.

Handling type parameters

I have a situation that could be summarised as follows:

val x = Option("Foo")
class Foo[T](x: Option[T])
wire[Foo[String]]

This doesn't compile with the following error:

Cannot find a value of type: [Option[T]]

Is this something that is going to be implemented in the future?

Thank you!

Ability to inject call-by-name dependencies

In rare cases you might have a circular dependency you're trying to break. One easy way to handle this in scala is to inject a => T instead of a T. Macwire also could create a Provider type, but I like the pure-scala approach of using call by name.

I think it would be nice if the wire macro detected call-by-name parameters in constructors and generated the necessary code to reference the appropriate property.

Using implicitly to provide DI values

This is an idea that came up in the ScalaX 2014 presentation. I haven't actually tried it out, so it might have some fundamental problem I'm missing.

You could optionally support implicits on the provider side. A separate macro iwire (because the user needs to know if he's expected to provide an implicit or not), which might expand to something as simple as this:

{
  // For each argument type required
  val $anon1 = implicitly[ArgType1]
  wire[...]
}

Using implicits on the provider side has lots of benefits: a powerful and well specified resolution algorithm, implicit imports, and compatibility with typeclass-based libraries. On the consumer side you don't want to make all parameters implicit, so you wire them with a macro.

Documentation - lazy factory?

I've seen this in the README and the DI guide:

lazy val factory = (arg: Arg) => wire[Stuff]

I don't see the point of making this factory lazy?
Shouldn't the documentation recommend this instead?

val factory = (arg: Arg) => wire[Stuff]

Documentation cleanup

The documentation is scattered across the README and http://di-in-scala.github.io/.
I suggest that everything that is on the documentation website be removed from the README.

I guess that one reason why this hasn't been done is because the website is not only about macwire but about dependency injection in scala. Unfortunately this is quite a broad topic and it turns out that the guide is mostly about macwire with a compilation of links to other frameworks, github projects or blog posts.

So I would do either one of these:

  1. change the name of the documentation website into macwire documentation and move everything that's not directly about macwire (that is, everything up to "Running example"), into stack overflow.
  2. same as 1 but leave the link compilation in http://di-in-scala.github.io/

My preference goes to 1) as the second option would leave us with a rather skinny website.

Support reachable non-public members

At the moment only public members are eligible for wiring. We should also accept:

  • protected members of parent classes/traits
  • package private/protected members if we're in the same package

Support JSR-330

I can see 2 things of interest:

  • @Qualifier - currently implemented with Tagging. I find the JSR meta-annotation to be less intrusive than the Tagging mechanic. That alone has an added value IMO.
  • Provider - currently implemented with call-by-name.

Although these features wouldn't be immensely useful as they already have scala-ish equivalents, they could be useful when integrating with java libraries.

What do you think? Would it be useful? doable?

scala 2.11.3

Would you please release a new version with up to date dependency to scala 2.11.3 ?

I get those annoying warnings otherwise :

[WARNING]  Expected all dependencies to require Scala version: 2.11.3
[WARNING]  net.hearthstats:companion:0.20.3-SNAPSHOT requires scala version: 2.11.3
[WARNING]  com.softwaremill.macwire:macros_2.11:0.7.3 requires scala version: 2.11.2
[WARNING] Multiple versions of scala libraries detected!

Documentation pattern - secondary constructors

If you're wondering how to deal with multiple constructors with macwire here is a pattern.

So you have this X class that provides default implementations for some of its parameters:

class X(a: A, b: B, c: C, d: D) {
  // secondary constructor, providing defaults for C and D
  def this(a: A, b: B) = {
    this(a, b, new DefaultC, new DefaultD)
  }
}

This can also be written using default parameters (as long as DefaultC or DefaultD does not depend on a or b), but down the line that's the same:

// default parameters, which will be turned into secondary constructors
class X(a: A, b: B, c: C = new DefaultC, d: D = new DefaultD)

(Un)fortunately macwire only looks at the primary constructor (BTW something could be said in the documentation about why this is better).
To workaround that "limitation" here is an alternative:

class X(a: A, b: B, c: C, d: D) {
  // no secondary constructor
}

object X {
    import Defaults._

    def apply(a: A, b: B) : X = wire[X]

    trait Defaults {
       lazy val c = wire[DefaultC]
       lazy val d = wire[DefaultD]
    }
    object Defaults extends Defaults   
}

We can now re-use the defaults provided by X if we're interested in them:

class MyModule extends X.Defaults {
   lazy val a: A = wire[AImpl]
   lazy val b: B = wire[BImpl]
   lazy val x: X = wire[X]
}

Things can get a little more tricky if DefaultC needs some parameters that we're unable to provide. In that case we'll resort to factories:

object X {
    import Defaults._

    def apply(a: A, b: B, cDep: CDep) : X = { 
       val c = buildC(cDep)
       wire[X]
   }

    trait Defaults {
       def buildC(cDep: CDep) = wire[DefaultC]
       lazy val d = wire[DefaultD]
    }
    object Defaults extends Defaults   
}

And in your module

class MyModule extends X.Defaults {
   lazy val a: A = wire[AImpl]
   lazy val b: B = wire[BImpl]
   lazy val cDep: CDep = wire[CDepImpl]
   lazy val c: C = buildC(cDep)
   lazy val x: X = wire[X]
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.