Arc Forumnew | comments | leaders | submitlogin
2 points by thaddeus 3851 days ago | link | parent

> is that you can't save booleans as booleans.

Hmm. Well I see that as a separate issue. I think nil and boolean false are very different things. nil means no value or false while false means a false value. In fact I was recently going to suggest that arc should support proper booleans and that arc tables should also store both boolean values, while maintaining it's nil pruning feature. Which really stems from wanting to easily transform to/from json or edn like formats. Currently one has to fudge arc's write-json by passing around symbols for 'true / 'false.

> how would you design templates if you were doing so from scratch?

Well that's interesting that you ask, because I did just that when I implemented templates in Clojure. Of course Clojure supports true boolean values and maps can hold nil values, so it was trivial and very useful.

My Clojure templates are on steroids though. Not only do they match most of the features in arc, but they are cumulative, and accept anonymous functions as field values. The values can also refer to other fields inputs or results.

So for example I just did this one:

  => (deftem :article
      :id        #(UUID) 
      :published true       
      :text      nil 
      :msec      #(msec)
      :seconds   #(seconds (% :msec))
      :date      #(format-date (date (% :msec)) "yyyy/MM/dd HH:mm:ss"))
  
  #result 
  
  => (invoke :article :text "cool" :published nil)
  
    {:id #uuid "08692e96-de87-49aa-9ad1-e33bcd61e712", 
     :text "cool", 
     :msec 1382644173685, 
     :seconds 1382644173, 
     :date "2013/10/24 15:49:33"}
Notice how :seconds and :date use :msec as an input argument? ... Well that's how I would do it. :)


1 point by akkartik 3851 days ago | link

Wow, I'd love to see that code!

Thanks also for the connection to json. There's a lot to consider here.

-----

3 points by thaddeus 3851 days ago | link

The code is

  1. In bad shape (wrote it early on) 
  2. Includes partial features not fully implemented.
  3. Has references to functions I can not split out without  
     creating a bunch of work.
  4. Includes features you would not care for (datomicish).
  5. Has oddities that make you wonder "why like that",
     until you realize you can pass in say a map of args   
     instead. 
With the above reasons, I was going to say I'll pass on releasing the code, but at long as you're ok just getting the scaffolding that will not run for you then here you go:

  (def index* (ref (hash-map)))
  (def templates* (ref (hash-map)))
  (def mutes* (ref (hash-map)))
  (def selfs* (ref (hash-map)))

  (defmacro deftem [name & fields]
    `(let [tem#   (quote ~name)
	   order# (evens (list ~@fields))
	   fmaps# (apply hash-map (list ~@fields))]
	(dosync (alter templates* assoc tem# fmaps#)
	        (alter index* assoc tem# order#)
	    fmaps#)))

  (defmacro defmute [name & fields]
    `(let [tem#   (quote ~name)
	  items# (list ~@fields)]
      (dosync (alter mutes* assoc tem# items#)
        items#)))

  (defmacro defself [name & fields]
   `(let [tem#   (quote ~name)
	  items# (list ~@fields)]
      (dosync (alter selfs* assoc tem# items#)
        items#)))

  (defn invoke-fields
   ([tem base fields allowables]
     (invoke-fields tem base fields allowables nil))
   ([tem base fields allowables testfn]
    (let [fks   (keys fields)
          selfs (@selfs* tem)]
      (reduce
       (fn [m k]
          (assoc m k
	        (if (detect? k fks); must use 'detect' opposed to 'find' for nil vals must be inserted.
		          (aifn (fields k)
	                  (try (it m)
	                    (catch Exception e (it)))
	                  (let [bfn (base k)]
                      (if (and (detect? k selfs)(fn? bfn))
                          (try (bfn (merge m {k it}))
	                          (catch Exception e (bfn)))
                           it)))
              (aifn (base k)
                    (try (it m)
                         (catch Exception e (it)))
                     it))))
		 (hash-map) allowables))))


  (defn invoke [tem & fields]
    (let [temx  (split-name tem)
          tem1  (first-identity temx)
	  atem? (is (last temx) "+")
	  xfn   (type-fn tem)
          temk  (xfn tem1)
	  base  (@templates* temk)
	  prox  (@mutes* temk)
	  fval  (first fields)
	  fmap  (cond (map? fval) fval ; for file loading map of saved records
	              (coll? fval) (apply hash-map fval)
	           :else (apply hash-map fields))
	  imap  (invoke-fields temk base fmap (@index* temk))]
       (reduce
	   (fn [m [k v]]
		   (if (or (missing? k base)(nil? v)(detect? k prox))
	         (dissoc m k)
		       (assoc m (if atem? (nsify temk k) k) v)))
	   	(hash-map) imap)))

-----