As of yesterday, my couch_docs gem now supports the
!code
macro (which comes from couchapp). This allows me to DRY up my design documents (CouchDB map-reduces and show/list functions).The couch_docs gem assembles CouchDB design documents from the filesystem—directories are keys in the design document and files are the values. This lets me work with
.js
files rather than functions inside of JSON documents (which is where the Javascript functions reside on the CouchDB server). The couch_docs gem expects to find design documents in a directory named _design
. As of yesterday, it expects to find "library" files for DRYing up these design documents in _design/__lib
.But what of the
__lib
files on the server? Do I exclude the before uploading? I think not. They should not cause any harm on the CouchDB server unless they were accessed and I think the double underscore in the name ought to be sufficient to indicate that "Hey! These aren't going to do anything if you access them!" Uploading to the server has the added benefit of making them dump-able later—with no changes to the current code.Ooooh. That bring up an interesting point though. On the CouchDB server, the
!code
macros have been evaluated, leaving them very much un-DRY. If I dump them, they ought to go back to being DRY. To accomplish that, I will need to delineate the expanded !code
when uploading it to CouchDB so that I can replace it with !code
macros when dumping them. Since I am using functional / full stack RSpec example for this bit of code, I can simply update my expecations:
it "should process code macros when assembling" doThat example fails, but is easy enough to get passing:
@it.to_hash['x'].
should == {
'z' =>
"// !begin code foo.js\n" +
"function foo () { return \"foo\"; }\n" +
"// !end code foo.js\n" +
"function bar () { return \"bar\"; }\n"
}
end
def process_code_macro(line)Now that I denote where the
if line =~ %r{\s*//\s*!code\s*(\S+)\s*}
"// !begin code #{$1}\n" +
read_from_lib($1)
"// !end code #{$1}\n"
else
line
end
end
!code
macros were evaluated when uploading design documents, how to de-evaluate them when dumping design documents to the file system? I would like it to work something like:it "should strip lib code when dumping" doThere is relatively little change-the-message/make-it-pass to be done here. I need the
js = <<_JS
// !begin code foo.js
function foo () { return 'foo'; }
// !end code foo.js
// !begin code bar.js
function bar () { return 'bar'; }
// !end code bar.js
function baz () { return 'baz'; }
_JS
@it.
remove_code_macros(js).
should == "// !code foo.js\n" +
"// !code bar.js\n" +
"function baz () { return 'baz'; }\n"
end
remove_code_macros
method to remove a code fragment, call itself again, remove another code fragment and keep doing so until there are no more !code
fragments to be removed. I accomplish that will some regular expression fun and, of course a little recursion:def remove_code_macros(js)With that, I should have an end-to-end mechanism for keeping my CouchDB design documents DRY. I will try this out with real design documents tomorrow to make sure I have not overlooked anything. Then it is onto the next item in my couch_docs 1.1 TODO list.
js =~ %r{// !begin code ([.\w]+)$}m
lib = $1
if lib and js =~ %r{// !end code #{lib}$}m
remove_code_macros(js.sub(%r{// !begin code #{lib}.+// !end code #{lib}}m, "// !code #{lib}"))
else
js
end
end
Day #35
Thanks for blogging this. It looks really cool!
ReplyDeleteWhen writing CouchApp, we've been very careful to take portability into account. Hence the support for both `couchapp push` and `couchapp clone`. I'm glad to see you decided to keep the lib directory in the design document. This means if someone else clones your app, and pushes it, they should be able to edit the code and run it again.