How do I create optional "options" in a facade? - scala.js

I am trying to create a facade for the bootstrap popover function that can take up to 11 optional parameters. In my scalajs code I would like to only pass in the parameters I need to override from library maintained sensible defaults ie: PopoverOptions(animation = false) just like I would do in javascript. This seems to be the recommend way to make a facade but it makes all the parameters required:
trait PopoverOptions extends js.Object {
val animation: String = js.native
val container: String = js.native
val content: String = js.native
...
}
object PopoverOptions {
def apply(animation: String, container: String, content: String, ...): PopoverOptions = {
js.Dynamic.literal(animation=animation, container= container, content = content, ...).asInstanceOf[PopoverOptions ]
}
}
It looks like one way is to define an apply for every possible permutation of parameters but when there are lots of override parameters this gets excessive quick:
def apply(animation: String): ...
def apply(container: String): ...
def apply(animation: String, container: String): ...
...
What is the idiomatic way to create an options parameter facade with lots of override parameters that typically have sensible library maintained defaults?
Note: both answers have pros/cons so to decide it might be helpful to see both ways without leaving SO so here is a summary of JSOptionBuilder method:
import org.querki.jquery.JQuery
import org.querki.jsext._
#js.native
trait PopoverOptions extends js.Object
object PopoverOptions extends PopoverOptionBuilder(noOpts)
class PopoverOptionBuilder(val dict:OptMap) extends JSOptionBuilder[PopoverOptions, PopoverOptionBuilder](new PopoverOptionBuilder(_))
{
def animation(v:String) = jsOpt("animation", v)
def container(v:String) = jsOpt("container", v)
def content(v:String) = jsOpt("content", v)
...
}
To use: PopoverOptions.animation("yay").container("container").content("bottom")._result

You can use Scala's default values for parameters, combined with js.UndefOrs of the types of elements, like this:
object PopoverOptions {
#inline
def apply(
animation: js.UndefOr[String] = js.undefined,
container: js.UndefOr[String] = js.undefined,
content: js.UndefOr[String] = js.undefined,
...): PopoverOptions = {
val result = js.Dynamic.literal()
animation.foreach(result.animation = _)
container.foreach(result.container = _)
content.foreach(result.content = _)
...
result.asInstanceOf[PopoverOptions]
}
}
Then you can call with PopoverOptions(animation = false), as you wished.
It's a bit verbose at definition site, but it will get the job done.

The alternative approach is to use JSOptionBuilder, which was created for this purpose. (I write many jQuery facades, and they always have this problem.) JSOptionBuilder isn't quite as critical as it used to be (this was a major problem before the "|" operator was introduced), but I still find it's usually the best way to deal with complex facades.
Here's the full description of this approach. (Note that that's enormously detailed -- the first half is the key stuff, and the rest deals with all the somewhat-common edge cases.)

Related

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.

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

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"

Scala forward or delegate methods to encapsulated object

Is there any possibility to implicitly forward some of class methods to encapsulated object?
case class Entity(id: Int, name: String,) {
private lazy val lastScan = new LastScan
def getLastScanDate = lastScan.getLastScanDate
def updateLastScanDate = lastScan.updateLastScanDate
}
I want to avoid creating def updateLastScanDate = lastScan.updateLastScanDate just to forward methods to wrapped object.
In the plain language this is not possible. There used to be a compiler plugin by Kevin Wright to achieve this automatic delegation.
He seems to be working on an Autorproxy "Rebooted" version now that is macro based, making it straight forward to include in your project. I'm pasting here an example from its test sources:
trait Bippy {
def bippy(i : Int): String
}
object SimpleBippy extends Bippy {
def bippy(i: Int) = i.toString
}
#delegating class RawParamWrapper(#proxy pivot: Bippy)
val wrapper = new RawParamWrapper(SimpleBippy)
assert(wrapper.bippy(42) == "42")

How do I create a class hierarchy of typed factory method constructors and access them from Scala using abstract types?

(Essentially I need some kind of a synthesis of these two questions (1, 2), but I'm not smart enough to combine them myself.)
I have a set of JAXB representations in Scala like this:
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass())
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString()
}
}
class Order extends Representation {
#BeanProperty
var name: String = _
...
}
class Invoice extends Representation { ... }
The problem I have is with my unmarshalling "constructor" methods:
def unmarshalFromJson(marshalledData: String): {{My Representation Subclass}} = {
val mapper = new ObjectMapper()
mapper.getDeserializationConfig().withAnnotationIntrospector(new JaxbAnnotationIntrospector())
mapper.readValue(marshalledData, this.getClass())
}
def unmarshalFromXml(marshalledData: String): {{My Representation Subclass}} = {
val context = JAXBContext.newInstance(this.getClass())
val representation = context.createUnmarshaller().unmarshal(
new StringReader(marshalledData)
).asInstanceOf[{{Type of My Representation Subclass}}]
representation // Return the representation
}
Specifically, I can't figure out how to attach these unmarshalling methods in a typesafe and DRY way to each of my classes, and then to call them from Scala (and hopefully sometimes by using only abstract type information). In other words, I would like to do this:
val newOrder = Order.unmarshalFromJson(someJson)
And more ambitiously:
class Resource[R <: Representation] {
getRepresentation(marshalledData: String): R =
{{R's Singleton}}.unmarshalFromXml(marshalledData)
}
In terms of my particular stumbling blocks:
I can't figure out whether I should define my unmarshalFrom*() constructors once in the Representation class, or in a singleton Representation object - if the latter, I don't see how I can automatically inherit that down through the class hierarchy of Order, Invoice etc.
I can't get this.type (as per this answer) to work as a way of self-typing unmarshalFromJson() - I get a compile error type mismatch; found: ?0 where type ?0 required: Representation.this.type on the readValue() call
I can't figure out how to use the implicit Default[A] pattern (as per this answer) to work down my Representation class hierarchy to call the singleton unmarshalling constructors using type information only
I know this is a bit of a mammoth question touching on various different (but related) issues - any help gratefully received!
Alex
The key is to not try and attach the method to the class but rather pass it in as a parameter. To indicate the type you are expecting and let the type system handle passing it in. I tried to make the unmarshal invocation something that reads a little DSL like.
val order = UnMarshalXml( xml ).toRepresentation[Order]
The following is a fully testable code snippet
abstract class Representation {
def marshalToXml(): String = {
val context = JAXBContext.newInstance(this.getClass)
val writer = new StringWriter
context.createMarshaller.marshal(this, writer)
writer.toString
}
}
#XmlRootElement
class Order extends Representation {
#BeanProperty
var name: String = _
}
case class UnMarshalXml( xml: String ) {
def toRepresentation[T <: Representation](implicit m:Manifest[T]): T = {
JAXBContext.newInstance(m.erasure).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
}
object test {
def main( args: Array[String] ) {
val order = new Order
order.name = "my order"
val xml = order.marshalToXml()
println("marshalled: " + xml )
val received = UnMarshalXml( xml ).toRepresentation[Order]
println("received order named: " + received.getName )
}
}
You should see the following output if you run test.main
marshalled: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><order><name>my order</name></order>
received name: my order
Here's the updated version of Neil's code which I used to support the second use case as well as the first:
case class UnmarshalXml(xml: String) {
def toRepresentation[T <: Representation](implicit m: Manifest[T]): T =
toRepresentation[T](m.erasure.asInstanceOf[Class[T]])
def toRepresentation[T <: Representation](typeT: Class[T]): T =
JAXBContext.newInstance(typeT).createUnmarshaller().unmarshal(
new StringReader(xml)
).asInstanceOf[T]
}
This supports simple examples like so:
val order = UnmarshalXml(xml).toRepresentation[Order]
But also for abstract type based usage, you can use like this:
val order = UnmarshalXml(xml).toRepresentation[T](typeOfT)
(Where you have grabbed and stored typeOfT using another implicit Manifest at the point of declaring T.)

Dynamic Proxy without explicitely specifying the type in scala

is it possible to have a method that takes an arbitrary instance and returns a java.reflection.Proxy or similar that has the same type as the original argument?
I guess it should look something like this:
def createProxy[S](model: S)(implicit manifest: Manifest[S]): S = {...}
or this
def createProxy[S, T<:S](model: S)(implicit manifest: Manifest[S]): T = {...}
where T is the subtype of S which results from a combination of all the implemented interfaces, since it doesn't seem like I can Proxy an actual class, but only interfaces.
I think the following should do the trick. Note that it can't return an S since it's likely S is not an interface.
import java.lang.reflect._
def createProxy[S](model: S)(implicit manifest: Manifest[S]) = {
val clazz = manifest.erasure
Proxy.newProxyInstance(clazz.getClassLoader, clazz.getInterfaces, new InvocationHandler() {
def invoke(proxy:Object, method:Method, args:scala.Array[Object]) = {
method.invoke(model, args:_*)
}
})
}
In the situations when you want to proxy an object using single interface you can do something like this (based on Hiram Chirino answer):
import java.lang.reflect.{Method, InvocationHandler, Proxy}
def createProxy[I](proxee: I, interfaceClass: Class[I]): I = {
assert(interfaceClass.isInterface, "interfaceClass should be an inteface class")
Proxy.newProxyInstance(interfaceClass.getClassLoader, Array(interfaceClass), new InvocationHandler() {
override def invoke(proxy: Object, method: Method, args: Array[Object]) = {
println("before")
val result = method.invoke(proxee, args: _*)
println("after")
result
}
}).asInstanceOf[I]
}
And having an interface SessionSvc use it like this:
val sessionSvc = createProxy(new SessionSvcMongo, classOf[SessionSvc])
This way I managed to introduce rather painless AOP into cake pattern based codebase.