Skip to content

Commit

Permalink
Got the REST examples written
Browse files Browse the repository at this point in the history
  • Loading branch information
dpp committed Mar 4, 2011
1 parent dd55c3b commit 3f24f54
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Boot {
LiftRules.statelessDispatchTable.append(BasicExample.extractFindItem)

LiftRules.statelessDispatchTable.append(BasicWithHelper)
LiftRules.statelessDispatchTable.append(FullRest)

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object BasicExample {
*/
lazy val extractFindItem: LiftRules.DispatchPF = {
// path with extractor
case Req("simple2" :: "item" :: FindItem(item) :: Nil,
case Req("simple2" :: "item" :: Item(item) :: Nil,
suffix, GetRequest) =>
// a function that returns the response
() => Full(toResponse(suffix, item))
Expand Down
76 changes: 65 additions & 11 deletions samples/http_rest/src/main/scala/code/lib/BasicWithHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,90 @@ import net.liftweb._
import common._
import http._
import rest._
import json._
import scala.xml._

/**
* A simple example of a REST style interface
* using the basic Lift tools
*/
object BasicWithHelper extends RestHelper {
/*
* Serve the URL, but have a helpful error message when you
* return a 404 if the item is not found
*/
serve {
case "simple3" :: "item" :: itemId :: Nil JsonGet _ =>
for {
// find the item, and if it's not found,
// return a nice message for the 404
item <- Item.find(itemId) ?~ "Item Not Found"
} yield JsonResponse(item)
} yield item: JValue

case "simple3" :: "item" :: itemId :: Nil XmlGet _ =>
for {
item <- Item.find(itemId) ?~ "Item Not Found"
} yield XmlResponse(item)
} yield item: Node
}



serve {
case "simple4" :: "item" :: FindItem(item) :: Nil JsonGet _ =>
JsonResponse(item)
case "simple4" :: "item" :: FindItem(item) :: Nil XmlGet _ =>
XmlResponse(item)
// Prefix notation
case JsonGet("simple4" :: "item" :: Item(item) :: Nil, _) =>
// no need to explicitly create a LiftResponse
// Just make it JSON and RestHelper does the rest
item: JValue

// infix notation
case "simple4" :: "item" :: Item(item) :: Nil XmlGet _ =>
item: Node
}

serve ( "simple5" / "item" >> {
case Nil JsonGet _ => JsonResponse(Item.inventoryItems)
case Nil XmlGet _ => XmlResponse(Item.inventoryItems)
case FindItem(item) :: Nil JsonGet _ => JsonResponse(item)
case FindItem(item) :: Nil XmlGet _ => XmlResponse(item)
// serve a bunch of items given a single prefix
serve ( "simple5" / "item" prefix {
// all the inventory
case Nil JsonGet _ => Item.inventoryItems: JValue
case Nil XmlGet _ => Item.inventoryItems: Node

// a particular item
case Item(item) :: Nil JsonGet _ => item: JValue
case Item(item) :: Nil XmlGet _ => item: Node
})

/**
* Here's how we convert from an Item
* to JSON or XML depending on the request's
* Accepts header
*/
implicit def itemToResponseByAccepts: JxCvtPF[Item] = {
case (JsonSelect, c, _) => c: JValue
case (XmlSelect, c, _) => c: Node
}

/**
* serve the response by returning an item
* (or a Box[Item]) and let RestHelper determine
* the conversion to a LiftResponse using
* the itemToResponseByAccepts partial function
*/
serveJx[Item] {
case "simple6" :: "item" :: Item(item) :: Nil Get _ => item
case "simple6" :: "item" :: "other" :: item :: Nil Get _ =>
Item.find(item) ?~ "The item you're looking for isn't here"
}

/**
* Same as the serveJx example above, but we've
* used prefixJx to avoid having to copy the path
* prefix over and over again
*/
serveJx[Item] {
"simple7" / "item" prefixJx {
case Item(item) :: Nil Get _ => item
case "other" :: item :: Nil Get _ =>
Item.find(item) ?~ "The item you're looking for isn't here"
}
}

}
30 changes: 0 additions & 30 deletions samples/http_rest/src/main/scala/code/lib/DependencyFactory.scala

This file was deleted.

49 changes: 49 additions & 0 deletions samples/http_rest/src/main/scala/code/lib/FullRest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package code
package lib

import model._

import net.liftweb._
import common._
import http._
import rest._
import json._
import scala.xml._

/**
* A full REST example
*/
object FullRest extends RestHelper {

// Serve /api/item and friends
serve( "api" / "item" prefix {

// /api/item returns all the items
case Nil JsonGet _ => Item.inventoryItems: JValue

// /api/item/count gets the item count
case "count" :: Nil JsonGet _ => JInt(Item.inventoryItems.length)

// /api/item/item_id gets the specified item (or a 404)
case Item(item) :: Nil JsonGet _ => item: JValue

// /api/item/search/foo or /api/item/search?q=foo
case "search" :: q JsonGet _ =>
(for {
searchString <- q ::: S.params("q")
item <- Item.search(searchString)
} yield item).distinct: JValue

// DELETE the item in question
case Item(item) :: Nil JsonDelete _ =>
Item.delete(item.id).map(a => a: JValue)

// PUT adds the item if the JSON is parsable
case Nil JsonPut Item(item) -> _ => Item.add(item): JValue

// POST if we find the item, merge the fields from the
// the POST body and update the item
case Item(item) :: Nil JsonPost json -> _ =>
Item(mergeJson(item, json)).map(Item.add(_): JValue)
})
}
71 changes: 55 additions & 16 deletions samples/http_rest/src/main/scala/code/model/Item.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ case class Item(id: String, name: String,
price: BigDecimal, taxable: Boolean,
weightInGrams: Int, qnty: Int)

/**
* An object with a helpful extractor
* useful for pattern matching
*/
object FindItem {
def unapply(id: String): Option[Item] = Item.find(id)
}

/**
* The Item companion object
*/
Expand All @@ -40,6 +32,40 @@ object Item {
*/
implicit def toJson(item: Item): JValue = Extraction.decompose(item)

/**
* Convert a JValue to an Item if possible
*/
def apply(in: JValue): Box[Item] = Helpers.tryo{in.extract[Item]}

/**
* Extract a String (id) to an Item
*/
def unapply(id: String): Option[Item] = Item.find(id)

/**
* Extract a JValue to an Item
*/
def unapply(in: JValue): Option[Item] = apply(in)

/**
* The default unapply method for the case class.
* We needed to replicate it here because we
* have overloaded unapply methods
*/
def unapply(in: Any): Option[(String, String,
String,
BigDecimal, Boolean,
Int, Int)] = {
in match {
case i: Item => Some((i.id, i.name, i.description,
i.price, i.taxable,
i.weightInGrams, i.qnty))
case _ => None
}
}



/**
* Convert an item to XML
*/
Expand Down Expand Up @@ -115,14 +141,8 @@ object Item {
*/
def add(item: Item): Item = {
synchronized {
find(item.id) match {
case Full(oldItem) => {
val newItem = item.copy(qnty = item.qnty + oldItem.qnty)
items = newItem :: items.filter(_ ne oldItem)
newItem
}
case _ => items ::= item; item
}
items = item :: items.filterNot(_.id == item.id)
item
}
}

Expand All @@ -137,6 +157,25 @@ object Item {
i.name.toLowerCase.indexOf(strLC) >= 0 ||
i.description.toLowerCase.indexOf(strLC) >= 0)
}

/**
* Deletes the item with id and returns the
* deleted item or Empty if there's no match
*/
def delete(id: String): Box[Item] = synchronized {
var ret: Box[Item] = Empty

val Id = id // an upper case stable ID for pattern matching

items = items.filter {
case i@Item(Id, _, _, _, _, _, _) =>
ret = Full(i) // side effect
false
case _ => true
}

ret
}

}

Expand Down
16 changes: 0 additions & 16 deletions samples/http_rest/src/main/scala/code/snippet/HelloWorld.scala

This file was deleted.

0 comments on commit 3f24f54

Please sign in to comment.