The problem
Yesterday night I've written command line interface (CLI) for GNU Artanis, after took care of my daughter to sleep, I had only one hour to hack something.
My aim is to add `art create proj-name' command to create a bunch of files/directories for initializing a new Artanis web app. Just like Rails does. Well, since my Turing brain has limited time for this hack, I have to choose my favorite way to traverse the tree for this job, the recursive way. ;-)
Depth first traversal
One of the proper way to mkdir a directory tree is to use DFS. I chose the pre-order DFS.
If you have well structured Tree implementation like this, then the pseudo code below could be simple enough to try.
;; This is pseudo code (define (preorder node visit) (cond ((leaf? node) #t) (else (visit node) (for-each preorder (children node)))))
Actually, the same algorithm is used in my unfinished data-taistructure lib Nashkel. Could be optimized to iterative one though...
Tree structure
Here's a problem: I don't known if I'll add/remove directories in the future, so I need a flexible design to let me do this job easily. A proper tree design is a good start.
For example:
|-- app | |-- controller | |-- model | `-- view |-- lib |-- log |-- prv |-- pub | |-- css | |-- img | | `-- upload | `-- js |-- sys | |-- i18n | `-- pages |-- test | |-- benchmark | |-- functional | `-- unit `-- tmp `-- cache
In Scheme, the most convenient data structure is the list:
(define *dir-arch* '((app (model controller view)) ; MVC stuff (sys (pages i18n)) ; system stuff (log) ; log files (lib) ; libs (pub ((img (upload)) css js)) ; public assets (prv) ; private stuff, say, something dedicated config or tokens (tmp (cache)) ; temporary files (test (unit functional benchmark)))) ; tests stuffs
Let me explain this tree structure built with list, since the algorithm implementation has something to do with the data structure design. I gave the rule below:
((node1 (child1_1 child1_2 ...)) (node2 ((node3 (child3_1 child3_2 ...)) child2_1 child2_2 (node4 (child4_1 child4_2 ...)) ...)) (node5) ; no any children ...)
The disadvantage of this tree is hard to modify dynamically (alright, you may use set-car! and set-cdr!). But it's easy to handle statically, because the hierarchy is clear.
Design the high-order-function
Now that I'm using Scheme, I'll choose FP way containly. High order function just like design pattern of FP. Design a good and generic one will save your lot time in the future development.
Here is mine:
(define (dfs tree visit level) (match tree (() #t) (((r (children ...)) rest ...) (visit r level) (for-each (lambda (x) (dfs (list x) visit (cons r level))) children) (dfs rest visit level)) (((r) rest ...) (visit r level) (dfs rest visit level)) ((children ...) (visit (car children) level) (dfs (cdr children) visit level))))
So it's easy to mkdir a directory tree now:
(define (create-framework) (define (->path x l) (format #f "~{~a~^/~}" (reverse (cons x l)))) (dfs *dir-arch* (lambda (x l) (mkdir (->path x l))) '()))
This CLI feature won't appear in 0.0.3, and because it'll bring great change to GNU Artanis, maybe it should be in 0.1.0. Dunno...
Happy hacking!