There is class:
case class A (client: Client) {
def handle(s: String):String = s match {
case sc if (sc.notEmpty) => ""
case sc => s
}
val c = {
val v = client.getValue()
handle(v)
}
}
The task is to test the result of c-variable, while mocking getValue()-invoke
val client = mock[Client]
(client.getValue) expects() returns("A")
val a = A
a.c should be "A"
I expected, test returns "A", but it return "".
Mocking getValue()-invoke does not works
Related
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")
// }
//}
When serializing functions in Scala, we should take care not to capture fields that are not serializable.
class Foo(s: Set[Int] = Set(1,2,3)) {
def replicate(): () => Foo = () => new Foo(s)
}
val foo = new Foo()
val fn = foo.replicate()
serialize(fn) // java.io.NotSerializableException: Foo
In the above code snippet serializing msg will fail because the function captures this and Foo is not serializable.
To avoid capturing this SIP-21 proposes spores for Scala.
Unfortunately, spores only exist for Scala 2.11.x (see this note).
Luckily, I found in this tread (this is also described in SIP-21) that one can avoid capturing the this reference, by manually creating local values as shown in the code snippet below.
class Foo(s: Set[Int] = Set(1,2,3)) {
def replicate(): () => Foo = {
() => {
{
val localS = this.s
new Foo(localS)
}
}
}
}
Unfortunately, in Scala 2.13.1 this does not work and still captures this (try out the complete code snippet below).
Hence, we cannot serialize the function returned by the replicate method.
Now, I found the following workaround, but it's a rather ugly one.
class Foo(s: Set[Int] = Set(1,2,3)) {
def indirection() = replicate(s)
def replicate(set: Set[Int]): () => Foo = () => new Foo(set)
}
val foo = new Foo()
val msg = foo.indirection()
toStr(msg) // Works :)
By adding an extra indirection, we avoid capturing this and we can serialize the function without requiring Foo to be serializable. However, I don't want to introduce these extra indirections in all the objects I want to serialize this way. How can I avoid this (in Scala 2.13.1) ?
The complete code snippet:
// Start writing your ScalaFiddle code here
import java.io._
import java.util.Base64
def serialize(o: Any) = {
val baos = new ByteArrayOutputStream();
val oos = new ObjectOutputStream( baos );
oos.writeObject( o );
oos.close();
Base64.getEncoder().encodeToString(baos.toByteArray())
}
def deserialize(s: String) = {
val data = Base64.getDecoder().decode( s );
val ois = new ObjectInputStream(new ByteArrayInputStream( data ) );
val o = ois.readObject();
ois.close();
o
}
//////////// TRY 1
try {
class Foo(val s: Set[Int] = Set(1,2,3)) {
def replicate(): () => Foo = () => new Foo(s)
}
val foo = new Foo()
val msg = foo.replicate()
val str = serialize(msg)
val reconstructedFoo = deserialize(str).asInstanceOf[() => Foo]()
println(s"Try 1: success, reconstructedFoo.s = ${reconstructedFoo.s}")
} catch {
case e: NotSerializableException => println("TRY 1: Not serializable.")
}
//////////// TRY 2
try {
class Foo(val s: Set[Int] = Set(1,2,3)) {
def replicate(): () => Foo = {
() => {
{
val localS = this.s
new Foo(localS)
}
}
}
}
val foo = new Foo()
val msg = foo.replicate()
val str = serialize(msg)
val reconstructedFoo = deserialize(str).asInstanceOf[() => Foo]()
println(s"Try 2: success, reconstructedFoo.s = ${reconstructedFoo.s}")
} catch {
case e: NotSerializableException => println("TRY 2: Not serializable.")
}
//////////// Solution (ugly)
try {
class Foo(val s: Set[Int] = Set(1,2,3)) {
def indirection() = replicate(s)
def replicate(set: Set[Int]): () => Foo = () => new Foo(set)
}
val foo = new Foo()
val msg = foo.indirection()
val str = serialize(msg)
val reconstructedFoo = deserialize(str).asInstanceOf[() => Foo]()
println(s"Try 3: success, reconstructedFoo.s = ${reconstructedFoo.s}")
} catch {
case e: NotSerializableException => println("TRY 3: Not serializable.")
}
I have been trying to make a simple dependency injection container in Scala all using reflection. The issue I am having is how to correctly pass arguments to MethodMirror. I currently get an exception:
java.lang.IllegalArgumentException: argument type mismatch at
java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
Method) at
Container class:
import scala.reflect.runtime.{universe => ru}
import ru._
import extensions.MapExtension._
class RegisterConfig[TSource: TypeTag](container: Container){
def as[TTrait >: TSource : TypeTag] = {
container.typeTable += typeOf[TTrait] -> typeOf[TSource]
container
}
}
class InstanceConfig[TSource: TypeTag](container: Container, instance: TSource){
def as[TTrait >: TSource : TypeTag] = {
container.typeTable += typeOf[TTrait] -> typeOf[TSource]
container.instances += typeOf[TTrait] -> instance
container
}
}
class ContainerConfig(container: Container) {
def register[TSource: TypeTag] = new RegisterConfig(container)
def instance[TSource: TypeTag](instance: TSource) = new InstanceConfig(container, instance)
}
class Container {
var typeTable = Map[Type, Type]()
var instances = Map[Type, Any]()
def config() = new ContainerConfig(this)
def resolve[TSource: TypeTag] = resolveDynamic(typeOf[TSource]).asInstanceOf[TSource]
private def resolveDynamic(requestedType: Type) = {
val resultType = typeTable.getOrElseWithCompare(requestedType, requestedType, (x, y) => x =:= y)
val instance = instances.getOrElseWithCompare(resultType, createInstance(resultType), (x, y) => x =:= y)
instances += resultType -> instance
instance
}
private def createInstance(tpe: Type): Any = {
val mirror: ru.Mirror = ru.runtimeMirror(getClass.getClassLoader)
val clsSym: ru.ClassSymbol = tpe.typeSymbol.asClass
val clsMirror: ru.ClassMirror = mirror.reflectClass(clsSym)
val constructorSym: ru.MethodSymbol = tpe.decl(ru.termNames.CONSTRUCTOR).asMethod
val constructorParameterTypes = constructorSym.paramLists.head.map(x => x.typeSignature)
// Recursive step
val params = constructorParameterTypes.map(x => resolveDynamic(x)).toArray
// Passing `params` does not work! I get an exception here ...
val constructorMirror: ru.MethodMirror = clsMirror.reflectConstructor(constructorSym)
val instance = constructorMirror(params)
instance
}
}
Unit test:
import org.scalatest.FlatSpec
trait TestTrait
class TestClass extends TestTrait
class TestRecClass(x : TestTrait)
class ContainerTest extends FlatSpec {
"Given Trait" should "return instance (with recursion)" in {
val container = new Container()
.config()
.register[TestClass]
.as[TestTrait]
val instance = container.resolve[TestRecClass]
assert(instance match {
case _: TestRecClass => true
case null => false
})
}
}
I should have used "splat" operator to correctly pass array of arguments to a method that takes variable-arguments
val instance = constructorMirror(params : _*)
Im trying to understand basics of serialization in Scala. When i run the first example below I get the following output on the last line: res1: A.Mao = A$$anonfun$main$1$Mao$1#78e67e0a
#SerialVersionUID(1L)
class Poo(val aa:Int) extends Serializable {
override def toString() = "Hola"
}
#SerialVersionUID(1L)
class Mao(val hi: Poo) extends Serializable
def serialize() = {
val test = new Mao(new Poo(1))
try{
val fout = new FileOutputStream("c:\\misc\\address.ser");
val oos = new ObjectOutputStream(fout);
oos.writeObject(test);
oos.close();
System.out.println("Done");
}catch {
case ex => ex.printStackTrace();
}
}
serialize()
def ReadObjectFromFile[A](filename: String)(implicit m:scala.reflect.Manifest[A]): A = {
val input = new ObjectInputStream(new FileInputStream(filename))
val obj = input.readObject()
obj match {
case x if m.erasure.isInstance(x) => x.asInstanceOf[A]
case _ => sys.error("Type not what was expected when reading from file")
}
}
ReadObjectFromFile[Mao]("c:\\misc\\address.ser")
If I change the example and use case classes instead things works as expected with the output
res1: A.Mao = Mao(Hola)
case class Poo(val aa:Int) {
override def toString() = "Hola"
}
case class Mao(val hi: Poo)
def serialize() = {
val test = new Mao(new Poo(1))
try{
val fout = new FileOutputStream("c:\\misc\\address.ser");
val oos = new ObjectOutputStream(fout);
oos.writeObject(test);
oos.close();
System.out.println("Done");
}catch {
case ex => ex.printStackTrace();
}
}
def ReadObjectFromFile[A](filename: String)(implicit m:scala.reflect.Manifest[A]): A = {
val input = new ObjectInputStream(new FileInputStream(filename))
val obj = input.readObject()
obj match {
case x if m.erasure.isInstance(x) => x.asInstanceOf[A]
case _ => sys.error("Type not what was expected when reading from file")
}
}
ReadObjectFromFile[Mao]("c:\\misc\\address.ser")
So my questions are:
What do I need to do to get class to give the same output as case class?
Why does case class work without adding any explicit information about serialization?
This has nothing to do with the de/serialization (which seems correct) - it's just the way the result is displayed:
Scala REPL (and Worksheets) use the value's toString method to display it. Case classes override the default toString() method, therefore the output is displayed nicely (as expected). For non-case classes, the defeault implementation of Object.toString() is called, and results in the class name and address that you see.
You can implement toString for the non-case class too, to get the same result:
class Mao(val hi: Poo) extends Serializable {
override def toString = s"Mao($hi)"
}
// program prints:
// Done
// Mao(Hola)
I want to be able to do something like this:
prepare form:
val formDescription = formBuilder(_.textField[User](_.firstName)
.textField[User](_.lastName)
).build
showForm(formDescription)
extract data from user filled form, using User:
//contains data of a form submitted by a user:
val formData: Map[String, String] = getFormData
val newUser = User(id = randomUuid, firstName = formData.extract[User](_.firstName))
One solution I see is to use a dynamic proxy that extends provided class and remembers what was invoked on him:
def getFieldName[T:Manifest](foo: T => Any) = {
val clazz = implicitly[Manifest[T]].erasure
val proxy = createDynamicProxy(clazz)
foo(proxy)
proxy.lastInvokedMethodName
}
Is there a better way to do it? Is there any lib that implements it already?
This reflective approach takes a case class and invokes its companion apply, calling getField and fetching default args if the field is not in the data.
import scala.reflect.runtime.{currentMirror => cm, universe => uni}
import uni._
def fromXML(xml: Node): Option[PluginDescription] = {
def extract[A]()(implicit tt: TypeTag[A]): Option[A] = {
// extract one field
def getField(field: String): Option[String] = {
val text = (xml \\ field).text.trim
if (text == "") None else Some(text)
}
val apply = uni.newTermName("apply")
val module = uni.typeOf[A].typeSymbol.companionSymbol.asModule
val ts = module.moduleClass.typeSignature
val m = (ts member apply).asMethod
val im = cm reflect (cm reflectModule module).instance
val mm = im reflectMethod m
def getDefault(i: Int): Option[Any] = {
val n = uni.newTermName("apply$default$" + (i+1))
val m = ts member n
if (m == NoSymbol) None
else Some((im reflectMethod m.asMethod)())
}
def extractArgs(pss: List[List[Symbol]]): List[Option[Any]] =
pss.flatten.zipWithIndex map (p => getField(p._1.name.encoded) orElse getDefault(p._2))
val args = extractArgs(m.paramss)
if (args exists (!_.isDefined)) None
else Some(mm(args.flatten: _*).asInstanceOf[A])
}
// check the top-level tag
xml match {
case <plugin>{_*}</plugin> => extract[PluginDescription]()
case _ => None
}
}
The idea was to do something like:
case class User(id: Int = randomUuid, firstName: String, lastName: String)
val user = extract[User]()
That's my own solution:
package utils
import javassist.util.proxy.{MethodHandler, MethodFilter, ProxyFactory}
import org.specs2.mutable._
import javassist.util.proxy.Proxy
import java.lang.reflect.{Constructor, Method}
class DynamicProxyTest extends Specification with MemberNameGetter {
"Dynamic proxy" should {
"extract field name" in {
memberName[TestClass](_.a) must ===("a")
memberName[TestClass](_.i) must ===("i")
memberName[TestClass](_.b) must ===("b")
memberName[TestClass](_.variable) must ===("variable")
memberName[TestClass](_.value) must ===("value")
memberName[TestClass](_.method) must ===("method")
}
}
}
trait MemberNameGetter {
def memberName[T: Manifest](foo: T => Any) = {
val mf = manifest[T]
val clazz = mf.erasure
val proxyFactory = new ProxyFactory
proxyFactory.setSuperclass(clazz)
proxyFactory.setFilter(new MethodFilter {
def isHandled(p1: Method) = true
})
val newClass = proxyFactory.createClass()
var lastInvokedMethod: String = null
val mh = new MethodHandler {
def invoke(p1: Any, p2: Method, p3: Method, p4: Array[AnyRef]) = {
lastInvokedMethod = p2.getName
p3.invoke(p1, p4: _*)
}
}
val constructor = defaultConstructor(newClass)
val parameters = defaultConstructorParameters(constructor)
// val proxy = constructor.newInstance("dsf", new Integer(0))
val proxy2 = constructor.newInstance(parameters: _*)
proxy2.asInstanceOf[Proxy].setHandler(mh)
foo(proxy2.asInstanceOf[T])
lastInvokedMethod
}
private def defaultConstructor(c: Class[_]) = c.getConstructors.head
private def defaultConstructorParameters(constructor: Constructor[_]) = {
val parameterTypes = constructor.getParameterTypes
parameterTypes.map{
case Integer.TYPE => Integer.valueOf(0)
case _ => null
}
}
}
case class TestClass(a: String, i: Int, b: Boolean) {
var variable = "asdf"
val value = "asdfasdfasd"
def method = "method"
}
val mh = new MethodHandler {
def invoke(p1: Any, p2: Method, p3: Method, p4: Array[AnyRef]) = {
lastInvokedMethod = p2.getName
p3.invoke(p1, p4: _*)
}
}
val constructor = defaultConstructor(newClass)
val parameters = defaultConstructorParameters(constructor)
// val proxy = constructor.newInstance("dsf", new Integer(0))
val proxy2 = constructor.newInstance(parameters: _*)
proxy2.asInstanceOf[Proxy].setHandler(mh)
foo(proxy2.asInstanceOf[T])
lastInvokedMethod
}
private def defaultConstructor(c: Class[_]) = c.getConstructors.head
private def defaultConstructorParameters(constructor: Constructor[_]) = {
val parameterTypes = constructor.getParameterTypes
parameterTypes.map{
case Integer.TYPE => Integer.valueOf(0)
case java.lang.Double.TYPE => java.lang.Double.valueOf(0)
case java.lang.Long.TYPE => java.lang.Long.valueOf(0)
case java.lang.Boolean.TYPE => java.lang.Boolean.FALSE
case _ => null
}
}
}
case class TestClass(a: String, i: Int, b: Boolean) {
var variable = "asdf"
val value = "asdfasdfasd"
def method = "method"
}