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!