ExternalJSEnv doesn't see javascript dependencies in launcher-webpage - scala.js

Simple setup :
lazy val root = project.in(file("."))
.settings(scalaJSSettings: _*)
.settings(utestJsSettings: _*)
.settings(persistLauncher := true)
.settings(persistLauncher in Test := false)
.settings(
name := "bindings",
version := "0.0.1-SNAPSHOT",
scalaVersion := "2.11.2",
libraryDependencies ++= Seq(
"org.scala-lang.modules.scalajs" %%% "scalajs-dom" % "0.6",
"com.lihaoyi" %%% "utest" % "0.2.3" % "test"
),
jsDependencies += "org.webjars" % "d3js" % "3.4.11" / "d3.js", //d3.min.js
autoCompilerPlugins := true,
test in Test := (test in(Test, fastOptStage)).value,
requiresDOM := true,
traceLevel := 0
)
phantomjs-launcher-webpage.html contains really super valid stuff :
<script type="text/javascript" src="/path/scalajs-bindings/target/scala-2.11/classes/d3.js"></script>
<script type="text/javascript" src="/path/scalajs-bindings/target/scala-2.11/bindings-fastopt.js"></script>
<script type="text/javascript">
// Phantom.js code launcher
// Origin: /path/scalajs-bindings/target/scala-2.11/bindings-launcher.js
window.addEventListener('load', function() {
((typeof global === "object" && global &&
global["Object"] === Object) ? global : this)["com"]["whatever"]["scalajs"]["Appp"]().main();
}, false);
</script>
Now, in browser it can call d3, I tried with equivalent html, but using PhantomJS or NodeJS if I do fastOptStage::run or fastOptStage::test I get
TypeError: undefined is not an object (evaluating 'd3["scale"]')
The code looks like this:
object Appp extends JSApp {
override def main(): Unit = {
val fill = d3.scale.category20()
println(fill)
}
}

You can have your object d3 extend js.GlobalScope and it will be looked for in the global JavaScript scope (i.e. where d3.js will put it):
object Appp extends JSApp {
object d3 extends js.GlobalScope {
def scale: js.Dynamic = ???
}
override def main(): Unit = {
val fill = d3.scale.category20()
println(fill)
}
}
However, in the case of d3, you might want to consider having the d3 package object itself extend js.GlobalScope:
package mbostock
package object d3 extends js.GlobalScope {
def scale: js.Dynamic = ???
}
I strongly recommend reading the JavaScript interoperability guide, notably the part about calling JavaScript from Scala.js.

The reason it wasn't working was that I had d3 object in a package object :
package object mbostock {
object d3 {...}
}
It also doesn't work as an inner class :
object Appp extends JSApp {
object d3 {...}
override def main(): Unit = {...}
}

Related

Scala conditional compilation

I'm writing a Scala program and I want it to work with two version of a big library.
This big library's version 2 changes the API very slightly (only one class constructor signature has an extra parameter).
// Lib v1
class APIClass(a: String, b:Integer){
...
}
// Lib v2
class APIClass(a: String, b: Integer, c: String){
...
}
// And my code extends APIClass.. And I have no #IFDEF
class MyClass() extends APIClass("x", 1){ // <-- would be APIClass("x", 1, "y") in library v2
...
}
I really don't want to branch my code. Because then I'd need to maintain two branches, and tomorrow 3,4,..branches for tiny API changes :(
Ideally we'd have a simple preprocessor in Scala, but the idea was rejected long ago by Scala community.
A thing I don't really couldn't grasp is: can Scalameta help simulating a preprocessor in this case? I.e. parsing two source files conditionally to - say - an environmental variable known at compile time?
If not, how would you approach this real life problem?
1. C++ preprocessors can be used with Java/Scala if you run cpp before javac or scalac (also there is Manifold).
2. If you really want to have conditional compilation in Scala you can use macro annotation (expanding at compile time)
macros/src/main/scala/extendsAPIClass.scala
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro paradise")
class extendsAPIClass extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro ExtendsAPIClassMacro.impl
}
object ExtendsAPIClassMacro {
def impl(c: blackbox.Context)(annottees: c.Tree*): c.Tree = {
import c.universe._
annottees match {
case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" :: tail =>
def updateParents(parents: Seq[Tree], args: Seq[Tree]) =
q"""${tq"APIClass"}(..$args)""" +: parents.filter { case tq"scala.AnyRef" => false; case _ => true }
val parents1 = sys.env.get("LIB_VERSION") match {
case Some("1") => updateParents(parents, Seq(q""" "x" """, q"1"))
case Some("2") => updateParents(parents, Seq(q""" "x" """, q"1", q""" "y" """))
case None => parents
}
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents1 { $self => ..$stats }
..$tail
"""
}
}
}
core/src/main/scala/MyClass.scala (if LIB_VERSION=2)
#extendsAPIClass
class MyClass
//Warning:scalac: {
// class MyClass extends APIClass("x", 1, "y") {
// def <init>() = {
// super.<init>();
// ()
// }
// };
// ()
//}
build.sbt
ThisBuild / name := "macrosdemo"
lazy val commonSettings = Seq(
scalaVersion := "2.13.2",
organization := "com.example",
version := "1.0.0",
scalacOptions ++= Seq(
"-Ymacro-debug-lite",
"-Ymacro-annotations",
),
)
lazy val macros: Project = (project in file("macros")).settings(
commonSettings,
libraryDependencies ++= Seq(
scalaOrganization.value % "scala-reflect" % scalaVersion.value,
)
)
lazy val core: Project = (project in file("core")).aggregate(macros).dependsOn(macros).settings(
commonSettings,
)
)
3. Alternatively you can use Scalameta for code generation (at the time before compile time)
build.sbt
ThisBuild / name := "scalametacodegendemo"
lazy val commonSettings = Seq(
scalaVersion := "2.13.2",
organization := "com.example",
version := "1.0.0",
)
lazy val common = project
.settings(
commonSettings,
)
lazy val in = project
.dependsOn(common)
.settings(
commonSettings,
)
lazy val out = project
.dependsOn(common)
.settings(
sourceGenerators in Compile += Def.task {
Generator.gen(
inputDir = sourceDirectory.in(in, Compile).value,
outputDir = sourceManaged.in(Compile).value
)
}.taskValue,
commonSettings,
)
project/build.sbt
libraryDependencies += "org.scalameta" %% "scalameta" % "4.3.10"
project/Generator.scala
import sbt._
object Generator {
def gen(inputDir: File, outputDir: File): Seq[File] = {
val finder: PathFinder = inputDir ** "*.scala"
for(inputFile <- finder.get) yield {
val inputStr = IO.read(inputFile)
val outputFile = outputDir / inputFile.toURI.toString.stripPrefix(inputDir.toURI.toString)
val outputStr = Transformer.transform(inputStr)
IO.write(outputFile, outputStr)
outputFile
}
}
}
project/Transformer.scala
import scala.meta._
object Transformer {
def transform(input: String): String = {
val (v1on, v2on) = sys.env.get("LIB_VERSION") match {
case Some("1") => (true, false)
case Some("2") => (false, true)
case None => (false, false)
}
var v1 = false
var v2 = false
input.tokenize.get.filter(_.text match {
case "// Lib v1" =>
v1 = true
false
case "// End Lib v1" =>
v1 = false
false
case "// Lib v2" =>
v2 = true
false
case "// End Lib v2" =>
v2 = false
false
case _ => (v1on && v1) || (v2on && v2) || (!v1 && !v2)
}).mkString("")
}
}
common/src/main/scala/com/api/APIClass.scala
package com.api
class APIClass(a: String, b: Integer, c: String)
in/src/main/scala/com/example/MyClass.scala
package com.example
import com.api.APIClass
// Lib v1
class MyClass extends APIClass("x", 1)
// End Lib v1
// Lib v2
class MyClass extends APIClass("x", 1, "y")
// End Lib v2
out/target/scala-2.13/src_managed/main/scala/com/example/MyClass.scala
(after sbt out/compile if LIB_VERSION=2)
package com.example
import com.api.APIClass
class MyClass extends APIClass("x", 1, "y")
Macro annotation to override toString of Scala function
How to merge multiple imports in scala?
I see some options but none if them is "conditional compilation"
you can create 2 modules in your build - they would have a shared source directory and each of them you have a source directory for code specific to it. Then you would publish 2 versions of your whole library
create 3 modules - one with your library and an abstract class/trait that it would talk to/through and 2 other with version-specific implementation of the trait
The problem is - what if you build the code against v1 and user provided v2? Or the opposite? You emitted the bytecode but JVM expects something else and it all crashes.
Virtually every time you have such compatibility breaking changes, library either refuses to update or fork. Not because you wouldn't be able to generate 2 versions - you would. Problem is in the downstream - how would your users deal with this situation. If you are writing an application you can commit to one of these. If you are writing library and you don't want to lock users to your choices... you have to publish separate version for each choice.
Theoretically you could create one project, with 2 modules, which share the same code and use different branches like #ifdef macros in C++ using Scala macros or Scalameta - but that is a disaster if you want to use IDE or publish sourcecode that your users can use in IDE. No source to look at. No way to jump to the definition's source. Disassembled byte code at best.
So the solution that you simply have separate source directories for mismatching versions is much easier to read, write and maintain in a long run.

How to use jquery-ui with JSImport

I want to access the jquery ui library in my scala js project. I have tried defining the following main module:
import org.scalajs.jquery.JQueryStatic
import scala.scalajs.js
import org.scalajs.dom
import scalatags.JsDom.all._
import scala.scalajs.js.annotation.JSImport
#JSImport("jquery", JSImport.Namespace)
#js.native
object JQuery extends JQueryStatic
#js.native
trait JQueryUI extends JQueryStatic {
def spinner(options: js.Object = js.Dynamic.literal()): JQueryUI = js.native
}
#JSImport("jquery-ui", JSImport.Namespace)
#js.native
object JQueryUI extends JQueryUI
object App {
def main(args: Array[String]): Unit = {
dom.document.getElementById("root").appendChild(div(input(id := "input")).render)
JQuery("#input").asInstanceOf[JQueryUI].spinner()
}
}
And my build.sbt is as follows:
enablePlugins(ScalaJSBundlerPlugin)
lazy val opexCounter = project.in(file(".")).settings(
name := "Repro",
scalaVersion := "2.12.8",
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % "0.9.6",
"com.lihaoyi" %%% "scalatags" % "0.6.7",
"be.doeraene" %%% "scalajs-jquery" % "0.9.4"
),
npmDependencies in Compile ++= Seq(
"jquery" -> "2.2.1",
"jquery-ui" -> "1.12.1",
),
mainClass in Compile := Some("App"),
scalaJSUseMainModuleInitializer := true,
webpackDevServerPort := 3000
)
But when I load my page I get the following error in my console:
TypeError: qual$1.spinner is not a function
Is this not the correct way to import the library and if not what is?
The complete source for the project can be found here
I changed my npm dependency from jquery-ui to jquery-ui-bundle and imported it. I also had to make an explicit reference to my JQueryUIImport object in order to ensure it was instantiated. Those 2 changes fixed the problem

Using scalamock outside of MockFactory

I want to add integration tests to my project using cucumber feature files. I have got this working using this project as an example: https://github.com/jecklgamis/cucumber-jvm-scala-example
The problem I am running into is when I want to mock some objects. ScalaMock and EasyMock all seem to need scalatest or something similar.
My build.sbt has these lines:
libraryDependencies ++= Seq(
"io.cucumber" %% "cucumber-scala" % "2.0.1" % Test,
"io.cucumber" % "cucumber-junit" % "2.0.1" % Test,
"org.scalamock" %% "scalamock" % "4.0.0" % Test,
"org.scalatest" %% "scalatest" % "3.0.1" % Test,
etc..
My stepdef file has this:
import com.typesafe.config.{Config, ConfigFactory}
import cucumber.api.scala.{EN, ScalaDsl}
import eu.xeli.jpigpio.JPigpio
class StepDefs extends ScalaDsl with EN {
var config: Config = null
var jpigpio: JPigpio = null
Given("""^an instance of pigpio$""") { () =>
jpigpio = mock[JPigpio]
}
}
The mock[JPigpio] call gives a symbol not found error. I assume because this class does not extend MockFactory.
How can I use scalamock outside of an MockFactory class?
Bit of a quick and dirty example that does not pull in Scalatest, but I'm sure you can piece the rest together. I'd actually be curious to see this working with Cucumber :)
import org.scalamock.MockFactoryBase
import org.scalamock.clazz.Mock
object NoScalaTestExample extends Mock {
trait Cat {
def meow(): Unit
def isHungry: Boolean
}
class MyMockFactoryBase extends MockFactoryBase {
override type ExpectationException = Exception
override protected def newExpectationException(message: String, methodName: Option[Symbol]): Exception =
throw new Exception(s"$message, $methodName")
def verifyAll(): Unit = withExpectations(() => ())
}
implicit var mc: MyMockFactoryBase = _
var cat: Cat = _
def main(args: Array[String]): Unit = {
// given: I have a mock context
mc = new MyMockFactoryBase
// and am mocking a cat
cat = mc.mock[Cat]
// and the cat meows
cat.meow _ expects() once()
// and the cat is always hungry
cat.isHungry _ expects() returning true anyNumberOfTimes()
// then the cat needs feeding
assert(cat.isHungry)
// and the mock verifies
mc.verifyAll()
}
}
This will actually throw as the meows expectation is not satisfied (just to demo)
Exception in thread "main" java.lang.Exception: Unsatisfied expectation:
Expected:
inAnyOrder {
<mock-1> Cat.meow() once (never called - UNSATISFIED)
<mock-1> Cat.isHungry() any number of times (called once)
}
Actual:
<mock-1> Cat.isHungry(), None
at NoScalaTestExample$MyMockFactoryBase.newExpectationException(NoScalaTestExample.scala:13)
at NoScalaTestExample$MyMockFactoryBase.newExpectationException(NoScalaTestExample.scala:10)
at org.scalamock.context.MockContext$class.reportUnsatisfiedExpectation(MockContext.scala:45)
at NoScalaTestExample$MyMockFactoryBase.reportUnsatisfiedExpectation(NoScalaTestExample.scala:10)

scala: delimited continuation issues

Here is a code I have problem to compile.
My question is very simple.
How to fix compilation error?
I did try to run this from sbt and from command prompt.
SBT configuration below. I did add references to continuation plugin!
But looks like this is not helping!
See error below.
I've got this from scaladocs: http://www.scala-lang.org/files/archive/api/2.11.8/scala-continuations-library/#scala.util.continuations.package
object MainApp
{
def main(args: Array[String]): Unit =
{
import scala.util.continuations._
val uuidGen : String = "UniqueValue"
def ask(prompt: String): Int #cps[Unit] =
shift {
k: (Int => Unit) => {
val id = uuidGen
printf("%s\nrespond with: submit(0x%x, ...)\n", prompt, id)
}
}
def go =
reset {
println("Welcome!")
val first = ask("Please give me a number")
val second = ask("Please enter another number")
printf("The sum of your numbers is: %d\n", first + second)
}
go
}
}
Sbt configuration for the refrences
name := """scala-testing"""
version := "0.1.0"
scalaVersion := "2.11.2"
autoCompilerPlugins := true
addCompilerPlugin(
"org.scala-lang.plugins" % "scala-continuations-plugin_2.11.6" % "1.0.2")
libraryDependencies +=
"org.scala-lang.plugins" %% "scala-continuations-library" % "1.0.2"
fork in run := true

Mockito Stubbing consecutive calls (iterator-style stubbing), exceptions and return void

I'm struggling to get the following test working, basically I want the mocked service call to throw an exception when first called, and work ok on the 2nd invocation. The service method returns nothing (void/Unit), written in scala
import org.mockito.{Mockito}
import org.scalatest.mock.MockitoSugar
import org.scalatest.{BeforeAndAfter, FeatureSpec}
import org.mockito.Mockito._
class MyMocksSpec extends FeatureSpec with BeforeAndAfter with MockitoSugar {
var myService: MyService = _
var myController: MyController = _
before {
myService = mock[MyService]
myController = new MyController(myService)
}
feature("a feature") {
scenario("a scenario") {
Mockito.doThrow(new RuntimeException).when(myService.sideEffect())
Mockito.doNothing().when(myService.sideEffect())
myController.showWebPage()
myController.showWebPage()
verify(myService, atLeastOnce()).sayHello("tony")
verify(myService, atLeastOnce()).sideEffect()
}
}
}
class MyService {
def sayHello(name: String) = {
"hello " + name
}
def sideEffect(): Unit = {
println("well i'm not doing much")
}
}
class MyController(myService: MyService) {
def showWebPage(): Unit = {
myService.sideEffect()
myService.sayHello("tony")
}
}
Here is the build.sbt file
name := """camel-scala"""
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies ++= {
val scalaTestVersion = "2.2.4"
Seq(
"org.scalatest" %% "scalatest" % scalaTestVersion % "test",
"org.mockito" % "mockito-all" % "1.10.19")
}
Unfinished stubbing detected here:
-> at MyMocksSpec$$anonfun$2$$anonfun$apply$mcV$sp$1.apply$mcV$sp(MyMocksSpec.scala:24)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, you naughty developer!
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
I followed the example (I think) here
Turns out the I was setting up the mock incorrectly, solution below..
Mockito.doThrow(new RuntimeException).when(myService).sideEffect()
Mockito.doNothing().when(myService).sideEffect()
instead of the incorrect
Mockito.doThrow(new RuntimeException).when(myService.sideEffect())
Mockito.doNothing().when(myService.sideEffect())