How to override the toString() method on JS objects to use JSON.stringify()? - scala.js

I'm tired of writing blah: "${JSON.stringify(target)}" when I deal with my DTO objects, I just want to write blah: "$target".
My DTOs look like:
#js.native
trait AuthConnectionDetails extends js.Object {
def clientId: String = js.native
def accountHostname: String = js.native
}
These DTOs are used to parse the content of some REST API calls, like:
val response = js.JSON.parse(xmlHttpRequest.responseText).
asInstanceOf[AuthConnectionDetails]
I don't mind changing how I define my DTO objects to do this (maybe I should be using case classes for my DTOs or something, instead of native js traits?), but I can't figure out how to do it.
I tried writing a trait that I could mixin, but that didn't work and I tried writing an implicit extension method but that didn't work either.
My implicit code that didn't seem to work for toString:
object JsonToString {
implicit class JsObjectExtensions(val target: js.Object) extends AnyVal {
override def toString:String = JSON.stringify(target)
def json:String = JSON.stringify(target)
}
}
So I can do blah: "${target.json}", which is better - but I'd especially like to get rid of those braces.
Is there any way to do this with scala.js?

No, there is no way to do this. That's because string interpolation will always use the toString() method of the object itself, no matter what is declared in its types or in implicit classes (this is a Scala thing in general).
The only way you could achieve this would be to actually modify the objects by patching them up with a custom toString() method every time you create one. That would include when you parse them from a JSON string. I'm pretty sure that would be worse than calling .json when you stringify them.

If you really want to, you could write your custom string interpolator:
implicit class JsonHelper(private val sc: StringContext) extends AnyVal {
def dejson(args: Any*): JSONObject = {
sc.checkLengths(args)
s(args.map(jsonify))
}
private def jsonify(arg: Any) = arg match {
case obj: js.Object => JSON.stringify(obj)
case _ => arg.toString
}
}
You can now use this like this:
dejson"hello: $target, world: $target2"

Related

Modify constructor arguments before passing it to superclass constructor in Scala

I have a superclass:
class Filter(val param: ComplexFilterParams){
def this(config: String) = this(parseStrConfig(config))
And I need to create a subclass that gets a String argument and then parses it in another way and creates ComplexFilterParams.
Something like that:
class NewFilter(str:String) extends Filter {
Is there a way to do it?
I got one solution. But I think it's ugly. I create companion object, define there a convert method and do next:
class NewFilter(str:String) extends Filter(NewFilter.convert(str)) {
You can go mush easier with another apply implementation in companion object like:
class NewFilter(val param: ComplexFilterParams) extends Filter(param){
//other implementations
}
object NewFilter {
def apply(str: String) = new NewFilter(convert(str))
def convert(str: String): ComplexFilterParams = ...
}
val filter = NewFilter("config string")

Way to enhance a class with function delegation

I have the following classes in Scala:
class A {
def doSomething() = ???
def doOtherThing() = ???
}
class B {
val a: A
// need to enhance the class with both two functions doSomething() and doOtherThing() that delegates to A
// def doSomething() = a.toDomething()
// def doOtherThing() = a.doOtherThing()
}
I need a way to enhance at compile time class B with the same function signatures as A that simply delegate to A when invoked on B.
Is there a nice way to do this in Scala?
Thank you.
In Dotty (and in future Scala 3), it's now available simply as
class B {
val a: A
export a
}
Or export a.{doSomething, doOtherThing}.
For Scala 2, there is unfortunately no built-in solution. As Tim says, you can make one, but you need to decide how much effort you are willing to spend and what exactly to support.
You can avoid repeating the function signatures by making an alias for each function:
val doSomething = a.doSomething _
val doOtherthing = a.doOtherThing _
However these are now function values rather than methods, which may or may not be relevant depending on usage.
It might be possible to use a trait or a macro-based solution, but that depends on the details of why delegation is being used.
Implicit conversion could be used for delegation like so
object Hello extends App {
class A {
def doSomething() = "A.doSomething"
def doOtherThing() = "A.doOtherThing"
}
class B {
val a: A = new A
}
implicit def delegateToA(b: B): A = b.a
val b = new B
b.doSomething() // A.doSomething
}
There is this macro delegate-macro which might just be what you are looking for. Its objective is to automatically implement the delegate/proxy pattern, so in your example your class B must extend class A.
It is cross compiled against 2.11, 2.12, and 2.13. For 2.11 and 2.12 you have to use the macro paradise compile plugin to make it work. For 2.13, you need to use flag -Ymacro-annotations instead.
Use it like this:
trait Connection {
def method1(a: String): String
def method2(a: String): String
// 96 other abstract methods
def method100(a: String): String
}
#Delegate
class MyConnection(delegatee: Connection) extends Connection {
def method10(a: String): String = "Only method I want to implement manually"
}
// The source code above would be equivalent, after the macro expansion, to the code below
class MyConnection(delegatee: Connection) extends Connection {
def method1(a: String): String = delegatee.method1(a)
def method2(a: String): String = delegatee.method2(a)
def method10(a: String): String = "Only method I need to implement manually"
// 96 other methods that are proxied to the dependency delegatee
def method100(a: String): String = delegatee.method100(a)
}
It should work in most scenarios, including when type parameters and multiple argument lists are involved.
Disclaimer: I am the creator of the macro.

Scala Type Classes Understanding Interface Syntax

I'm was reading about cats and I encountered the following code snippet which is about serializing objects to JSON!
It starts with a trait like this:
trait JsonWriter[A] {
def write(value: A): Json
}
After this, there are some instances of our domain object:
final case class Person(name: String, email: String)
object JsonWriterInstances {
implicit val stringWriter: JsonWriter[String] =
new JsonWriter[String] {
def write(value: String): Json =
JsString(value)
}
implicit val personWriter: JsonWriter[Person] =
new JsonWriter[Person] {
def write(value: Person): Json =
JsObject(Map(
"name" -> JsString(value.name),
"email" -> JsString(value.email)
))
}
// etc...
}
So far so good! I can then use this like this:
import JsonWriterInstances._
Json.toJson(Person("Dave", "dave#example.com"))
Later on I come across something called the interface syntax, which uses extension methods to extend existing types with interface methods like below:
object JsonSyntax {
implicit class JsonWriterOps[A](value: A) {
def toJson(implicit w: JsonWriter[A]): Json =
w.write(value)
}
}
This then simplifies the call to serializing a Person as:
import JsonWriterInstances._
import JsonSyntax._
Person("Dave", "dave#example.com").toJson
What I don't understand is that how is the Person boxed into JsonWriterOps such that I can directly call the toJson as though toJson was defined in the Person case class itself. I like this magic, but I fail to understand this one last step about the JsonWriterOps. So what is the idea behind this interface syntax and how does this work? Any help?
This is actually a standard Scala feature, since JsonWriterOps is marked implicit and is in scope, the compiler can apply it at compilation-time when needed.
Hence scalac will do the following transformations:
Person("Dave", "dave#example.com").toJson
new JsonWriterOps(Person("Dave", "dave#example.com")).toJson
new JsonWriterOps[Person](Person("Dave", "dave#example.com")).toJson
Side note:
It's much more efficient to implicit classes as value classes like this:
implicit class JsonWriterOps[A](value: A) extends AnyVal
This makes the compiler also optimize away the new object construction, if possible, compiling the whole implicit conversion + method call to a simple function call.

How to swap JSON Writes Converter for Play controller Action

I've built a microservice using Scala and Play and now I need to create a new version of the service that returns the same data as the previous version of the service but in a different JSON format. The service currently uses implicit Writes converters to do this. My controller looks something like this, where MyJsonWrites contains the implicit definitions.
class MyController extends Controller with MyJsonWrites {
def myAction(query: String) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
}
trait MyJsonWrites {
implicit val writes1: Writes[SomeDataType]
implicit val writes2: Writes[SomeOtherDataType]
...
}
Now I need a new version of myAction where the JSON is formatted differently. The first attempt I made was to make MyController a base class and have subclasses extend it with their own trait that has the implicit values. Something like this.
class MyNewContoller extends MyController with MyNewJsonWrites
This doesn't work though because the implicit values defined on MyNewJsonWrites are not available in the methods of the super class.
It would be ideal if I could just create a new action on the controller that somehow used the converters defined in MyNewJsonWrites. Sure, I could change the trait to an object and import the implicit values in each method but then I'd have to duplicate the method body of myAction so that the implicits are in scope when I call Json.toJson. I don't want to pass them as implicit parameters to a base method because there are too many of them. I guess I could pass a method as a parameter to the base method that actually does the imports and Json.toJson call. Something like this. I just thought maybe there'd be a better way.
def myBaseAction(query: String, toJson: Seq[MyResultType] => JsValue) = Action.async {
getData(query).map {
results =>
Ok(Json.toJson(results))
}
}
def myActionV1(query: String) = {
def toJson(results: Seq[MyResultType]) = {
import MyJsonWritesV2._
Json.toJson(results)
}
myBaseAction(query, toJson)
}
Instead of relying on scala implicit resolution, you can call your writes directly:
def myBaseAction(query: String, writes: Writes[MyResultType]) = Action.async {
getData(query).map { results =>
val seqWrites: Writes[Seq[MyResultType]] = Writes.seq(writes)
Ok(seqWrites.writes(results))
}
}
def myActionV1(query: String) = myBaseAction(query, MyJsonWritesV1)
def myActionV2(query: String) = myBaseAction(query, MyJsonWritesV2)

Testing a unit with implicit class in Scala

Imagine I have a service:
class ServiceA(serviceB: ServiceB) {
import Extractor._
def send(typeA: A) = serviceB.send(typeA.extract)
}
object Extractor {
implicit class Extractor(type: A) {
def extract = ???
}
}
I want the extract method to be an implicitly defined because it doesn't directly relate to A type/domain and is a solution specific adhoc extension.
Now I would like to write a very simple unit test that confirms that serviceB.send is called.
For that, I mock service and pass a mocked A to send. Then I could just assert that serviceB.send was called with the mocked A.
As seen in the example, the send method also does some transformation on typeA parameter so I would need to mock extract method to return my specified value. However, A doesn't have extract method - it comes from the implicit class.
So the question is - how do I mock out the implicit class as in the example above as imports are not first class citizens.
If you want to specify a bunch of customised extract methods, you can do something like this:
sealed trait Extractor[T] {
// or whatever signature you want
def extract(obj: T): String
}
object Extractor {
implicit case object IntExtractor extends Extractor[Int] {
def extract(obj: Int): String = s"I am an int and my value is $obj"
}
implicit case object StringExtractor extends Extractor[String] {
def extract(obj: String): String = s"I am "
}
def apply[A : Extractor](obj: A): String = {
implicitly[Extractor[A]].extract(obj)
}
}
So you have basically a sealed type family that's pre-materialised through case objects, which are arguably only useful in a match. But that would let you decouple everything.
If you don't want to mix this with Extractor, call them something else and follow the same approach, you can then mix it all in with a context bound.
Then you can use this with:
println(Extractor(5))
For testing, simply override the available implicits if you need to. A bit of work, but not impossible, you can simply control the scope and you can spy on whatever method calls you want.
e.g instead of import Extractor._ have some other object with test only logic where you can use mocks or an alternative implementation.