Giter Club home page Giter Club logo

grafter's People

Contributors

13h3r avatar aeons avatar amrhassan avatar danielkarch avatar duergner avatar etorreborre avatar gitter-badger avatar jcranky avatar perploug avatar pjan avatar richardmiller avatar shanethehat avatar taojang 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

grafter's Issues

Intellij plugin

Intellij does not understand the macros used by Grafter, because it does not compile Scala code to perform its syntax checks. This is an issue because Intellij will fail to detect required imports correctly, and may clean up imports it believes to be unused which will break an application.

This is probably a band-aid rather than a properly viable solution, but I've created a basic Intellij plugin to recreate the work that the macros are doing within the Intellij AST. I'm finding it useful, but feedback would be appreciated.

https://plugins.jetbrains.com/idea/plugin/9387-grafter-macro-support
https://github.com/shanethehat/grafter-intellij-plugin

Rework the documentation

Do a pass on the documentation to link pages with previous/next page so that it can be understood as a long tutorial.

Rewriter/Singleton question

From the documentation / @etorreborre's talk, I was under the impression that when using a singleton/rewriter, it makes sure the specific module only gets initialized once. However, when running the example below (dependencies akka-cluster & grafter), the ActorSystemProvider is being initialized twice.

I'm fairly sure the mistake is probably somewhere on my end, but at the same time am unable to track it down, or deduce from the documentation what I am doing wrong. Any ideas here?

package demo

import akka.actor._
import cats.Eval
import cats.data.Reader
import org.zalando.grafter._
import org.zalando.grafter.GenericReader._
import org.zalando.grafter.macros._
import org.zalando.grafter.syntax.rewriter._

@readers
case class ApplicationConfig(
    akka: AkkaConfig
)

case class AkkaConfig(
    clusterName: String
)

trait DB

object DB {
  implicit def reader: Reader[ApplicationConfig, DB] =
    AkkaDB.reader
}

case class AkkaDB(actorSystemProvider: ActorSystemProvider) extends DB with Start {
  override def start: Eval[StartResult] = StartResult.eval("DB") {}
}

object AkkaDB {
  implicit def reader: Reader[ApplicationConfig, AkkaDB] =
    genericReader
}

trait ActorSystemProvider {
  def system: ActorSystem
}

object ActorSystemProvider {
  implicit def reader[A](implicit akkaConfigReader: Reader[A, AkkaConfig]): Reader[A, ActorSystemProvider] =
    DefaultActorSystemProvider.reader[A]
}

@dependentReader
final case class DefaultActorSystemProvider private (config: AkkaConfig)
    extends ActorSystemProvider
    with Start
    with Stop {

  lazy val systemEval: Eval[ActorSystem] =
    Eval.later(ActorSystem(config.clusterName))

  lazy val system: ActorSystem =
    systemEval.value

  override def start: Eval[StartResult] = {
    StartResult.eval("akka")(system)
  }

  override def stop: Eval[StopResult] = {
    StopResult.eval("akka")(system.terminate())
  }
}

object DefaultActorSystemProvider {
  implicit def reader[A](implicit akkaConfigReader: Reader[A, AkkaConfig]): Reader[A, ActorSystemProvider] =
    akkaConfigReader.map(DefaultActorSystemProvider.apply)
}

@reader[ApplicationConfig]
case class Application(actorSystemProvider: ActorSystemProvider, db: DB)

object Main extends App {

  pureconfig.loadConfig[ApplicationConfig]("app") match {
    case Left(error)  println(error)
    case Right(applicationConfig) 
      val application = GenericReader[ApplicationConfig, Application]
        .run(applicationConfig)
        .singleton[ActorSystemProvider]

      Rewriter
        .start(application)
        .flatMap {
          case results if results.exists(!_.success) 
            println(results)
            Eval.now(())
          case _ 
            Eval.now(())
        }
        .value

  }

}

Filter nodes to display but respect transitive dependencies

The current "Visualize" allows to create a graph from an application and then filter nodes from it. A consequence is that the children nodes of the filtered out nodes are simply removed from the graph. It would be nicer to just exclude the filtered out nodes and keep their children (unless filtered out themselves).

Add some utility functions

  1. the possibility to create singletons with a predicate function
  2. collect (breadth-first) the first component of a given type

Report the success of `replace`

At the moment the replace method returns the application graph with no indication of the success of the replacement. We could add a method replaceAtLeastOnce which would return Option[Application] with None as a result if nothing was replaced.

The order in which singletons are being applied should not be relevant

If we apply the singleton rewriting over the following graph, we don't get the same results depending on the order we do the rewriting:

case class U(name: String) {
  override def toString = s"${getClass.getSimpleName}($name: ${System.identityHashCode(this)})"
  override def equals(a: Any) = System.identityHashCode(this) == System.identityHashCode(a)
}
case class V(name: String, u: U) {
  override def toString = s"${getClass.getSimpleName}($name: ${System.identityHashCode(this)}, $u)"
  override def equals(a: Any) = System.identityHashCode(this) == System.identityHashCode(a)
}
case class T(u: U, v1: V, v2: V){
  override def toString = s"${getClass.getSimpleName}(${System.identityHashCode(this)}, $u, $v1, $v2)"
}

val v = V("v1", U("u2"))
val graph = T(U("u1"), v, v)

   T
 / |\
U1 | \
   | /
   V
   |
   U2

graph.singleton[U].singleton[V] !== graph.singleton[V].singleton[U]

Improve the error messages

When an implicit Reader definition is missing somewhere in the graph, Shapeless will not produce a very clear message

[error]       could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[A,Repr]
[error]       genericReader
[error]       ^
[error] one error found

It would be a lot better to read

[error]  missing reader: Reader[AppConfig, HttpConfig]

I have unfortunately no clue how to do this :-)

Display a graph of components

It would be very useful to be able to display a graph of components as a dot file, showing dependencies and where singletons have been made, with various options to remove unnecessary information (display dependencies only for example).

readers annotation generating reader for self

The following bit of code (excluded all imports and ceremony for brevity) fails to compile. (The usecase might be specific, but it would be great if it would also just work)

@readers
case class ApplicationConfig(
  dbConfig: DbConfig
)

@reader[ApplicationConfig]
case class Foo(config: ApplicationConfig, db: DB)

It fails because there's no implicit reader instance (Reader[ApplicationConfig, ApplicationConfig]) to be found.

It would be resolved if the @readers annotation would also generate the following

object ApplicationConfig {
  implicit val reader: Reader[ApplicationConfig, ApplicationConfig] =
    Reader.apply(identity)
}

Add a @Reader macro annotation

Creating the implicit reader definitions for a given ApplicationConfig class is repetitive and can probably provided by creating a @Reader macro annotation.

Instead of writing

case class ApplicationConfig(
  threadPoolConfig: ThreadPoolConfig,
  serverConfig: ServerConfig
)

object ApplicationConfig {
   implicit def threadPoolConfigReader: Reader[ApplicationConfig, ThreadPoolConfig] = 
      Reader(_.threadPoolConfig)

   implicit def serverConfigReader: Reader[ApplicationConfig, ServerConfig] = 
      Reader(_.serverConfig)
}

There could be an annotation generating the implicit definitions automatically:

@Reader
case class ApplicationConfig(
  threadPoolConfig: ThreadPoolConfig,
  serverConfig: ServerConfig
)

Note that all the attributes of a config must have a different type. Otherwise we will generate conflicting Reader instances. There should be a compiler error in that case.

Configuration with type parameters

I have some external components, that I can't really create through grafter's functionality (they are created by an fs2.Stream).

I had the idea to add some kind of context case class to my configuration, with the "external" dependencies. However, doing it this way requires a type parameter in my config class (case class Config[F[_]](...)), which gives this error:

[error] ...: A1 takes no type parameters, expected: one
[error] @readers

Should I just not do it this way? If not, is there a way to do something like this?

Add an annotation to create default instances for interfaces

For interfaces we need to manually specify which instance need to be selected:

trait Database

object Database {
  implicit def reader: Reader[ApplicationConfig, Database] =
    PostgresDatabase.reader
}

It would be nice to have an annotation for this:

@default[PostgresDatabase]
trait Database

scala.js support?

Has anyone tried compiling grafter with scala.js? Does grafter use anything JVM-specific?

Replace a service before starting the application

When I do:

val application: Application =
  GenericReader[ApplicationConfig, Application].
    run(testConfiguration).
    replace[Database](mockDb)

This actually instantiate the default Database and then replace it by the mock.

This behaviour is exactly what we don't want when mocking because in my case it throw an exception as it checks the validity of some credentials.

Scala 2.12 support

I was thinking that Scala 2.12 support would be nice. I see that all the dependencies (after upgrading some) have 2.12-compatible releases.

Reduce genericReader boilerplate

In a similar vein to the @readers annotation, it would be nice to provide a way to remove the fairly common genericReader boilerplate:

object Foo {
  implicit def reader: Reader[ApplicationConfig, Foo] = genericReader
}

Would another macro annotation like @genericReader be a suitable solution?

Change `Rewriter.start` with `Rewriter.replaceAll`

In order to avoid the confusion when starting a component implementing the Start interface:

  • is start the extension method provided by org.zalando.grafter.syntax.rewriter._ to start all nested components?
  • or is it the start method implemented by the component?

Visualize components attributes

It would be nice to be able to display attributes when producing an application graph.

Typically we can show as attributes any case class member which is not a dependency itself. For examples "configuration" case classes members fall in that category:

@reader
case class HttpServer(config: HttpServerConfig)

case class HttpServerConfig(host: String, port: Int)

Then we should see host and port printed in the .dot graph for the HttpServer.

Define reader for trait in libaries

Hi,
I'm trying to write a reader for ElasticClient (Elasticsearch) that we usually put in its companion object.
Unfortunately, it's a library and I can't add it in the companion object and then I get this could not find implicit value for parameter gen: shapeless.LabelledGeneric.Aux[A,Repr] for genericReader.

Do you know how can I make the right import of the implicit?
importing the implicit reader Reader[A, ElasticClient] in scope doesn't seems to work.

Here is a snipet example:

import cats.data.Reader
import org.zalando.grafter.GenericReader._

case class ElasticsearchConfig(host: String)

case class ApplicationConfig(elastic: ElasticsearchConfig)

object ApplicationConfig {

  implicit def elasticReader: Reader[ApplicationConfig, ElasticsearchConfig] =
    Reader(_.elastic)
}

case class ElasticClient(host: String)

object DefaultElasticClient {

  implicit def reader[A](implicit elasticConfigReader: Reader[A, ElasticsearchConfig]): Reader[A, ElasticClient] =
    elasticConfigReader.map(conf => ElasticClient(conf.host))
}

case class Application(elastic: ElasticClient)

object Application {
  import DefaultElasticClient._
  implicit def reader: Reader[ApplicationConfig, Application] = genericReader
}

`VisualizeSpec` fails sometimes

And just sometimes, with an error like this:

[info] VisualizeSpec
[info] 
[error]  x The example graph must be correctly serialized into .dot format
[error]  'strict digraph {
[error]    "A" [shape=box];
[error]    "B # 1/2" [shape=box];
[error]    "B # 2/2" [shape=box];
[error]    "C # 1/2" [shape=box];
[error]    "C # 2/2" [shape=box];
[error]    "D" [shape=box];
[error]    "B # 1/2" -> "A"
[error]    "B # 2/2" -> "A"
[error]    "C # 1/2" -> "A"
[error]    "C # 1/2" -> "B # 1/2"
[error]    "C # 1/2" -> "B # 2/2"
[error]    "C # 2/2" -> "A"
[error]    "C # 2/2" -> "B # 2/2"
[error]    "D" -> "C # 1/2"
[error]    "D" -> "C # 2/2"
[error]  }' != 'strict digraph {
[error]    "A" [shape=box];
[error]    "B # 1/2" [shape=box];
[error]    "B # 2/2" [shape=box];
[error]    "C # 1/2" [shape=box];
[error]    "C # 2/2" [shape=box];
[error]    "D" [shape=box];
[error]    "B # 1/2" -> "A"
[error]    "B # 2/2" -> "A"
[error]    "C # 1/2" -> "A"
[error]    "C # 1/2" -> "B # 1/2"
[error]    "C # 1/2" -> "B # 2/2"
[error]    "C # 2/2" -> "A"
[error]    "C # 2/2" -> "B # 1/2"
[error]    "C # 2/2" -> "B # 2/2"
[error]    "D" -> "C # 1/2"
[error]    "D" -> "C # 2/2"
[error]  }' (VisualizeSpec.scala:24)
[error] Actual:   ...aph {
[error] ... "A" [shape=box];
[error] ...1/2" [shape=box];
[error] ...2/2" [shape=box];
[error] ...1/2" [shape=box];
[error] ...2/2" [shape=box];
[error] ... "D" [shape=box];
[error] ...> "A"
[error] ...> "A"
[error] ...> "A"
[error] ... 1/2"
[error] ... 2/2"
[error] ...> "A"
[error] ..."B # [2]/2"
[error]   "[D]" ... "[C] # [1]/2"
[error] ..."C # [2]/2"
[error] [}]
[error] Expected: ...aph {
[error] ... "A" [shape=box];
[error] ...1/2" [shape=box];
[error] ...2/2" [shape=box];
[error] ...1/2" [shape=box];
[error] ...2/2" [shape=box];
[error] ... "D" [shape=box];
[error] ...> "A"
[error] ...> "A"
[error] ...> "A"
[error] ... 1/2"
[error] ... 2/2"
[error] ...> "A"
[error] ..."B # [1]/2"
[error]   "[C # 2/2]" ... "[B] # [2]/2"
[error] ..."C # [1]/2"
[error] [  "D" -> "C # 2/2"]

Visualize is broken for scala 2.12.2

The error message:

[error] (run-main-0) java.lang.ClassCastException: scala.collection.immutable.Vector cannot be cast to scala.collection.immutable.Queue
java.lang.ClassCastException: scala.collection.immutable.Vector cannot be cast to scala.collection.immutable.Queue
	at scala.collection.immutable.Queue.$plus$plus(Queue.scala:111)
	at org.bitbucket.inkytonik.kiama.relation.Relation$.loop$1(Relation.scala:61)
	at org.bitbucket.inkytonik.kiama.relation.Relation$.fromOneStepGraph(Relation.scala:64)
	at org.bitbucket.inkytonik.kiama.relation.Relation$.fromOneStep(Relation.scala:73)
	at org.zalando.grafter.Visualize.asDotString(Visualize.scala:20)
	at org.zalando.grafter.Visualize.asDotString$(Visualize.scala:16)
	at org.zalando.grafter.Visualize$.asDotString(Visualize.scala:82)
	at org.zalando.grafter.VisualizeSyntax$VisualizeSyntaxOps.asDotString(Visualize.scala:111)
	at org.zalando.grafter.VisualizeSyntax$VisualizeSyntaxOps.asDotString(Visualize.scala:107)
	at com.example.grafterhttp4sexample.main.Visualize$.delayedEndpoint$com$example$grafterhttp4sexample$main$Visualize$1(Visualize.scala:13)
	at com.example.grafterhttp4sexample.main.Visualize$delayedInit$body.apply(Visualize.scala:5)
	at scala.Function0.apply$mcV$sp(Function0.scala:34)
	at scala.Function0.apply$mcV$sp$(Function0.scala:34)
	at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
	at scala.App.$anonfun$main$1$adapted(App.scala:76)
	at scala.collection.immutable.List.foreach(List.scala:389)
	at scala.App.main(App.scala:76)
	at scala.App.main$(App.scala:74)
	at com.example.grafterhttp4sexample.main.Visualize$.main(Visualize.scala:5)
	at com.example.grafterhttp4sexample.main.Visualize.main(Visualize.scala)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)

To reproduce:

  1. create a new application with my template, and be sure to set the scala version to 2.12.2 when prompted
  2. run sbt run
  3. choose Visualize

Migrate to the new scala macros

The current macro annotations are using Scalameta but they are showing an issue which might not be solved so soon.

The new scalamacros could solve this problem. We need to test on one annotation if:

  • scalamacros has enough features already to support the grafter use case
  • the issue in Scalameta is fixed

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.