annotate demo/more/dbgrid.ur @ 930:51bc7681c47e

Nullable columns *might* be working, but too much JS is generated for the page to load in finite time
author Adam Chlipala <adamc@hcoop.net>
date Sat, 12 Sep 2009 15:08:16 -0400
parents 5e8b6fa5b48f
children 2422360c78a3
rev   line source
adamc@915 1 con rawMeta = fn t :: Type =>
adamc@915 2 {New : transaction t,
adamc@915 3 Inj : sql_injectable t}
adamc@915 4
adamc@915 5 con colMeta' = fn (row :: {Type}) (t :: Type) =>
adamc@915 6 {Header : string,
adamc@915 7 Project : $row -> transaction t,
adamc@915 8 Update : $row -> t -> transaction ($row),
adamc@915 9 Display : t -> xbody,
adamc@915 10 Edit : t -> xbody,
adamc@915 11 Validate : t -> signal bool}
adamc@915 12
adamc@915 13 con colMeta = fn (row :: {Type}) (global_t :: (Type * Type)) =>
adamc@915 14 {Initialize : transaction global_t.1,
adamc@915 15 Handlers : global_t.1 -> colMeta' row global_t.2}
adamc@915 16
adamc@915 17 structure Direct = struct
adamc@930 18 con metaBase = fn actual_input :: (Type * Type) =>
adamc@930 19 {Display : actual_input.2 -> xbody,
adamc@930 20 Edit : actual_input.2 -> xbody,
adamc@930 21 Initialize : actual_input.1 -> transaction actual_input.2,
adamc@930 22 Parse : actual_input.2 -> signal (option actual_input.1)}
adamc@930 23
adamc@930 24 datatype metaBoth actual input =
adamc@930 25 NonNull of metaBase (actual, input) * metaBase (option actual, input)
adamc@930 26 | Nullable of metaBase (actual, input)
adamc@930 27
adamc@915 28 con meta = fn global_actual_input :: (Type * Type * Type) =>
adamc@915 29 {Initialize : transaction global_actual_input.1,
adamc@915 30 Handlers : global_actual_input.1
adamc@930 31 -> metaBoth global_actual_input.2 global_actual_input.3}
adamc@915 32
adamc@915 33 con editableState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
adamc@915 34 fun editable [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
adamc@915 35 (editableState ts) =
adamc@930 36 let
adamc@930 37 fun doMr mr = {Header = name,
adamc@930 38 Project = fn r => mr.Initialize r.nm,
adamc@930 39 Update = fn r s =>
adamc@930 40 vo <- current (mr.Parse s);
adamc@930 41 return (case vo of
adamc@930 42 None => r
adamc@930 43 | Some v => r -- nm ++ {nm = v}),
adamc@930 44 Display = mr.Display,
adamc@930 45 Edit = mr.Edit,
adamc@930 46 Validate = fn s => vo <- mr.Parse s; return (Option.isSome vo)}
adamc@930 47 in
adamc@930 48 {Initialize = m.Initialize,
adamc@930 49 Handlers = fn data => case m.Handlers data of
adamc@930 50 NonNull (mr, _) => doMr mr
adamc@930 51 | Nullable mr => doMr mr}
adamc@930 52 end
adamc@915 53
adamc@915 54 con readOnlyState (ts :: (Type * Type * Type)) = (ts.1, ts.3)
adamc@915 55 fun readOnly [ts] [rest] [nm :: Name] [[nm] ~ rest] name (m : meta ts) : colMeta ([nm = ts.2] ++ rest)
adamc@915 56 (readOnlyState ts) =
adamc@930 57 let
adamc@930 58 fun doMr mr = {Header = name,
adamc@930 59 Project = fn r => mr.Initialize r.nm,
adamc@930 60 Update = fn r _ => return r,
adamc@930 61 Display = mr.Display,
adamc@930 62 Edit = mr.Display,
adamc@930 63 Validate = fn _ => return True}
adamc@930 64 in
adamc@930 65 {Initialize = m.Initialize,
adamc@930 66 Handlers = fn data => case m.Handlers data of
adamc@930 67 NonNull (mr, _) => doMr mr
adamc@930 68 | Nullable mr => doMr mr}
adamc@930 69 end
adamc@915 70
adamc@915 71 con metaBasic = fn actual_input :: (Type * Type) =>
adamc@930 72 {Display : actual_input.2 -> xbody,
adamc@930 73 Edit : source actual_input.2 -> xbody,
adamc@930 74 Initialize : actual_input.1 -> actual_input.2,
adamc@930 75 InitializeNull : actual_input.2,
adamc@930 76 IsNull : actual_input.2 -> bool,
adamc@930 77 Parse : actual_input.2 -> option actual_input.1}
adamc@915 78
adamc@915 79 con basicState = source
adamc@915 80 fun basic [ts ::: (Type * Type)] (m : metaBasic ts) : meta (unit, ts.1, basicState ts.2) =
adamc@915 81 {Initialize = return (),
adamc@930 82 Handlers = fn () => NonNull (
adamc@930 83 {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
adamc@915 84 Edit = m.Edit,
adamc@915 85 Initialize = fn v => source (m.Initialize v),
adamc@930 86 Parse = fn s => v <- signal s; return (m.Parse v)},
adamc@930 87 {Display = fn s => <xml><dyn signal={v <- signal s; return (m.Display v)}/></xml>,
adamc@930 88 Edit = m.Edit,
adamc@930 89 Initialize = fn v => source (case v of
adamc@930 90 None => m.InitializeNull
adamc@930 91 | Some v => m.Initialize v),
adamc@930 92 Parse = fn s => v <- signal s;
adamc@930 93 return (if m.IsNull v then
adamc@930 94 Some None
adamc@930 95 else
adamc@930 96 case m.Parse v of
adamc@930 97 None => None
adamc@930 98 | Some v' => Some (Some v'))})}
adamc@930 99
adamc@930 100 fun nullable [global] [actual] [input] (m : meta (global, actual, input)) =
adamc@930 101 {Initialize = m.Initialize,
adamc@930 102 Handlers = fn d => case m.Handlers d of
adamc@930 103 Nullable _ => error <xml>Don't stack calls to Direct.nullable!</xml>
adamc@930 104 | NonNull (_, ho) => Nullable ho}
adamc@915 105
adamc@915 106 type intGlobal = unit
adamc@915 107 type intInput = basicState string
adamc@915 108 val int : meta (intGlobal, int, intInput) =
adamc@915 109 basic {Display = fn s => <xml>{[s]}</xml>,
adamc@915 110 Edit = fn s => <xml><ctextbox source={s}/></xml>,
adamc@915 111 Initialize = fn n => show n,
adamc@930 112 InitializeNull = "",
adamc@930 113 IsNull = eq "",
adamc@915 114 Parse = fn v => read v}
adamc@915 115
adamc@915 116 type stringGlobal = unit
adamc@915 117 type stringInput = basicState string
adamc@915 118 val string : meta (stringGlobal, string, stringInput) =
adamc@915 119 basic {Display = fn s => <xml>{[s]}</xml>,
adamc@915 120 Edit = fn s => <xml><ctextbox source={s}/></xml>,
adamc@915 121 Initialize = fn s => s,
adamc@930 122 InitializeNull = "",
adamc@930 123 IsNull = eq "",
adamc@915 124 Parse = fn s => Some s}
adamc@915 125
adamc@915 126 type boolGlobal = unit
adamc@915 127 type boolInput = basicState bool
adamc@915 128 val bool : meta (boolGlobal, bool, boolInput) =
adamc@915 129 basic {Display = fn b => <xml>{[b]}</xml>,
adamc@915 130 Edit = fn s => <xml><ccheckbox source={s}/></xml>,
adamc@915 131 Initialize = fn b => b,
adamc@930 132 InitializeNull = False,
adamc@930 133 IsNull = fn _ => False,
adamc@915 134 Parse = fn b => Some b}
adamc@915 135
adamc@915 136 functor Foreign (M : sig
adamc@915 137 con row :: {Type}
adamc@915 138 con t :: Type
adamc@915 139 val show_t : show t
adamc@915 140 val read_t : read t
adamc@915 141 val eq_t : eq t
adamc@915 142 val inj_t : sql_injectable t
adamc@915 143 con nm :: Name
adamc@915 144 constraint [nm] ~ row
adamc@915 145 table tab : ([nm = t] ++ row)
adamc@915 146 val render : $([nm = t] ++ row) -> string
adamc@915 147 end) = struct
adamc@915 148 open M
adamc@915 149
adamc@915 150 con global = list (t * string)
adamc@930 151 con input = source string * option (t * $row)
adamc@915 152
adamc@915 153 val getChoices = List.mapQuery (SELECT * FROM tab AS T)
adamc@915 154 (fn r => (r.T.nm, render r.T))
adamc@915 155
adamc@915 156 fun getChoice k =
adamc@915 157 r <- oneRow (SELECT T.{{row}} FROM tab AS T WHERE T.{nm} = {[k]});
adamc@915 158 return r.T
adamc@915 159
adamc@930 160 val meta : meta (global, M.t, input) =
adamc@915 161 {Initialize = getChoices,
adamc@915 162 Handlers = fn choices =>
adamc@930 163 NonNull (
adamc@930 164 {Display = fn (_, kr) => case kr of
adamc@930 165 None => error <xml>Unexpected Foreign null</xml>
adamc@930 166 | Some (k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
adamc@930 167 Edit = fn (s, kr) =>
adamc@915 168 <xml><cselect source={s}>
adamc@915 169 {List.mapX (fn (k', rend) =>
adamc@930 170 <xml><coption value={show k'} selected={case kr of
adamc@930 171 None => False
adamc@930 172 | Some (k, _) =>
adamc@930 173 k' = k}>{[rend]}</coption>
adamc@915 174 </xml>)
adamc@915 175 choices}
adamc@915 176 </cselect></xml>,
adamc@915 177 Initialize = fn k => s <- source (show k);
adamc@915 178 r <- rpc (getChoice k);
adamc@930 179 return (s, Some (k, r)),
adamc@930 180 Parse = fn (s, _) => k <- signal s; return (read k : option t)},
adamc@930 181 {Display = fn (_, kr) => case kr of
adamc@930 182 None => <xml>NULL</xml>
adamc@930 183 | Some (k, r) => <xml>{[render ({nm = k} ++ r)]}</xml>,
adamc@930 184 Edit = fn (s, kr) =>
adamc@930 185 <xml><cselect source={s}>
adamc@930 186 <coption value="" selected={case kr of
adamc@930 187 None => True
adamc@930 188 | _ => False}>NULL</coption>
adamc@930 189 {List.mapX (fn (k', rend) =>
adamc@930 190 <xml><coption value={show k'} selected={case kr of
adamc@930 191 None => False
adamc@930 192 | Some (k, _) =>
adamc@930 193 k' = k}>{[rend]}</coption>
adamc@930 194 </xml>)
adamc@930 195 choices}
adamc@930 196 </cselect></xml>,
adamc@930 197 Initialize = fn k => case k of
adamc@930 198 None =>
adamc@930 199 s <- source "";
adamc@930 200 return (s, None)
adamc@930 201 | Some k =>
adamc@930 202 s <- source (show k);
adamc@930 203 r <- rpc (getChoice k);
adamc@930 204 return (s, Some (k, r)),
adamc@930 205 Parse = fn (s, _) => ks <- signal s;
adamc@930 206 return (case ks of
adamc@930 207 "" => Some None
adamc@930 208 | _ => case read ks : option t of
adamc@930 209 None => None
adamc@930 210 | Some k => Some (Some k))})}
adamc@915 211 end
adamc@915 212 end
adamc@915 213
adamc@915 214 con computedState = (unit, xbody)
adamc@915 215 fun computed [row] [t] (_ : show t) name (f : $row -> t) : colMeta row computedState =
adamc@915 216 {Initialize = return (),
adamc@915 217 Handlers = fn () => {Header = name,
adamc@915 218 Project = fn r => return <xml>{[f r]}</xml>,
adamc@915 219 Update = fn r _ => return r,
adamc@915 220 Display = fn x => x,
adamc@915 221 Edit = fn _ => <xml>...</xml>,
adamc@915 222 Validate = fn _ => return True}}
adamc@915 223 fun computedHtml [row] name (f : $row -> xbody) : colMeta row computedState =
adamc@915 224 {Initialize = return (),
adamc@915 225 Handlers = fn () => {Header = name,
adamc@915 226 Project = fn r => return (f r),
adamc@915 227 Update = fn r _ => return r,
adamc@915 228 Display = fn x => x,
adamc@915 229 Edit = fn _ => <xml>...</xml>,
adamc@915 230 Validate = fn _ => return True}}
adamc@915 231
adamc@915 232 functor Make(M : sig
adamc@915 233 con key :: {Type}
adamc@915 234 con row :: {Type}
adamc@915 235 constraint key ~ row
adamc@915 236 table tab : (key ++ row)
adamc@915 237
adamc@915 238 val raw : $(map rawMeta (key ++ row))
adamc@915 239
adamc@915 240 con cols :: {(Type * Type)}
adamc@915 241 val cols : $(map (colMeta (key ++ row)) cols)
adamc@915 242
adamc@915 243 val keyFolder : folder key
adamc@915 244 val rowFolder : folder row
adamc@915 245 val colsFolder : folder cols
adamc@915 246 end) = struct
adamc@915 247 open Grid.Make(struct
adamc@915 248 val list = query (SELECT * FROM {{M.tab}} AS T) (fn r rs => return (r.T :: rs)) []
adamc@915 249
adamc@915 250 val wholeRow = @Folder.concat ! M.keyFolder M.rowFolder
adamc@915 251
adamc@915 252 fun ensql [env] (r : $(M.key ++ M.row)) =
adamc@915 253 map2 [rawMeta] [id] [sql_exp env [] []]
adamc@915 254 (fn [t] meta v => @sql_inject meta.Inj v)
adamc@915 255 [_] wholeRow M.raw r
adamc@915 256
adamc@915 257 val new =
adamc@915 258 row <- Monad.mapR [rawMeta] [id]
adamc@915 259 (fn [nm :: Name] [t :: Type] meta => meta.New)
adamc@915 260 [_] wholeRow M.raw;
adamc@915 261 dml (insert M.tab (ensql row));
adamc@915 262 return row
adamc@915 263
adamc@915 264 fun selector (r : $(M.key ++ M.row)) : sql_exp [T = M.key ++ M.row] [] [] bool =
adamc@915 265 foldR2 [rawMeta] [id]
adamc@915 266 [fn key => rest :: {Type} -> [rest ~ key] => sql_exp [T = key ++ rest] [] [] bool]
adamc@915 267 (fn [nm :: Name] [t :: Type] [key :: {Type}] [[nm] ~ key]
adamc@915 268 (meta : rawMeta t) (v : t)
adamc@915 269 (exp : rest :: {Type} -> [rest ~ key] => sql_exp [T = key ++ rest] [] [] bool)
adamc@915 270 [rest :: {Type}] [rest ~ [nm = t] ++ key] =>
adamc@915 271 (WHERE T.{nm} = {@sql_inject meta.Inj v} AND {exp [[nm = t] ++ rest] !}))
adamc@915 272 (fn [rest :: {Type}] [rest ~ []] => (WHERE TRUE))
adamc@915 273 [_] M.keyFolder (M.raw --- map rawMeta M.row) (r --- M.row)
adamc@915 274 [_] !
adamc@915 275
adamc@915 276 fun save {Old = row, New = row'} =
adamc@915 277 dml (update [M.key ++ M.row] !
adamc@915 278 (ensql row')
adamc@915 279 M.tab
adamc@915 280 (selector row))
adamc@915 281
adamc@915 282 fun delete row =
adamc@915 283 dml (Basis.delete M.tab (selector row))
adamc@915 284
adamc@915 285 val cols = M.cols
adamc@915 286
adamc@915 287 val folder = M.colsFolder
adamc@915 288 end)
adamc@915 289 end