Last active
November 18, 2020 22:47
-
-
Save snuggs/f5ab108a8cf596fbf188 to your computer and use it in GitHub Desktop.
Law of Demeter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.