TDD - Top Down Design
21 September 2015
Note: This is post number one in a series on building an application without a proper ORM.
Backstory: For a long time, I’ve wanted an app I could put recipes in that showed each step of the recipe, in order, on a singular page, with nothing else. I have trouble finding/keeping my place when working through a recipe and this seemed like an easily solvable problem.
I intend to use/display the Step
like (using mote template engine):
<step>
% if step.has_ingredients?
<ingredient>
{{ step.ingredients }}
</ingredients>
% end
<directions>
{{ step.directions }}
</direction>
</step>
Now I have a starting place for a few Step
tests:
describe Step do
let(:directions) { "Mix a lot, sir" }
let(:ingredients) { [ :pears, :honey ] }
def build_a_step(directions: "", ingredients: [])
Step.new(directions: directions, ingredients: ingredients)
end
it "can have ingredients" do
step = build_a_step(ingredients: ingredients)
expect(step.ingredients).to eq(ingredients)
end
it "knows if it has any ingredients" do
step = build_a_step
expect(step.has_ingredients?).to be(false)
step_with_ingredients = build_a_step(ingredients: ingredients)
expect(step_with_ingredients.has_ingredients?).to be true
end
it "has directions" do
step = build_a_step(directions: directions)
expect(step.directions).to eq(directions)
end
end
This is a pretty simple / easy to build class. Now we need a way of
finding/creating a Step
for handing into the view. I don’t want to think about
the database schema, so we’ll just make a silly class method find
that at
least pretends like it does what we need.
it "has a .find interface" do
dummy_db = double
allow(dummy_db).to receive(:get_step)
.with(recipe_id: 1, step_number: 2) { { directions: directions } }
expect(Step.find(recipe_id: 1, step_number: 2, database_wrapper: dummy_db)).to be_a(Step)
end
class Step
def self.find(recipe_id:, step_number:, database_wrapper:)
new(database_wrapper.get_step(recipe_id: recipe_id, step_number: step_number))
end
end
ERMEHGERD A MOCK!!! Is that terrible? Probably. But it affords us the ability to insert a fake database wrapper with fake data and not even consider the database or schema.
class FakeDB
def self.get_step(recipe_id:, step_number:)
recipes = [ [ { directions: "Mix a lot, sir!" } ] ]
recipes[(recipe_id.to_i - 1)][(step_number.to_i - 1)]
end
end
Note that I’m preemptively basing a Step
on a recipe id which may or may not be a good idea,
time will tell. For now, it seems like we should have something that takes the recipe id,
and a step number and gives us a displayable Step
.
For the application side: I’ve been tinkering with cuba:
on "recipes/(\\d+)-step-(\\d+)" do |recipe_id, step_number|
step = Step.find(recipe_id: recipe_id, step_number: step_number, database_wrapper: FakeDB)
res.write view("recipes/step", step: step)
end
This works for routes like recipes/1-step-1
, and we have a place to put some fake recipe/step
data. We have less than 100 lines of code, and a pretty useless (but working) example of a
recipe step, but it’s not much use without an actual recipe.
The commit from this post can be found here
Coming soon: Post 2 - Something Something Recipes