3 title
= "blog/Factor file system monitor"
10 prototype
= "@BlogEntry"
23 html_meta_keywords
= "factor, factorcode, file, monitor,"
24 html_meta_description
= "Filesystem monitor implementation in the factor programming language"
25 redirect_destination
= ""
26 xssfilter_allowed_tags
= ""
27 http_cache_control
= ""
31 Sometimes I find myself editing config scripts that won't be reloaded until the program using them is restarted, and I find the editing/stopping/starting loop really annoying, so I thought it would be nice to hack a little tool that started an application and then monitors for changes in a directory tree root, stopping and restarting the app each time a change is detected... now, Factor is the only language, AFAIK, with a portable [file system monitoring library](http://docs.factorcode.org/content/vocab-io.monitors.html). It's important to note that it doesn't fall back to active polling, but use the underlying notifications API for each platform.
33 In case you didn't know, Factor is a concatenative language, a sort of modern Forth on steroids with a lispy touch, created by [Slava Pestov](http://factorcode.org/slava/). Slava himself blogged about a [simple aplication](http://factor-language.blogspot.com/2008/02/simple-application-using-iomonitor-tail.html) of this library, and probably this blog post won't add much to the original article if you already know a bit of Factor, but in any case it will be yet another example of useful factor code.
35 If you've never played with Factor, you can download the right version for your platform [here](http://www.factorcode.org). Then visit the [getting started](http://concatenative.org/wiki/view/Factor/Learning) page. It pays off spending some time browsing the docs, because the factor team has made an effort to make the introduction as gentle as possible...
39 <span style="color: #cd5c5c;">! Vim: ts=4 :expandtab
40 ! Copyright (C) Joan Arnaldich Bernal
41 ! See http://factorcode.org/license.txt for BSD license.
42 </span><span style="color: PaleYellow;">USING:</span><span style="color: #7fffd4;"> accessors combinators fry io io.launcher io.monitors
43 kernel namespaces prettyprint sequences </span><span style="color: PaleYellow;">;</span>
44 <span style="color: PaleYellow;">IN:</span> <span style="color: #7fffd4;">spinner</span>
46 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">reset-image</span><span style="color: #cd5c5c;"> ( progname proc -- progname newproc )</span>
47 kill-process dup run-detached <span style="color: PaleYellow;">;</span>
49 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">change-description</span><span style="color: #cd5c5c;"> ( change -- str )</span>
50 <span style="color: #9acd32;">changed>></span> first {
51 { +modify-file+ [ <span style="color: #ffa500;">"FILE MODIFIED"</span> ] }
52 { +add-file+ [ <span style="color: #ffa500;">"FILE ADDED"</span> ] }
53 { +remove-file+ [ <span style="color: #ffa500;">"FILE REMOVED"</span> ] }
54 { +rename-file+ [ <span style="color: #ffa500;">"FILE RENAMED"</span> ] }
55 { +rename-file-new+ [ <span style="color: #ffa500;">"FILE RENAMED"</span> ] }
56 { +rename-file-old+ [ <span style="color: #ffa500;">"FILE RENAMED"</span> ] }
58 } case <span style="color: PaleYellow;">;</span>
60 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">describe-change</span><span style="color: #cd5c5c;"> ( change -- )</span>
61 [ change-description ] [ <span style="color: #9acd32;">path>></span> ] bi
62 <span style="color: #ffa500;">" @ "</span> swap append append print nl <span style="color: PaleYellow;">;</span>
64 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">print-path</span><span style="color: #cd5c5c;"> ( desc -- )</span>
65 next-change describe-change flush <span style="color: PaleYellow;">;</span> <span style="color: #cd5c5c;">!
67 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">watch-loop</span><span style="color: #cd5c5c;"> ( monitor progname proc -- )</span>
68 [ dup print-path ] 2dip
70 watch-loop <span style="color: PaleYellow;">;</span>
72 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">watch-directory</span><span style="color: #cd5c5c;"> ( path progname proc -- )</span>
73 [ watch-loop ] curry curry
74 '[ <span style="color: PaleYellow;">t</span> _ with-monitor ] curry with-monitors <span style="color: PaleYellow;">;</span>
76 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">start-spinning</span><span style="color: #cd5c5c;"> ( path progname -- )</span>
77 dup run-detached watch-directory <span style="color: PaleYellow;">;</span>
79 <span style="color: PaleYellow;">:</span> <span style="color: #9acd32;">main</span><span style="color: #cd5c5c;"> ( -- )</span>
80 <span style="color: #ffa500;">"path"</span> get-global
81 <span style="color: #ffa500;">"exec"</span> get-global
82 start-spinning <span style="color: PaleYellow;">;</span>
84 <span style="color: PaleYellow;">MAIN:</span> <span style="color: #7fffd4;">main</span>
87 There's really not much to comment on this code. We start by reading
88 the two params from globals (MAIN is the only place where I allow
89 myself to use globals), and then push them onto the stack to call the
90 function `start-spinning`, that starts the process for the first time
91 and then calls `watch-directory`, which first builds a specialized
92 watch loop through currying... I guess this is the concatenative
93 equivalent of a HOF (ain't concatenative nice?) and then submits this
94 newly created function to `with-monitors`. The loop, as seen in this
95 function, just blocks for an event (`next-change` is a blocking call),
96 prints a readable message, and keeps looping.
98 This will allow run it at the command-line like this:
101 factor -exec=<em>program-to-reset</em> -path=<em>path-to-monitor</em>
112 creation_time
= "1296313923"