Ruby Smells
 
Pivorak Edition
code example caveats
instance state denoted via @instance_variables
assume those instance variables are
set in constructors | dependency injected
(constructors skipped for simplicity)
(yes, I would use getters in actual code)
(yes, some examples are very badly refactored)
c l a s s M e e t u p
d e f w e a t h e r _ a s s e s s m e n t
i f @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y ) . s k i e s = = : c l e a r
' T o o s u n n y '
e l s i f @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y ) . t e m p > 2 0
' T o o h o t '
e l s e
' P e r f e c t f o r c o d i n g '
e n d
e n d
e n d
duplicate method call
the same call made more than once
refactoring: factor out a single call
caveat: readability
c l a s s M e e t u p
d e f w e a t h e r _ a s s e s s m e n t
w e a t h e r = @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y )
i f w e a t h e r . s k i e s = = : c l e a r
' T o o s u n n y '
e l s i f w e a t h e r . t e m p > 2 0
' T o o h o t '
e l s e
' P e r f e c t f o r c o d i n g '
e n d
e n d
e n d
m o d u l e A r t D e c o m p
c l a s s S e p s
d e f s e p a r a t e s ? ( r o w , c o l )
m a t r i x [ r o w ] a n d m a t r i x [ r o w ] [ c o l ] . n o n z e r o ?
e n d
e n d
e n d
m o d u l e A r t D e c o m p
c l a s s A r c h S i z e r
d e f m i n _ q u a r t e r s
c a s e
w h e n i . z e r o ? , o . z e r o ? t h e n 0
w h e n i < = 5 t h e n ( o / 2 . 0 ) . c e i l
e l s e [ ( i / 5 . 0 ) . c e i l , ( o / 2 . 0 ) . c e i l ] . m a x
e n d
e n d
e n d
e n d
c l a s s M e e t u p
d e f d i r e c t i o n s ( s o u r c e _ l a t , s o u r c e _ l o n )
@ n a v _ s o u r c e . d i r e c t i o n s ( f r o m : [ s o u r c e _ l a t , s o u r c e _ l o n ] , t o : [ @ l a t , @ l o n ] )
e n d
d e f d i s t a n c e ( s o u r c e _ l a t , s o u r c e _ l o n )
@ n a v _ s o u r c e . d i s t a n c e ( f r o m : [ s o u r c e _ l a t , s o u r c e _ l o n ] , t o : [ @ l a t , @ l o n ] )
e n d
d e f n e e d s _ p a s s p o r t ? ( s o u r c e _ l a t , s o u r c e _ l o n )
@ n a v _ s o u r c e . b o r d e r s ? ( f r o m : [ s o u r c e _ l a t , s o u r c e _ l o n ] , t o : [ @ l a t , @ l o n ] )
e n d
e n d
data clump
the same set of variables passed in many contexts
refactoring: create a composite (value?) object
caveat: make sure the clumped variables are related
L o c a t i o n = S t r u c t . n e w ( : l a t , : l o n )
c l a s s M e e t u p
d e f d i r e c t i o n s ( s o u r c e _ l o c a t i o n )
@ n a v _ s o u r c e . d i r e c t i o n s ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
d e f d i s t a n c e ( s o u r c e _ l o c a t i o n )
@ n a v _ s o u r c e . d i s t a n c e ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
d e f n e e d s _ p a s s p o r t ? ( s o u r c e _ l o c a t i o n )
@ n a v _ s o u r c e . b o r d e r s ? ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
e n d
c l a s s M e e t u p
d e f d i r e c t i o n s ( s o u r c e _ l o c a t i o n )
i f @ l o c a t i o n
@ n a v _ s o u r c e . d i r e c t i o n s ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
e n d
d e f d i s t a n c e ( s o u r c e _ l o c a t i o n )
i f @ l o c a t i o n
@ n a v _ s o u r c e . d i s t a n c e ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
e n d
d e f n e e d s _ p a s s p o r t ? ( s o u r c e _ l o c a t i o n )
i f @ l o c a t i o n
@ n a v _ s o u r c e . b o r d e r s ? ( f r o m : s o u r c e _ l o c a t i o n , t o : @ l o c a t i o n )
e n d
e n d
e n d
repeated conditional
the same condition is checked in many places
refactoring: factor out LocatedMeetup
refactoring: make NavSource work with NullLocation
caveat: sometimes readability trumps purer design
c l a s s M e e t u p
d e f i n i t i a l i z e ( n a m e = n i l )
@ n a m e = n a m e
e n d
d e f a c r o n y m ?
! ! @ n a m e . m a t c h ( /  A (  p { U p p e r } ) +  z / ) u n l e s s @ n a m e . n i l ?
e n d
e n d
nil check
usually means there’s a missing type in the system
refactoring: find the missing type (a null object?)
caveat: make sure you don’t sacrifice simplicity
c l a s s U n n a m e d M e e t u p
d e f a c r o n y m ?
f a l s e # o r i s i t ? : )
e n d
e n d
c l a s s M e e t u p
d e f s e l f . n e w ( n a m e = n i l )
n a m e ? s u p e r : U n n a m e d M e e t u p . n e w
e n d
d e f i n i t i a l i z e ( n a m e )
@ n a m e = n a m e
e n d
d e f a c r o n y m ?
! ! @ n a m e . m a t c h ( /  A (  p { U p p e r } ) +  z / )
e n d
e n d
c l a s s M e e t u p
d e f r u b y ? ( s t r i c t = t r u e )
i f s t r i c t
@ l a n g u a g e s = [ ' R u b y ' ]
e l s e
@ l a n g u a g e s . i n c l u d e ? ( ' R u b y ' )
e n d
e n d
e n d
boolean parameter
the caller directly controls calee’s code execution
refactoring: split into dedicated methods (classes?)
caveat: consider caller usability
c l a s s M e e t u p
d e f s u r e l y _ r u b y ?
@ l a n g u a g e s = [ ' R u b y ' ]
e n d
d e f p o s s i b l y _ r u b y ?
@ l a n g u a g e s . i n c l u d e ? ( ' R u b y ' )
e n d
e n d
c l a s s M e e t u p
d e f i n t e r e s t i n g ? ( a t t e n d e e )
c a s e a t t e n d e e
w h e n ' p y t h o n i s t a ' t h e n d y n a m i c ?
w h e n ' r u b y i s t ' t h e n l a i d _ b a c k ?
w h e n ' r u s t a c e a n ' t h e n s y s t e m ?
e n d
e n d
e n d
control parameter
the caller knows which path the code will take
refactoring: split into dedicated methods
caveat: consider caller usability (more likely)
c l a s s M e e t u p
d e f i n t e r e s t i n g _ t o _ p y t h o n i s t a s ?
d y n a m i c ?
e n d
d e f i n t e r e s t i n g _ t o _ r u b y i s t s ?
l a i d _ b a c k ?
e n d
d e f i n t e r e s t i n g _ t o _ r u s t a c e a n s ?
s y s t e m ?
e n d
e n d
c l a s s M e e t u p
d e f s u c c e s s f u l ? ( a t t e n d e e s )
e x c i t e d , i n d i f f e r e n t = a t t e n d e e s . p a r t i t i o n ( & : e x c i t e d ? )
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
e n d
utility function
a method that does not depend on instance state
can be moved anywhere in the system
refactoring: move it to where it belongs
caveat: sometimes it’s just a (private?) helper
c l a s s M e e t u p
d e f s u c c e s s f u l ?
e x c i t e d , i n d i f f e r e n t = @ a t t e n d e e s . p a r t i t i o n ( & : e x c i t e d ? )
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
e n d
c l a s s M e e t u p
d e f s u c c e s s f u l ?
@ a t t e n d e e s . e x c i t e d ?
e n d
e n d
c l a s s A t t e n d e e s < A r r a y
d e f e x c i t e d ?
e x c i t e d , i n d i f f e r e n t = p a r t i t i o n ( & : e x c i t e d ? )
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
e n d
r e q u i r e ' f o r w a r d a b l e '
c l a s s A t t e n d e e s
e x t e n d F o r w a r d a b l e
d e f i n i t i a l i z e ( c o l l e c t i o n )
@ c o l l e c t i o n = c o l l e c t i o n
e n d
d e f e x c i t e d ?
e x c i t e d , i n d i f f e r e n t = p a r t i t i o n ( & : e x c i t e d ? )
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
d e l e g a t e p a r t i t i o n : : @ c o l l e c t i o n
e n d
c l a s s M e e t u p
d e f s u c c e s s f u l ? ( a t t e n d e e s )
e x c i t e d , i n d i f f e r e n t = a t t e n d e e s . p a r t i t i o n d o | a t t e n d e e |
a t t e n d e e . e x c i t e d ? | | @ l a n g u a g e = = ' R u b y ' & & a t t e n d e e . r u b y i s t ?
e n d
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
e n d
feature envy
partially refers to instance, but mostly to arguments
refactoring: move code to one of the arguments
caveat: make sure the knowledge is local to the
problem
c l a s s M e e t u p
d e f s u c c e s s f u l ?
@ a t t e n d e e s . e x c i t e d ? ( l a n g u a g e : @ l a n g u a g e )
e n d
e n d
c l a s s A t t e n d e e s
d e f e x c i t e d ? ( l a n g u a g e : n i l )
e x c i t e d , i n d i f f e r e n t = p a r t i t i o n { | a t t e n d e e | a t t e n d e e . e x c i t e d ? ( l a n g u a g e : l a n g u a g e ) }
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
e n d
c l a s s A t t e n d e e
d e f e x c i t e d ? ( l a n g u a g e : n i l )
@ e x c i t e d | | @ r u b y i s t & & l a n g u a g e = = ' R u b y '
e n d
e n d
c l a s s M e e t u p
d e f i n i t i a l i z e ( c i t y : , n a m e : )
@ c i t y = c i t y
@ n a m e = n a m e
e n d
a t t r _ a c c e s s o r : c i t y , : n a m e
e n d
attribute
setters: bad
getters: it depends
refactoring: tell, don’t ask
refactoring: immutability via #update (or #with)
caveat: an overkill in simplest (riiight…) projects
c l a s s M e e t u p
d e f i n i t i a l i z e ( c i t y : , n a m e : )
@ c i t y = c i t y
@ n a m e = n a m e
e n d
a t t r _ r e a d e r : c i t y , : n a m e
d e f u p d a t e ( c i t y : @ c i t y , n a m e : @ n a m e )
s e l f . c l a s s . n e w ( c i t y : c i t y , n a m e : n a m e )
e n d
e n d
real world examples
(433 lines)
(759 lines)
(1595 lines)
Matrix::EigenvalueDecomposition#hessenberg_to_real_schur
TkItemConfigMethod#__itemconfiginfo_core
RDoc::Markdown#_Code
so… how can we find code
smells in our Ruby apps?
by hand
using Reek
very opinionated
static code analyser
for finding smells
RuboCop for architecture
Reek usage
$ g e m i n s t a l l r e e k
$ r e e k - - n o - w i k i - l i n k s m e e t u p . r b
m e e t u p . r b - - 1 0 w a r n i n g s :
[ 4 0 , 4 2 ] : D u p l i c a t e M e t h o d C a l l : M e e t u p # w e a t h e r _ a s s e s s m e n t c a l l s @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y
[ 2 1 , 2 7 , 3 3 ] : D a t a C l u m p : M e e t u p t a k e s p a r a m e t e r s [ s o u r c e _ l a t , s o u r c e _ l o n ] t o 3 m e t h o d s
[ 2 2 , 2 8 , 3 4 ] : R e p e a t e d C o n d i t i o n a l : M e e t u p t e s t s @ l a t & & @ l o n a t l e a s t 3 t i m e s
[ 5 7 ] : N i l C h e c k : M e e t u p # a c r o n y m ? p e r f o r m s a n i l - c h e c k
[ 6 ] : C o n t r o l P a r a m e t e r : M e e t u p # i n t e r e s t i n g ? i s c o n t r o l l e d b y a r g u m e n t a t t e n d e e
[ 1 3 ] : B o o l e a n P a r a m e t e r : M e e t u p # r u b y ? h a s b o o l e a n p a r a m e t e r ' s t r i c t '
[ 1 4 ] : C o n t r o l P a r a m e t e r : M e e t u p # r u b y ? i s c o n t r o l l e d b y a r g u m e n t s t r i c t
[ 5 1 , 5 1 ] : F e a t u r e E n v y : M e e t u p # s u c c e s s f u l ? r e f e r s t o a t t e n d e e m o r e t h a n s e l f ( m a y b e m o v e i t t o a
[ 2 ] : A t t r i b u t e : M e e t u p # c i t y i s a w r i t a b l e a t t r i b u t e
[ 1 ] : I r r e s p o n s i b l e M o d u l e : M e e t u p h a s n o d e s c r i p t i v e c o m m e n t
configuration: .reek
D a t a C l u m p :
m a x _ c o p i e s : 2
m i n _ c l u m p _ s i z e : 2
L o n g P a r a m e t e r L i s t :
m a x _ p a r a m s : 3
o v e r r i d e s :
i n i t i a l i z e :
m a x _ p a r a m s : 5
T o o M a n y I n s t a n c e V a r i a b l e s :
m a x _ i n s t a n c e _ v a r i a b l e s : 4
T o o M a n y S t a t e m e n t s :
e x c l u d e :
- i n i t i a l i z e
m a x _ s t a t e m e n t s : 5
excludes: from Class#method to /meth/
configuration: code comments
# : r e e k : U t i l i t y F u n c t i o n
d e f s u c c e s s f u l ? ( a t t e n d e e s )
e x c i t e d , i n d i f f e r e n t = a t t e n d e e s . p a r t i t i o n ( & : e x c i t e d ? )
e x c i t e d . c o u n t > i n d i f f e r e n t . c o u n t
e n d
# : r e e k : D u p l i c a t e M e t h o d C a l l : { m a x _ c a l l s : 2 }
d e f w e a t h e r _ a s s e s s m e n t
i f @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y ) . s k i e s = = : c l e a r
' T o o s u n n y '
e l s i f @ w e a t h e r _ s o u r c e . w e a t h e r _ a t ( @ c i t y ) . t e m p > 2 0
' T o o h o t '
e l s e
' P e r f e c t f o r c o d i n g '
e n d
e n d
per­directory configuration
caveat emptor
making Reek happy with my code
taught me the most about OOP
in the last year
but
always be critical of such tools
Piotr Solnica, Clean Code Cowboy
Piotr Solnica, Clean Code Cowboy
world domination plans
try out 
maybe add it to  ?
don’t be overwhelmed by the potential smells
reek--todo might be a good start!
please let us know what doesn’t work
have fun with
refactoring | enlightened whitelisting
Reek
Code Climate
thanks!
Piotr Szotkowski
@chastell
talks.chastell.net

Piotr Szotkowski about "Ruby smells"