I'm new to spray. Im playing around with building the routes, and while I manage to get parameters out of the query string using the parameters directive, I'm having trouble when I want one of the parameters to be a list.
For this example I've defined this case class:
case class Person(name: String, friends: Int)
my route currently looks like this:
path("test") {
get { parameters('name, 'friend ).as(Person) { p => complete(p) } }
}
this works fine and I can do a get: localhost:8080/test?name=jo&friends=12
and get what I expect.
I want to pass a list of friends ids, rather than just the number of friends, so I started by changing my case class like so:
case class Person(name: String, friends: Array[Int])
and my call to: localhost:8080/test?name=jo&friends=1,2
this does not compile. I get a type mismatch:
found : Person.type
required: spray.routing.HListDeserializer[shapeless.::[String,shapeless.::[String,shapeless.HNil]],?]
get { parameters('name, 'friend ).as(Person) { p =>
^ comment: this points at the P in .as(Person)
Any idea on what I'm doing wrong? I'd love an answer on how to do it. Even better would be an explanation to what is this shapeless type that it's looking for. Thanks
The first example worked since the parameter 'friend could be automatically converted from String to Int, hence satisfying the requirements of the Person case class.
The latter doesn't work because there's no String => Array[Int] conversion available, so it's impossible to materialize a Person from two strings.
You can tell that it's treating both 'friend and 'name as strings by looking at the message error
spray.routing.HListDeserializer[shapeless.::[String,shapeless.::[String,shapeless.HNil]],?]
can be simplified to something like
String :: String :: HNil
i.e. it's looking for something that can deserialize two strings into something else.
Bottom line, you will need to provide a custom deserializer in order to parse "1,2" into an Array[Int].
Here's the relevant documentation: http://spray.io/documentation/1.1-SNAPSHOT/spray-httpx/unmarshalling/#unmarshalling
Related
I have a field that may come from a rest api as a String or Int, but when I read it I always want to read it as a String, i.e. if it comes as an Int I want to do a toString on it
case class ZoneList(
someField: Int,
targetField: String
)
object ZoneList {
implicit val rw: ReadWriter[ZoneList] = macroRW
}
targetField is the field in question
Looking at http://www.lihaoyi.com/upickle/#CustomPicklers, but still dont think I have enough of a handle to start a custom pickler
edit:
ended up doing this
implicit val anyToStringReader: Reader[Option[String]] =
reader[ujson.Value].map[Option[String]] { j =>
Try(j.toString()).toOption
}
Would have preferred if I could single out the targetField attribute only but my actual case class has a lot of fields and don't think I can do that and also utilize the default macro. If anyone knows how let me know
Solved by lihaoyi in the upickle gitter:
"if you want to single out that attribute, give it a new type that’s a wrapper around Option String and write your pickler for that type"
I am reading this example from their docs:
class Email(val username: String, val domainName: String)
object Email {
def fromString(emailString: String): Option[Email] = {
emailString.split('#') match {
case Array(a, b) => Some(new Email(a, b))
case _ => None
}
}
}
println(Email.fromString("scala.center#epfl.ch"))
val scalaCenterEmail = Email.fromString("scala.center#epfl.ch")
scalaCenterEmail match {
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""")
case None => println("Error: could not parse email")
}
My questions:
What is Some and Option?
What is a factory method (just some function that creates a new object and returns it?)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
What is Some and Option?
Option is a data structure that represents optionality, as the name suggests. Whenever a computation may not return a value, you can return an Option. Option has two cases (represented as two subclasses): Some or None.
In the example above, the method Email.fromString can fail and not return a value. This is represented with Option. In order to know whether the computation yielded a value or not, you can use match and check whether it was a Some or a None:
Email.fromString("scala.center#epfl.ch") match {
case Some(email) => // do something if it's a Some
case None => // do something it it's a None
}
This is much better than returning null because now whoever calls the method can't possibly forget to check the return value.
For example compare this:
def willReturnNull(s: String): String = null
willReturnNull("foo").length() // NullPointerException!
with this
def willReturnNone(s: String): Option[String] = None
willReturnNone("foo").length() // doesn't compile, because 'length' is not a member of `Option`
Also, note that using match is just a way of working with Option. Further discussion would involve using map, flatMap, getOrElse or similar methods defined on Option, but I feel it would be off-topic here.
What is a factory method (just some function that creates a new object and returns it?)
This is nothing specific to Scala. A "factory method" is usually a static method that constructs the value of some type, possibly hiding the details of the type itself. In this case fromString is a factory method because it allows you create an Email without calling the Email constructor with new Email(...)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
As a first approximation, yes. Scala doesn't have static members of a class. Instead, you can have an object associated with that class where you define everything that is static.
E.g. in Java you would have:
public class Email {
public String username;
public String domain;
public static Optional<Email> fromString(String: s) {
// ...
}
}
Where as in Scala you would define the same class as roughly:
class Email(val username: String, val domain: String)
object Email {
def fromString(s: String): Option[Email] = {
// ...
}
}
I would like to add some examples/information to the third question.
If you use akka in companion object you can put every message that you use in case method (it should proceed and use by actor). Moreover, you can add some val for a name of actors or other constant values.
If you work with JSON you should create a format for it (sometimes custom reads and writes). This format you should put inside companion object. Methods to create instances too.
If you go deeper to Scala you can find case classes. So a possibility to create an object of this class without new is because there is a method apply in "default" companion object.
But in general, it's a place where you can put every "static" method etc.
About Option, it provides you a possibility to avoid some exception and make something when you don't have any values.
Gabriele put an example with email, so I'll add another one.
You have a method that sends email, but you take email from User class. The user can have this field empty, so if we have something like it
val maybeEmail: Option[String] = user.email you can use for example map to send an email
maybeEmail.map(email => sendEmail(email))
So if you use it, during writing methods like above you don't need to think that user specify his email or not :)
I am trying to model (in my Scala application) a list of options presented in my web page and am scratching my head coming up with a solution for mapping a String value posted from the client to it's corresponding object in the backend.
eg. Let's say it is a list of Animals and the user can choose 1 which gets posted to the backend.
Animals
Polar Bear
Rabbit
Great White Shark
When a request comes in, I want to convert the Great White Shark String to an Animal but not sure on how best to match the
String to the appropriate type in the backend.
So far I have this.
sealed abstract class Animal(val name: String)
case object GreatWhite extends Animal("Great White Shark")
case object PolarBear extends Animal("Polar Bear")
Which allows me to do this to match the String from the UI to it's corresponding case object in my Scala application.
def matcher(animal: String) = animal match {
case GreatWhite.name => GreatWhite
case PolarBear.name => PolarBear
}
Problem
If the List of Animal's grows long however, this matcher is going to be very cumbersome since I need to have a case expression for every Animal.
I would much appreciate any experienced Scala guys giving me a pointer on a more elegant solution.
It's looks like what you need is simply have a hash table of String to Animal.
Such approach gives you ability to get result in constant time O(1) even with extensivly growing list.
val mapping = Map[String, Animal]("Rabbit" -> Rabbit, "Polar Bear" -> PolarBear /* ... */ )
// matcher
mapping.get(animal)
UPD.
Some useful comments below.
sealed abstract class Animal(val name: String)
case object GreatWhite extends Animal("Great White Shark")
case object PolarBear extends Animal("Polar Bear")
val mapping: Map[String, Animal] = Seq(GreatWhite, PolarBear).map(x => x.name -> x).toMap
mapping
Have you looked at Enums? If they are usable for you, Enums have a .withName method http://yefremov.net/blog/scala-enum-by-name/
I have this case class with a lot of parameters:
case class Document(id:String, title:String, ...12 more params.. , keywords: Seq[String])
For certain parameters, I need to do some string cleanup (trim, etc) before creating the object.
I know I could add a companion object with an apply function, but the LAST thing I want is to write the list of parameters TWICE in my code (case class constructor and companion object's apply).
Does Scala provide anything to help me on this?
My general recommendations would be:
Your goal (data preprocessing) is the perfect use case of a companion object -- so it is maybe the most idiomatic solution despite the boilerplate.
If the number of case class parameters is high the builder pattern definitely helps, since you do not have to remember the order of the parameters and your IDE can help you with calling the builder member functions. Using named arguments for the case class constructor allows you to use a random argument order as well but, to my knowledge, there is not IDE autocompletion for named arguments => makes a builder class slightly more convenient. However using a builder class raises the question of how to deal with enforcing the specification of certain arguments -- the simple solution may cause runtime errors; the type-safe solution is a bit more verbose. In this regard a case class with default arguments is more elegant.
There is also this solution: Introduce an additional flag preprocessed with a default argument of false. Whenever you want to use an instance val d: Document, you call d.preprocess() implemented via the case class copy method (to avoid ever typing all your arguments again):
case class Document(id: String, title: String, keywords: Seq[String], preprocessed: Boolean = false) {
def preprocess() = if (preprocessed) this else {
this.copy(title = title.trim, preprocessed = true) // or whatever you want to do
}
}
But: You cannot prevent a client to initialize preprocessed set to true.
Another option would be to make some of your parameters a private val and expose the corresponding getter for the preprocessed data:
case class Document(id: String, title: String, private val _keywords: Seq[String]) {
val keywords = _keywords.map(kw => kw.trim)
}
But: Pattern matching and the default toString implementation will not give you quite what you want...
After changing context for half an hour, I looked at this problem with fresh eyes and came up with this:
case class Document(id: String, title: String, var keywords: Seq[String]) {
keywords = keywords.map(kw => kw.trim)
}
I simply make the argument mutable adding var and cleanup data in the class body.
Ok I know, my data is not immutable anymore and Martin Odersky will probably kill a kitten after seeing this, but hey.. I managed to do what I want adding 3 characters. I call this a win :)
In my Play 2 controller (Scala) I've a method that looks like the following.
start(id:String, keywords:Option[List[String]])
Basically I want to get pass a list of string as keywords where it's optional.
The following doesn't work and gives me a compile error.
GET /start start(id:String,options:Option[List[String]])
The error makes sense because even if this route compiled I'm not sure how I would pass a list of Strings in my GET URL.
I'm looking for suggestions to resolve this.
Since you're just using keywords, how about comma-separated values in the query string?
GET /start/:id controllers.Sample.start(id: String, options: Option[String])
/start/1233?options=key,word,test
Then in your controller convert to Option[List[String]]:
def start(id: String, options: Option[String]) = Action {
val opts: Option[List[String]] = options.map(_.split(',').filter(_.nonEmpty))
...
}