- This is part 2 of a multi-part series. Read part 1 here.
If you do not already know who Avdi Grimm is then please, go check out his Confident Ruby when you have time. It is an AMAZING read. If you know about Confident Ruby already then you get to see some examples here.
Essential this style of coding uses liberal casting to keep nils and conditionals out of your code. It is also a style of Ruby that adheres to the Tell, don’t ask principle. Because co-opting nils is evil, as is reaching into an object’s state in order to decide what it should do.
Let’s go back to our deck of cards. I put it into a repository for you this time so you can work along with me. Once that is forked, clone it down. You will find all the tests are there, just like I promised in part 1!
You can also compare that to the origin gist to see where I started from if you aren’t down to read part 1.
Step 1 - Change the Structs to an immutable object
Since we are going for a mostly functional approach here, I don’t want the option to mutate these cards. To get closer to accomplishing that, we need to replace the structs that
Deck is composed of,
Value objects from Tim Crayford’s library, Values
These are pretty much like structs, however they are immutable. I will point out here that there is a bug in the gem version that doesn’t seem to be in the github version. Because of that bug we will use the
Class version instead of my preferred ‘struct block to const’ method.
I made one more change here to be “Confident”. I cast rank to a string. That removes any chance of getting a nil value! I may get an empty string, but I wont get a nil. Then for style consistency I capitalize the first letter and lowercase the rest.
Now onto the Card struct where do similar work:
Can you spot what I did here to make my code more confident?
suit I get the value of
@suit OR ‘naked’ in the case when
nil, then again, for the sake of consistency in my formatting, I cast it to a symbol. We do something similar in
to_s where I cast rank to a string, and I do for suit what I did for
We might get some ugly results if passed ugly data. But what it WONT do is barf all over you. It is also as the great Jim Weirich would say, “Polite Ruby”.
The origin Deck itself requires no modification here for swapping out those Structs for Values. That is a benefit of good design and the Single Responsibility principle. We needed to change some objects under the hood, yet we didn’t have to change the object that uses them.
That is it for this post. I will be back very soon to explain why the approach I took to
Deck#all_ranks is functional, and how it is superior to an typical imperative approach.