Yesterday was pretty much a mess. I am adding generators to my couch_docs gem, which pushes to / dumps from CouchDB. Hopefully today's link in the chain will go a little better.
First up, picking up where I left off:
I left myself a failing RSpec example to point the way today. Specifically, I need to be able to generate a library template. The spec is failing with a
SystemExit
exception because the lib
generator is not known. So, let's make it known:# More to come...That should get things passing... But:
GENERATORS = %w{lib}
#...
if @command == "generate"
generator = args.shift
if GENERATORS.include? generator
Generator.generate(generator.to_sym, *args)
else
say "Invalid generator #{generator}"
exit
end
else
#...
1)Ah, I have finally gotten to the point at which I can create the
NameError in 'CouchDocs::CommandLine generate should be able to generate a library template'
uninitialized constant CouchDocs::CommandLine::Generator
./spec/command_line_spec.rb:36:in `new'
./spec/command_line_spec.rb:36:
module CouchDocsWith, that... my example still fails:
class Generator
def self.generate(generator, *args)
end
end
end
1)Ah, I've got everything in the wrong place here. I am trying to generate the template in the same place that I am parsing options. Once I split the generate command out into the
Spec::Mocks::MockExpectationError in 'CouchDocs::CommandLine generate should be able to generate a library template'
<CouchDocs::Generator (class)> expected :generate with (:lib, "utils") once, but received it 0 times
./spec/command_line_spec.rb:37:
run
portion of the CommandLine
:#...Everything works. Well, "works" is a bit of a stretch. All of the specs pass, but the
when "generate"
Generator.generate(@options[:generator].to_sym,
*@options[:args])
#...
Generator
is on the sparse side.I am working through the
lib
template generator first because it is the simplest. It will generate only a single, empty template in the _design/__lib
directory. The couch_docs gem will insert the contents of that file in the presence of a !code
macro. From a generator perspective, all I need to do is create a blank template. The template RSpec example:describe CouchDocs::Generator doTo make that pass:
context "lib" do
it "should pull in the lib template" do
Generator.
should_receive(:lib_template)
Generator.generate(:lib, 'utils')
end
end
end
module CouchDocsNext up, I need to make the
class Generator
def self.generate(generator, *args)
template =
self.send("#{generator}_template".to_sym)
end
protected
def self.lib_template; "" end
end
_design/__lib
directory if it doesn't already exist:it "should make the __lib directory (if it doesn't alreaddy exist)" doMaking that pass is easy enough:
FileUtils.
should_receive(:mkdir_p).
with("_design/__lib")
Generator.generate(:lib, 'utils')
end
def self.generate(generator, *args)Finally, I need to write that template:
template =
self.send("#{generator}_template".to_sym)
FileUtils.mkdir_p("_design/__lib")
end
it "should open the blank lib template for writing" doThe
File.
should_receive(:new).
with("_design/__lib/utils.js", "a").
and_return(@file)
Generator.generate(:lib, 'utils')
end
@file
in there is a null object setup in the before(:each)
block. I am opening the file in append mode so that files do not accidentally over-write existing code. Ideally I would prefer a dialog system similar to Rails, but, as I found yesterday, rubigen feels like overkill. To get that example passing, I add the File.new
code:def self.generate(generator, *args)I drive the write behavior with a similar example.
template =
self.send("#{generator}_template".to_sym)
FileUtils.mkdir_p("_design/__lib")
File.new("_design/__lib/#{args.first}.js", "w")
end
This is a decent stopping point for the day. Next up, I need to drive the slightly more sophisticate map-reduce generators. This has been a real struggle so far. I know how the Rails generator works. I really want to start breaking this out into classes that hold manifests, write templates, etc.. But I am staying true to BDD and will see where it leads me.
Day #42
No comments:
Post a Comment