How to take zero or more params with Lift Web Framework - lift

How can I write a snippet that will take either zero or more params and make them available to me in the render method?
I can't seem to find a complete example anywhere, basically what I want is to be able to take
localhost:8080
localhost:8080/
localhost:8080/somevalue
and be able to access a case object that is either Full(someValue) or Empty.
Something like this basically, from https://www.assembla.com/spaces/liftweb/wiki/Location_Parameters
menu = Menu.params[(Product,ProductReview)](
"ProdInfo",
"Product Information",
ids => {
case prodId :: revId :: Nil =>
(findProductById(prodId), findReviewById(revId)) match {
case (Full(product), Full(review)) => Full((product, review))
case _ => Empty
}
case _ =>
Empty
}
productandreview => {
case (product, review) =>
getIdForProduct(product) :: getIdForReview(review) :: Nil
}
) / "products" >> If(() => S.loggedIn_?, () => RedirectResponse("/login"))
I want to do an even simpler version of that and would appreciate any help. I have been working with the "shop with me" and "pocketchange" example projects, but haven't figured out how to connect all the dots yet.

If you want quicker responses you should probably look into the Lift's official support channel, and that is their mailing list. https://groups.google.com/forum/#!forum/liftweb
To handle the URL you have several options. Ones that I know of are:
just get the URL and handle it manually, via S.uri / s.request.*.uri
use REST. You can specify an unknown or optional path there. https://www.assembla.com/wiki/show/liftweb/REST_Web_Services

One of the easiest solutions would be to have a wrapper, like:
case class ProductTupleMode(items:Box[(Product,ProductReview)] = Empty)
Then you can would setup your site rule to be something like below:
menu = Menu.params[ProductTupleMode](
"ProdInfo",
"Product Information",
ids => {
case prodId :: revId :: Nil =>
(findProductById(prodId), findReviewById(revId)) match {
case (Full(product), Full(review)) =>
Full(ProductTupleMode(Box !! (product, review)))
case _ => Full(ProductTupleMode())
}
case _ =>
Full(ProductTupleMode())
},
productandreview => {
case ProductTupleMode(Full((product, review))) =>
getIdForProduct(product) :: getIdForReview(review) :: Nil
}
) / "products" / ** >> If(() => S.loggedIn_?, () => RedirectResponse("/login"))
And in your snippet snippet you could then use either value:
class MySnippet(m:ProductTupleMode) {
def render = m match {
case Full(m.items) => ...
case _ => ...
}
}
I think that would be the cleanest and easiest to follow, but if you really need to have the signature Box[(Product, ProductReview)] in your snippet then you could also return Box[Box[(Product, Product)]] like this:
menu = Menu.params[Box[(Product,ProductReview)]](
"ProdInfo",
"Product Information",
ids => {
case prodId :: revId :: Nil =>
(findProductById(prodId), findReviewById(revId)) match {
case (Full(product), Full(review)) => Full(Full((product, review)))
case _ => Full(Empty)
}
case _ =>
Full(Empty)
},
productandreview => {
case Full((product, review)) =>
getIdForProduct(product) :: getIdForReview(review) :: Nil
}
) / "products" / ** >> If(() => S.loggedIn_?, () => RedirectResponse("/login"))

Related

Scala case match a String

I'm trying to match case against a String and have the following code:
val selectedAnswers: List[Int] = questionType match {
case "CHECK_BOX" => {
answerCheckBox match {
case Some(answers) => answers
case None => List()
}
}
case "RADIO_BUTTON" => {
answerRadio match {
case Some(answer) => List(answer)
case None => List()
}
}
case _ => {
List()
}
}
Why is it not falling through the _ case when the String is not RADIO_BUTTON or CHECK_BOX?
The values for answerRadio and answerCheckbox are actually coming from the form that I'm submitting to the controller.
val (currentQuesId, questionType, answerRadio, answerCheckBox) = runExamForm.bindFromRequest.get
And the form declaration looks like:
val runExamForm = Form(
tuple(
"currentQuestionId" -> number,
"questionType" -> text,
"answerRadio" -> optional(number),
"answerCheckbox" -> optional(list(number))
)
)
This is "equivalent" version of your code:
val selectedAnswers: List[Int] = questionType match {
case "CHECK_BOX" => answerCheckBox.toList.flatten
case "RADIO_BUTTON" => answerRadio.toList
case _ => List()
}
Does it work as expected?
It's a long shot, but try replacing the _ with some other name (x is fine) and make sure that your code contains nothing but regular whitespace.
Very rarely I've seen bizarre errors like this caused by other non-printing characters in the code, which always seemed to be caused by pasting code from a chat on the OSX Skype client.
Also... Can you confirm in your code sample which line the MatchError occurs on?

Lift: Menu with multiple params [Menu.params]

How to build an url with two or many params?
i've a case class:
case class PageDetail(index: String, id: String)
i'm trying to do a menu val:
val menu = Menu.params[(String,String)]( "pageDetail", "Page Detail",
ids => { case Full(index) :: Full(id) :: Nil => Full((index, id))},
pi => { case (index, id) => index :: id :: Nil }) / "admin" / "detail"
i would like to obtain a link as .../admin/detail/indexxxxxxx/idddddddddd where indexxxxxxx and idddddddddd are my params.
as is doesn't work. Error in compile time. How can i do?
Thanks
Most likely, the issue is in your extractor pattern. When you are matching on your list here:
case Full(index) :: Full(id) :: Nil => Full((index, id))
The parameters are always going to be defined, so the Full is not possible. You can use functions, such as AsInt to require the parameter to be an Int, or else it will look for a String. You'd most likely want to start with the following (Or some variation on that):
case index :: id :: Nil => Full((index, id))
If you are using Empty to mean the parameter is optional, then you would simply add a second case statement after it with the parameter omitted.
Also, you probably need to add / ** to the end of you / "admin" / "detail" mapping so it knows to grab the parameters from there.
So, the code should look something like this:
val menu = Menu.params[(String,String)]( "pageDetail", "Page Detail",
{
case index :: id :: Nil => Full((index, id))
}, {
case (index, id) => index :: id :: Nil
}
) / "admin" / "detail" / **

Scala match case on regex directly

I am trying to do something like the following:
list.foreach {x =>
x match {
case """TEST: .*""" => println( "TEST" )
case """OXF.*""" => println("XXX")
case _ => println("NO MATCHING")
}
}
The idea is to use it like groovy switch case regex match. But I can't seem to get to to compile. Whats the right way to do it in scala?
You could either match on a precompiled regular expression (as in the first case below), or add an if
clause. Note that you typically don't want to recompile the same regular expression on each case evaluation, but rather have it on an object.
val list = List("Not a match", "TEST: yes", "OXFORD")
val testRegex = """TEST: .*""".r
list.foreach { x =>
x match {
case testRegex() => println( "TEST" )
case s if s.matches("""OXF.*""") => println("XXX")
case _ => println("NO MATCHING")
}
}
See more information here and some background here.
Starting Scala 2.13, it's possible to directly pattern match a String by unapplying a string interpolator:
// val examples = List("Not a match", "TEST: yes", "OXFORD")
examples.map {
case s"TEST: $x" => x
case s"OXF$x" => x
case _ => ""
}
// List[String] = List("", "yes", "ORD")

How to use a Map value in a match case statement in Scala

I have a map like this:
val mealIdsMap: Map[String, String] =
Map (
"breakfast" -> "omelet",
"lunch" -> "steak",
"dinner" -> "salad"
)
Then I try to use it in a match statement like this:
"omelet" match
{
case mealIdsMap("breakfast") => "Thank God"
}
And I get this error:
error: value mealIdsMap is not a case class constructor,
nor does it have an unapply/unapplySeq method
case mealIdsMap("breakfast") => "Thank God"
Anyone know how to use a map like this in a match/case statement?
Thanks alot for your help.
You should read what is the purpose of pattern matching from a tutorial, may be from this one (first non trivial example on google).
You have inverted the test:
mealIdsMap("breakfast") match {
case "omelet" => "Thank God"
case _ => "Don't forget a default"
}
And if you're not sure that the key is present (and even if you are, if you want to write idiomatic scala, you should prefer:
mealIdsMap.get("breakfast") match {
case Some("omelet") => "Thank God"
case _ => "Don't forget a default"
}
Where getreturns an option, avoiding you to try catch your code or to let it break silently.
Though, its still interesting to try to achieve such a behavior. have a look at this example:
case class InvMatcher (m:Map[String, String]){
def unapply(v:String):Option[String] = {
return m collectFirst {case (k, `v`) => k}
}
}
This class helps you to inverse-match a map.
usage:
val ma = InvMatcher (Map (
"breakfast" -> "omelet",
"lunch" -> "steak",
"dinner" -> "salad"
));
"steak" match {
case ma(s) => s match {
case "breakfast" => print("Thank God")
case "lunch" => print("whatever")
case _ => print("dont forget default")
}
case _ => print("dont forget default")
}
This is nearly as you wanted it though you need a second match-statement (which doesnt need a default case here...)

How to match a string on a prefix and get the rest?

I can write the code like this:
str match {
case s if s.startsWith("!!!") => s.stripPrefix("!!!")
case _ =>
}
But I want to know is there any better solutions. For example:
str match {
case "!!!" + rest => rest
case _ =>
}
val r = """^!!!(.*)""".r
val r(suffix) = "!!!rest of string"
So suffix will be populated with rest of string, or a scala.MatchError gets thrown.
A different variant would be:
val r = """^(!!!){0,1}(.*)""".r
val r(prefix,suffix) = ...
And prefix will either match the !!! or be null. e.g.
(prefix, suffix) match {
case(null, s) => "No prefix"
case _ => "Prefix"
}
The above is a little more complex than you might need, but it's worth looking at the power of Scala's regexp integration.
Starting Scala 2.13, it's now possible to pattern match a String by unapplying a string interpolator:
"!!!hello" match {
case s"!!!$rest" => rest
case _ => "oups"
}
// "hello"
If it's the sort of thing you do often, it's probably worth creating an extractor
object BangBangBangString{
def unapply(str:String):Option[String]= {
str match {
case s if s.startsWith("!!!") => Some(s.stripPrefix("!!!"))
case _ => None
}
}
}
Then you can use the extractor as follows
str match{
case BangBangBangString(rest) => println(rest)
case _ => println("Doesn't start with !!!")
}
or even
for(BangBangBangString(rest)<-myStringList){
println("rest")
}
Good question !
Even i was trying a lot to find out the answer.
Here is a good link where I found the answer
object _04MatchExpression_PatternGuards {
def main(args: Array[String]): Unit = {
val url: String = "Jan";
val monthType = url match {
case url if url.endsWith(".org") => "Educational Websites";
case url if url.endsWith(".com") => "Commercial Websites";
case url if url.endsWith(".co.in") => "Indian Websites"
case _ => "Unknow Input";
}
}
}