summaryrefslogtreecommitdiff
path: root/demo/more/dbgrid.ur
diff options
context:
space:
mode:
authorBenjamin Barenblat <bbaren@mit.edu>2017-07-23 09:50:04 -0400
committerBenjamin Barenblat <bbaren@mit.edu>2017-07-23 09:50:04 -0400
commit0cccdb0ae595cd7e3e136e984ac7b95b99f71a53 (patch)
tree491d3b13813610943c60460d3e178d3a73916346 /demo/more/dbgrid.ur
Import urweb_20170720+dfsg.orig.tar.gz
[dgit import orig urweb_20170720+dfsg.orig.tar.gz]
Diffstat (limited to 'demo/more/dbgrid.ur')
-rw-r--r--demo/more/dbgrid.ur430
1 files changed, 430 insertions, 0 deletions
diff --git a/demo/more/dbgrid.ur b/demo/more/dbgrid.ur
new file mode 100644
index 0000000..fc59353
--- /dev/null
+++ b/demo/more/dbgrid.ur
@@ -0,0 +1,430 @@
+con rawMeta = fn t :: Type =>
+ {New : transaction t,
+ Inj : sql_injectable t}
+
+con colMeta' = fn (row :: {Type}) (input :: Type) (filter :: Type) =>
+ {Header : string,
+ Project : $row -> transaction input,
+ Update : $row -> input -> transaction ($row),
+ Display : input -> xbody,
+ Edit : input -> xbody,
+ Validate : input -> signal bool,
+ CreateFilter : transaction filter,
+ DisplayFilter : filter -> xbody,
+ Filter : filter -> $row -> signal bool,
+ Sort : option ($row -> $row -> bool)}
+
+con colMeta = fn (row :: {Type}) (global_input_filter :: (Type * Type * Type)) =>
+ {Initialize : transaction global_input_filter.1,
+ Handlers : global_input_filter.1 -> colMeta' row global_input_filter.2 global_input_filter.3}
+
+con aggregateMeta = fn (row :: {Type}) (acc :: Type) =>
+ {Initial : acc,
+ Step : $row -> acc -> acc,
+ Display : acc -> xbody}
+
+structure Direct = struct
+ con metaBase = fn actual_input_filter :: (Type * Type * Type) =>
+ {Display : actual_input_filter.2 -> xbody,
+ Edit : actual_input_filter.2 -> xbody,
+ Initialize : actual_input_filter.1 -> transaction actual_input_filter.2,
+ Parse : actual_input_filter.2 -> signal (option actual_input_filter.1),
+ CreateFilter : transaction actual_input_filter.3,
+ DisplayFilter : actual_input_filter.3 -> xbody,
+ Filter : actual_input_filter.3 -> actual_input_filter.1 -> signal bool,
+ Sort : actual_input_filter.1 -> actual_input_filter.1 -> bool}
+
+ datatype metaBoth actual input filter =
+ NonNull of metaBase (actual, input, filter) * metaBase (option actual, input, filter)
+ | Nullable of metaBase (actual, input, filter)
+
+ con meta = fn global_actual_input_filter =>
+ {Initialize : transaction global_actual_input_filter.1,
+ Handlers : global_actual_input_filter.1
+ -> metaBoth global_actual_input_filter.2 global_actual_input_filter.3
+ global_actual_input_filter.4}
+
+ con editableState (ts :: (Type * Type * Type * Type)) = (ts.1, ts.3, ts.4)
+ fun editable [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
+ (editableState ts) =
+ let
+ fun doMr (mr : metaBase (ts.2, ts.3, ts.4)) : colMeta' ([nm = ts.2] ++ rest) ts.3 ts.4 =
+ {Header = name,
+ Project = fn r => mr.Initialize r.nm,
+ Update = fn r s =>
+ vo <- current (mr.Parse s);
+ return (case vo of
+ None => r
+ | Some v => r -- nm ++ {nm = v}),
+ Display = mr.Display,
+ Edit = mr.Edit,
+ Validate = fn s => vo <- mr.Parse s; return (Option.isSome vo),
+ CreateFilter = mr.CreateFilter,
+ DisplayFilter = mr.DisplayFilter,
+ Filter = fn i r => mr.Filter i r.nm,
+ Sort = Some (fn r1 r2 => mr.Sort r1.nm r2.nm)}
+ in
+ {Initialize = m.Initialize,
+ Handlers = fn data => case m.Handlers data of
+ NonNull (mr, _) => doMr mr
+ | Nullable mr => doMr mr}
+ end
+
+ con readOnlyState (ts :: (Type * Type * Type * Type)) = (ts.1, ts.3, ts.4)
+ fun readOnly [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
+ (readOnlyState ts) =
+ let
+ fun doMr mr = {Header = name,
+ Project = fn r => mr.Initialize r.nm,
+ Update = fn r _ => return r,
+ Display = mr.Display,
+ Edit = mr.Display,
+ Validate = fn _ => return True,
+ CreateFilter = mr.CreateFilter,
+ DisplayFilter = mr.DisplayFilter,
+ Filter = fn i r => mr.Filter i r.nm,
+ Sort = Some (fn r1 r2 => mr.Sort r1.nm r2.nm)}
+ in
+ {Initialize = m.Initialize,
+ Handlers = fn data => case m.Handlers data of
+ NonNull (mr, _) => doMr mr
+ | Nullable mr => doMr mr}
+ end
+
+ con metaBasic = fn actual_input_filter :: (Type * Type * Type) =>
+ {Display : actual_input_filter.2 -> xbody,
+ Edit : source actual_input_filter.2 -> xbody,
+ Initialize : actual_input_filter.1 -> actual_input_filter.2,
+ InitializeNull : actual_input_filter.2,
+ IsNull : actual_input_filter.2 -> bool,
+ Parse : actual_input_filter.2 -> option actual_input_filter.1,
+ CreateFilter : actual_input_filter.3,
+ DisplayFilter : source actual_input_filter.3 -> xbody,
+ Filter : actual_input_filter.3 -> actual_input_filter.1 -> bool,
+ FilterIsNull : actual_input_filter.3 -> bool,
+ Sort : actual_input_filter.1 -> actual_input_filter.1 -> bool}
+
+ con basicState = source
+ con basicFilter = source
+ fun basic [ts ::: (Type * Type * Type)] (m : metaBasic ts) : meta (unit, ts.1, basicState ts.2, basicFilter ts.3) =
+ {Initialize = return (),
+ Handlers = fn () => NonNull (
+ {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
+ Edit = m.Edit,
+ Initialize = fn v => source (m.Initialize v),
+ Parse = fn s => v <- signal s; return (m.Parse v),
+ CreateFilter = source m.CreateFilter,
+ DisplayFilter = m.DisplayFilter,
+ Filter = fn f v => f <- signal f;
+ return (if m.FilterIsNull f then
+ True
+ else
+ m.Filter f v),
+ Sort = m.Sort},
+ {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
+ Edit = m.Edit,
+ Initialize = fn v => source (case v of
+ None => m.InitializeNull
+ | Some v => m.Initialize v),
+ Parse = fn s => v <- signal s;
+ return (if m.IsNull v then
+ Some None
+ else
+ case m.Parse v of
+ None => None
+ | Some v' => Some (Some v')),
+ CreateFilter = source m.CreateFilter,
+ DisplayFilter = m.DisplayFilter,
+ Filter = fn f v => f <- signal f;
+ return (if m.FilterIsNull f then
+ True
+ else
+ case v of
+ None => False
+ | Some v => m.Filter f v),
+ Sort = fn x y =>
+ case (x, y) of
+ (None, _) => True
+ | (Some x', Some y') => m.Sort x' y'
+ | _ => False})}
+
+ fun nullable [global] [actual] [input] [filter] (m : meta (global, actual, input, filter)) =
+ {Initialize = m.Initialize,
+ Handlers = fn d => case m.Handlers d of
+ Nullable _ => error <xml>Don't stack calls to Direct.nullable!</xml>
+ | NonNull (_, ho) => Nullable ho}
+
+ type intGlobal = unit
+ type intInput = basicState string
+ type intFilter = basicFilter string
+ val int : meta (intGlobal, int, intInput, intFilter) =
+ basic {Display = fn s => <xml>{[s]}</xml>,
+ Edit = fn s => <xml><ctextbox size={5} source={s}/></xml>,
+ Initialize = fn n => show n,
+ InitializeNull = "",
+ IsNull = eq "",
+ Parse = fn v => read v,
+ CreateFilter = "",
+ DisplayFilter = fn s => <xml><ctextbox size={5} source={s}/></xml> : xbody,
+ Filter = fn s n =>
+ case read s of
+ None => True
+ | Some n' => n' = n,
+ FilterIsNull = eq "",
+ Sort = le}
+
+ type stringGlobal = unit
+ type stringInput = basicState string
+ type stringFilter = basicFilter string
+ val string : meta (stringGlobal, string, stringInput, stringFilter) =
+ basic {Display = fn s => <xml>{[s]}</xml>,
+ Edit = fn s => <xml><ctextbox source={s}/></xml>,
+ Initialize = fn s => s,
+ InitializeNull = "",
+ IsNull = eq "",
+ Parse = fn s => Some s,
+ CreateFilter = "",
+ DisplayFilter = fn s => <xml><ctextbox source={s}/></xml> : xbody,
+ Filter = fn s n =>
+ case read s of
+ None => True
+ | Some n' => n' = n,
+ FilterIsNull = eq "",
+ Sort = le}
+
+ type boolGlobal = unit
+ type boolInput = basicState bool
+ type boolFilter = basicFilter string
+ val bool : meta (boolGlobal, bool, boolInput, boolFilter) =
+ basic {Display = fn b => <xml>{[b]}</xml>,
+ Edit = fn s => <xml><ccheckbox source={s}/></xml>,
+ Initialize = fn b => b,
+ InitializeNull = False,
+ IsNull = fn _ => False,
+ Parse = fn b => Some b,
+ CreateFilter = "",
+ DisplayFilter = fn s => <xml><cselect source={s}>
+ <coption/>
+ <coption value="0">False</coption>
+ <coption value="1">True</coption>
+ </cselect></xml> : xbody,
+ Filter = fn s b =>
+ case s of
+ "0" => b = False
+ | "1" => b = True
+ | _ => True,
+ FilterIsNull = eq "",
+ Sort = le}
+
+ functor Foreign (M : sig
+ con row :: {Type}
+ con t :: Type
+ val show_t : show t
+ val read_t : read t
+ val eq_t : eq t
+ val ord_t : ord t
+ val inj_t : sql_injectable t
+ con nm :: Name
+ constraint [nm] ~ row
+ table tab : ([nm = t] ++ row)
+ val render : $([nm = t] ++ row) -> string
+ end) = struct
+ open M
+
+ type global = list (t * string)
+ type input = source string * option (t * $row)
+ type filter = source string
+
+ val getChoices = List.mapQuery (SELECT * FROM tab AS T)
+ (fn r => (r.T.nm, render r.T))
+
+ fun getChoice k =
+ r <- oneRow (SELECT T.{{row}} FROM tab AS T WHERE T.{nm} = {[k]});
+ return r.T
+
+ val meta : meta (global, M.t, input, filter) =
+ {Initialize = getChoices,
+ Handlers = fn choices =>
+ NonNull (
+ {Display = fn (_, kr) => case kr of
+ None => error <xml>Unexpected Foreign null</xml>
+ | Some (k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
+ Edit = fn (s, kr) =>
+ <xml><cselect source={s}>
+ {List.mapX (fn (k', rend) =>
+ <xml><coption value={show k'} selected={case kr of
+ None => False
+ | Some (k, _) =>
+ k' = k}>{[rend]}</coption>
+ </xml>)
+ choices}
+ </cselect></xml>,
+ Initialize = fn k => s <- source (show k);
+ r <- rpc (getChoice k);
+ return (s, Some (k, r)),
+ Parse = fn (s, _) => k <- signal s; return (read k : option t),
+ CreateFilter = source "",
+ DisplayFilter = fn s =>
+ <xml><cselect source={s}>
+ <coption/>
+ {List.mapX (fn (k, rend) =>
+ <xml><coption value={show k}>{[rend]}</coption></xml>)
+ choices}
+ </cselect></xml> : xbody,
+ Filter = fn s k => s <- signal s;
+ return (case read s : option t of
+ None => True
+ | Some k' => k' = k),
+ Sort = le},
+ {Display = fn (_, kr) => case kr of
+ None => <xml>NULL</xml>
+ | Some (k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
+ Edit = fn (s, kr) =>
+ <xml><cselect source={s}>
+ <coption value="" selected={case kr of
+ None => True
+ | _ => False}>NULL</coption>
+ {List.mapX (fn (k', rend) =>
+ <xml><coption value={show k'} selected={case kr of
+ None => False
+ | Some (k, _) =>
+ k' = k}>{[rend]}</coption>
+ </xml>)
+ choices}
+ </cselect></xml>,
+ Initialize = fn k => case k of
+ None =>
+ s <- source "";
+ return (s, None)
+ | Some k =>
+ s <- source (show k);
+ r <- rpc (getChoice k);
+ return (s, Some (k, r)),
+ Parse = fn (s, _) => ks <- signal s;
+ return (case ks of
+ "" => Some None
+ | _ => case read ks : option t of
+ None => None
+ | Some k => Some (Some k)),
+ CreateFilter = source "",
+ DisplayFilter = fn s =>
+ <xml><cselect source={s}>
+ <coption/>
+ <coption value="0">NULL</coption>
+ {List.mapX (fn (k, rend) =>
+ <xml><coption value={"1" ^ show k}>{[rend]}</coption>
+ </xml>)
+ choices}
+ </cselect></xml> : xbody,
+ Filter = fn s ko => s <- signal s;
+ return (case s of
+ "" => True
+ | "0" => ko = None
+ | _ =>
+ case read (String.substring s {Start = 1,
+ Len = String.length s - 1})
+ : option t of
+ None => True
+ | Some k => ko = Some k),
+ Sort = le})}
+ end
+end
+
+con computedState = (unit, xbody, unit)
+fun computed [row] [t] (_ : show t) name (f : $row -> t) : colMeta row computedState =
+ {Initialize = return (),
+ Handlers = fn () => {Header = name,
+ Project = fn r => return <xml>{[f r]}</xml>,
+ Update = fn r _ => return r,
+ Display = fn x => x,
+ Edit = fn _ => <xml>...</xml>,
+ Validate = fn _ => return True,
+ CreateFilter = return (),
+ DisplayFilter = fn _ => <xml/>,
+ Filter = fn _ _ => return True,
+ Sort = None}}
+fun computedHtml [row] name (f : $row -> xbody) : colMeta row computedState =
+ {Initialize = return (),
+ Handlers = fn () => {Header = name,
+ Project = fn r => return (f r),
+ Update = fn r _ => return r,
+ Display = fn x => x,
+ Edit = fn _ => <xml>...</xml>,
+ Validate = fn _ => return True,
+ CreateFilter = return (),
+ DisplayFilter = fn _ => <xml/>,
+ Filter = fn _ _ => return True,
+ Sort = None}}
+
+functor Make(M : sig
+ con key :: {Type}
+ con row :: {Type}
+ constraint key ~ row
+ table tab : (key ++ row)
+
+ val raw : $(map rawMeta (key ++ row))
+
+ con cols :: {(Type * Type * Type)}
+ val cols : $(map (colMeta (key ++ row)) cols)
+
+ val keyFolder : folder key
+ val rowFolder : folder row
+ val colsFolder : folder cols
+
+ con aggregates :: {Type}
+ val aggregates : $(map (aggregateMeta (key ++ row)) aggregates)
+ val aggFolder : folder aggregates
+
+ val pageLength : option int
+ end) = struct
+ open Grid.Make(struct
+ fun keyOf r = r --- M.row
+
+ val list = query (SELECT * FROM {{M.tab}} AS T) (fn r rs => return (r.T :: rs)) []
+
+ val wholeRow = @Folder.concat ! M.keyFolder M.rowFolder
+
+ fun ensql [env] (r : $(M.key ++ M.row)) =
+ @map2 [rawMeta] [ident] [sql_exp env [] []]
+ (fn [t] meta v => @sql_inject meta.Inj v)
+ wholeRow M.raw r
+
+ val new =
+ row <- @Monad.mapR _ [rawMeta] [ident]
+ (fn [nm :: Name] [t :: Type] meta => meta.New)
+ wholeRow M.raw;
+ dml (insert M.tab (ensql row));
+ return row
+
+ fun selector (r : $M.key) : sql_exp [T = M.key ++ M.row] [] [] bool =
+ @foldR2 [rawMeta] [ident]
+ [fn key => rest :: {Type} -> [rest ~ key] => sql_exp [T = key ++ rest] [] [] bool]
+ (fn [nm :: Name] [t :: Type] [key :: {Type}] [[nm] ~ key]
+ (meta : rawMeta t) (v : t)
+ (exp : rest :: {Type} -> [rest ~ key] => sql_exp [T = key ++ rest] [] [] bool)
+ [rest :: {Type}] [rest ~ [nm = t] ++ key] =>
+ (WHERE T.{nm} = {@sql_inject meta.Inj v} AND {exp [[nm = t] ++ rest]}))
+ (fn [rest :: {Type}] [rest ~ []] => (WHERE TRUE))
+ M.keyFolder (M.raw --- map rawMeta M.row) r
+ [_] !
+
+ fun save key row =
+ dml (update [M.key ++ M.row]
+ (ensql row)
+ M.tab
+ (selector key))
+
+ fun delete key =
+ dml (Basis.delete M.tab (selector key))
+
+ val cols = M.cols
+
+ val folder = M.colsFolder
+
+ val aggregates = M.aggregates
+
+ val aggFolder = M.aggFolder
+
+ val pageLength = M.pageLength
+ end)
+end