-
-
Save snuggs/f5ab108a8cf596fbf188 to your computer and use it in GitHub Desktop.
def store(model) | |
# 99 of 100 times we will only need to call #to_h and be done with model | |
persist model.to_h # Breaks demeter's law | |
# this usually encourages poking around in the model | |
# which is no concern of store | |
end | |
def store(record) #decoupled | |
persist record #because that's all we want to do. | |
# If it doesn't work as expected RTST (Read The Stack Trace) | |
# will probably say something like "Cannot find keys? in persist:22" | |
end | |
store model.to_h #usage | |
# Now #store() AND model#to_h() can be tested independently of one another. | |
# with def store(model); model.to_h; end | |
# an actual model needs to be "let"ed within the context of the store when testing. | |
# The integration of the two is probably a level up in the abstraction and can be stubbed accordingly. |
As for comment on line 16, with doubles, they can still be tested entirely independently.
True on the line 3 breaking. That's something I added. ;-) An Idea i've been playing with. 6 in one hand and half a dozen in the other. However it does remove the need to pass in a double with the #to_hash method defined when testing. As one can just use the hash they are stubbing. I'm not a fan of CHANGING code just to make the test work (unless it's untestable). Although it does remove one less thing for #store
to know about. Nice input Tom. That was more of a passive questioning than a concrete methodology.
I was thinking this. Say you had a toy you wanted a child to play with A toy knife. And we add a feature that childproofs the toy before giving it to the child. Meaning now a toy can have two representations.
usually we’d have:
# what if we want to childproof that toy
# we’d have to mutate the toy first (which I don’t like)
child.play_with(toy) # calls #to_hash
toy.childproof! # This is a mutation and now we have to test #childproof! via #to_h.
# which puts responsibility on #to_h
# or even worse return a boolean.
child.play_with(toy) # then calls to_hash vs.
child.play_with(toy.to_h) # unsafe toy
child.play_with(toy.childproof_to_h) #which needs no "bang" mutator and can be tested directly independently.
# The child has no concern if the toy is safe or unsafe.
# It just wants a representation of the toy to play with.
All you need to do is pass in a double that expects #to_h to be called, and returns another double "model_hash"
What does persist do? I assume it calls DB code, which would be doubled-out too.
Here's the method I'm using:
class Store
def initialize config = { }
@db = (config[:db_klass] || Pg).new config # allows for double
end
def store(model)
persist model.to_h
end
private
def persist model_hash
@db.write model_hash
end
end
Human.play child, toy
Also, I fucking hate the idea of multiple representations, other than encoding
Better to have a a Toy and a ToyChildProof
Or an attribute, child_proofed (boolean)
def Toybox.return(toy)
if toy.child_proofed
return 'OK'
else
return 'NOT OK'
end
end
# I'd just return toy.status_message and throw the logic in the toy.
def Toybox.return(toy)
fail Exceptions::Forbidden unless toy.child_proofed
end
def Toybox.return(toy)
fail Exceptions::Forbidden unless toy.child_proofed
@contents << toy
end
def handle request
catch :response do
dispatch request
end
rescue
Responses::ServerErrors::Internal.new
end
I do not believe line 3 breaks Demeter's law (from Wikipedia)
• Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
• Each unit should only talk to its friends; don't talk to strangers.
• Only talk to your immediate friends.
I believe an entity representation qualifies as a friend to a storage engine.