diff demo/more/dbgrid.ur @ 915:5e8b6fa5b48f

Start 'more' demo with dbgrid
author Adam Chlipala <adamc@hcoop.net>
date Tue, 08 Sep 2009 07:48:57 -0400
parents
children 51bc7681c47e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/demo/more/dbgrid.ur	Tue Sep 08 07:48:57 2009 -0400
@@ -0,0 +1,207 @@
+con rawMeta = fn t :: Type =>
+                 {New : transaction t,
+                  Inj : sql_injectable t}
+
+con colMeta' = fn (row :: {Type}) (t :: Type) =>
+                  {Header : string,
+                   Project : $row -> transaction t,
+                   Update : $row -> t -> transaction ($row),
+                   Display : t -> xbody,
+                   Edit : t -> xbody,
+                   Validate : t -> signal bool}
+
+con colMeta = fn (row :: {Type}) (global_t :: (Type * Type)) =>
+                 {Initialize : transaction global_t.1,
+                  Handlers : global_t.1 -> colMeta' row global_t.2}
+
+structure Direct = struct
+    con meta = fn global_actual_input :: (Type * Type * Type) =>
+                  {Initialize : transaction global_actual_input.1,
+                   Handlers : global_actual_input.1
+                              -> {Display : global_actual_input.3 -> xbody,
+                                  Edit : global_actual_input.3 -> xbody,
+                                  Initialize : global_actual_input.2 -> transaction global_actual_input.3,
+                                  Parse : global_actual_input.3 -> signal (option global_actual_input.2)}}
+
+    con editableState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
+    fun editable [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
+                                                                                     (editableState ts) =
+       {Initialize = m.Initialize,
+        Handlers = fn data => {Header = name,
+                               Project = fn r => (m.Handlers data).Initialize r.nm,
+                               Update = fn r s =>
+                                           vo <- current ((m.Handlers data).Parse s);
+                                           return (case vo of
+                                                       None => r
+                                                     | Some v => r -- nm ++ {nm = v}),
+                               Display = (m.Handlers data).Display,
+                               Edit = (m.Handlers data).Edit,
+                               Validate = fn s => vo <- (m.Handlers data).Parse s; return (Option.isSome vo)}}
+        
+    con readOnlyState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
+    fun readOnly [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
+                                                                                     (readOnlyState ts) =
+       {Initialize = m.Initialize,
+        Handlers = fn data => {Header = name,
+                               Project = fn r => (m.Handlers data).Initialize r.nm,
+                               Update = fn r _ => return r,
+                               Display = (m.Handlers data).Display,
+                               Edit = (m.Handlers data).Display,
+                               Validate = fn _ => return True}}
+
+    con metaBasic = fn actual_input :: (Type * Type) =>
+                  {Display : actual_input.2 -> xbody,
+                   Edit : source actual_input.2 -> xbody,
+                   Initialize : actual_input.1 -> actual_input.2,
+                   Parse : actual_input.2 -> option actual_input.1}
+
+    con basicState = source
+    fun basic [ts ::: (Type * Type)] (m : metaBasic ts) : meta (unit, ts.1, basicState ts.2) =
+        {Initialize = return (),
+         Handlers = fn () => {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)}}
+
+    type intGlobal = unit
+    type intInput = basicState string
+    val int : meta (intGlobal, int, intInput) =
+        basic {Display = fn s => <xml>{[s]}</xml>,
+               Edit = fn s => <xml><ctextbox source={s}/></xml>,
+               Initialize = fn n => show n,
+               Parse = fn v => read v}
+
+    type stringGlobal = unit
+    type stringInput = basicState string
+    val string : meta (stringGlobal, string, stringInput) =
+        basic {Display = fn s => <xml>{[s]}</xml>,
+               Edit = fn s => <xml><ctextbox source={s}/></xml>,
+               Initialize = fn s => s,
+               Parse = fn s => Some s}
+
+    type boolGlobal = unit
+    type boolInput = basicState bool
+    val bool : meta (boolGlobal, bool, boolInput) =
+        basic {Display = fn b => <xml>{[b]}</xml>,
+               Edit = fn s => <xml><ccheckbox source={s}/></xml>,
+               Initialize = fn b => b,
+               Parse = fn b => Some b}
+
+    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 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
+
+        con global = list (t * string)
+        con input = source string * t * $row
+
+        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 =
+            {Initialize = getChoices,
+             Handlers = fn choices =>
+                           {Display = fn (_, k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
+                            Edit = fn (s, k, _) => 
+                                      <xml><cselect source={s}>
+                                        {List.mapX (fn (k', rend) =>
+                                                       <xml><coption value={show k'} selected={k' = k}>{[rend]}</coption>
+                                                       </xml>)
+                                                   choices}
+                                      </cselect></xml>,
+                            Initialize = fn k => s <- source (show k);
+                                            r <- rpc (getChoice k);
+                                            return (s, k, r),
+                            Parse = fn (s, _, _) => k <- signal s; return (read k)}}
+    end
+end
+
+con computedState = (unit, xbody)
+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}}
+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}}
+
+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)}
+                 val cols : $(map (colMeta (key ++ row)) cols)
+
+                 val keyFolder : folder key
+                 val rowFolder : folder row
+                 val colsFolder : folder cols
+             end) = struct
+    open Grid.Make(struct
+                       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] [id] [sql_exp env [] []]
+                           (fn [t] meta v => @sql_inject meta.Inj v)
+                           [_] wholeRow M.raw r
+
+                       val new =
+                           row <- Monad.mapR [rawMeta] [id]
+                                              (fn [nm :: Name] [t :: Type] meta => meta.New)
+                                              [_] wholeRow M.raw;
+                           dml (insert M.tab (ensql row));
+                           return row
+
+                       fun selector (r : $(M.key ++ M.row)) : sql_exp [T = M.key ++ M.row] [] [] bool =
+                         foldR2 [rawMeta] [id]
+                                [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 --- M.row)
+                                [_] !
+
+                       fun save {Old = row, New = row'} =
+                           dml (update [M.key ++ M.row] !
+                                       (ensql row')
+                                       M.tab
+                                       (selector row))
+
+                       fun delete row =
+                           dml (Basis.delete M.tab (selector row))
+
+                       val cols = M.cols
+
+                       val folder = M.colsFolder
+                   end)
+end