How to get Ref to component in scalsjs-react? - scala.js

This doc https://github.com/japgolly/scalajs-react/blob/master/doc/USAGE.md#refs
is a bit unclear.
I've created small example: "squareViewer" showes "square" by click
How can I get Ref to component "square" in method squareViewer.Backend.show?
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.prefix_<^._
object squareViewer {
class Backend($: BackendScope[Unit, Unit]) {
def show() = Callback {
//???
//val r = ref to square instance
//r.backend.show()
}
def render() = {
<.div(
<.button("Show square", ^.onClick-->show()),
square.component.withRef("id1")()
)
}
}
val component = ReactComponentB[Unit]("squareViewer")
.renderBackend[Backend]
.buildU
}
object square {
case class State(visible: Boolean)
class Backend($: BackendScope[Unit, State]) {
def show() = $.setState(State(true))
def hide() = $.setState(State(false))
def render(s: State) = {
<.div("Yo!",
^.width:="100px", ^.height:="100px",
^.position:="absolute", ^.top:=0, ^.left:=0,
^.fontSize:="300%",
^.backgroundColor:="red",
!s.visible ?= ^.display.none,
^.onClick-->hide()
)
}
}
val component = ReactComponentB[Unit]("square")
.initialState(State(false))
.renderBackend[Backend]
.buildU
}

this should do the trick:
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.prefix_<^._
object squareViewer {
val squareRef = Ref.to(square.component, "square")
class Backend($: BackendScope[Unit, Unit]) {
def show() =
squareRef($).map(_.backend.show()).getOrElse(Callback.empty)
def render() = {
<.div(
<.button("Show square", ^.onClick-->show()),
square.component.withRef(squareRef)()
)
}
}
val component = ReactComponentB[Unit]("squareViewer")
.renderBackend[Backend]
.buildU
}
object square {
case class State(visible: Boolean)
class Backend($: BackendScope[Unit, State]) {
def show() = $.setState(State(true))
def hide() = $.setState(State(false))
def render(s: State) = {
<.div("Yo!",
^.style:="width:100px;height:100px;position:absolute;top:0;left:0;font-size:300%;background-color:red;",
!s.visible ?= ^.display.none,
^.onClick-->hide()
)
}
}
val component = ReactComponentB[Unit]("square")
.initialState(State(false))
.renderBackend[Backend]
.buildU
}
Edit: Correted a typo, this should work now.

Related

how to efficiently/cleanly override a copy method

I have a super and a subclass as follows:
class Animal(var x: Int) {
def greeting: String = "hi im an animal"
def copy: Animal = new Animal(x)
}
class Lion(override var x: Int) extends Animal(x){
override def greeting: String = "hi im a lion"
override def copy: Lion = new Lion(x)
}
I want both of them to have the exact same copy function (imagine it being larger than what I've given), except for the return type, I would like the Lion class to return a Lion when copy is invoked.
How can I cleanly override the Animal copy method without having code duplication?
In principle, methods apply/unapply, canEqual/equals/hashCode, toString, copy, productArity/productElement/productIterator/productPrefix can be generated with Shapeless case classes a la carte although I'm not sure whether this works with class hierarchies.
Anyway, you can generate apply with a macro annotation
import scala.annotation.{StaticAnnotation, compileTimeOnly}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
#compileTimeOnly("enable macro annotations")
class copy extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro CopyMacro.impl
}
object CopyMacro {
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 =>
val paramNamess = paramss.map(_.map {
case q"$_ val $tname: $_ = $_" => tname
case q"$_ var $tname: $_ = $_" => tname
})
val tparamNames = tparams.map {
case q"$_ type $tpname[..$_] = $_" => tpname
}
val doesOverrideCopy = parents.map {
case q"${parent#tq"$_[..$_]"}(...$_)" => parent
case parent#tq"$_[..$_]" => parent
}.exists(tree =>
c.typecheck(tree.duplicate, mode = c.TYPEmode)
.tpe
.member(TermName("copy")) != NoSymbol
)
val copyMod = if (doesOverrideCopy) Modifiers(Flag.OVERRIDE) else NoMods
q"""
$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self =>
..$stats
$copyMod def copy: $tpname[..$tparamNames] = new $tpname[..$tparamNames](...$paramNamess)
}
..$tail
"""
}
}
}
Usage:
#copy
class Animal(val x: Int) {
def greeting: String = "hi im an animal"
}
#copy
class Lion(override val x: Int) extends Animal(x) {
override def greeting: String = "hi im a lion"
}
//scalac: {
// class Animal extends scala.AnyRef {
// <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// def greeting: String = "hi im an animal";
// def copy: Animal = new Animal(x)
// };
// ()
//}
//scalac: {
// class Lion extends Animal(x) {
// override <paramaccessor> val x: Int = _;
// def <init>(x: Int) = {
// super.<init>();
// ()
// };
// override def greeting: String = "hi im a lion";
// override def copy: Lion = new Lion(x)
// };
// ()
//}
Alternatively, since class Animal(val x: Int) is case-class-like you can try to use shapeless.Generic
implicit class CopyOps[A](a: A)(implicit generic: Generic[A]) {
def copy: A = generic.from(generic.to(a))
}

Facing issue while using reflection class concept in scala

I have one main class like this:
class Test {
def exe(first:String, second:String, task:String):String = {
task match {
case "A" => {
val obj = new A(first)
obj.defineSecond(second)
}
case "B" => {
val obj = new B(first)
obj.defineSecond(second)
}
case "C" => {
val obj = new C(first)
obj.defineSecond(second)
}
....so many cases
}
}
}
Instead of writing case in my Test class everytime a new class is added, I tried using the concept of reflection in scala.
Below is what I trying:
val m = ru.runtimeMirror(getClass.getClassLoader)
val classTest = ru.typeOf[Test].typeSymbol.asClass
val cm = m.reflectClass(classTest)
But getting error as "class Test is inner class, use reflectClass on an InstaneMirror to obtain its classMirror".
Can anyone knows how can I can avoid adding cases to my main class everytime a new class is created, instead I can write my main class in a way it will work for every case.
I guess you haven't provided all necessary information in your question. It's written that "class Test is inner class" in your error message but Test is not inner in your code snippet. If you want your runtime-reflection code to be fixed please provide code snippet that reflects actual use case.
Meanwhile you can try a macro (working at compile time)
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
class Test {
def exe(first: String, second: String, task: String): String = macro Test.exeImpl
}
object Test {
def exeImpl(c: blackbox.Context)(first: c.Tree, second: c.Tree, task: c.Tree): c.Tree = {
import c.universe._
val cases = Seq("A", "B", "C").map(name =>
cq"""${Literal(Constant(name))} => {
val obj = new ${TypeName(name)}($first)
obj.defineSecond($second)
}"""
)
q"$task match { case ..$cases }"
}
}
Usage:
class A(s: String) {
def defineSecond(s1: String): String = ""
}
class B(s: String) {
def defineSecond(s1: String): String = ""
}
class C(s: String) {
def defineSecond(s1: String): String = ""
}
new Test().exe("first", "second", "task")
//scalac: "task" match {
// case "A" => {
// val obj = new A("first");
// obj.defineSecond("second")
// }
// case "B" => {
// val obj = new B("first");
// obj.defineSecond("second")
// }
// case "C" => {
// val obj = new C("first");
// obj.defineSecond("second")
// }
//}

How to modify Scala actor to return List[String]?

Following the Akka tutorials https://doc.akka.io/docs/akka/current/typed/guide/tutorial_1.html I have modified sample code to send a message to an akka actor every 3 seconds:
scheduler.scheduleAtFixedRate(
initialDelay = Duration(0, TimeUnit.SECONDS),
interval = Duration(3, TimeUnit.SECONDS))(
runnable = task)
}
I'm unable to safely compiling changing the message return in Main from String to List[String]. So instead of firstRef ! "printit" , change to firstRef ! List("printit") but this causes compiler error:
To fix the compiler error I make changes to:
class Main(context: ActorContext[String]) extends AbstractBehavior[List[String]](context) {
override def onMessage(msg: String): Behavior[List[String]] =
def apply(): Behavior[List[String]] =
where previously this contained:
class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
override def onMessage(msg: String): Behavior[String] =
def apply(): Behavior[String] =
What needs to change in order to return List[String] instead of String in the Main actor ?
complete code (without changes):
import java.util.concurrent.TimeUnit
import akka.actor.typed.ActorSystem
import akka.actor.typed.Behavior
import akka.actor.typed.scaladsl.AbstractBehavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import map.QTableRow
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
import scala.collection.mutable.ListBuffer
object PrintMyActorRefActor {
def apply(): Behavior[String] =
Behaviors.setup(context => new PrintMyActorRefActor(context))
}
class PrintMyActorRefActor(context: ActorContext[String]) extends AbstractBehavior[String](context) {
override def onMessage(msg: String): Behavior[String] =
msg match {
case "printit" =>
val secondRef = context.spawn(Behaviors.empty[String], "second-actor")
println(s"Second: $secondRef")
this
}
}
object Main {
def apply(): Behavior[String] =
Behaviors.setup(context => new Main(context))
}
class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
override def onMessage(msg: String): Behavior[String] =
msg match {
case "getdata" =>
val firstRef = context.spawn(PrintMyActorRefActor(), "first-actor"+String.valueOf(System.currentTimeMillis()))
println(s"First: $firstRef")
firstRef ! "printit"
this
}
}
object ActorHierarchyExperiments extends App {
val testSystem = ActorSystem(Main(), "testSystem")
val scheduler = testSystem.scheduler
val task = new Runnable { def run() {
testSystem ! "getdata" }
}
scheduler.scheduleAtFixedRate(
initialDelay = Duration(0, TimeUnit.SECONDS),
interval = Duration(3, TimeUnit.SECONDS))(
runnable = task)
}
The firstRef actor reference references an instance of PrintMyActorRefActor, which is a Behavior[ String ], so to send any List[ String ]to the ref, the PrintMyActorRefActor needs to be able to recieve it. Changing this, including the signature of its recieve message, should make the snippet compile. Now, of course, you'll need to change the behaviour of the PrintMyActorRefActor.onMessage( msg: List[ String ] ): Behavior[ List[ String ] ] to deal with the list instead of the single string...
Complete transformed code snippet:
import java.util.concurrent.TimeUnit
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors}
import akka.actor.typed.{ActorSystem, Behavior}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
object PrintMyActorRefActor {
def apply(): Behavior[List[String]] =
Behaviors.setup(context => new PrintMyActorRefActor(context))
}
class PrintMyActorRefActor(context: ActorContext[List[String]]) extends AbstractBehavior[List[String]](context) {
override def onMessage(msg: List[String]): Behavior[List[String]] =
msg match {
case "printit" :: xs => // ignores all but the head element
val secondRef = context.spawn(Behaviors.empty[String], "second-actor")
println(s"Second: $secondRef")
this
}
}
object Main {
def apply(): Behavior[String] =
Behaviors.setup(context => new Main(context))
}
class Main(context: ActorContext[String]) extends AbstractBehavior[String](context) {
override def onMessage(msg: String): Behavior[String] =
msg match {
case "getdata" =>
val firstRef = context.spawn(PrintMyActorRefActor(), "first-actor" + String.valueOf(System.currentTimeMillis()))
println(s"First: $firstRef")
firstRef ! List("printit")
this
}
}
object ActorHierarchyExperiments extends App {
val testSystem = ActorSystem(Main(), "testSystem")
val scheduler = testSystem.scheduler
val task = new Runnable {
def run() {
testSystem ! "getdata"
}
}
scheduler.scheduleAtFixedRate(
initialDelay = Duration(0, TimeUnit.SECONDS),
interval = Duration(3, TimeUnit.SECONDS))(
runnable = task)
}

Is it possible to pass values from two instances of the same Scala class

Say I have this situation
class Pipe {
var vel = 3.4
var V = 300
var a = 10.2
var in = ???
var TotV = V+in
var out = TotV*a/vel
}
val pipe1 = new Pipe
val pipe2 = new Pipe
The in variable is were my problem is, what i'd like to do is get the out variable from pipe1 and feed that in as the in variable for pipe 2 effectively to join the two pipes but I cant figure out if this is even possible in the same class. So I can do it manually but need to know if its possible to do in the class.
pipe2.in = pipe1.out
my attempted fix was to add an ID field then try and use that to reference an instance with a higher id field but that doesnt seem doable. ie
class Pipe(id:Int) {
var vel = 3.4
var V = 300
var a = 10.2
var in = Pipe(id+1).out //this is the sticking point, I want to reference instances of this class and use their out value as in value for instances with a lower ID
var TotV = V+in
var out = TotV*a/vel
}
any help would be appreciated
You can do this by defining a companion object for the class and passing in the upstream pipe as an optional parameter to the factory method, then extracting its in value and passing it to the class constructor, as follows:
object Pipe {
def apply(upstreamPipe: Option[Pipe]): Pipe = {
val inValue = upstreamPipe match {
case Some(pipe) => pipe.out
case None => 0 // or whatever your default value is
new Pipe(inValue)
}
You would then call
val pipe1 = Pipe(None)
val pipe2 = Pipe(Some(pipe1))
Unfortunately your question is not clear now. Under certain assumptions what you describe looks like what is now called "FRP" aka "Functional Reactive Programming". If you want to do it in a serious way, you probably should take a look at some mature library such as RxScala or Monix that handle many important in the real world details such as error handling or scheduling/threading and many others.
For a simple task you might roll out a simple custom implementation like this:
trait Observable {
def subscribe(subscriber: Subscriber): RxConnection
}
trait RxConnection {
def disconnect(): Unit
}
trait Subscriber {
def onChanged(): Unit
}
trait RxOut[T] extends Observable {
def currentValue: Option[T]
}
class MulticastObservable extends Observable with Subscriber {
private val subscribers: mutable.Set[Subscriber] = mutable.HashSet()
override def onChanged(): Unit = subscribers.foreach(s => s.onChanged())
override def subscribe(subscriber: Subscriber): RxConnection = {
subscribers.add(subscriber)
new RxConnection {
override def disconnect(): Unit = subscribers.remove(subscriber)
}
}
}
abstract class BaseRxOut[T](private var _lastValue: Option[T]) extends RxOut[T] {
private val multicast = new MulticastObservable()
protected def lastValue: Option[T] = _lastValue
protected def lastValue_=(value: Option[T]): Unit = {
_lastValue = value
multicast.onChanged()
}
override def currentValue: Option[T] = lastValue
override def subscribe(subscriber: Subscriber): RxConnection = multicast.subscribe(subscriber)
}
class RxValue[T](initValue: T) extends BaseRxOut[T](Some(initValue)) {
def value: T = this.lastValue.get
def value_=(value: T): Unit = {
this.lastValue = Some(value)
}
}
trait InputConnector[T] {
def connectInput(input: RxOut[T]): RxConnection
}
class InputConnectorImpl[T] extends BaseRxOut[T](None) with InputConnector[T] {
val inputHolder = new RxValue[Option[(RxOut[T], RxConnection)]](None)
private def updateValue(): Unit = {
lastValue = for {inputWithDisconnect <- inputHolder.value
value <- inputWithDisconnect._1.currentValue}
yield value
}
override def connectInput(input: RxOut[T]): RxConnection = {
val current = inputHolder.value
if (current.exists(iwd => iwd._1 == input))
current.get._2
else {
current.foreach(iwd => iwd._2.disconnect())
inputHolder.value = Some(input, input.subscribe(() => this.updateValue()))
updateValue()
new RxConnection {
override def disconnect(): Unit = {
if (inputHolder.value.exists(iwd => iwd._1 == input)) {
inputHolder.value.foreach(iwd => iwd._2.disconnect())
inputHolder.value = None
updateValue()
}
}
}
}
}
}
abstract class BaseRxCalculation[Out] extends BaseRxOut[Out](None) {
protected def registerConnectors(connectors: InputConnectorImpl[_]*): Unit = {
connectors.foreach(c => c.subscribe(() => this.recalculate()))
}
private def recalculate(): Unit = {
var newValue = calculateOutput()
if (newValue != lastValue) {
lastValue = newValue
}
}
protected def calculateOutput(): Option[Out]
}
case class RxCalculation1[In1, Out](func: Function1[In1, Out]) extends BaseRxCalculation[Out] {
private val conn1Impl = new InputConnectorImpl[In1]
def conn1: InputConnector[In1] = conn1Impl // show to the outer world only InputConnector
registerConnectors(conn1Impl)
override protected def calculateOutput(): Option[Out] = {
for {v1 <- conn1Impl.currentValue}
yield func(v1)
}
}
case class RxCalculation2[In1, In2, Out](func: Function2[In1, In2, Out]) extends BaseRxCalculation[Out] {
private val conn1Impl = new InputConnectorImpl[In1]
def conn1: InputConnector[In1] = conn1Impl // show to the outer world only InputConnector
private val conn2Impl = new InputConnectorImpl[In2]
def conn2: InputConnector[In2] = conn2Impl // show to the outer world only InputConnector
registerConnectors(conn1Impl, conn2Impl)
override protected def calculateOutput(): Option[Out] = {
for {v1 <- conn1Impl.currentValue
v2 <- conn2Impl.currentValue}
yield func(v1, v2)
}
}
// add more RxCalculationN if needed
And you can use it like this:
def test(): Unit = {
val pipe2 = new RxCalculation1((in: Double) => {
println(s"in = $in")
val vel = 3.4
val V = 300
val a = 10.2
val TotV = V + in
TotV * a / vel
})
val in1 = new RxValue(2.0)
println(pipe2.currentValue)
val conn1 = pipe2.conn1.connectInput(in1)
println(pipe2.currentValue)
in1.value = 3.0
println(pipe2.currentValue)
conn1.disconnect()
println(pipe2.currentValue)
}
which prints
None
in = 2.0
Some(905.9999999999999)
in = 3.0
Some(909.0)
None
Here your "pipe" is RxCalculation1 (or other RxCalculationN) which wraps a function and you can "connect" and "disconnect" other "pipes" or just "values" to various inputs and start a chain of updates.

How to use fixture-context objects with async specs in ScalaTest?

I'm trying to use fixture-context objects with async testing in ScalaTest.
The naive approach of simply combining the two doesn't compile. For example:
import org.scalatest.AsyncWordSpec
import scala.collection.GenTraversableOnce
import scala.concurrent.{ExecutionContext, Future}
import scala.math.Numeric.IntIsIntegral
trait Adder[T] {
implicit def num: Numeric[T]
def add(number: T): Unit
def result: Future[T]
}
object Foo {
def doubleSum[T](adder: Adder[T], numbers: GenTraversableOnce[T])(implicit ec: ExecutionContext): Future[T] = {
numbers.foreach(adder.add)
val num = adder.num
import num._
adder.result.map(result => result + result)
}
}
class FooSpec extends AsyncWordSpec {
trait IntAdder {
val adder = new Adder[Int] {
override implicit val num = IntIsIntegral
private var sum = Future.successful(num.zero)
override def add(number: Int): Unit = sum = sum.map(_ + number)
override def result: Future[Int] = sum
}
}
"Testing" should {
"be productive" in new IntAdder {
Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}
}
}
This fails to compile with:
Error:(37, 11) type mismatch;
found : FooSpec.this.IntAdder
required: scala.concurrent.Future[org.scalatest.compatible.Assertion]
new IntAdder {
This is a legitimate error but I'm wondering what ways there are of solving this in a ScalaTest style.
I want to keep the fixture-context object since that allows me to use the stackable trait pattern.
What about:
import org.scalatest.compatible.Assertion
class FooSpec extends AsyncWordSpec {
def withIntAdder(test: Adder[Int] => Future[Assertion]): Future[Assertion] = {
val adder = new Adder[Int] { ... }
test(adder)
}
"Testing" should {
"be productive" in withIntAdder { adder =>
Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}
}
}
Or
class FooSpec extends AsyncWordSpec {
trait IntAdder {
val adder = new Adder[Int] {
override implicit val num = IntIsIntegral
private var sum = Future.successful(num.zero)
override def add(number: Int): Unit = sum = sum.map(_ + number)
override def result: Future[Int] = sum
}
}
trait SomeMoreFixture {
}
"Testing" should {
"be productive" in {
val fixture = new IntAdder with SomeMoreFixture
import fixture._
Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}
}
}
The best solution I have come up with so far is to do something like:
class FooSpec extends AsyncWordSpec {
trait IntAdder {
... // Same as in the question
val assertion: Future[compatible.Assertion]
}
"Testing" should {
"be productive" in new IntAdder {
val assertion = Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}.assertion
}
}
I was hoping to reduce it down slightly more into:
class FooSpec extends AsyncWordSpec {
trait IntAdder extends (() => Future[compatible.Assertion]) {
... // Same as in the question
val assertion: Future[compatible.Assertion]
override def apply(): Future[Assertion] = assertion
}
"Testing" should {
"be productive" in new IntAdder {
val assertion = Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}()
}
}
However this also doesn't compile due to:
Error:(42, 10) ';' expected but '(' found.
}()
You could have a mix of the fixture-context objects and the loan-fixtures methods patterns.
Something like this:
class FooSpec extends AsyncWordSpec {
// Fixture-context object
trait IntAdder {
val adder = new Adder[Int] {
override implicit val num = IntIsIntegral
private var sum = Future.successful(num.zero)
override def add(number: Int): Unit = sum = sum.map(_ + number)
override def result: Future[Int] = sum
}
}
// Loan-fixture method
def withContext(testCode: IntAdder => Future[compatible.Assertion]): Future[compatible.Assertion] = {
val context = new IntAdder {}
testCode(context)
}
"Testing" should {
"be productive" in withContext { context =>
import context._
Foo.doubleSum(adder, Seq(1, 2, 3)).map(sum => assert(sum == 12))
}
}
}