box(Str) -> {'div',[{class,"box"}], {pre,[], yaws_api:htmlize(Str)}}. tbox(T) -> box(lists:flatten(io_lib:format("~p",[T]))). ssi(File) -> {'div',[{class,"box"}], {pre,[], {ssi, File,[],[]}}}. ss(A, File) -> {ok, B} = file:read_file( filename:join([A#arg.docroot, File])), box(binary_to_list(B)). out(A) -> [{ssi, "TAB.inc", "%%",[{"json_intro", "choosen"}]}, {ehtml, {'div', [{id, "entry"}], [{h1, [], "AJAX through JSON RPC"}, {p, [], {i, [], ["Note: this documentation used to refer to the module " "'yaws_jsonrpc', but that module was deprecated in favor of " "'yaws_rpc', which handles JSON RPC, haXe and SOAP remoting. " "For more specific information about SOAP, refer to ", {a, [{href, "/soap_intro.yaws"}], "the SOAP page."}]}}, {p, [], ["The Yaws JSON-RPC binding is a way to have JavaScript code in the " "browser evaluate a remote procedure call (RPC) in the Yaws server. " "JSON itself as described at ", {a, [{href, "http://www.json.org/"}], "http://www.json.org/ "}, "is basically a simple marshaling format which can be used " "from a variety of different programming languages, and " "naturally it's completely straightforward to implement " "in JavaScript itself. JSON-RPC version 2.0, the version Yaws " "supports, is described here:"]}, {p, [], [{a, [{href, "http://groups.google.com/group/json-rpc/web/json-rpc-2-0"}], "http://groups.google.com/group/json-rpc/web/json-rpc-2-0"}]}, {p, [], "The Yaws JSON-RPC implementation consist of JavaScript clients and a " "server side library that must be explicitly invoked by Erlang " "code in a .yaws page, appmod, etc."}, {p,[], "It is not particularly easy to show and explain an AJAX setup " "through JSON-RPC, but here is an attempt:" }, {p,[], "First we have an HTML page which:"}, {ol, [], [ {li,[],{p,[], ["Includes the client side of the JSON library. " "The library is included in the Yaws distribution " "and it is found under ", {a, [{href, "https://github.com/klacke/yaws/blob/master/www/jsolait/jsolait.js"}], "\"www/jsolait/jsolait.js\""}, "."]}}, {li,[],{p,[],"Second, the HTML code defines the name of a method, " "i.e. the name of a server-side function that shall be " "called by the client side JavaScript code."}}, {li,[],{p,[],"Finally the HTML code defines a FORM that's " "used to invoke the RPC. This is just a really simple " "example, really any JavaScript code can invoke any RPC in " "more interesting scenarios than submitting a form."}}]}, {p, [], "The HTML code appears as shown below:"}, ss(A,"json_sample.html"), {p, [], ["This HTML code resides in file ", {a,[{href, "json_sample.html"}], "json_sample.html"}, " and it is the HTML code that is the AJAX GUI."]}, {p, [], "Following that we need to take a look at json_sample.yaws " " (shown below), which is the \"serviceURL\" according to " "the JavaScript code. This code defines the function to be " "called. Remember that the JavaScript code defined one method, " "called \"test1\"; this information will be passed to the " "serviceURL. The code looks like:"}, ss(A, "json_sample.yaws"), {p,[], "The two important lines on the server side are"}, {ol,[], [ {li,[], {pre,[],"yaws_rpc:handler_session(A2, {sample_mod, counter})."}}, {li,[], {pre,[],"counter([{ip, IP}] = _State, {call, test1, Value} = _Request, Session)"}}]}, {p,[], ["The first line tells Yaws to forward all JSON-RPC methods to the " " \"counter\" function in the \"sample_mod\" module. " "The second line is the head of the counter function that will be " "called when the client invokes a method called 'test1'. We would " "duplicate this line with a different name than 'test1' for each RPC " "function we wish to implement. Note that the first atom in the " "request tuple will either be 'call' or 'notification' to indicate " "the type of request. As per the ", {a,[{href,"http://groups.google.com/group/json-rpc/web/json-rpc-2-0"}], "JSON-RPC 2.0 specification"}, ", a 'call' is a regular request-reply while a 'notification' is a " "one-way message that does not have a corresponding reply."]}, {p,[],"On the client side we have"}, box(" var methods = [ \"test1\" ]; var jsonrpc = imprt(\"jsonrpc\"); var service = new jsonrpc.ServiceProxy(serviceURL, methods); "), {p,[],"This registers the Yaws page with the JSON-RPC handler and " "gives it a list of methods that the Yaws page can satisfy. " "In this case, the only method called 'test1'."}, {p, [], "When we wish to return structured data, we simply let " "the user-defined RPC function return JSON structures such as "}, box("{struct, [{field1, \"foo\"}, {field2, \"bar\"}]} "), {p, [], " for a structure and "}, box("{array, [\"foo\", \"bar\"]}"), {p, [],"for an array. We can nest arrays and structs in each other."}, {p, [], "Finally, we must stress that this example is extremely simple. " "In order to build a proper AJAX application in Yaws, a lot of " "client side work is required, all Yaws provides is the basic " "mechanism whereby the client side JavaScript code can RPC the " "web server for data which can be subsequently used to populate " "the DOM. Also required to build a good AJAX application is " "good knowledge of how the DOM in the browser works"}, {p, [], ["The yaws_rpc:handler will also call: M:F(cookie_expire) " "which is expected to return a proper Cookie expire string. This " "makes it possible to setup the Cookie lifetime. If this callback " "function is non-existent, the default behaviour is to not set a " "cookie expiration time, i.e., it will live for this session only."]}, {h3, [], "One more example "}, {p, [], ["Here is yet another example, stolen from ", {a, [{href,"http://www.redhoterlang.com/entry/ac061493b201e3d1b4490cdc3f911068"}], "Tobbe's blog."} ]}, {h4, [], "Setup the DOM"}, {p, [], "In the file ''ex1.html'' we create the DOM with a little HTML and add some JavaScript that will talk with the Erlang server side."}, box("
This is one
This is two
This is three
"), {h4, [], "The erlang server side"}, {p, [], "This is the code that needs to be installed and " "execute on the server side. It nicely illustrates how " "to return JSON structs to the client. "}, box(" -module(ex1). -export([out/1]). out(A) -> L = yaws_api:parse_query(A), dispatch(lkup(\"op\", L, false), A, L). dispatch(\"ex1\", A, L) -> ex1(A, L). ex1(_A, L) -> J = json2:encode(array(what(lkup(\"what\", L, false)))), return_json(J). what(\"one\") -> one(); what(\"two\") -> one() ++ two(); what(\"three\") -> one() ++ two() ++ three(). array(L) -> {array, L}. one() -> obj(\"one\"). two() -> obj(\"two\"). three() -> obj(\"three\"). obj(M) -> obj(M, \"r\"). %%% %%% How ::= \"r\" | \"a\" , r=replace, a=append %%% obj(M, How) -> C = now2str(), [{struct, [{\"who\", M}, {\"how\", How}, {\"what\", C ++\" \"++M++\" content\"}]}]. return_json(Json) -> {content, \"application/json; charset=iso-8859-1\", Json}. now2str() -> {A,B,C} = erlang:now(), i2l(A)++\"-\"++i2l(B)++\"-\"++i2l(C). i2l(I) when is_integer(I) -> integer_to_list(I); i2l(L) when is_list(L) -> L. lkup(Key, List, Def) -> case lists:keysearch(Key, 1, List) of {value,{_,Value}} -> Value; _ -> Def end. ") , {h2, [], "The json library"}, {p, [], ["The Yaws JSON library contains 3 simple functions, " " one for encoding and two for decoding. See source code ", {a, [{href, "https://github.com/klacke/yaws/blob/master/src/json2.erl"}], "json2.erl"}, " for detailed instructions on usage."]} ]}}, {ssi, "END2",[],[]} ].