From f8bc09a6579f590bd6404acd09aee7e37d74f902 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Sat, 31 Jan 2015 01:08:57 +0100 Subject: [PATCH 01/10] Add acyclic plugin to check for cyclic dependencies --- build.sbt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 1c8c645..c26fe9d 100644 --- a/build.sbt +++ b/build.sbt @@ -21,7 +21,9 @@ lazy val root = project.in(file(".")). "org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-js" %%% "scalajs-dom" % "0.7.0" ), - resolvers += Resolver.sonatypeRepo("releases") + resolvers += Resolver.sonatypeRepo("releases"), + autoCompilerPlugins := true, + addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2") ) From f564f59c769a9ff08d0dcea797d9f59e87227c73 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Thu, 5 Feb 2015 23:41:37 +0100 Subject: [PATCH 02/10] Upgrade to Scalajs 0.6 --- build.sbt | 12 ++++++------ project/build.properties | 2 +- project/plugins.sbt | 4 +--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/build.sbt b/build.sbt index c26fe9d..7f61cc4 100644 --- a/build.sbt +++ b/build.sbt @@ -19,11 +19,9 @@ lazy val root = project.in(file(".")). name := "scalajs-angulate", libraryDependencies ++= Seq( "org.scala-lang" % "scala-reflect" % scalaVersion.value, - "org.scala-js" %%% "scalajs-dom" % "0.7.0" + "org.scala-js" %%% "scalajs-dom" % "0.8.0" ), - resolvers += Resolver.sonatypeRepo("releases"), - autoCompilerPlugins := true, - addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2") + resolvers += Resolver.sonatypeRepo("releases") ) @@ -31,11 +29,13 @@ lazy val tests = project. dependsOn(root). enablePlugins(ScalaJSPlugin). settings(commonSettings: _*). - settings(utest.jsrunner.Plugin.utestJsSettings: _*). settings( publish := {}, scalacOptions ++= angulateDebugFlags, - scalaJSStage := FastOptStage, + scalaJSStage in Test := FastOptStage, + testFrameworks += new TestFramework("utest.runner.Framework"), + requiresDOM := true, + libraryDependencies += "com.lihaoyi" %%% "utest" % "0.3.0" % "test", jsDependencies += RuntimeDOM, jsDependencies += "org.webjars" % "angularjs" % "1.3.8" / "angular.min.js" % "test", jsDependencies += "org.webjars" % "angularjs" % "1.3.8" / "angular-mocks.js" % "test" diff --git a/project/build.properties b/project/build.properties index 64abd37..748703f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.6 +sbt.version=0.13.7 diff --git a/project/plugins.sbt b/project/plugins.sbt index d5c8816..40a67d6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,3 +1 @@ -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0-RC2") - -addSbtPlugin("com.lihaoyi" % "utest-js-plugin" % "0.2.5-RC1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.0") From 79b0cfbbacbd4e4b48250341216c67c4b8a7afc2 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Fri, 6 Feb 2015 00:17:04 +0100 Subject: [PATCH 03/10] A simple \ does wonder at escaping chars in Scaladoc --- src/main/scala/biz/enef/angular/Module.scala | 8 ++++---- src/main/scala/biz/enef/angular/Scope.scala | 12 ++++++------ src/main/scala/biz/enef/angular/core/Location.scala | 4 ++-- .../scala/biz/enef/angular/ext/RouteProvider.scala | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/scala/biz/enef/angular/Module.scala b/src/main/scala/biz/enef/angular/Module.scala index 192b145..e162e0f 100644 --- a/src/main/scala/biz/enef/angular/Module.scala +++ b/src/main/scala/biz/enef/angular/Module.scala @@ -21,7 +21,7 @@ trait Module extends js.Object { def name: String = js.native /** - * Defines an animation hook that can be later used with the $animate service and directives that use this service. + * Defines an animation hook that can be later used with the \$animate service and directives that use this service. * * @note animations take effect only if the ngAnimate module is loaded * @@ -33,7 +33,7 @@ trait Module extends js.Object { def animation(name: String, animationFactory: js.Function): Module = js.native /** - * Defines an animation hook that can be later used with the $animate service and directives that use this service. + * Defines an animation hook that can be later used with the \$animate service and directives that use this service. * * @note animations take effect only if the ngAnimate module is loaded * @@ -159,7 +159,7 @@ trait Module extends js.Object { def filter(name: String, filterFactory: js.Array[Any]): Module = js.native /** - * Register a provider function with the $injector. + * Register a provider function with the \$injector. * * @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key. * @param constructor Provider constructor function @@ -169,7 +169,7 @@ trait Module extends js.Object { def provider(name: String, constructor: js.Function): Module = js.native /** - * Register a provider function with the $injector. + * Register a provider function with the \$injector. * * @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key. * @param constructor Array containing the names of the dependencies to be injected and diff --git a/src/main/scala/biz/enef/angular/Scope.scala b/src/main/scala/biz/enef/angular/Scope.scala index a38813e..8646263 100644 --- a/src/main/scala/biz/enef/angular/Scope.scala +++ b/src/main/scala/biz/enef/angular/Scope.scala @@ -10,7 +10,7 @@ import scala.scalajs.js /** * Defines the bindings to the global angular object. * - * @see [[https://docs.angularjs.org/api/ng/type/$rootScope.Scope]] + * @see [[https://docs.angularjs.org/api/ng/type/\$rootScope.Scope]] */ trait Scope extends js.Object { @@ -37,7 +37,7 @@ trait Scope extends js.Object { def $apply(exp: js.Any = js.native) : js.Any = js.native /** - * Schedule the invocation of {{{`$apply`}}} to occur at a later time. + * Schedule the invocation of `\$apply` to occur at a later time. * * @param exp An angular expression to be executed (string or a `function(scope)`) */ @@ -106,7 +106,7 @@ trait Scope extends js.Object { /** * Registers a listener callback to be executed whenever the `watchExpression` changes. * - * @param watchExpression Called on every {{{`$digest()`}}} and should return the value that should be watched + * @param watchExpression Called on every `\$digest()` and should return the value that should be watched * @param listener Callback function which is only executed when the `watchExpression` has changed. * Called with three arguments: new value, old value, current scope * @return de-registration function @@ -116,7 +116,7 @@ trait Scope extends js.Object { /** * Registers a listener callback to be executed whenever the `watchExpression` changes. * - * @param watchExpression Called on every {{{`$digest()`}}} and should return the value that should be watched + * @param watchExpression Called on every `\$digest()` and should return the value that should be watched * @param listener Callback function which is only executed when the `watchExpression` has changed * Called with three arguments: new value, old value, current scope * @return de-registration function @@ -124,7 +124,7 @@ trait Scope extends js.Object { //def $watch(watchExpression: js.Function, listener: js.Function3[js.Any,js.Any,Scope,Unit]) : js.Function = js.native /** - * A variant of {{{`$watch`}}} where it watches an array of `watchExpressions` + * A variant of `\$watch` where it watches an array of `watchExpressions` * * @param watchExpressions Array of expressions that will be individually watched using `$$watch` * @param listener Callback function which is executed whenever any of the expressions in `watchExpression` has changed @@ -136,7 +136,7 @@ trait Scope extends js.Object { /** * Shallow watches the properties of an object and fires whenever any of the properties change. * - * @param obj Collection watched via {{{`$watch`}}} + * @param obj Collection watched via `\$watch` * @param listener Callback function with three arguments: new collection, old collection, current scope * @return De-registration function */ diff --git a/src/main/scala/biz/enef/angular/core/Location.scala b/src/main/scala/biz/enef/angular/core/Location.scala index f2b8457..391b309 100644 --- a/src/main/scala/biz/enef/angular/core/Location.scala +++ b/src/main/scala/biz/enef/angular/core/Location.scala @@ -8,9 +8,9 @@ package biz.enef.angular.core import scala.scalajs.js /** - * Defines the bindings to the {{{$location}}} service. + * Defines the bindings to the \$location service. * - * @see [[https://docs.angularjs.org/api/ng/service/$location]] + * @see [[https://docs.angularjs.org/api/ng/service/\$location]] */ trait Location extends js.Object { diff --git a/src/main/scala/biz/enef/angular/ext/RouteProvider.scala b/src/main/scala/biz/enef/angular/ext/RouteProvider.scala index a8dc2f0..472d9bf 100644 --- a/src/main/scala/biz/enef/angular/ext/RouteProvider.scala +++ b/src/main/scala/biz/enef/angular/ext/RouteProvider.scala @@ -8,9 +8,9 @@ package biz.enef.angular.ext import scala.scalajs.js /** - * Defines the bindings to the {{{ngRoute.$rootProvider}}} API and enhancements provided by scalajs-angulate. + * Defines the bindings to the ngRoute.\$rootProvider API and enhancements provided by scalajs-angulate. * - * @see [[https://docs.angularjs.org/api/ngRoute/provider/$routeProvider]] + * @see [[https://docs.angularjs.org/api/ngRoute/provider/\$routeProvider]] */ trait RouteProvider extends js.Object { @@ -22,9 +22,9 @@ trait RouteProvider extends js.Object { } /** - * Mapping information to be assigned to {{{$route.current}}} on match. + * Mapping information to be assigned to \$route.current on match. * - * @see [[https://docs.angularjs.org/api/ngRoute/provider/$routeProvider]] + * @see [[https://docs.angularjs.org/api/ngRoute/provider/\$routeProvider]] */ trait Route extends js.Object { From f9e603700575c4fa0a8123c20c8aa2e13b7c62e8 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Fri, 6 Feb 2015 00:18:50 +0100 Subject: [PATCH 04/10] Use acyclic to avoid cyclic dependencies in compilation units --- build.sbt | 10 ++++++++-- src/main/scala/biz/enef/angular/Angular.scala | 1 + src/main/scala/biz/enef/angular/Controller.scala | 1 + src/main/scala/biz/enef/angular/Directive.scala | 1 + src/main/scala/biz/enef/angular/Module.scala | 1 + src/main/scala/biz/enef/angular/Scope.scala | 1 + src/main/scala/biz/enef/angular/Service.scala | 2 ++ src/main/scala/biz/enef/angular/annotations.scala | 1 + src/main/scala/biz/enef/angular/core/Attributes.scala | 1 + src/main/scala/biz/enef/angular/core/Compile.scala | 1 + src/main/scala/biz/enef/angular/core/Http.scala | 1 + src/main/scala/biz/enef/angular/core/Injector.scala | 1 + src/main/scala/biz/enef/angular/core/JQLite.scala | 1 + src/main/scala/biz/enef/angular/core/Location.scala | 1 + src/main/scala/biz/enef/angular/core/QPromise.scala | 1 + src/main/scala/biz/enef/angular/core/Timeout.scala | 1 + .../scala/biz/enef/angular/core/impl/HttpMacros.scala | 1 + .../scala/biz/enef/angular/ext/RouteProvider.scala | 1 + src/main/scala/biz/enef/angular/impl/AngularImpl.scala | 1 + .../scala/biz/enef/angular/impl/ControllerMacros.scala | 1 + .../scala/biz/enef/angular/impl/DirectiveMacros.scala | 1 + src/main/scala/biz/enef/angular/impl/MacroBase.scala | 1 + .../scala/biz/enef/angular/impl/ModuleMacros.scala | 1 + .../scala/biz/enef/angular/impl/ServiceMacros.scala | 1 + 24 files changed, 32 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index 7f61cc4..b0d1595 100644 --- a/build.sbt +++ b/build.sbt @@ -5,8 +5,14 @@ lazy val commonSettings = Seq( version := "0.2-SNAPSHOT", scalaVersion := "2.11.5", scalacOptions ++= Seq("-deprecation","-feature","-Xlint"), - // work around for a bug during publishing - scalacOptions in (Compile,doc) ~= { _.filterNot(_.contains("scalajs-compiler_")) } + autoCompilerPlugins := true, + addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.2"), + libraryDependencies += "com.lihaoyi" %% "acyclic" % "0.1.2" % "provided", + scalacOptions ++= (if (isSnapshot.value) Seq.empty else Seq({ + val a = baseDirectory.value.toURI.toString.replaceFirst("[^/]+/?$", "") + val g = "https://raw.githubusercontent.com/jokade/scalajs-angulate" + s"-P:scalajs:mapSourceURI:$a->$g/v${version.value}/" + })) ) diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index 7261408..84d207c 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import biz.enef.angular.core.Injector import scala.language.experimental.macros diff --git a/src/main/scala/biz/enef/angular/Controller.scala b/src/main/scala/biz/enef/angular/Controller.scala index 94d5f10..931aefe 100644 --- a/src/main/scala/biz/enef/angular/Controller.scala +++ b/src/main/scala/biz/enef/angular/Controller.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import biz.enef.angular.core.{Attributes, JQLite} import scala.scalajs.js diff --git a/src/main/scala/biz/enef/angular/Directive.scala b/src/main/scala/biz/enef/angular/Directive.scala index 0a53216..f50ab28 100644 --- a/src/main/scala/biz/enef/angular/Directive.scala +++ b/src/main/scala/biz/enef/angular/Directive.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import biz.enef.angular.core.{Attributes, JQLite} import scala.scalajs.js diff --git a/src/main/scala/biz/enef/angular/Module.scala b/src/main/scala/biz/enef/angular/Module.scala index e162e0f..30a1a4c 100644 --- a/src/main/scala/biz/enef/angular/Module.scala +++ b/src/main/scala/biz/enef/angular/Module.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import scala.scalajs.js /** diff --git a/src/main/scala/biz/enef/angular/Scope.scala b/src/main/scala/biz/enef/angular/Scope.scala index 8646263..50c23b2 100644 --- a/src/main/scala/biz/enef/angular/Scope.scala +++ b/src/main/scala/biz/enef/angular/Scope.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import scala.scalajs.js /** diff --git a/src/main/scala/biz/enef/angular/Service.scala b/src/main/scala/biz/enef/angular/Service.scala index 1843e3f..3ee2b96 100644 --- a/src/main/scala/biz/enef/angular/Service.scala +++ b/src/main/scala/biz/enef/angular/Service.scala @@ -5,6 +5,8 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file + /** * Marks a class that can be used as a AngularJS service */ diff --git a/src/main/scala/biz/enef/angular/annotations.scala b/src/main/scala/biz/enef/angular/annotations.scala index 841b363..c443280 100644 --- a/src/main/scala/biz/enef/angular/annotations.scala +++ b/src/main/scala/biz/enef/angular/annotations.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular +import acyclic.file import scala.annotation.StaticAnnotation /** diff --git a/src/main/scala/biz/enef/angular/core/Attributes.scala b/src/main/scala/biz/enef/angular/core/Attributes.scala index 7eb13ea..589ea7d 100644 --- a/src/main/scala/biz/enef/angular/core/Attributes.scala +++ b/src/main/scala/biz/enef/angular/core/Attributes.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js import scala.scalajs.js.UndefOr import scala.scalajs.js.annotation.JSBracketAccess diff --git a/src/main/scala/biz/enef/angular/core/Compile.scala b/src/main/scala/biz/enef/angular/core/Compile.scala index 084d1a4..741eb62 100644 --- a/src/main/scala/biz/enef/angular/core/Compile.scala +++ b/src/main/scala/biz/enef/angular/core/Compile.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js trait Compile extends js.Object { diff --git a/src/main/scala/biz/enef/angular/core/Http.scala b/src/main/scala/biz/enef/angular/core/Http.scala index d7e37c0..95a8f4d 100644 --- a/src/main/scala/biz/enef/angular/core/Http.scala +++ b/src/main/scala/biz/enef/angular/core/Http.scala @@ -7,6 +7,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.concurrent.Future import scala.language.experimental.macros import scala.scalajs.js diff --git a/src/main/scala/biz/enef/angular/core/Injector.scala b/src/main/scala/biz/enef/angular/core/Injector.scala index cdd507f..1935998 100644 --- a/src/main/scala/biz/enef/angular/core/Injector.scala +++ b/src/main/scala/biz/enef/angular/core/Injector.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js trait Injector extends js.Object { diff --git a/src/main/scala/biz/enef/angular/core/JQLite.scala b/src/main/scala/biz/enef/angular/core/JQLite.scala index c37d030..9267c8f 100644 --- a/src/main/scala/biz/enef/angular/core/JQLite.scala +++ b/src/main/scala/biz/enef/angular/core/JQLite.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import org.scalajs.dom.Element import scala.scalajs.js diff --git a/src/main/scala/biz/enef/angular/core/Location.scala b/src/main/scala/biz/enef/angular/core/Location.scala index 391b309..51cbe62 100644 --- a/src/main/scala/biz/enef/angular/core/Location.scala +++ b/src/main/scala/biz/enef/angular/core/Location.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js /** diff --git a/src/main/scala/biz/enef/angular/core/QPromise.scala b/src/main/scala/biz/enef/angular/core/QPromise.scala index aa71087..fbec552 100644 --- a/src/main/scala/biz/enef/angular/core/QPromise.scala +++ b/src/main/scala/biz/enef/angular/core/QPromise.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js trait QPromise extends js.Object { diff --git a/src/main/scala/biz/enef/angular/core/Timeout.scala b/src/main/scala/biz/enef/angular/core/Timeout.scala index 891fb3b..491c4f3 100644 --- a/src/main/scala/biz/enef/angular/core/Timeout.scala +++ b/src/main/scala/biz/enef/angular/core/Timeout.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core +import acyclic.file import scala.scalajs.js trait Timeout extends js.Object { diff --git a/src/main/scala/biz/enef/angular/core/impl/HttpMacros.scala b/src/main/scala/biz/enef/angular/core/impl/HttpMacros.scala index 2ce4f65..fdb1fa6 100644 --- a/src/main/scala/biz/enef/angular/core/impl/HttpMacros.scala +++ b/src/main/scala/biz/enef/angular/core/impl/HttpMacros.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core.impl +// don't import acyclic.file, there is a known circular dependency with core.Http import biz.enef.angular.core.{HttpError, HttpPromise} import biz.enef.angular.impl.MacroBase diff --git a/src/main/scala/biz/enef/angular/ext/RouteProvider.scala b/src/main/scala/biz/enef/angular/ext/RouteProvider.scala index 472d9bf..b3686b9 100644 --- a/src/main/scala/biz/enef/angular/ext/RouteProvider.scala +++ b/src/main/scala/biz/enef/angular/ext/RouteProvider.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.ext +import acyclic.file import scala.scalajs.js /** diff --git a/src/main/scala/biz/enef/angular/impl/AngularImpl.scala b/src/main/scala/biz/enef/angular/impl/AngularImpl.scala index 39faec8..f0456c7 100644 --- a/src/main/scala/biz/enef/angular/impl/AngularImpl.scala +++ b/src/main/scala/biz/enef/angular/impl/AngularImpl.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import scala.language.experimental.macros import scala.reflect.macros.blackbox.Context diff --git a/src/main/scala/biz/enef/angular/impl/ControllerMacros.scala b/src/main/scala/biz/enef/angular/impl/ControllerMacros.scala index 272fc5a..4205afb 100644 --- a/src/main/scala/biz/enef/angular/impl/ControllerMacros.scala +++ b/src/main/scala/biz/enef/angular/impl/ControllerMacros.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import biz.enef.angular._ import scala.language.experimental.macros diff --git a/src/main/scala/biz/enef/angular/impl/DirectiveMacros.scala b/src/main/scala/biz/enef/angular/impl/DirectiveMacros.scala index 7b2995f..6dce6dd 100644 --- a/src/main/scala/biz/enef/angular/impl/DirectiveMacros.scala +++ b/src/main/scala/biz/enef/angular/impl/DirectiveMacros.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import scala.reflect.macros.blackbox protected[angular] class DirectiveMacros(val c: blackbox.Context) extends MacroBase with ControllerMacroUtils { diff --git a/src/main/scala/biz/enef/angular/impl/MacroBase.scala b/src/main/scala/biz/enef/angular/impl/MacroBase.scala index 7715dad..06f09bb 100644 --- a/src/main/scala/biz/enef/angular/impl/MacroBase.scala +++ b/src/main/scala/biz/enef/angular/impl/MacroBase.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import biz.enef.angular.named import scala.reflect.macros.blackbox diff --git a/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala b/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala index ca8fa11..13696c9 100644 --- a/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala +++ b/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import scala.reflect.macros.blackbox protected[angular] class ModuleMacros(val c: blackbox.Context) extends MacroBase { diff --git a/src/main/scala/biz/enef/angular/impl/ServiceMacros.scala b/src/main/scala/biz/enef/angular/impl/ServiceMacros.scala index 9f6b2a4..0f8ef06 100644 --- a/src/main/scala/biz/enef/angular/impl/ServiceMacros.scala +++ b/src/main/scala/biz/enef/angular/impl/ServiceMacros.scala @@ -5,6 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.impl +import acyclic.file import scala.reflect.macros.blackbox protected [angular] class ServiceMacros(val c: blackbox.Context) extends MacroBase { From 879c381088ef7b96cac14ecebd0e4317e9a41d85 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Mon, 9 Feb 2015 23:23:52 +0100 Subject: [PATCH 05/10] Introduce AnnotatedFunction --- src/main/scala/biz/enef/angular/Angular.scala | 51 ++++++++++++++----- .../biz/enef/angular/AnnotatedFunction.scala | 16 ++++++ .../scala/biz/enef/angular/core/Http.scala | 2 +- ...{AngularImpl.scala => AngularMacros.scala} | 9 ++-- .../enef/angular/impl/AnnotationMacros.scala | 17 +++++++ .../biz/enef/angular/impl/MacroBase.scala | 14 ++--- src/main/scala/biz/enef/angular/package.scala | 7 +++ .../src/test/scala/test/ControllerTest.scala | 2 +- tests/src/test/scala/test/HttpTest.scala | 4 +- tests/src/test/scala/test/ModuleTest.scala | 6 +-- tests/src/test/scala/test/ServiceTest.scala | 2 +- 11 files changed, 97 insertions(+), 33 deletions(-) create mode 100644 src/main/scala/biz/enef/angular/AnnotatedFunction.scala rename src/main/scala/biz/enef/angular/impl/{AngularImpl.scala => AngularMacros.scala} (87%) create mode 100644 src/main/scala/biz/enef/angular/impl/AnnotationMacros.scala create mode 100644 src/main/scala/biz/enef/angular/package.scala diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index 84d207c..44f4452 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -8,8 +8,6 @@ package biz.enef.angular import acyclic.file import biz.enef.angular.core.Injector -import scala.language.experimental.macros - import scala.scalajs.js /** @@ -22,9 +20,30 @@ trait Angular extends js.Object { def injector(modules: js.Any, strictDi: Boolean = false) : Injector = js.native /** - * Creates or retrieves an Angular module. + * Retrieves an Angular module. + * + * @param name The name of the module to retrieve. + * + * @see [[https://docs.angularjs.org/api/ng/function/angular.module]] + */ + def module(name: String) : Module = js.native + + /** + * Creates an Angular module. * - * @param name The name of the module to create or retrieve. + * @param name The name of the module to create . + * @param requires Array with the names of other modules required by this module. + * If specified then a new module is being created. If unspecified then the + * module is being retrieved for further configuration. + * + * @see [[https://docs.angularjs.org/api/ng/function/angular.module]] + */ + def module(name: String, requires: js.Array[String]) : Module = js.native + + /** + * Creates an Angular module. + * + * @param name The name of the module to create. * @param requires Array with the names of other modules required by this module. * If specified then a new module is being created. If unspecified then the * module is being retrieved for further configuration. @@ -32,7 +51,7 @@ trait Angular extends js.Object { * * @see [[https://docs.angularjs.org/api/ng/function/angular.module]] */ - def module(name: String, requires: js.Array[String] = null, configFn: js.Function = null) : Module = js.native + def module(name: String, requires: js.Array[String], configFn: js.Array[_]) : Module = js.native /** * Serializes input into a JSON-formatted string. @@ -58,14 +77,20 @@ object Angular { /** * Returns the global Angular object */ - def apply() : Angular = js.Dynamic.global.angular.asInstanceOf[Angular] //macro impl.AngularImpl.apply + def apply() : Angular = js.Dynamic.global.angular.asInstanceOf[Angular] - /** - * Creates a new Angular - * @param name - * @param requires - * @return - */ - def module(name: String, requires: Iterable[String]) : Module = macro impl.AngularImpl.module + implicit class RichAngular(val self: Angular) extends AnyVal { + import scala.scalajs.js.JSConverters._ + import AnnotatedFunction._ + + /** + * Creates a new Angular module + * @param name + * @param requires + * @return + */ + def createModule(name: String, requires: Iterable[String] = Seq(), configFn: AnnotatedFunction[js.Function] = none) : Module = + if (configFn == none) self.module(name, requires.toJSArray) else self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) + } } diff --git a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala new file mode 100644 index 0000000..6737fc7 --- /dev/null +++ b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala @@ -0,0 +1,16 @@ +package biz.enef.angular + +import scala.scalajs.js + +class AnnotatedFunction[F <: js.Function](val inlineArrayAnnotatedFn: js.Array[js.Any]) extends AnyVal + +object AnnotatedFunction { + + import scala.language.implicitConversions + import scala.language.experimental.macros + + @inline implicit def annotatedFunction[F <: js.Function](f: F): AnnotatedFunction[F] = macro impl.AnnotationMacros.functionDIArray + + val none = new AnnotatedFunction[js.Function](js.Array()) + +} diff --git a/src/main/scala/biz/enef/angular/core/Http.scala b/src/main/scala/biz/enef/angular/core/Http.scala index 95a8f4d..a272bee 100644 --- a/src/main/scala/biz/enef/angular/core/Http.scala +++ b/src/main/scala/biz/enef/angular/core/Http.scala @@ -7,7 +7,7 @@ // Distributed under the MIT License (see included file LICENSE) package biz.enef.angular.core -import acyclic.file +// don't import acyclic.file, there is a known circular dependency with impl.HttpMacros import scala.concurrent.Future import scala.language.experimental.macros import scala.scalajs.js diff --git a/src/main/scala/biz/enef/angular/impl/AngularImpl.scala b/src/main/scala/biz/enef/angular/impl/AngularMacros.scala similarity index 87% rename from src/main/scala/biz/enef/angular/impl/AngularImpl.scala rename to src/main/scala/biz/enef/angular/impl/AngularMacros.scala index f0456c7..c45f726 100644 --- a/src/main/scala/biz/enef/angular/impl/AngularImpl.scala +++ b/src/main/scala/biz/enef/angular/impl/AngularMacros.scala @@ -9,21 +9,20 @@ import acyclic.file import scala.language.experimental.macros import scala.reflect.macros.blackbox.Context -protected[angular] class AngularImpl(val c: Context) { +protected[angular] class AngularMacros(val c: Context) extends MacroBase { import c.universe._ private def printCode(tree: Tree) = c.info( c.enclosingPosition, showCode(tree), true ) val angular = q"scala.scalajs.js.Dynamic.global.angular.asInstanceOf[biz.enef.angular.Angular]" - - def apply() = angular - +/* def module(name: c.Expr[String], requires: c.Expr[Iterable[String]]) = { - // create js.Array from Iterable with dependies + // create js.Array from Iterable with dependencies val dependencies = q"new scala.scalajs.js.JSConverters.JSRichGenTraversableOnce($requires).toJSArray" // create call to Angular.module() val tree = q"$angular.module($name,$dependencies)" //printCode(tree) tree } +*/ } diff --git a/src/main/scala/biz/enef/angular/impl/AnnotationMacros.scala b/src/main/scala/biz/enef/angular/impl/AnnotationMacros.scala new file mode 100644 index 0000000..6ffd1c4 --- /dev/null +++ b/src/main/scala/biz/enef/angular/impl/AnnotationMacros.scala @@ -0,0 +1,17 @@ +package biz.enef.angular.impl + +import scala.reflect.macros.blackbox.Context + +protected[angular] class AnnotationMacros(val c: Context) extends MacroBase { + import c.universe._ + + def functionDIArray(f: c.Tree) = { + val diArray = createFunctionDIArray(f) + + q"""{import biz.enef.angular.AnnotatedFunction + new AnnotatedFunction($diArray) + }""" + + } + +} diff --git a/src/main/scala/biz/enef/angular/impl/MacroBase.scala b/src/main/scala/biz/enef/angular/impl/MacroBase.scala index 06f09bb..75e02c4 100644 --- a/src/main/scala/biz/enef/angular/impl/MacroBase.scala +++ b/src/main/scala/biz/enef/angular/impl/MacroBase.scala @@ -26,21 +26,21 @@ protected[angular] abstract class MacroBase { * @param tree * @param msg */ - def printCode(tree: Tree, msg: String = "") = + protected[this] def printCode(tree: Tree, msg: String = "") = c.info( c.enclosingPosition, s"""$msg |${showCode(tree)} """.stripMargin, true ) - def makeArgsList(f: MethodSymbol) = { + protected[this] def makeArgsList(f: MethodSymbol) = { f.paramLists.head.map( p => { val name = TermName(c.freshName("x")) (q"$name: ${p.typeSignature}", q"$name") }).unzip } - def getDINames(f: MethodSymbol) = { + protected[this] def getDINames(f: MethodSymbol) = { f.paramLists.head.map{ p=> p.annotations.find( _.tree.tpe =:= namedAnnotation ).map { a => val name = a.tree.children.tail.head.toString @@ -57,7 +57,7 @@ protected[angular] abstract class MacroBase { * * @param ct class type */ - def createDIArray(ct: Type) = { + protected[this] def createDIArray(ct: Type) = { val m = getConstructor(ct) val deps = getDINames(m) val (params,args) = makeArgsList(m) @@ -65,16 +65,16 @@ protected[angular] abstract class MacroBase { } - def getConstructor(ct: Type) = ct.decls.filter( _.isConstructor ).collect{ case m: MethodSymbol => m}.head + protected[this] def getConstructor(ct: Type) = ct.decls.filter( _.isConstructor ).collect{ case m: MethodSymbol => m}.head // TODO: support DI name annotations - def createFunctionDIArray(t: c.Tree) = { + protected[this] def createFunctionDIArray(t: c.Tree) = { val (f,params) = analyzeFunction(t) val diNames = params.map( p => p._2.toString ) q"js.Array[Any](..$diNames, $f:js.Function)" } - def analyzeFunction(t: c.Tree) = { + protected[this] def analyzeFunction(t: c.Tree) = { val (m:Tree,params:List[ValDef]) = t match { case q"(..$params) => $body" => (t,params) case q"{(..$params) => $body}" => (t.children.head,params) diff --git a/src/main/scala/biz/enef/angular/package.scala b/src/main/scala/biz/enef/angular/package.scala new file mode 100644 index 0000000..8f9de26 --- /dev/null +++ b/src/main/scala/biz/enef/angular/package.scala @@ -0,0 +1,7 @@ +package biz.enef + +package object angular { + + val angular = Angular() + +} diff --git a/tests/src/test/scala/test/ControllerTest.scala b/tests/src/test/scala/test/ControllerTest.scala index 8265700..2fb6188 100644 --- a/tests/src/test/scala/test/ControllerTest.scala +++ b/tests/src/test/scala/test/ControllerTest.scala @@ -13,7 +13,7 @@ import scala.scalajs.js object ControllerTest extends AngulateTestSuite { override val tests = TestSuite { - implicit val module = Angular.module("test", Nil) + implicit val module = angular.createModule("test") 'ScopeController-{ diff --git a/tests/src/test/scala/test/HttpTest.scala b/tests/src/test/scala/test/HttpTest.scala index 1f8d9ea..d7b61df 100644 --- a/tests/src/test/scala/test/HttpTest.scala +++ b/tests/src/test/scala/test/HttpTest.scala @@ -5,7 +5,7 @@ // Distributed under the MIT License (see included file LICENSE) package test -import biz.enef.angular.Angular +import biz.enef.angular._ import biz.enef.angular.core.HttpService import utest._ @@ -16,7 +16,7 @@ import js.Dynamic.literal object HttpTest extends AngulateTestSuite { override val tests = TestSuite { - implicit val module = Angular.module("test", Seq("ngMockE2E")) + implicit val module = angular.createModule("test", Seq("ngMockE2E")) module.run( ($httpBackend: js.Dynamic) => { $httpBackend.whenGET("/ok").respond( literal(id = 200) ) $httpBackend.whenGET("/error").respond(404,"resource not found") diff --git a/tests/src/test/scala/test/ModuleTest.scala b/tests/src/test/scala/test/ModuleTest.scala index cf0527f..9592cf4 100644 --- a/tests/src/test/scala/test/ModuleTest.scala +++ b/tests/src/test/scala/test/ModuleTest.scala @@ -5,16 +5,16 @@ // Distributed under the MIT License (see included file LICENSE) package test -import biz.enef.angular.Angular +import biz.enef.angular._ import utest._ object ModuleTest extends AngulateTestSuite { override val tests = TestSuite { 'new-{ - val module1 = Angular.module("module1", Nil) + val module1 = angular.createModule("module1") assert( module1.name == "module1" ) - val module2 = Angular.module("module2", Seq("module1")) + val module2 = angular.createModule("module2", Seq("module1")) assert( module2.name == "module2" ) } diff --git a/tests/src/test/scala/test/ServiceTest.scala b/tests/src/test/scala/test/ServiceTest.scala index 79397a2..6f454ad 100644 --- a/tests/src/test/scala/test/ServiceTest.scala +++ b/tests/src/test/scala/test/ServiceTest.scala @@ -13,7 +13,7 @@ import scala.scalajs.js object ServiceTest extends AngulateTestSuite { override val tests = TestSuite { - implicit val module = Angular.module("test", Nil) + implicit val module = angular.createModule("test") 'serviceOf-{ From 4d920e9f0292ea01666f7bedd132e22544586c61 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Mon, 9 Feb 2015 23:29:55 +0100 Subject: [PATCH 06/10] Simplify AnnotatedFunction, no need for none --- src/main/scala/biz/enef/angular/Angular.scala | 19 +++++++++++++++++-- .../biz/enef/angular/AnnotatedFunction.scala | 2 -- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index 44f4452..b365fb6 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -83,14 +83,29 @@ object Angular { import scala.scalajs.js.JSConverters._ import AnnotatedFunction._ + /** + * Creates a new Angular module + * @param name + * @return + */ + def createModule(name: String) : Module = self.module(name, js.Array()) + + /** + * Creates a new Angular module + * @param name + * @param requires + * @return + */ + def createModule(name: String, requires: Iterable[String]) : Module = self.module(name, requires.toJSArray) + /** * Creates a new Angular module * @param name * @param requires * @return */ - def createModule(name: String, requires: Iterable[String] = Seq(), configFn: AnnotatedFunction[js.Function] = none) : Module = - if (configFn == none) self.module(name, requires.toJSArray) else self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) + def createModule(name: String, requires: Iterable[String] = Seq(), configFn: AnnotatedFunction[js.Function]) : Module = + self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) } } diff --git a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala index 6737fc7..7181f14 100644 --- a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala +++ b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala @@ -11,6 +11,4 @@ object AnnotatedFunction { @inline implicit def annotatedFunction[F <: js.Function](f: F): AnnotatedFunction[F] = macro impl.AnnotationMacros.functionDIArray - val none = new AnnotatedFunction[js.Function](js.Array()) - } From 23fec9fdf5a9d60876f102b896d60dcae362bebc Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Wed, 11 Feb 2015 23:59:48 +0100 Subject: [PATCH 07/10] Annotated functions 0 to 5 --- src/main/scala/biz/enef/angular/Angular.scala | 3 +-- .../biz/enef/angular/AnnotatedFunction.scala | 16 +++++++++++++--- tests/src/test/scala/test/ModuleTest.scala | 9 +++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index b365fb6..52450d2 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -81,7 +81,6 @@ object Angular { implicit class RichAngular(val self: Angular) extends AnyVal { import scala.scalajs.js.JSConverters._ - import AnnotatedFunction._ /** * Creates a new Angular module @@ -104,7 +103,7 @@ object Angular { * @param requires * @return */ - def createModule(name: String, requires: Iterable[String] = Seq(), configFn: AnnotatedFunction[js.Function]) : Module = + def createModule(name: String, requires: Iterable[String], configFn: AnnotatedFunction) : Module = self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) } diff --git a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala index 7181f14..4b6a483 100644 --- a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala +++ b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala @@ -1,14 +1,24 @@ package biz.enef.angular import scala.scalajs.js +import scala.language.implicitConversions -class AnnotatedFunction[F <: js.Function](val inlineArrayAnnotatedFn: js.Array[js.Any]) extends AnyVal +@inline class AnnotatedFunction(val inlineArrayAnnotatedFn: js.Array[_]) extends AnyVal object AnnotatedFunction { - import scala.language.implicitConversions import scala.language.experimental.macros - @inline implicit def annotatedFunction[F <: js.Function](f: F): AnnotatedFunction[F] = macro impl.AnnotationMacros.functionDIArray + @inline implicit def annotatedFunction(f: Function0[Nothing]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + + @inline implicit def annotatedFunction(f: Function1[Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + + @inline implicit def annotatedFunction(f: Function2[Nothing, Any, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + + @inline implicit def annotatedFunction(f: Function3[Nothing, Any, Any, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + + @inline implicit def annotatedFunction(f: Function4[Nothing, Any, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + + @inline implicit def annotatedFunction(f: Function5[Nothing, Nothing, Nothing, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray } diff --git a/tests/src/test/scala/test/ModuleTest.scala b/tests/src/test/scala/test/ModuleTest.scala index 9592cf4..5f1c2c1 100644 --- a/tests/src/test/scala/test/ModuleTest.scala +++ b/tests/src/test/scala/test/ModuleTest.scala @@ -8,6 +8,8 @@ package test import biz.enef.angular._ import utest._ +import scala.scalajs.js + object ModuleTest extends AngulateTestSuite { override val tests = TestSuite { 'new-{ @@ -18,5 +20,12 @@ object ModuleTest extends AngulateTestSuite { assert( module2.name == "module2" ) } + + 'newWithConfig-{ + val module3 = angular.createModule("module3", Nil, ($logProvider: js.Dynamic) => { + $logProvider.debugEnabled(true) + }) + assert( module3.name == "module3" ) + } } } From 28b08ccebc9556d9bc1fb3019526591367b52b82 Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Thu, 12 Feb 2015 00:44:46 +0100 Subject: [PATCH 08/10] Replace ModuleMacros by rich wrapper class --- src/main/scala/biz/enef/angular/Angular.scala | 11 +- .../biz/enef/angular/AnnotatedFunction.scala | 10 +- src/main/scala/biz/enef/angular/Module.scala | 206 ++++++++---------- .../biz/enef/angular/impl/ModuleMacros.scala | 34 --- .../test/scala/test/AngulateTestSuite.scala | 9 +- tests/src/test/scala/test/HttpTest.scala | 6 +- 6 files changed, 115 insertions(+), 161 deletions(-) delete mode 100644 src/main/scala/biz/enef/angular/impl/ModuleMacros.scala diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index 52450d2..49a7928 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -6,6 +6,7 @@ package biz.enef.angular import acyclic.file +import biz.enef.angular.Module.RichModule import biz.enef.angular.core.Injector import scala.scalajs.js @@ -51,7 +52,7 @@ trait Angular extends js.Object { * * @see [[https://docs.angularjs.org/api/ng/function/angular.module]] */ - def module(name: String, requires: js.Array[String], configFn: js.Array[_]) : Module = js.native + def module(name: String, requires: js.Array[String], configFn: js.Array[Any]) : Module = js.native /** * Serializes input into a JSON-formatted string. @@ -79,7 +80,7 @@ object Angular { */ def apply() : Angular = js.Dynamic.global.angular.asInstanceOf[Angular] - implicit class RichAngular(val self: Angular) extends AnyVal { + @inline final implicit class RichAngular(val self: Angular) extends AnyVal { import scala.scalajs.js.JSConverters._ /** @@ -87,7 +88,7 @@ object Angular { * @param name * @return */ - def createModule(name: String) : Module = self.module(name, js.Array()) + @inline def createModule(name: String) : RichModule = self.module(name, js.Array()) /** * Creates a new Angular module @@ -95,7 +96,7 @@ object Angular { * @param requires * @return */ - def createModule(name: String, requires: Iterable[String]) : Module = self.module(name, requires.toJSArray) + @inline def createModule(name: String, requires: Iterable[String]) : RichModule = self.module(name, requires.toJSArray) /** * Creates a new Angular module @@ -103,7 +104,7 @@ object Angular { * @param requires * @return */ - def createModule(name: String, requires: Iterable[String], configFn: AnnotatedFunction) : Module = + @inline def createModule(name: String, requires: Iterable[String], configFn: AnnotatedFunction) : RichModule = self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) } diff --git a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala index 4b6a483..87d0e13 100644 --- a/src/main/scala/biz/enef/angular/AnnotatedFunction.scala +++ b/src/main/scala/biz/enef/angular/AnnotatedFunction.scala @@ -3,21 +3,21 @@ package biz.enef.angular import scala.scalajs.js import scala.language.implicitConversions -@inline class AnnotatedFunction(val inlineArrayAnnotatedFn: js.Array[_]) extends AnyVal +@inline class AnnotatedFunction(val inlineArrayAnnotatedFn: js.Array[Any]) extends AnyVal object AnnotatedFunction { import scala.language.experimental.macros - @inline implicit def annotatedFunction(f: Function0[Nothing]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + @inline implicit def annotatedFunction(f: Function0[Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray @inline implicit def annotatedFunction(f: Function1[Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray - @inline implicit def annotatedFunction(f: Function2[Nothing, Any, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + @inline implicit def annotatedFunction(f: Function2[Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray - @inline implicit def annotatedFunction(f: Function3[Nothing, Any, Any, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + @inline implicit def annotatedFunction(f: Function3[Nothing, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray - @inline implicit def annotatedFunction(f: Function4[Nothing, Any, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray + @inline implicit def annotatedFunction(f: Function4[Nothing, Nothing, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray @inline implicit def annotatedFunction(f: Function5[Nothing, Nothing, Nothing, Nothing, Nothing, Any]): AnnotatedFunction = macro impl.AnnotationMacros.functionDIArray diff --git a/src/main/scala/biz/enef/angular/Module.scala b/src/main/scala/biz/enef/angular/Module.scala index 30a1a4c..79c2ae4 100644 --- a/src/main/scala/biz/enef/angular/Module.scala +++ b/src/main/scala/biz/enef/angular/Module.scala @@ -21,18 +21,6 @@ trait Module extends js.Object { */ def name: String = js.native - /** - * Defines an animation hook that can be later used with the \$animate service and directives that use this service. - * - * @note animations take effect only if the ngAnimate module is loaded - * - * @param name animation name - * @param animationFactory Factory function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#animate]] - */ - def animation(name: String, animationFactory: js.Function): Module = js.native - /** * Defines an animation hook that can be later used with the \$animate service and directives that use this service. * @@ -46,15 +34,6 @@ trait Module extends js.Object { */ def animation(name: String, animationFactory: js.Array[Any]): Module = js.native -// /** -// * Use this method to register work which needs to be performed on module loading. -// * -// * @param configFn This function is executed on module load -// * -// * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#config]] -// */ -// def config(configFn: js.Function): Module = js.native - /** * Use this method to register work which needs to be performed on module loading. * @@ -75,16 +54,6 @@ trait Module extends js.Object { */ def constant(name: String, value: js.Any): Module = js.native - /** - * Registers a controller. - * - * @param name The name of the controller - * @param constructor Controller construction function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#controller]] - */ - def controller(name: String, constructor: js.Function): Module = js.native - /** * Registers a controller. * @@ -96,16 +65,6 @@ trait Module extends js.Object { */ def controller(name: String, constructor: js.Array[Any]): Module = js.native - /** - * Register a new directive with the compiler. - * - * @param name Name of the directive in camel-case (ie `ngBind`) - * @param directiveFactory Directive constructor function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#directive]] - */ - def directive(name: String, directiveFactory: js.Function): Module = js.native - /** * Register a new directive with the compiler. * @@ -117,16 +76,6 @@ trait Module extends js.Object { */ def directive(name: String, directiveFactory: js.Array[Any]): Module = js.native - /** - * Register a service factory. - * - * @param name The name of the service - * @param constructor Service constructor function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#factory]] - */ - def factory(name: String, constructor: js.Function): Module = js.native - /** * Register a service factory. * @@ -138,16 +87,6 @@ trait Module extends js.Object { */ def factory(name: String, constructor: js.Array[Any]): Module = js.native - /** - * Register a filter factory. - * - * @param name The name of the filter - * @param filterFactory Filter constructor function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#filter]] - */ - def filter(name: String, filterFactory: js.Function): Module = js.native - /** * Register a filter factory. * @@ -159,16 +98,6 @@ trait Module extends js.Object { */ def filter(name: String, filterFactory: js.Array[Any]): Module = js.native - /** - * Register a provider function with the \$injector. - * - * @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key. - * @param constructor Provider constructor function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#provider]] - */ - def provider(name: String, constructor: js.Function): Module = js.native - /** * Register a provider function with the \$injector. * @@ -180,15 +109,6 @@ trait Module extends js.Object { */ def provider(name: String, constructor: js.Array[Any]): Module = js.native -// /** -// * Use this method to register work which should be performed when the injector is done loading all modules. -// * -// * @param initializationFn This function is executed when all modules have been loaded -// * -// * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#run]] -// */ -// def run(initializationFn: js.Function): Module = js.native - /** * Use this method to register work which should be performed when the injector is done loading all modules. * @@ -199,16 +119,6 @@ trait Module extends js.Object { */ def run(initializationFn: js.Array[Any]): Module = js.native - /** - * Register a service constructor which will be invoked with `new` to create the service instance. - * - * @param name The name of the service - * @param constructor A class constructor function - * - * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#service]] - */ - def service(name: String, constructor: js.Function): Module = js.native - /** * Register a service constructor which will be invoked with `new` to create the service instance. * @@ -226,7 +136,12 @@ object Module { import scala.language.experimental.macros import scala.language.implicitConversions - final implicit class RichModule(val self: Module) extends AnyVal { + @inline final implicit class RichModule(val self: Module) extends AnyVal { + + /** + * The name of the module + */ + @inline def name: String = self.name //------------------------------ ENHANCEMENTS ------------------------------ /** @@ -236,7 +151,7 @@ object Module { * * @tparam T Controller class */ - def controllerOf[T <: NGController]: Module = macro impl.ControllerMacros.controllerOf[T] + @inline def controllerOf[T <: NGController]: Module = macro impl.ControllerMacros.controllerOf[T] /** * Registers the specified controller using an explicitly given controller name. @@ -246,7 +161,7 @@ object Module { * @param name The controller name * @tparam T Controller class */ - def controllerOf[T <: NGController](name: String): Module = macro impl.ControllerMacros.controllerOfWithName[T] + @inline def controllerOf[T <: NGController](name: String): Module = macro impl.ControllerMacros.controllerOfWithName[T] /** * Registers the specified class as Angular service. @@ -256,7 +171,7 @@ object Module { * * @tparam T Service class */ - def serviceOf[T <: Service]: Module = macro impl.ServiceMacros.serviceOf[T] + @inline def serviceOf[T <: Service]: Module = macro impl.ServiceMacros.serviceOf[T] /** * Registers the specified class as Angular service using the explicitly given service name. @@ -266,7 +181,7 @@ object Module { * @param name The service name * @tparam T Service class */ - def serviceOf[T <: Service](name: String): Module = macro impl.ServiceMacros.serviceOfWithName[T] + @inline def serviceOf[T <: Service](name: String): Module = macro impl.ServiceMacros.serviceOfWithName[T] /** * Registers the specified class as Angular directive. @@ -277,7 +192,7 @@ object Module { * * @tparam T Class defining the directive */ - def directiveOf[T <: Directive]: Module = macro impl.DirectiveMacros.directiveOf[T] + @inline def directiveOf[T <: Directive]: Module = macro impl.DirectiveMacros.directiveOf[T] /** * Registers the specified class as Angular directive under the given name. @@ -287,32 +202,99 @@ object Module { * @param name The name of the directive * @tparam T Class defining the directive */ - def directiveOf[T <: Directive](name: String): Module = macro impl.DirectiveMacros.directiveOfWithName[T] - - def config(f: Function0[Any]): Module = macro impl.ModuleMacros.config + @inline def directiveOf[T <: Directive](name: String): Module = macro impl.DirectiveMacros.directiveOfWithName[T] - def config(f: Function1[Nothing, Any]): Module = macro impl.ModuleMacros.config - - def config(f: Function2[Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.config - - def config(f: Function3[Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.config + /** + * Defines an animation hook that can be later used with the \$animate service and directives that use this service. + * + * @note animations take effect only if the ngAnimate module is loaded + * + * @param name animation name + * @param animationFactory Factory function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#animate]] + */ + @inline def animation(name: String, animationFactory: AnnotatedFunction): Module = self.animation(name, animationFactory.inlineArrayAnnotatedFn) - def config(f: Function4[Nothing, Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.config + /** + * Use this method to register work which needs to be performed on module loading. + * + * @param configFn This function is executed on module load + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#config]] + */ + @inline def config(configFn: AnnotatedFunction): Module = self.config(configFn.inlineArrayAnnotatedFn) - def config(f: Function5[Nothing, Nothing, Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.config + /** + * Registers a controller. + * + * @param name The name of the controller + * @param constructor Controller construction function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#controller]] + */ + @inline def controller(name: String, constructor: AnnotatedFunction): Module = self.controller(name, constructor.inlineArrayAnnotatedFn) - def run(f: Function0[Any]): Module = macro impl.ModuleMacros.run + /** + * Register a new directive with the compiler. + * + * @param name Name of the directive in camel-case (ie `ngBind`) + * @param directiveFactory Directive constructor function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#directive]] + */ + @inline def directive(name: String, directiveFactory: AnnotatedFunction): Module = self.directive(name, directiveFactory.inlineArrayAnnotatedFn) - def run(f: Function1[Nothing, Any]): Module = macro impl.ModuleMacros.run + /** + * Register a service factory. + * + * @param name The name of the service + * @param constructor Service constructor function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#factory]] + */ + @inline def factory(name: String, constructor: AnnotatedFunction): Module = self.factory(name, constructor.inlineArrayAnnotatedFn) - def run(f: Function2[Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.run + /** + * Register a filter factory. + * + * @param name The name of the filter + * @param filterFactory Filter constructor function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#filter]] + */ + @inline def filter(name: String, filterFactory: AnnotatedFunction): Module = self.filter(name, filterFactory.inlineArrayAnnotatedFn) - def run(f: Function3[Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.run + /** + * Register a provider function with the \$injector. + * + * @param name The name of the instance. NOTE: the provider will be available under name + 'Provider' key. + * @param constructor Provider constructor function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#provider]] + */ + @inline def provider(name: String, constructor: AnnotatedFunction): Module = self.provider(name, constructor.inlineArrayAnnotatedFn) - def run(f: Function4[Nothing, Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.run + /** + * Use this method to register work which should be performed when the injector is done loading all modules. + * + * @param initializationFn This function is executed on module fully loaded + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#run]] + */ + @inline def run(initializationFn: AnnotatedFunction): Module = self.run(initializationFn.inlineArrayAnnotatedFn) - def run(f: Function5[Nothing, Nothing, Nothing, Nothing, Nothing, Any]): Module = macro impl.ModuleMacros.run + /** + * Register a service constructor which will be invoked with `new` to create the service instance. + * + * @param name The name of the service + * @param constructor A class constructor function + * + * @see [[https://docs.angularjs.org/api/ng/type/angular.Module#service]] + */ + @inline def service(name: String, constructor: AnnotatedFunction): Module = self.service(name, constructor.inlineArrayAnnotatedFn) + @inline implicit def autoUnwrapRichModule(m: RichModule): Module = m.self } } diff --git a/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala b/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala deleted file mode 100644 index 13696c9..0000000 --- a/src/main/scala/biz/enef/angular/impl/ModuleMacros.scala +++ /dev/null @@ -1,34 +0,0 @@ -// - Project: scalajs-angulate (https://github.com/jokade/scalajs-angulate) -// Description: Provides various macro enhancements to the Module API -// -// Copyright (c) 2015 Johannes Kastner -// Distributed under the MIT License (see included file LICENSE) -package biz.enef.angular.impl - -import acyclic.file -import scala.reflect.macros.blackbox - -protected[angular] class ModuleMacros(val c: blackbox.Context) extends MacroBase { - import c.universe._ - - // print generated code to console during compilation - private lazy val logCode = c.settings.exists( _ == "biz.enef.angular.ModuleMacros.debug" ) - - def config(f: c.Tree) = { - val module = Select(c.prefix.tree, TermName("self")) - val tree = q"$module.config( ${createFunctionDIArray(f)} )" - - if(logCode) printCode(tree) - - tree - } - - def run(f: c.Tree) = { - val module = Select(c.prefix.tree, TermName("self")) - val tree = q"$module.run( ${createFunctionDIArray(f)} )" - - if(logCode) printCode(tree) - - tree - } -} diff --git a/tests/src/test/scala/test/AngulateTestSuite.scala b/tests/src/test/scala/test/AngulateTestSuite.scala index 7b4b064..b5c59d5 100644 --- a/tests/src/test/scala/test/AngulateTestSuite.scala +++ b/tests/src/test/scala/test/AngulateTestSuite.scala @@ -5,12 +5,13 @@ // Distributed under the MIT License (see included file LICENSE) package test -import biz.enef.angular.{Controller, Scope, Module, Angular} +import biz.enef.angular.Module.RichModule +import biz.enef.angular.{Scope, Angular} import utest._ import scala.concurrent.Promise import scala.scalajs.js -import scala.scalajs.js.{Dictionary, UndefOr} +import scala.scalajs.js.UndefOr import js.Dynamic.literal /** @@ -25,7 +26,7 @@ trait AngulateTestSuite extends TestSuite { * @tparam T * @return */ - def injection[T](name: String)(implicit module: Module) : T = + def injection[T](name: String)(implicit module: RichModule) : T = Angular().injector(js.Array("ng",module.name)).get(name).asInstanceOf[T] /** @@ -42,7 +43,7 @@ trait AngulateTestSuite extends TestSuite { * @param module * @tparam T the scope type */ - def controller[T](name: String)(implicit module: Module) : T = { + def controller[T](name: String)(implicit module: RichModule) : T = { val $controller = injection[js.Function2[String,js.Object,js.Any]]("$controller") val $rootScope = injection[Scope]("$rootScope") val scope = $rootScope.$new(false) diff --git a/tests/src/test/scala/test/HttpTest.scala b/tests/src/test/scala/test/HttpTest.scala index d7b61df..f1153c6 100644 --- a/tests/src/test/scala/test/HttpTest.scala +++ b/tests/src/test/scala/test/HttpTest.scala @@ -13,11 +13,15 @@ import scala.scalajs.js import scala.util.{Failure, Success} import js.Dynamic.literal +import scala.language.implicitConversions +import AnnotatedFunction._ +import Module._ + object HttpTest extends AngulateTestSuite { override val tests = TestSuite { implicit val module = angular.createModule("test", Seq("ngMockE2E")) - module.run( ($httpBackend: js.Dynamic) => { + module.run ( ($httpBackend: js.Dynamic) => { $httpBackend.whenGET("/ok").respond( literal(id = 200) ) $httpBackend.whenGET("/error").respond(404,"resource not found") $httpBackend.whenPOST("/empty").respond(204) From cde41f3cd8044b14e38861070761c441e096f84c Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Sat, 14 Feb 2015 00:58:29 +0100 Subject: [PATCH 09/10] Add bootstrap function --- src/main/scala/biz/enef/angular/Angular.scala | 16 ++++++++++++++++ tests/src/test/scala/test/ModuleTest.scala | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/src/main/scala/biz/enef/angular/Angular.scala b/src/main/scala/biz/enef/angular/Angular.scala index 49a7928..a2e411a 100644 --- a/src/main/scala/biz/enef/angular/Angular.scala +++ b/src/main/scala/biz/enef/angular/Angular.scala @@ -8,6 +8,7 @@ package biz.enef.angular import acyclic.file import biz.enef.angular.Module.RichModule import biz.enef.angular.core.Injector +import org.scalajs.dom.html.Element import scala.scalajs.js @@ -71,8 +72,14 @@ trait Angular extends js.Object { */ def uppercase(string: String) : String = js.native + def bootstrap(element: Element, modules: js.Array[Any]): Injector = js.native + + def bootstrap(element: Element, modules: js.Array[Any], config: AngularConfiguration): Injector = js.native + } +case class AngularConfiguration(strictDi: Boolean = false) + object Angular { /** @@ -106,6 +113,15 @@ object Angular { */ @inline def createModule(name: String, requires: Iterable[String], configFn: AnnotatedFunction) : RichModule = self.module(name, requires.toJSArray, configFn.inlineArrayAnnotatedFn) + + @inline def bootstrap(element: Element, modules: Iterable[String]) = self.bootstrap(element, modules.toJSArray.asInstanceOf[js.Array[Any]]) + + @inline def bootstrap(element: Element, modules: Seq[AnnotatedFunction]) = self.bootstrap(element, modules.map(_.inlineArrayAnnotatedFn).toJSArray.asInstanceOf[js.Array[Any]]) + + @inline def bootstrap(element: Element, modules: Iterable[String], config: AngularConfiguration) = self.bootstrap(element, modules.toJSArray.asInstanceOf[js.Array[Any]], config) + + @inline def bootstrap(element: Element, modules: Seq[AnnotatedFunction], config: AngularConfiguration) = self.bootstrap(element, modules.map(_.inlineArrayAnnotatedFn).toJSArray.asInstanceOf[js.Array[Any]], config) + } } diff --git a/tests/src/test/scala/test/ModuleTest.scala b/tests/src/test/scala/test/ModuleTest.scala index 5f1c2c1..1c9642f 100644 --- a/tests/src/test/scala/test/ModuleTest.scala +++ b/tests/src/test/scala/test/ModuleTest.scala @@ -9,6 +9,7 @@ import biz.enef.angular._ import utest._ import scala.scalajs.js +import org.scalajs.dom object ModuleTest extends AngulateTestSuite { override val tests = TestSuite { @@ -22,10 +23,14 @@ object ModuleTest extends AngulateTestSuite { } 'newWithConfig-{ + var configFnCalled = false val module3 = angular.createModule("module3", Nil, ($logProvider: js.Dynamic) => { $logProvider.debugEnabled(true) + configFnCalled = true }) + angular.bootstrap(dom.document.body, Seq("module3")) assert( module3.name == "module3" ) + assert( configFnCalled ) } } } From c314a20807947b2e94d80769782db31be307197f Mon Sep 17 00:00:00 2001 From: Ludovic Claude Date: Mon, 16 Feb 2015 00:02:47 +0100 Subject: [PATCH 10/10] Document AnnotatedFunction --- README.md | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8ed6087..5381edd 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ import biz.enef.angular._ import biz.enef.angular.core.HttpService import biz.enef.angular.ext.{Route, RouteProvider} -val module = Angular.module("app", Seq("ui.bootstrap","ngRoute")) +val module = angular.createModule("app", Seq("ui.bootstrap","ngRoute")) module.serviceOf[UserService] // - or, setting the service name explicitly: @@ -86,7 +86,7 @@ Classes extending `Controller` export all their public `var`s, `val`s and `def`s Defining a custom `Scope` type is not required. However, instantiating the controller in the template requires the new AngularJS `as` syntax: ```scala -val module Angular.module("counter", Nil) +val module = angular.createModule("counter") module.controllerOf[CounterCtrl] class CounterCtrl extends Controller { @@ -134,7 +134,7 @@ class Ctrl($scope: Scope) extends Controller { Classes extending `ScopeController` are "old-style" AngularJS controllers, where the scope available in the template must be injected explicitly: ```scala -val module Angular.module("counter", Nil) +val module = angular.createModule("counter") module.controllerOf[CounterCtrl] /* Option A: using a custom defined Scope type */ @@ -195,7 +195,7 @@ class UserCtrl(@named("$http") httpService: HttpService) extends Controller { ``` -DI is also supported for functions passed to `Module.config()` and `Module.run()`: +DI is also supported for functions passed to methods such as `Module.config()` and `Module.run()`: ```scala module.config( ($routeProvider: RouteProvider) => { /* ... */ @@ -206,7 +206,17 @@ def routing($routeProvider: RouteProvider) = $routeProvider.when( /* ... */ ) module.config( routing _ ) ``` -However, the `@named` annoation is not supported for function DI, i.e. the _parameter names must match the services_ to be injected for this to work. +However, the `@named` annotation is not supported for function DI, i.e. the _parameter names must match the services_ to be injected for this to work. + +Functions are implicitely converted to AnnotatedFunction which transform the function into an array following the [Inline Array Annotation](https://docs.angularjs.org/guide/di). +You can use this to declare the function in one place and use it in another: +```scala +val configFn: AnnotatedFunction = ($logProvider: js.Dynamic) => { + /* ... */ +} + +val module = angular.createModule("app", Nil, configFn) +``` ### Services Services can be implemented as plain classes extending the `Service` trait. As with controllers,