Friday, March 5, 2010

Two Steps Back, One Forward is Excellent Progress

‹prev | My Chain | next›

After yesterday, I am down to two items on my TODO list for 1.1 of couch_docs:
  • Better command line experience.
    • Should default to current directory.
    • Should print help without args / better format
  • Should use the bulk docs
  • Should support the !json and !code macros from couchapp
  • Should support a flag to only work on design docs (mostly for export).
  • Should create the DB if it doesn't already exist
Next up is the !json and !code macros from couchapp. When these macros are encountered, they are replaced with contents of other files before being uploaded to CouchDB. They are a great way to keep things DRY in CouchApp and I would like to be able to do the same when working on map-reduce views as well as my own list and show functions.

I spend a lot of time on the !json macro tonight, making decent progress on it, before deciding to abandon it.

It was hard to give up because of the time invested in it. At first I tried class level methods to locate the source file and to replace the !json macro in source code with the contents of that source file. Those seemed like "class level" operations at first. After BDD'ing for a bit, I realized that I was passing around the root directory (specified when instantiating a DesignDirectory object) too much. I eventually converted to instance methods which made for much cleaner implementation.

I was actually pretty happy with the code at this point—all the more so for having fought my way through an initial poor choice. I ultimately decided to abandon the code after a Pomodoro 5 break. Pomodoro 5 minute breaks (after 25 minutes of intense coding) are great for taking a step back. My mind got so wrapped up doing the implementation that I had not stepped back to ask if I should.

Part of the reason I should not support !json (at least in couch_docs 1.1) is that it is going to be hard to implement. I can locate the source file based on the macro, but that is only half of the problem. I would still need to quote escape the contents of the file so that they could be assigned to a Javascript variable. That is not too hard, but it is still work. To fully support CouchApp's !json, I would also need to map dot notation in the macro into directory structure on the file system and then back to Javascript objects. To illustrate, this macro:
 // !json
would expect a file bar.js (or any file with a basename of "bar" in the foo sub-directory). The resulting code to be uploaded would then need to look like:
 var foo = {"bar": "contents of bar.js here"};
Again, do-able, but not trivial.

Ultimately though, I am not shying away from effort here. It is a simple matter that I do not need the !json macro. That macro makes a lot of sense in CouchApp where it is often used to hold HTML templates. I have no intention of reimplementing CouchApp in Ruby. I am really only interested in keeping my Javascript views DRY. If I need common configuration, I can define a configuration function. Both of those things can be accomplished with the !code macro.

Even better, the !code macro is simple. Consider this:
 // !code foo/bar.js
Before pushing this to to CouchDB, that macro should be replaced with the contents of the foo/bar.js file. Easy peasy.

With heavy heart, I revert my !json work to date and write two RSpec
examples for the !code macro:
    it "should replace !code macros with the contents of the referenced file in lib" do
@it.stub!(:read_from_lib).and_return("awesome javascript")

process_code_macro(" // !code foo/bar.js ").
should == "awesome javascript"

it "should not affect normal lines when processing macros" do
process_code_macro(" var foo = 'bar'; ").
should == " var foo = 'bar'; "
The first example describes at a high level what should happen in the presence of the !code macro. The second example states that processing code macros on normal lines should have no effect. Both of these examples fail because I have not defined the process_code_macro method:
NoMethodError in 'CouchDocs::DesignDirectory a valid directory should replace !code macros with the contents of the referenced file in lib'
undefined method `process_code_macro' for #<CouchDocs::DesignDirectory:0xb74b97c4>
I define the method, give it the right arity and then make the second example pass by returning the input back:
    def process_code_macro(line)
Finally, I can make the first example pass by reading the contents of the specified file from the library directory:
    def process_code_macro(line)
if line =~ %r{\s*//\s*!code\s*(\S+)\s*}
What the library directory is or where it resides I will decide tomorrow along with the rest of read_from_lib. For now I have reached a happy stopping point. I have less code than I did 30 minutes ago, but that is almost always a good thing.

Day #33

No comments:

Post a Comment