Created
August 27, 2012 01:24
-
-
Save hjast/3484895 to your computer and use it in GitHub Desktop.
NamedCallableFunction.scala
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import net.liftweb.http.SHtml._ | |
import ElemAttr._ | |
import net.liftweb.util.Helpers._ | |
object LiftExtensions { | |
/**This callable function creates a named function which takes a initialize obj and returns | |
* a json value which gets passed to the callback functions. | |
* @param name the name of the function | |
* @param func - the function which takes a json value and returns another json value to return to server | |
*/ | |
case class NamedCallableFunction(name:String, func:(JsonAST.JValue)=>JsonAST.JValue) extends JsCmd { | |
override val toJsCmd = | |
Function(name, List("paramObj"), | |
jsonCallJValue( | |
JsVar("paramObj"), | |
AjaxContext.json(Full("paramObj.success"), Full("paramObj.failure")), | |
func)).toJsCmd | |
} | |
/**This is a json call that takes a ajax context instead of a jscontext ( which btw I don't understand | |
* why the traditional call doesn't take an ajaxContext. | |
* @param jsCalcValue - the js to calculate the value | |
* @param ajaxContext - the context for the callbacks | |
* @param func - the function we are fmaping | |
* @return the function ID and JavaScript that makes the call | |
*/ | |
def jsonCallAjaxContext_*(jsCalcValue: JsExp, | |
ajaxContext: AjaxContext, | |
func: AFuncHolder): (String, JsExp) = { | |
fmapFunc((func))(name => | |
(name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(JSON.stringify(" + jsCalcValue.toJsCmd + "))"), ajaxContext))) | |
} | |
/** | |
* Build a JSON function that will perform a JSON call based on a value calculated in JavaScript, and will return | |
* a JSON value to the client. | |
* @param jsCalcValue the JavaScript to calculate the value to be sent to the server | |
* @param jsContext the context instance that defines JavaScript to be executed on call success or failure | |
* @param func the function which takes the data given and returns the json to be returned | |
* @return the function ID and JavaScript that makes the call | |
*/ | |
def jsonCallJValue(jsCalcValue: JsExp, jsContext: AjaxContext, func: JsonAST.JValue => JsonAST.JValue): GUIDJsExp = | |
jsonCallAjaxContext_*(jsCalcValue, jsContext, SFuncHolder(s => parseOpt(s).map(func) getOrElse JsFalse)) | |
} | |
Hey guys, | |
I wanted to share with you something I cooked up today to some JSON handling, since it might be of use to some of you, especially those working with a single site application framework (Backbone, ember, etc...). It is basically some simple syntactic sugar on top of some of the SHtml magic. | |
My Motivation | |
A.) I wanted support for returning JSON from a call made from Lift and providing callbacks for this data. This is helpful when you are writing a quick method to interact with the javascript, don't want to write a full blown API call, but you want to use it within different contexts on the site and have some extendibility. | |
This is the use case I had in mind and how I use this: | |
Note: I like object's for javascript parameters, since it allows for both default parameters (using a default array then extending that) and it seems cleaner to me for larger functions (just like named parameter seems cleaner to me for functions with arity over 3). | |
Sample Javascript Usecase | |
editFoo({ | |
foodentifer: $(this).attr("data-foo"); | |
newStuffToAdd: { | |
facialHair: "mustache" | |
}, | |
success: function(foo, status, xhr) { | |
alert("Foo has a new " + foo.facialHair) // Perhaps prints out "Foo has a new mustache" | |
//Or does something like keeps client + server side in sync after a update operations with a larger JSON payload. | |
//x and y actually also have information about the | |
//I use multiple callbacks so I return a JValue object with the "status" field instead so I know what callback to use | |
} | |
}); | |
And finally ... the code -- (gist version (commented) | |
object LiftExtenstions { | |
import net.liftweb.http.SHtml._ | |
import ElemAttr._ | |
import net.liftweb.util.Helpers._ | |
/**This callable function creates a named function which takes a initialize obj and returns | |
* a json value which gets passed to the callback functions. | |
* @param name the name of the function | |
* @param func - the function which takes a json value and returns another json value to return to server | |
*/ | |
case class NamedCallableFunction(name:String, func:(JsonAST.JValue)=>JsonAST.JValue) extends JsCmd { | |
override val toJsCmd = | |
Function(name, List("paramObj"), | |
jsonCallJValue( | |
JsVar("paramObj"), | |
AjaxContext.json(Full("paramObj.success"), Full("paramObj.failure")), | |
func)).toJsCmd | |
} | |
/**This is a json call that takes a ajax context instead of a jscontext ( which btw I don't understand | |
* why the traditional call doesn't take an ajaxContext. | |
* @param jsCalcValue - the js to calculate the value | |
* @param ajaxContext - the context for the callbacks | |
* @param func - the function we are fmaping | |
* @return the function ID and JavaScript that makes the call | |
*/ | |
def jsonCallAjaxContext_*(jsCalcValue: JsExp, | |
ajaxContext: AjaxContext, | |
func: AFuncHolder): (String, JsExp) = { | |
fmapFunc((func))(name => | |
(name, makeAjaxCall(JsRaw("'" + name + "=' + encodeURIComponent(JSON.stringify(" + jsCalcValue.toJsCmd + "))"), ajaxContext))) | |
} | |
/** | |
* Build a JSON function that will perform a JSON call based on a value calculated in JavaScript, and will return | |
* a JSON value to the client. | |
* @param jsCalcValue the JavaScript to calculate the value to be sent to the server | |
* @param jsContext the context instance that defines JavaScript to be executed on call success or failure | |
* @param func the function which takes the data given and returns the json to be returned | |
* @return the function ID and JavaScript that makes the call | |
*/ | |
def jsonCallJValue(jsCalcValue: JsExp, jsContext: AjaxContext, func: JsonAST.JValue => JsonAST.JValue): GUIDJsExp = | |
jsonCallAjaxContext_*(jsCalcValue, jsContext, SFuncHolder(s => parseOpt(s).map(func) getOrElse JsFalse)) | |
} | |
Let's write a function for the above example | |
case class FooEdits(facialHair: String) | |
case class FooParams(foodentifer: String, newStuffToAdd: FooEdits) | |
case class Foo(id: String, facialHair: String) | |
case class Rtn(msg: String, newData: Option[Foo] = None) | |
LiftExtensions("editFoo" , { paramObj: JsonAST.JValue => | |
val fooParams = paramObj.extract[FooParams] | |
//perhaps grab foo from db and update the foo | |
val foo: Foo = updateFooFromDB(fooParams) | |
val rtn: Rtn = if(success) Rtn("Success", Some(foo)) else Rtn("Failure") | |
//extract and return json | |
Extraction.decompose(rtn) | |
}) | |
Now bind the function to a page in a snippet and you have a editFoo method in javascript which has a success and a failure callback. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment