This gets your site's base link. note it's really only good if you are using FancyMain.
struct Envelope;
This is the JSON envelope format
bool success;
did the call succeed? false if it threw an exception
string type;
static type of the return value
string errorMessage;
if !success, this is exception.msg
string userData;
null unless the user request included passedThroughUserData
JSONValue result;
the return value of the function
class ApiProvider;
Everything should derive from this instead of the old struct namespace used before
Your class must provide a default constructor.
void _initialize();
Override this if you have initialization work that must be done *after* cgi and reflection is ready.
It should be used instead of the constructor for most work.
void _initializePerCall();
This one is called at least once per call. (initialize is only called once per process)
void _postProcess(Document document);
Override this if you want to do something special to the document
You should probably call super.postProcess at some point since I
might add some default transformations here.
void redirect(string location);
This tentatively redirects the user - depends on the envelope fomat
Element _sitemap();
Returns a list of links to all functions in this class or sub-classes
You can expose it publicly with alias: "alias sitemap sitemap;" for example.
Document _defaultPage();
If the user goes to your program without specifying a path, this function is called.
Element _getGenericContainer();
When the html document envelope is used, this function is used to get a html element
where the return value is appended.
It's the main function to override to provide custom HTML templates.
void _catchAll(string path);
If the given url path didn't match a function, it is passed to this function
for further handling. By default, it throws a NoSuchPageException.
Overriding it might be useful if you want to serve generic filenames or an opDispatch kind of thing.
(opDispatch itself won't work because it's name argument needs to be known at compile time!)
Document delegate(Throwable) _errorFunction;
When in website mode, you can use this to beautify the error message
class ApiObject;
Implement subclasses of this inside your main provider class to do a more object
oriented site.
struct ReflectionInfo;
Describes the info collected about your class
FunctionInfo[string] functions;
the methods
EnumInfo[string] enums;
.
StructInfo[string] structs;
.
const(ReflectionInfo)*[string] objects;
ApiObjects and ApiProviders
string name;
this is also used as the object name in the JS api
struct EnumInfo;
describes an enum, iff based on int as the underlying type
string name;
.
int[] values;
.
string[] names;
.
struct StructInfo;
describes a plain data struct
string name;
.
StructMemberInfo[] members;
.
struct StructMemberInfo;
.
string name;
.
string staticType;
.
string defaultValue;
.
struct FunctionInfo;
.
WrapperFunction dispatcher;
this is the actual function called when a request comes to it - it turns a string[][string] into the actual args
JSONValue delegate(Cgi cgi, in const(immutable(char)[][][string]) sargs) documentDispatcher;
This is used if you want a custom form - normally, on insufficient parameters, an automatic form is created. But if there's a functionName_Form method, it is used instead. FIXME: this used to work but not sure if it still does
This uses reflection info to generate Javascript that can call the server with some ease.
Also includes javascript base (see bottom of this file)
void run(Provider)(Cgi cgi, Provider instantiation, int pathInfoStartingPoint = 0);
If you're not using FancyMain, this is the go-to function to do most the work.
instantiation should be an object of your ApiProvider type.
pathInfoStartingPoint is used to make a slice of it, incase you already consumed part of the path info before you called this.
FIXME:
maybe it should just be a string/slice directly instead of an awkward starting point?
template FancyMain(T,Args...)
fancier wrapper to cgi.d's GenericMain - does most the work for you, so you can just write your class and be done with it
Form createAutomaticForm(Document document, in FunctionInfo func, string[string] fieldTypes = null);
Form createAutomaticForm(Document document, string action, in Parameter[] parameters, string submitText = "Submit", string method = "POST", string[string] fieldTypes = null);
Given a function from reflection, build a form to ask for it's params
string[] parameterNamesOfImpl(alias func)();
Returns the parameter names of the given function
Params:
func
the function alias to get the parameter names of
Returns:
an array of strings containing the parameter names
string toHtml(T)(T a);
Formats any given type as HTML. In custom types, you can write Element makeHtmlElement(Document document = null); to provide
custom html. (the default arg is important - it won't necessarily pass a Document in at all, and since it's silently duck typed,
not having that means your function won't be called and you can be left wondering WTF is going on.)
Alternatively, static Element makeHtmlArray(T[]) if you want to make a whole list of them. By default, it will just concat a bunch of individual
elements though.
string toJson(T)(T a);
Translates a given type to a JSON string.
TIP:
if you're building a Javascript function call by strings, toJson("your string"); will build a nicely escaped string for you of any type.
JSONValue toJsonValue(T, R = ApiProvider)(T a, string formatToStringAs = null, R api = null);
like toHtml - it makes a json value of any given type.
It can be used generically, or it can be passed an ApiProvider so you can do a secondary custom
format. (it calls api.formatAs!(type)(typeRequestString). Why would you want that? Maybe
your javascript wants to do work with a proper object,but wants to append it to the document too.
Asking for json with secondary format = html means the server will provide both to you.
Implement JSONValue makeJsonValue() in your struct or class to provide 100% custom Json.
Elements from DOM are turned into JSON strings of the element's html.
class InsufficientParametersException: object.Exception;
throw this if your function needs something that is missing.
Done automatically by the wrapper function
class InvalidParameterException: object.Exception;
throw this if a paramater is invalid. Automatic forms may present this to the user in a new form. (FIXME: implement that)
void badParameter(alias T)(string expected = "");
convenience for throwing InvalidParameterExceptions
class PermissionDeniedException: object.Exception;
throw this if the user's access is denied
class NoSuchPageException: object.Exception;
throw if the request path is not found. Done automatically by the default catch all handler.
type fromUrlParam(type)(string[] ofInterest);
turns a string array from the URL into a proper D type
WrapperFunction generateWrapper(alias getInstantiation, alias f, alias group, string funName, R)(ReflectionInfo* reflection, R api);
generates the massive wrapper function for each of your class' methods.
it is responsible for turning strings to params and return values back to strings.
void formatAs(T, R)(T ret, R api, ref JSONValue returnValue, string format, string formatJsonToStringAs = null);
This is the function called to turn return values into strings.
Implement a template called customFormat in your apiprovider class to make special formats.
Otherwise, this provides the defaults of html, table, json, etc.
call it like so: JSONValue returnValue; formatAs(value, this, returnValue, "type");
alias WrapperFunction;
The definition of the beastly wrapper function
string urlToBeauty(string url);
tries to take a URL name and turn it into a human natural name. so get rid of slashes, capitalize, etc.
string toUrlName(string name);
turns camelCase into dash-separated
string beautify(string name);
turns camelCase into human presentable capitalized words with spaces
string getSessionId(Cgi cgi);
meant to give a generic useful hook for sessions. kinda sucks at this point.
a convenience function to do filters on your doc and write it out. kinda useless still at this point.
string makeSaltedPasswordHash(string userSuppliedPassword, string salt = null);
These added a dependency on arsd.sha, but hashing passwords is somewhat useful in a lot of apps so I figured it was worth it.
use this to make the hash to put in the database...
When you call a method, it doesn't make the server request. Instead, it returns
an object describing the call. This means you can manipulate it (such as requesting
a custom format), pass it as an argument to other functions (thus saving http requests)
and finally call it at the end.
The operations are:
get(callback, args to callback...);
See below.
useToReplace(element) // pass an element reference. Example: useToReplace(document.querySelector(".name"));
useToReplace(element ID : string) // you pass a string, it calls document.getElementById for you
useToReplace sets the given element's innerHTML to the return value. The return value is automatically requested
to be formatted as HTML.
appendTo(element)
appendTo(element ID : String)
Adds the return value, as HTML, to the given element's inner html.
useToReplaceElement(element)
Replaces the given element entirely with the return value. (basically element.outerHTML = returnValue;)
useToFillForm(form)
Takes an object. Loop through the members, setting the form.elements[key].value = value.
Does not work if the return value is not a javascript object (so use it if your function returns a struct or string[string])
getSync()
Does a synchronous get and returns the server response. Not recommended.
get() :
The generic get() function is the most generic operation to get a response. It's arguments implement
partial application for you, so you can pass just about any callback to it.
Despite the name, the underlying operation may be HTTP GET or HTTP POST. This is determined from the
function's server side attributes. (FIXME: implement smarter thing. Currently it actually does it by name - if
the function name starts with get, do get. Else, do POST.)
Usage:
CoolApi.getABox('red').get(alert); // calls alert(returnedValue); so pops up the returned value
Since JS functions generally ignore extra params, this lets you call just about anything:
CoolApi.getABox('red').get(alert, "Success"); // pops a box saying "Success", ignoring the actual return value
Passing arguments to the functions let you reuse a lot of things that might not have been designed with this in mind.
If you use arsd.js, there's other little functions that let you turn properties into callbacks too.
Passing "this" to a callback via get is useful too since inside the callback, this probably won't refer to what you
wanted. As an argument though, it all remains sane.
Error Handling:
D exceptions are translated into Javascript exceptions by the serverCall function. They are thrown, but since it's
async, catching them is painful.
It will probably show up in your browser's error console, or you can set the returned object's onerror function
to something to handle it callback style. FIXME: not sure if this actually works right!