scala & lift: rewriting URL using variable declared outside LiftRules.statelessRewrite.append - scala

I'm looking for a solution for rewriting URLs in lift using a list declared outside the scope of LiftRules.statelessRewrite.append
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath("abc" :: Nil, _ , _ , _ ), _ , _ ) =>
RewriteResponse("index" :: Nil)
}
I'd like to have the following code working the same as the one above:
val requestList = "abc" :: Nil
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath(requestList, _ , _ , _ ), _ , _ ) =>
RewriteResponse("index" :: Nil)
}
Could anyone write how to get such functionality with lift 2.0?
[edit]
Could you also suggest the best way to access this list's suffix as parameter. What I would like to get is similar to:
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath(`requestList` ::: List(someId), _ , _ , _ ), _ , _ ) =>
RewriteResponse("index" :: Nil, Map("someId" -> someId))
}

Any lowercased variable in a case statement will create a new variable with that name, therefore requestList is going to be shadowed. Try this:
val requestList = "abc" :: Nil
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath(list, _ , _ , _ ), _ , _ ) if list == requestList =>
RewriteResponse("index" :: Nil)
}
Another approach would be to use backticks (Scala ref: ‘stable identifier patterns’):
LiftRules.statelessRewrite.append {
case RewriteRequest(ParsePath(`requestList`, _ , _ , _ ), _ , _ ) =>
RewriteResponse("index" :: Nil)
}
In your case, the second form would be the canonical one to choose, but in general the first form will be more powerful.
As a third alternative, you could also define val RequestList = requestList and match against the uppercased version, though I would advise against this unless you have a good reason for creating a capitalised RequestList.

Related

Find person and immediate neighbours in Seq[Person]

Given a Seq[Person], which contains 1-n Persons (and the minimum 1 Person beeing "Tom"), what is the easiest approach to find a Person with name "Tom" as well as the Person right before Tome and the Person right after Tom?
More detailed explanation:
case class Person(name:String)
The list of persons can be arbitrarily long, but will have at least one entry, which must be "Tom". So those lists can be a valid case:
val caseOne = Seq(Person("Tom"), Person("Mike"), Person("Dude"),Person("Frank"))
val caseTwo = Seq(Person("Mike"), Person("Tom"), Person("Dude"),Person("Frank"))
val caseThree = Seq(Person("Tom"))
val caseFour = Seq(Person("Mike"), Person("Tom"))
You get the idea. Since I already have "Tom", the task is to get his left neighbour (if it exists), and the right neighbour (if it exists).
What is the most efficient way to achieve to do this in scala?
My current approach:
var result:Tuple2[Option[Person], Option[Person]] = (None,None)
for (i <- persons.indices)
{
persons(i).name match
{
case "Tom" if i > 0 && i < persons.size-1 => result = (Some(persons(i-1)), Some(persons(i+1))) // (...), left, `Tom`, right, (...)
case "Tom" if i > 0 => result = (Some(persons(i-1)), None) // (...), left, `Tom`
case "Tom" if i < persons.size-1 => result = (Some(persons(i-1)), None) // `Tom`, right, (...)
case "Tom" => result = (None, None) // `Tom`
}
}
Just doesn't feel like I am doing it the scala way.
Solution by Mukesh prajapati:
val arrayPersons = persons.toArray
val index = arrayPersons.indexOf(Person("Tom"))
if (index >= 0)
result = (arrayPersons.lift(index-1), arrayPersons.lift(index+1))
Pretty short, seems to cover all cases.
Solution by anuj saxena
result = persons.sliding(3).foldLeft((Option.empty[Person], Option.empty[Person]))
{
case ((Some(prev), Some(next)), _) => (Some(prev), Some(next))
case (_, prev :: Person(`name`) :: next :: _) => (Some(prev), Some(next))
case (_, _ :: prev :: Person(`name`) :: _) => (Some(prev), None)
case (_, Person(`name`) :: next :: _) => (None, Some(next))
case (neighbours, _) => neighbours
}
First find out index where "Tom" is present, then use "lift". "lift" turns partial function into a plain function returning an Option result:
index = persons.indexOf("Tom")
doSomethingWith(persons.lift(index-1), persons.lift(index+1))
A rule of thumb: we should never access the content of a list / seq using indexes as it is prone to errors (like IndexNotFoundException).
If we want to use indexes, we better use Array as it provides us random access.
So to the current solution, here is my code to find prev and next element of a certain data in a Seq or List:
def findNeighbours(name: String, persons: Seq[Person]): Option[(Person, Person)] = {
persons.sliding(3).flatMap{
case prev :: person :: next :: Nil if person.name == name => Some(prev, next)
case _ => None
}.toList.headOption
}
Here the return type is in Option because there is a possibility that we may not find it here (in case of only one person is in the list or the required person is not in the list).
This code will pick the pair on the first occurrence of the person provided in the parameter.
If you have a probability that there might be several occurrences for the provided person, remove the headOption in the last line of the function findNeighbours. Then it will return a List of tuples.
Update
If Person is a case class then we can use deep match like this:
def findNeighbours(name: String, persons: Seq[Person]): Option[(Person, Person)] = {
persons.sliding(3).flatMap{
case prev :: Person(`name`) :: next :: Nil => Some(prev, next)
case _ => None
}.toList.headOption
}
For your solution need to add more cases to it (cchanged it to use foldleft in case of a single answer):
def findNeighboursV2(name: String, persons: Seq[Person]): (Option[Person], Option[Person]) = {
persons.sliding(3).foldLeft((Option.empty[Person], Option.empty[Person])){
case ((Some(prev), Some(next)), _) => (Some(prev), Some(next))
case (_, prev :: Person(`name`) :: next :: _) => (Some(prev), Some(next))
case (_, _ :: prev :: Person(`name`) :: _) => (Some(prev), None)
case (_, Person(`name`) :: next :: _) => (None, Some(next))
case (neighbours, _) => neighbours
}
}
You can use sliding function:
persons: Seq[Person] = initializePersons()
persons.sliding(size = 3).find { itr =>
if (itr(1).name = "Tom") {
val before = itr(0)
val middle = itr(1)
val after = itr(2)
}
}
If you know that there will be only one instance of "Tom" in your Seq use indexOf instead of looping by hand:
tomIndex = persons.indexOf("Tom")
doSomethingWith(persons(tomIndex-1), persons(tomIndex+1))
// Start writing your ScalaFiddle code here
case class Person(name: String)
val persons1 = Seq(Person("Martin"),Person("John"),Person("Tom"),Person("Jack"),Person("Mary"))
val persons2 = Seq(Person("Martin"),Person("John"),Person("Tom"))
val persons3 = Seq(Person("Tom"),Person("Jack"),Person("Mary"))
val persons4 = Seq(Person("Tom"))
def f(persons:Seq[Person]) =
persons
.sliding(3)
.filter(_.contains(Person("Tom")))
.maxBy {
case _ :: Person("Tom") :: _ => 1
case _ => 0
}
.toList
.take(persons.indexOf(Person("Tom")) + 2) // In the case where "Tom" is first, drop the last person
.drop(persons.indexOf(Person("Tom")) - 1) // In the case where "Tom" is last, drop the first person
println(f(persons1)) // List(Person(John), Person(Tom), Person(Jack))
println(f(persons2)) // List(Person(John), Person(Tom))
println(f(persons3)) // List(Person(Tom), Person(Jack))
println(f(persons4)) // List(Person(Tom))
Scalafiddle

How to take zero or more params with Lift Web Framework

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"))

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")