Monday, October 10, 2016

Baruco 2013: A Rubyist in Clojure-land, by David Chelimsky

act Dave times he was that the first person to make
me actually think about testing
beyond just the idea test I remember the first time I ever saw
Dave I he had this idea
when we come to really come around one thing I like about rather go back there
I like about Ralph
was a protesting right nothing you you know it kinda got white
the folder with this whole the fourth eventually realize I should look in
what this folder for its protests what I A
I should write some and eventually passed the puck around is that run your
tests first
you think this testing thing is awesome I write my test I write code
and at the end I can attest
the because rights as a hot but they've had
these other thoughts and Dave showed his wonderful new syntax for for how can be
I can write contest and wasn't just about his work at some point
it wasn't just that it was different Dave understood
that that the tool itself which in that case was aspect the tool
change to thinking and
that that it wasn't that you couldn't have your thoughts changing go back and
knees test unit it was that
those that the tool helped it all to actually made at $50,000 to do the right
thing then
impact the wrong thing and to help your the way you're thinking for example you
might learn chef
chef Mike teach you about convergence doesn't stop you gonna
doing conventions and bashes just that bash never really that made that
interesting an easy
aspect made it near changed his thoughts turned into tool
a change all I thought about at testing is day
would now like to share a new tool and how it changes
North again and now we all have to learn
with him paper two hands together for Dave to linski
brewing beer Barcelona
a so anyway so
there is that there's a line in new stuff for me and and I'm
was originally was going to do this talk I was gonna show you a bunch for closure
but does the Ruby conference you haven't gotten to see too much Ruby
so really when I want to talk about instead is how
working in closer has impacted the way I
one approach Ruby going forward so Wow
that just jumped ahead like through the whole thing
right so I'm
I'm not gonna be talking about closure I'm not be talking really about
functional programming Ruby
there's plenty of great resources for that a the
the to that I put up here one is a talk the talks about how to implement
the the like immutability and
something like i order functions in in ruby and the other ones a book
done a similar topic on but when I am going to talk about
is simplicity in the closer community
simplicity is is a big deal and we talk a lot about simplicity
a in the Rebbe community but arm
by the definitions that will talk about in a minute you'll see it a little bit
it kinda leveled up a bit so a according to use do and and
and Aaron the at this is this was on the primary
two primary things in riches inspiration for closure
simplicity and power these into think the drive everything else
and i think is simple
if it's not compound and rich likes to use this word complected
and if you've never seen any talks by rich Hickey and him talking about it
the word complex means to interleaver to braid things together
and this is something that we don't want and we talk about this all the time
we've got the solid principles that
teaches how to keep things small and make it easy to move stuff around
up but
a they take it
I think to it to a different level so the other thing I wanna talk about which
is a big topic in the roomie community is readability because we all
probably fell in love with really because it's a readable but
readable read readability is is the subjective
thing so I'm gonna just read but this is for the English speakers
address that you can probably figure it out this is written in Portuguese
and it says that if you're bored you speaker
this is probably legible if you're if you speak at a line over
or Spanish then it's probably comprehensible
so I said that's what I said and but for English speakers not so much
right so this is perfectly readable
for some subset of the audience it's pretty readable for some other subset
and it's really not readable at all for another subset so it's very
its contextual depend depends on your own level of familiarity
a we all know it a plus sign
does ahead and this is
obviously the result we would expect but what about when we had these two things
together what
Ruby in this context somewhat closer what is what do they add up to
so readability is contextual a
and what I wanted talk about is a
how I think we can do better at striving for both these things at the same time
and I should I should give you a little bit a context here
a since I've been working on closure
actually left my job a few months ago and I just been traveling in taking some
time off
so I haven't except for some contributions to open source projects
like r-spec and rails
I haven't really done the things that i'm gonna show you
in anger on a full-time project this is just my personal opinion
you should take it and and experiment with it and see what you think
a you won't be wrong if you think I'm wrong but
okay so
let's look a little bit echoed here a
looks kinda confusing what's a fact it's the first of all a little background
this from r-spec this is part in the back trace cleaner
a what we tried to do you is sensibly filter out
lines have a back trees when you get a failure message so that we could figure
out if
filter out some other noise like lines that are in the r-spec code base for
aren't really gonna help you discover what went wrong with the code that you
wrote a
there there are patterns regular expressions for
exclusions and regular expressions for inclusions
and we're gonna figure out based on what those patterns are
whether to include or exclude one
so the first thing
that's a little confusing here is this
where we assigned to the inclusion including patterns instance variable
so first we we say hey if inclusion patterns is nil
and that inclusion patterns at that point is referencing the parameter at
if thats nil then let's do all this other stuff in a sign the result is that
inclusion patterns instance variable and if it's not nil
and many in the else clause then we say let's go into
and a sign that that not your thing so to me that's backwards and we should
reverse that
so that this a little bit cleaner this way we say okay inclusion patterns or
brightness is a little more idiomatic Ruby
the next thing I noticed is they were handling Mills
coming in in two different ways a
it's perfectly legit 2 submit an ill for either argument or just
call this with an argument at all but for inclusion patterns
we default to know and then handled the new case
inside the initializer whereas
for the pollution patterns were sitting in the fall in the
in the parameter list so I'd rather do that the same way
just it just makes it a little bit easier to sort reading figure out what's
going on
so now this is super nit picky in this has nothing to do with nitrate enclosure
but when you have two arguments like this
you know the code small long argument last record we have like 45 arguments
for parameters
you wanna wrap up in in at object but
this is just a pair and they follow each other around everywhere
and it's a little pet peeve in mind when they show up in different
order I want and always been the same order because it's really easy to make
typos when they're
not so we can see that they're coming in
inclusion and exclusion but then I getting assigned in the other or
so let's reverse that except
some tests failed at this point I was actually doing this refactoring by the
way and running the test between every step so
and the problem is that there is a dependency
a said and ordering dependency we have to assign a value to exclusion patterns
before we can assign a value to inclusion patterns
a and it was a little bit hard for me to see because you can see here
a this is matt is an exclusion pattern which refers to this
method down here that's a method whereas here the syntax highlighter showing me a
hear these things that are related to each other but I didn't even really see
that we were doing anything else with exclusion parents so in line at
and that is much easier to see all the relationships between everything in
and since I can't switch the order up the assignment
and they do it the other way and switch the order love the parameters
it turned out that there is only one consumer this in the code base in this
completely harmless change to make a okay so the next thing is
exclude so the
did everybody know with the slap principle is the single
level obstruction principal as anybody not know what that is
okay so the idea is that code a little bit easier to read and understand
if everything inside the same method is operating at the same level of
seen an episode mentally jump down into the
the lower part to the language and then back up to some high-level abstraction
so this violates that plus we've only got one
invocation now this method so I'm just gonna in line the match as an exclusion
pattern method
and then the very last little bit is that inside that method
where handling the the the exclusions
with the inclusions first and then exclusions are gonna turn those around
and now everything is nice and symmetric a we've got less
coded it's simple a simple in the
not complected sense and it's very readable
a it well it's readable again this is subjective rate
for me since I've been doing a lot with iterators
a to me this is very readable for for people who aren't comfortable with the
then it might be less readable
so now there's still a tiny bit of duplication here and this is actually
how we got here
a somebody saw this tiny beautiful duplication
and said oh my god there's this tiny bit of duplication better extract a method
so that we don't
you know violate the dry principal a
two years ago I did its part a
about the misunderstanding of the drive principal essentially
and so for those of you who haven't seen that talk
if you're interested in my opinions about all that please look it up it was
at the Rizal rubicam from there a couple of others as well
a the talk is called maintaining balance while reducing duplication
so well here's a question a
if we have an operator with two operands and that happens twice
is that duplication we can extract and admitted
course not rent a up
and in this case we got an object in which is calling in though the
lower case a were calling method on with the argument
and then we call it again with a different argument you're probably not
gonna wrap that method
in another method in order to reduce the duplication
love calling a method twice that absurd
so there's definitely a threshold that we can probably all agree on
and then did just there's a scale in the middle of where things start to get
uncomfortable for you
in this particular case arm so in the top lines
exclusion patterns any so we've got collection we got an iterator and we
handed a plot
and the duplication is that we do this twice with two blocks to basically do
the same thing
a but what value ru gonna get out extracting a method for this
a any exclusion patterns in the methods
at the bottom is no more readable then exclusion patterns down any day
all that's left really is what's in the block which could be represented by
a match method and seek an extract out a small method which in this case has to
has to be a Lambda because we're gonna hand it to an iterator
a and the this isn't a nice way to do it it gives us
a lot of flexibility so we can now use that match
function with any iterator we get some usability out a bit
were composing small bits to create more complicated things
a but still for me a you know if you take a look at these four possibilities
arm you know we could debate about readability but I'd argue that the first
you're definitely the simpler if all these
because I don't have to go look at another method somewhere to understand
if so
what went wrong somewhere a
and I actually prefer the first one but that
admittedly completely subjective so
a in the end what we have in my opinion its intention revealing even in the
a it's a metric it it honors
the slap principal a we use Ruby
for the domain of computation like we're using just Ruby not our own custom
for the stuff that's rudeee like iterate through through a collection
I am we're not delegating to a method that doesn't really have that much
a so in summary this far a
we want to balance reliability and simplicity a
readability subjective simplicity is objective if something is not readable
really just ask yourself is it is it not readable because my own level
love familiarity or is is
you know should i maybe understand what this is because I'm I'm trying to
improve myself as a redevelopment
a and start with Ruby and then extract methods if they're gonna break up long
there's the long method could smell if they're going to reduce duplication
and thats a lotta context around that too
if they're gonna enhance a domain a
but in this case we weren't getting any any additional
enhancement demand but if you are going to check something out if you could
reduce the scope because then you get something that's more reusable
arm 7 talking for a little while
so I think now's a good time to just take a little paws this is Cory's
cat sack
and alright I feel refreshed
about the recipe /a right
so the last thing I want to talk about his data pipelines so
in functional languages a rather than having objects that whole behavior and
and data that it needs to implement the the behavior
a what we see is data structures
immutable data structures that we
push through functions to either get information out of them or transform
them in some way
and love it when we were when we transform them and use this from a lot
let's be straight about that we're not actually transforming those data
structures were creating new data structures based on those that
a that seem like a transformation alright so
a an example above
a data transformation in Ruby a very common one in Rails
this is an active support there's this duration class and you can build up
duration: by saying beautiful things like one day plus
one year and in this example you can even add the same kind of thing so I got
one day and then at the end there's two more days
and then you can call inspect on the result is that it gives you this nice
sentence one year two months and three days
internally inside the duration I
object that storied as this array of arrays that you see on the bottom
up and so we need is a function
that's going to get us from what we see on the bottom to
that my sentence so enclosure
you might do it in in this way a
so I've got a bunch a custom functions in here
and if his arm arm the keynote this morning you saw
a stuff about Jah
referential transparency in being able to replace functions with with their
symbols on
gonna blow those out you can actually see what's going on in them
and basically I'm not going to go into line by line detail here I just want to
get the overall concept
this operator here an operator at
function and really it's a macro a it takes the output in the first thing and
makes it the input to the second thing
and then the output to the second thing is the input to third thing just like
your pipeline commands on a unit ma'am
a and so basically what this does
is it converts the array first into
a series a little hash maps with one can one value then it does a
transformational and 2 1/2 map
merging the 32 days and Mondays in the three days
I Dennett it's sorted by the order I love the units of measure
so years ago before months a and then
a in sorting it the sort by function as it does in Ruby if it's given a hash
it breaks it back out into a vector adapters enclosure
una river is in Ruby then we transform the keys
the keywords lightyears becomes year and that's like the only little bit have
sort a transformation that we do so
we sing your eyes years into year a and
and turn that into slightly bigger sentences and put it all in one big
so this is how this is done in Active Record
released it wasn't a lie refactored and submitted it now
it now it does it not activated active sport now does it a different way
a although it's Justin the master branch
and so it's not in any release that you haven't yet working on it
so I am there's a few things going on here
a the the reason I found this code is because there's a particular anti
that I was looking for and this was an example of it so
you see how me see if I yet no that's not it
arm you see how a we map
over the array love
keogh symbols and then on the last line of the
the lambda for the the block that past to
the the mat method we say
if and is 90 so there's some conditional there don't even worry about what it is
but there's a conditional
and the result is going to be if if that conditional
returns false it's gonna be nil 3 build up a collection of things that may or
may not include some mills
and then we get rid of the nails after using compact
know what we really want to be doing is filtering out the things that we're not
gonna try to
transform beforehand right because
right now we've we're doing the filtering and the
the the transformation inside the same to
inside the same code so
there's a couple of other problems we've got special cases for Nils like I just
described got a special case for 0 seconds
did for Manning led logic is actually duplicated because
if we if we get past if if the
the arrest or empty after we map and compact
then we replace the whole thing with this array with 20 seconds in it
and if we would ever decide to format
that second Colin 0
we have to change that in both those two places we have to do it on the last line
of the
block and right here in the
in the replacement parts up
this is the stuff that the dry principle is about
a we're doing the same thing in two different ways
really really really easy to miss right
so the dry principal says every piece of knowledge should have a single
authoritative representation
doesn't mean it should only have one at representation in the system but it
should only have one source representation so other representations
come from the one authoritative source so in this case
were violating the drivers so I don't have the time to go step by step through
the whole refactoring but I'll show you a couple of important points in it
a the first thing I did was I busted the whole thing out into a series of small
now a you'll notice there's two maps in a row
and you know this is something we what probably want to try to avoid
a for efficiency reasons did kinda negligible with small collections but
up but the nice thing about breaking it out into
several steps that each do one thing is that became very easy to see
and move things around where the problems were so for example
a this line here
can you read that now that I have I did it in the public not so anyway
that line were saying okay if the if the collection
if the units collection is empty let's put this string inside and that's where
the duplicated for Manning logic is right
so I was able to just cut pic cut that line
and pasted up just below the Select and change 0 seconds to just the
keyword seconds and then let this line that does the formatting do you format
same same solution but now we don't have that application and it was 11 smaller
I once it was up there I realized will head a
where pulling out 0 seconds because
a it's not 90 and then we're sticking at right-back 10
maybe there's a better way to do that and so in the end result
I that it looks like this so
and actually we don't even have the temporary hash anymore that was used to
just go get values from
now we take the parts collection I hear you after shave
certain go through step by step we take the parts collection which is in a river
is converted into hash
when Ruby were able to do this part in one step were converted into the
consolidated hash
then we sort by and it blows it back out into
an array of arrays in the right order and then we converted into the little
sentences and then
put that all together in one sense smallest here's a chance rations
so we get it's much smaller
there's no temp data structure there's no shadowing
I don't know if you noticed in the original version but there is a parts
instance variable
local variable rather that had nothing to do with the parts
method nearly go back and show your
so this part is internal data structure in the object this part is this whole
new things search shadowing that
I am no duplication the formatting logic and here was the killer
so notice that hear it uses China
to poll like in if we want one year instead one year's
a it was using singular eyes before
and the thing is singular as is great when you don't know what
you dealing with when you don't know what the data is when you don't know
what the strings are
in this case we have the mall right in front of us and we could see that a
single arise these all we need to do is
use child which is a built-in really method and it turned out
when I made that change the performance improved by six times
not like 6 percent like six hundred percent
performance improvement now this is this is one method that you may call it 20
times in your apt
but little things like that can make a difference
I alright so 0 true story
when I made this contribution in my mind improving the code quality
Avril's a I came up tied with Brian helm camp
as number 82 you have the all-time rails contributors
so I'm very proud of that
sir anyway a so in summary
you know if you don't know the the numeral minute methods learn them
a it it's great to compose little pipelines a small transformations to do
big transformations
a and one nice thing that I didn't talk about
Michael feathers the to talk a few years ago where he was talking about
this idea we should use these generators more and one other comment he made was
these appear in every language even job 8 hasn't
you could change iterators together like this so not yet but soon
I and you can do it in routine you could do it in all the functional languages so
this becomes an
not only like a common language within Ruby but
it transfers to all other lives as well so tonight we do
data transmissions I don't have any more cats to show you some just move on to
the next section
so someone you might know that I care a lot about testing
and I care a lot about
how test express the
the desired behavior application a
so in the project I was working on I was working with 10 mg fields
Jay fields used to be very well known within the Ruby community because he
did a lot of great stuff and blog a lot
and he's got a great blog and check it out is better known now in the closure
community because he's
pretty much using closer exclusively for three or four years at this point
and he continues to blog very insightful
a and so
J will I be called expectations which had written for Ruby years ago and it
but then he wrote a closure version and this is what
expectations looks like this is this is a test and expectations is expected
function and that function has to return a value for it to pass
and that's the whole syntax it's really quite nice
a the the idea is without s names
and with this kinda simplicity it puts a lot more pressure on the design
for two really good be readable and speak for itself now
the downside is and I experienced this quite a bit
is when things don't quite work out so simply you end up
see a lot of comments in all the test and we're not storing that it and we're
not using that in any way
so for me I'd still this completely reinforced my love
readable test names a and
this is how we do that in our spec and
what you see is what you end up getting out and then
if PPP printed out with the format how many have you
use our spec a lot how many
keep your hand up if you use this for matter okay so the restive you should
at least check it out sometime because for me this was this is the whole reason
to do all the stuff that we do inside
on the input side is to create this output that gives us this great little
summary everything that we're trying to say about the object
makes it easy to see what's missing
I makes it easy to see we've duplicated some tests
on as with all things there's a cost which is that
you know the tests are not programmatically the names are not
programmatically down to the test
so you know just like a comment it can get away from what's actually going on
that's your responsibility but so anyway
are moving on to a different side of testing
expectations and assertions so
a in our spec we've got we've had for a long time you know
person for name should equal John Doe a
and just a little back story in that light years ago and when they are spread
project started party the idea was to be able to show these two
technical people this was before non-technical people this is before
there is anything like cucumber
arm at least in the Ruby space and
this it just kinda stuck in people like that and so we kept on doing it
a we recently changed the
I shouldn't say we because I'm not maintaining anymore and I didn't do any
other work on expect so that the r-spec team
recently changed to this expects intact
I not because anybody thought that it was
a superior syntax to should a but because they were problems with monkey
patching should
and should not on every object and and it wasn't the kind a problem that most
people were concerned about
people so if you monkey patch what happens if I have a should method on my
object and
head I never really saw that in the whole time I worked on our spec in any
bug reports a but the problem is that there are some classes
that lewd after r-spec has already
said okay on that patch the stuff onto object
okay let me strip away from me everything that's not these four methods
a and its trip to a should and should not and so we ran into problems with
with those situations you got many test
a or which is just looks just like test unit in this case
assert equal expected value a
and then many tests back right person for name must equal
so in you coming up with all these things we spent a lot of time trying to
figure out how to express these things and and their these great discussions
which is the superior word should or must stand and why
each one is valuable must play a valid opinions about it we spent a lot of time
on it
we're also doing a buncha monkey patching on the things a
so there's a library called wrong that
take this in a completely different direction
and its insertion library and it's got one method
a ser takes a block that block
is pure Ruby it's got a return true or false
well it's got a return true for the for the test to pass
and if it returns false or no or if there's an air
a wrong goes in and it actually parses
the content of the block in order to figure out what failure message to give
you an it does a really good job
now that sorta parsing you know people look at that think that's terribly evil
but keep in mind that that only happens when there's a failure
so it never gets in the way our performance in the test run
unless there's a problem that you need to go look at
a in which case a few few milliseconds is not the thing you worried about at
that point
a there's another library that's doing a similar thing this is our spec
given by and now there's many test given also
I'm by Jim Wyrick it's got this given when
then syntax that we're familiar with cucumber
arm and it provides failure message similarly by parsing block content
but it uses another library to do it so these things are
kinda going in the right direction but
there some problems with it so in our spec given for example
when you have a whole bunch of different contexts where the thing that's given
you need a budget different examples it can get really bloated
on the input side in the in the file
arm r-spec with Ron a with the whole bunch examples
we could tighten it up red and it's very readable
but the problem is we're violating the one expectation for example
guideline a now I don't adhere to that
religiously but the the point if it is that
if the first assertion bales
we going with get it to pass well what happens then if the second one fails
and then we get that passes third one fails
it's like it's like this weird TDD where we never get to
green and it it it can be very confusing so that's the whole reason for that
that principal arm
so we could may be improved on that a little by putting like things together
and breaking it up so
here where handling the first names in the last names
or maybe we could handelman ills in one case and blanks gracefully
but none at this is is really getting that much better
and in this case to spread em out
one one in each example so the problem
is that we've complected two things and I take full responsibility for this
complex chin a
specification specific examples
I've had occurred should work and a high-level district description
what we want the coded so
a better way I think would be to decouple the descriptions from the
expectations now this doesn't exist
and this looks a lot like the slide that I showed you before but
what's happening here is it now just returns another group examples instead o
an example and then example
gives you a nameless example accepted
its part love whatever it says right so
the the six examples in it handles Noel and blanks
gracefully really that's what we want to be able to see it handles
mills and whites gracefully to give me a bunch of examples that
so there's a very nice way to handle it and then the output could
do this so it would show you all the example online six failed
under the under the
cats first and last names i hack something together
the that work that does this so I know it can be done but
what I did is not production had
a alright so in summary
I still believe the test names and a lot about you
the tradeoff is they require thought maybe it's I think that's a
really good trade-off a expectation the S l's
aren't giving us a whole lotta value now
this doesn't include mocks
and Stubbs testable that's a completely different matter
and really you know this is just a little
tiny early-stage thought I
and I'm not sure that all look like when I start using Martin stubs in in that
framework but
a so that still need to be worked out and then
homework assignment for you because I'm on to other things somebody
please make it so you can decouple the descriptions from the examples
so in conclusion up
Ruby is a DSL
I we don't necessarily need more ideas
cells its already DSL for the general domain
a programming tasks right so when you've got
a something inside your method
that can be expressed perfectly well by Ruby start with Ruby
we can have readable and simple
we have to find some trade-off sometimes but I think it's a good thing to strive
for to get both do those things
and if you're writing Ruby
anyone to chew simplicity and choose Ruby
thank you
I so here
okay thanks so you mentioned about that Uruguay's
I mean that example about durations back method that
and you said that well actually replace
a single arise bike shop but well
that wouldn't work with internationalization actually I it's a
good point
there are no tests in in the code that
that prove that point and the
it is now broken in rails master so world
left to get that fixed and will have to take the penalty
unfortunately but thank you for bringing that up thanks
had I like to concept examples
so in your draft implementation is it
does it execute the before blocks
for every step oh yeah it works same way yet so what's what's your thought on
there seems like right I think which only excluded once
and then you keep it immutable to optimize
so should always be like that or is that a case
for not making that optimization well
I haven't seen that it sounds like a great idea
a so on the surface yes sounds like a great idea
and it should be able to be used in whatever this turns into
yeah things lemme began to get the date when I