desc "I describe the task below me!"
task :stuff
# do stuff here
endWhat's cool about the code above is that the desc method seems to magically know how to attach itself to the next task defined. When I run "rake -T", I will see the description associated with my :stuff task. Very slick.
Like most things involved in DSLs, this seems magical at first. How the heck would the description know to attach itself to the next task? It's cool and very easy to see the intent of the code, but how the heck does it work? In truth, implementing the spill-over context pattern is pretty darn simple. Let's look at an example DSL I created:
make_a_sandwich = Task.new 'make a sandwich' do
description "open up the fridge"
step :open_fridge do
# do fridge opening stuff
end
description "get some bread"
step :get_bread do
# bread stuff here
end
description "get peanut butter and jelly"
step :ingredients do
# get the good stuff
end
end
make_a_sandwich.show_taskThis DSL (which looks strikingly similiar to rake) lets us create tasks out of steps. We can describe each of the steps in plain english and then use the show_task method to print out a nice explanation of our task.
The code to implement this DSL is pretty easy to understand. At a high level, here's what happens. I've defined two instance methods on Task called describe and step. The describe method stores whatever sting is passed to it in an instance variable called @last_description. The step method does two things. First, it stores the block passed to it in a hash along with the step name as a key. Second, and most importantly, it checks to see if there is a description sitting in the instance variable @last_description. If so, it associates that description with the step it is creating, and then clears out the instance variable. That's all there is to spill-over context! The "magic" of the description attaching to a step is accomplished by storing the description in an instance variable and using it later. Very simple, but also a very powerful concept for creating interesting and usable DSLs.
Finally, the show_task method just iterates over the tasks and descriptions and prints them out nicely. So, without further ado, here's the implementation code:
@name = name
@steps = {}
@descriptions = []
instance_eval(&block) if block_given?
end
puts "Task: "
puts "Steps:"
@descriptions.each_with_index do |(step,desc),i|
puts " : - "
end
end
@steps[name] = block
@descriptions << [name,@last_description]
@last_description = nil
end
@last_description = text
end
end
0 comments:
Post a Comment