How to generalize a method? - scala

I'm trying to generalize the following code:
def fetchUrl = {
try {
val lineList = Source.fromURL(url).getLines.toList
process(lineList)
}
catch {
case ex: java.net.UnknownHostException => ex.printStackTrace()
}
}
I want to be able to fetch URL's (fromURL) and files (fromFile) with the same method. Is it possible to generalize this code to archive this?
I figured I could use pattern matching for this but I don't know how.
def fetchSource(src: Source, str: String) = src match {
case ??? => Source.fromURL(url).getLines.toList
case ??? => Source.fromFile(str).getLines.toList
}
Is there a way to get this right?
Thanks in advance.

You could check if a string is a Url (for this example I'm using java.net.URL, but you can do it with UrlValidator as well)
Something like this:
def isUrl(url: String) = try {
new java.net.URL(url)
true
} catch {
case _ => false
}
import scala.io.Source
def fetchSource(src:String) = if(isUrl(src)) {
Source.fromURL(src).getLines.toList
} else {
Source.fromFile(src).getLines.toList
}

The simplest solution is to have one method that will fetch a given Source, and two wrapper methods that build Source from File or URL.
def fetchSource(source: io.Source) =
try {
val lineList = source.getLines.toList
process(lineList)
} catch {
case ex: java.net.UnknownHostException => ex.printStackTrace()
}
def fetchFile(file: java.io.File) = fetchSource(io.Source.fromFile(file))
def fetchUrl(url: java.net.URL) = fetchSource(io.Source.fromURL(url))

Related

How to make only few datatype which is not related to each other acceptable by generics

There is a trait which works perfectly. However, I would like to refactor the part related to generic [T] in order to limit the data type which could be accepted by generic [T] (I need only Option[JsValue] , JsValue , StringEnumEntry , String ). Is it possible to solve this problem through shapeless coproduct? Maybe there are other solutions?
trait ParameterBinders extends Log {
def jsonBinder[T](json: T, jsonType: java.lang.String = "json"): ParameterBinderWithValue = {
val jsonObject = new PGobject()
jsonObject.setType(jsonType)
json match {
case json: Option[JsValue] =>
jsonObject.setValue(json.map(Json.stringify).orNull)
case json: JsValue =>
jsonObject.setValue(Json.stringify(json))
case json: StringEnumEntry =>
jsonObject.setValue(json.value)
case json: String =>
jsonObject.setValue(json)
case _ =>
logger.error("unexpected data type ")
}
if (jsonType == "JSONSCHEMATYPE" || jsonType == "SYSPROPERTYTYPE") {
ParameterBinder(this, (ps, i) => {
ps.setObject(i, jsonObject)
})
} else {
ParameterBinder(json, (ps, i) => {
ps.setObject(i, jsonObject)
})
}
}
}
The easiest way is to use an ADT as described in the link of the first comment.
If you don't want to change the types that are accepted in jsonBinder then you can solve the problem by using a typeclass.
e.g.
trait JsonBindValue[T] {
def value(t: T): String
}
you would then have to provide instances for your accepted datatypes
object JsonBindValue {
implicit val OptJsBinder = new JsonBindValue[Option[JsValue]] {
def value(t: Option[JsValue]): String = {
t.map(Json.stringify).orNull
}
}
... more instances here
}
finally your function would look like this:
def jsonBinder[T : JsonBindValue](json: T, jsonType: java.lang.String = "json"): ParameterBinderWithValue = {
val binder = implicitly[JsonBindValue[T]]
jsonObject.setType(jsonType)
jsonObject.setValue(binder.value(json))
...
}
if you call the function without a implicit instance in scope you will get a compile time error.

closing file pointer in Scala in Finally

In the following code, I am reading no. of lines from a file. If something goes wrong, I'll like to close the file pointer. But how can I find out if f contains valid pointer or not?
def countLines(filename:String):Option[Int] = {
try{
val f = Source.fromFile(filename)
println(s"no. of lines ${f.getLines().size}")
Some(f.getLines.size)
} catch {
case ex: FileNotFoundException => {
println(s"file ${filename} not found")
None
}
} finally {
//f might not be a valid pointer depending on when the error occured
}
}
The book I am reading uses var to maintain state (if f is valid or not) but I am trying to avoid it for sake of using only immutable variables.
def countLines(filename:String):Option[Int] = {
var f:Option[Source] = None
try{
f = Some(Source.fromFile(filename))
println(s"no. of lines ${f.get.getLines().size}")
Some(f.get.getLines.size)
} catch {
case ex: FileNotFoundException => {
println(s"file ${filename} not found")
None
}
} finally {
for(i<-f){
println("closing file")
i.close()
}
}
}
A double Try(). This closes the io resource even if the getLines() fails, but only if the fromFile() succeeds.
import scala.util.Try
def countLines(filename: String): Option[Int] =
Try(io.Source.fromFile(filename)).fold(_ => None, {f =>
val count = Try(f.getLines().length)
f.close()
count.toOption
})
What do you think about this?
If you want Scala-way - i think it's good example for your task:
def countLines(filename: String): Try[Int] = Try(Source.fromFile(filename).getLines.toList.size)
def test() = {
val filename = "/etc/passwd"
countLines(filename) match {
case Success(n) => println(n)
case Failure(f) => println(f)
}
}
When n - is a number of our lines, and f - is a Throwable.
How about this:
def countLines(filename: String): Option[Int] = {
val file = Try(Source.fromFile(filename))
val count = file.map(_.getLines().size)
(for {
_ <- count.recoverWith { case _ => file.map(_.close()) }
lineCount <- count
} yield lineCount).toOption
}
Let's analyze it:
If file does not exist we will have failed Try instance and method returns None. In this case you do not need to clear any resources as no actual stream was created.
If getLines fails for any reason or anything else during processing goes south we will close created stream in first line of for comprehension
Hope it helps
Simply, how about this:
def numLines(fileName:String):Option[Int] = {
try {
val f = scala.io.Source.fromFile(fileName)
try { Some(f.getLines.size) }
catch { case ex: IOException =>
Console.err.println("i/o excetion")
None
}
finally { f.close() }
}
catch {
case ex: FileNotFoundException =>
Console.err.println("file not found")
None
}
}

Primitive Try/match handling in scala

I wrote the following piece of code
def info(): MyCaseClass = {
Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} match {
case Failure(f) => {
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}
case Success(s) => s
}
}
Is there an even shorter way to deal with the fact that the file system operation could result in an exception so I handle it as above. Like can I not just somehow have to deal with failure case only. Like in case of Future exceptions, the future just does what it has to but for exceptions only we define recover and recoverWith. Something analogous is possible here?
Simply use try instead of Try:
def info(): MyCaseClass = {
try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} catch {
case f: Throwable => {
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}
}
}
Try has recover as well:
def info(): MyCaseClass = {
(Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
} recover {
case f =>
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
}).get
}
.getOrElse seems to be what you are looking for:
def info(): MyCaseClass = {
Try {
val fileSys = new File("somePath")
MyCaseClass(fileSys.getTotalSpace, fileSys.getUsableSpace)
}.getOrElse({
logger.error(s"Could not read information:${f.getStackTrace}")
MyCaseClass(0, 0)
})
}

How to dynamically select class type when parsing JSON to object in Play Framework?

The following sample code uses Play Framework to parse JSON to an object:
def createEvent(): Action[JsValue] = Action.async(parse.tolerantJson) {
request => {
request.body.validate[SomeEvent] match {
case o:JsSuccess[SomeEvent] => {
//do something
Future(Created)
}
}
}
}
Is it possible to generalise it so it can handle different event types? e.g.
def createEvent(): Action[JsValue] = Action.async(parse.tolerantJson) {
request => {
val eventType = request.contentType match {
case Some("SomeEventType") => SomeEvent
case Some("OtherEventType") => OtherEvent
}
request.body.validate[eventType] match {
case o:JsSuccess[eventType] => {
//do something
Future(Created)
}
}
}
}
Currently, the above code will fail in the line request.body.validate[eventType]
You can extract body.validate[T] into a function and call it from your patten matching construct with a proper type, i.e:
def extract[T: JsonFormat](implicit req: Request[AnyContent]) = req.body.valudate[T]
request.contentType match {
case Some("SomeEventType") => extract[SomeEvent]
case Some("OtherEventType") => extract[OtherEvent]
}
You can read and create class from contentType dynamically. But you can have problem, if will be no implicit Format for extracted type in scope:
error: No Json formatter found for type A. Try to implement an implicit Format for this type.
or
java.lang.ClassNotFoundException: models.Bazz
The available data:
model
package models
case class Bar(name: String)
request
request.contentType: Option[String] = Some("models.Bar")
incoming body
request.body: JsValue = """{"name": "bar name"}"""
format[Bar]
implicit val BarFormat = Json.format[models.Bar]
extractor:
def extract[A <: Any](ct: String, json: JsValue)(implicit format: Format[A]) = {
val manifest:Manifest[A] = Manifest.classType[A](Class.forName(ct))
json.validate[A]
}
request.contentType.map(extract(_, request.body))
res1: Option[play.api.libs.json.JsResult[models.Bar]] = Some(JsSuccess(Bar(bar name),/name))
you can see https://github.com/strongtyped/fun-cqrs/blob/demos/shop-sample/samples/shop/src/main/scala/funcqrs/json/TypedJson.scala for other way of deserializing self-contained json.

Is it possible to set Response Code from Marshaller in spray.io?

I have following code snippet where dataService returns Option[LocationDataResult].
I would like to set NotFound when dataService returns None and send the data back in case of Some( ...).
I have following code:
val route: Route = {
pathPrefix("service" / "data") {
pathPrefix( "infobox") {
get {
parameters(('mes.as[String], 'oks.as[String])) {
(me, okr) =>
val resp = dataService.ask(GetocationInfoboxData(me,okr)).mapTo[LocationInfoboxDataResult]
.map(remapInfoboxToResponseObject(_)).map { r =>
r match {
case None => StatusCodes.NotFound
case Some(dataToRespond) => dataToRespond
}
}
complete {
resp
}
}
}
}
}
}
implicit val responseMarhaller: Marshaller[LocationInfobox] = Marshaller.of[WikiLocationInfobox](ContentTypes.`application/json`) { (value, contentType, ctx) =>
val result: String = mapper.writeValueAsString(value)
ctx.marshalTo(HttpEntity(contentType, result))
}
I am not able to find a proper way from marshaller and from route via complete function I am not able to make it work.
Could someone more experienced give me a hint? Am I missing some important concept here?
Thx
UPDATE: Error message" Expression of type Future[Object] doesn't conform to expected type ToResponseMarsallable.
The code looks ok, not sure what doesn't work. Try to rewrite it with Spray directives for futures instead of completing the future itself:
val locationData = dataService.ask(GetocationInfoboxData(me,okr)).mapTo[LocationInfoboxDataResult]
onSuccess(locationData.map(remapInfoboxToResponseObject)) {
case None => complete(StatusCodes.NotFound)
case Some(data) => complete(data)
}
This works for me fine taking advantage of MetaMarshallers
val resp = dataService.ask(GetocationInfoboxData(me, okr)).mapTo[LocationInfoboxDataResult].map(remapInfoboxToResponseObject(_)).map{
r =>
r match {
case None => Left(StatusCodes.NotFound)
case Some(data) => Right(data)
}
}
complete(resp)