piotr szotkowski about "ruby smells"

59
Ruby Smells Pivorak Edition

Upload: pivorak-meetup

Post on 11-Apr-2017

176 views

Category:

Software


1 download

TRANSCRIPT

Page 1: Piotr Szotkowski about "Ruby smells"

Ruby Smells

 Pivorak Edition

Page 2: Piotr Szotkowski about "Ruby smells"
Page 3: Piotr Szotkowski about "Ruby smells"
Page 4: Piotr Szotkowski about "Ruby smells"

code example caveatsinstance state denoted via @instance_variables

assume those instance variables areset in constructors | dependency injected

(constructors skipped for simplicity)

(yes, I would use getters in actual code)

(yes, some examples are very badly refactored)

Page 5: Piotr Szotkowski about "Ruby smells"

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

Page 6: Piotr Szotkowski about "Ruby smells"

duplicate method callthe same call made more than once

refactoring: factor out a single call

caveat: readability

Page 7: Piotr Szotkowski about "Ruby smells"

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

Page 8: Piotr Szotkowski about "Ruby smells"

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

Page 9: Piotr Szotkowski about "Ruby smells"

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

Page 10: Piotr Szotkowski about "Ruby smells"

data clumpthe same set of variables passed in many contexts

refactoring: create a composite (value?) object

caveat: make sure the clumped variables are related

Page 11: Piotr Szotkowski about "Ruby smells"

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

Page 12: Piotr Szotkowski about "Ruby smells"

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

Page 13: Piotr Szotkowski about "Ruby smells"

repeated conditionalthe same condition is checked in many places

refactoring: factor out LocatedMeetup

refactoring: make NavSource work with NullLocation

caveat: sometimes readability trumps purer design

Page 14: Piotr Szotkowski about "Ruby smells"

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

Page 15: Piotr Szotkowski about "Ruby smells"

nil checkusually 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

Page 16: Piotr Szotkowski about "Ruby smells"

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

Page 18: Piotr Szotkowski about "Ruby smells"

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

Page 19: Piotr Szotkowski about "Ruby smells"

boolean parameterthe caller directly controls calee’s code execution

refactoring: split into dedicated methods (classes?)

caveat: consider caller usability

Page 20: Piotr Szotkowski about "Ruby smells"

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

Page 21: Piotr Szotkowski about "Ruby smells"

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

Page 22: Piotr Szotkowski about "Ruby smells"

control parameterthe caller knows which path the code will take

refactoring: split into dedicated methods

caveat: consider caller usability (more likely)

Page 23: Piotr Szotkowski about "Ruby smells"

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

Page 24: Piotr Szotkowski about "Ruby smells"

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

Page 25: Piotr Szotkowski about "Ruby smells"

utility functiona 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

Page 26: Piotr Szotkowski about "Ruby smells"

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

Page 27: Piotr Szotkowski about "Ruby smells"

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

Page 28: Piotr Szotkowski about "Ruby smells"

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

Page 29: Piotr Szotkowski about "Ruby smells"

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

Page 30: Piotr Szotkowski about "Ruby smells"

feature envypartially refers to instance, but mostly to arguments

refactoring: move code to one of the arguments

caveat: make sure the knowledge is local to theproblem

Page 31: Piotr Szotkowski about "Ruby smells"

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

Page 32: Piotr Szotkowski about "Ruby smells"

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

Page 33: Piotr Szotkowski about "Ruby smells"

attributesetters: bad

getters: it depends

refactoring: tell, don’t ask

refactoring: immutability via #update (or #with)

caveat: an overkill in simplest (riiight…) projects

Page 34: Piotr Szotkowski about "Ruby smells"

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

Page 36: Piotr Szotkowski about "Ruby smells"

real world examples

(433 lines)

(759 lines)

(1595 lines)

Matrix::EigenvalueDecomposition#hessenberg_to_real_schur

TkItemConfigMethod#__itemconfiginfo_core

RDoc::Markdown#_Code

Page 41: Piotr Szotkowski about "Ruby smells"

so… how can we find codesmells in our Ruby apps?

by hand

using Reek

Page 42: Piotr Szotkowski about "Ruby smells"

very opinionated

static code analyser

for finding smells

RuboCop for architecture

Page 43: Piotr Szotkowski about "Ruby smells"

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 t i m e s [ 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 n o t h e r c l a s s ? ) [ 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

Page 47: Piotr Szotkowski about "Ruby smells"

configuration: .reekD 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/

Page 49: Piotr Szotkowski about "Ruby smells"

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

Page 50: Piotr Szotkowski about "Ruby smells"

per­directory configuration

Page 51: Piotr Szotkowski about "Ruby smells"

caveat emptormaking Reek happy with my codetaught me the most about OOP

in the last year

but

always be critical of such tools

Page 52: Piotr Szotkowski about "Ruby smells"

Piotr Solnica, Clean Code Cowboy

Page 53: Piotr Szotkowski about "Ruby smells"

Piotr Solnica, Clean Code Cowboy

Page 56: Piotr Szotkowski about "Ruby smells"

world domination planstry out 

maybe add it to  ?

don’t be overwhelmed by the potential smellsreek --todo might be a good start!

please let us know what doesn’t work

have fun withrefactoring | enlightened whitelisting

Reek

Code Climate

Page 58: Piotr Szotkowski about "Ruby smells"
Page 59: Piotr Szotkowski about "Ruby smells"

thanks!Piotr Szotkowski

@chastell

talks.chastell.net