how to rate a rails application
DESCRIPTION
Talk given at Euruko 2010: the talk describes how to quickly evaluate the quality of a Rails codebase. Ruby metrics are explained in detail.TRANSCRIPT
How to rate a Rails application
Elise Huard @elise_huardhttp://jabberwocky.eu
Sunday 30 May 2010
Acquisition
Sunday 30 May 2010
Maintenance
Sunday 30 May 2010
Where to Start ?
Sunday 30 May 2010
Try the app
Björn Söderqvist on Flickr
Sunday 30 May 2010
Rails version
Sunday 30 May 2010
plugins and gems
Photo Lex on Flickr
Sunday 30 May 2010
Sunday 30 May 2010
Run Tests
Sunday 30 May 2010
Lots of code
Sunday 30 May 2010
config/routes.rb
match ‘/’, :to => ‘root#index’ match ‘root’, :to => ‘root#index’
namespace :admin do resources :grids do resources :nodes resources :edges do collection do post :all post :update_all end end resources :walkers endend resources :nodes, :only => [:show,:new,:create,:destroy] do put :directions, :on => :collectionend
resources :walkers, :only => [:show,:new,:create,:destroy] do get :select, :on => :collectionendresources :itineraries, :only => [:show]
map.four_oh_four '*path' , :controller => 'four_oh_fours'Sunday 30 May 2010
modelsrailroad -M | dot Tpng > models.png
rubymine ctrl-alt-D
Sunday 30 May 2010
names
“There are only two hard things in Computer Science: cache invalidation and naming things”
Phil Karlton
Sunday 30 May 2010
Metrics:Know thine Tools
docman on flickr
Sunday 30 May 2010
LOC
rake stats+----------------------+-------+-------+---------+---------+-----+-------+| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |+----------------------+-------+-------+---------+---------+-----+-------+| Controllers | 2702 | 2150 | 36 | 158 | 4 | 11 || Helpers | 358 | 303 | 0 | 22 | 0 | 11 || Models | 1358 | 1104 | 30 | 117 | 3 | 7 || Libraries | 2286 | 1655 | 38 | 152 | 4 | 8 || Integration tests | 0 | 0 | 0 | 0 | 0 | 0 || Functional tests | 1687 | 1322 | 31 | 195 | 6 | 4 || Unit tests | 1356 | 1079 | 27 | 158 | 5 | 4 |+----------------------+-------+-------+---------+---------+-----+-------+| Total | 9747 | 7613 | 162 | 802 | 4 | 7 |+----------------------+-------+-------+---------+---------+-----+-------+ Code LOC: 5212 Test LOC: 2401 Code to Test Ratio: 1:0.5
Sunday 30 May 2010
RubyParser and Parsetree
Ryan Davis and Eric Hodel(‘Ruby Sadists’)
Sunday 30 May 2010
RubyParser and Parsetree
Abstract syntax treeRubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’)
class Metrics def probe puts "good" end end
s(:class,:Metrics, nil, s(:scope, s(:defn,:probe, s(:args), s(:scope, s(:block, s(:call, nil, :puts, s(:arglist, s(:str, "good"))))))))
Symbolic Expression (Sexp)
Sunday 30 May 2010
RubyParser and Parsetree
Abstract syntax treeRubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’)
class Metrics def probe puts "good" end end
s(:class,:Metrics, nil, s(:scope, s(:defn,:probe, s(:args), s(:scope, s(:block, s(:call, nil, :puts, s(:arglist, s(:str, "good"))))))))
Symbolic Expression (Sexp)
Ruby2Ruby
Sunday 30 May 2010
RubyParser
Parsetree
pure ruby
ruby and inline c< 1.9
Sunday 30 May 2010
cyclomatic complexity
size
code complexity code similarities
test coverage
antipatterns
rake stats
Flay
ReekRoodi
Flog
Saikuro
Heckle Rcov
Rails
R_B_P
Sunday 30 May 2010
flog‘the pain your code
is in’
Sunday 30 May 2010
FLOG
flog lib/flog.rb 647.8: flog total 13.8: flog/method average
87.5: Flog#output_details 58.8: Flog#process_iter 54.2: Flog#flog 48.8: Flog#parse_options 34.1: Flog#none 23.2: Flog#output_method_details 22.1: Flog#score_method 16.0: Flog#process_block_pass 15.6: Flog#report 15.2: Flog#expand_dirs_to_files 15.0: Flog#klass_name
Sunday 30 May 2010
FLOG
Assignment Branch Condition (ABC) def score_method(tally) a, b, c = 0, 0, 0 tally.each do |cat, score| case cat when :assignment then a += score when :branch then b += score else c += score end end Math.sqrt(a*a + b*b + c*c) end
Sunday 30 May 2010
FLOG
Weighing the AST with factors def process_if(exp) add_to_score :branch process exp.shift # cond penalize_by 0.1 do process exp.shift # true process exp.shift # false end s() end
Sunday 30 May 2010
FLOG
MethodsVery good: < 20All Right: < 50
Sunday 30 May 2010
FLAYcode similarities
Sunday 30 May 2010
flay *.rb Total score (lower is better) = 621
1) IDENTICAL code found in :defn (mass*2 = 188) channel.rb:48 clip.rb:80
2) Similar code found in :defn (mass = 93) channel.rb:150 clip.rb:110 clip.rb:116
3) Similar code found in :defs (mass = 58) contact.rb:32 contact.rb:37
FLAY
Sunday 30 May 2010
RubyParser def mass @mass ||= self.structure.flatten.size end
Hash of structure of nodes with mass > threshold self.hashes[node.structural_hash] << node
analyze: if same hash = similarif same node = identical
FLAY
Sunday 30 May 2010
Saikuroサイクロ
cyclomatic complexity
Sunday 30 May 2010
Saikuro
Sunday 30 May 2010
Saikuro
ruby-lex
every keyword is interpreted into ‘state’
state used to calculateif, unless, while, until, for, elsif, when, rescue
(blocks)Recursively
Sunday 30 May 2010
Saikuro
Good:methods < 5
Sunday 30 May 2010
Roodi‘Ruby Object Oriented Design Inferometer’
nutmeg66 on flickr
Sunday 30 May 2010
Roodi
app/controllers/itineraries_controller.rb:4 - Method name "show" cyclomatic complexity is 14. It should be 8 or less.app/models/itinerary.rb:41 - Block cyclomatic complexity is 6. It should be 4 or less.app/controllers/itineraries_controller.rb:4 - Method "show" has 30 lines. It should have 20 or less.app/helpers/application_helper.rb:27 - Method "clippy" has 26 lines. It should have 20 or less.
Sunday 30 May 2010
Roodi
RubyParservisitor pattern visitor: checker (Configuration) visitable: parsed nodes= extensible
Sunday 30 May 2010
Reek
Sunday 30 May 2010
Reek
more OO-specific checksControl CoupleData ClumpFeature EnvyLarge ClassLong MethodLong Parameter ListSimulated PolymorphismUncommunicative Name
Sunday 30 May 2010
Reek
UserSessionsController has no descriptive comment (Irresponsible Module) UserSessionsController#destroy calls current_user_session twice (Duplication)app/controllers/users_controller.rb -- 5 warnings: UsersController has no descriptive comment (Irresponsible Module) UsersController tests @aid_app at least 4 times (Simulated Polymorphism) UsersController#create calls params 3 times (Duplication) UsersController#create calls params[:user] 3 times (Duplication)...
Sunday 30 May 2010
Reek
RubyParserextends parsed nodes
traverses nodesreturns code after Ruby2Ruby
Sunday 30 May 2010
rails_best_practices
kamoda on Flickr
Sunday 30 May 2010
./app/controllers/ws/vmg/aid_user_accounts_controller.rb:160 - move model logic into model (@aid_user_account called_count > 4)./app/controllers/ws/vmg/ipt_controller.rb:102 - move model logic into model (xml called_count > 4)./app/controllers/ws/vmg/ipt_controller.rb:102 - move model logic into model (pf called_count > 4)./config/routes.rb:3 - overuse route customizations (customize_count > 3)./config/routes.rb:35 - overuse route customizations (customize_count > 3)./app/models/vmg/scenario.rb:41 - keep finders on their own model
rails_best_practices
Sunday 30 May 2010
rails_best_practices
Visitor patternvisitor : checking_visitorvisitable: visitable sexp
Sunday 30 May 2010
Churn
Sunday 30 May 2010
Churn
+-------------------------------------------------+---------------+| file_path | times_changed |+-------------------------------------------------+---------------+| db/schema.rb | 26 || config/routes.rb | 24 || app/controllers/application_controller.rb | 22 || app/controllers/add_apps_controller.rb | 22 || config/environment.rb | 20 || app/views/layouts/application.html.erb | 20 || app/models/ability.rb | 18 |...
Sunday 30 May 2010
Churn
Not only classes but also methods(RubyParser)
Version control: git, Hg, svnLocates changes in source using logs
(as in git log)
Sunday 30 May 2010
Churn
common sense ... mostly useful in maintenance phase
Sunday 30 May 2010
Rcov
Sunday 30 May 2010
Rcov
Total coverage: comments included
Sunday 30 May 2010
Rcov
Executes testkeeps track of the executed linesUsing C extension when possible
to hook into MRI
(experimental for 1.9)
Sunday 30 May 2010
Rcov
good: 100% coverage
Sunday 30 May 2010
Heckle
Sunday 30 May 2010
Heckle
ParseTree + Ruby2Rubymutate
time-consuming: combinatorialsmore for small programs (gems,
scripts)doesn’t work for ruby 1.9
(ParseTree)
Sunday 30 May 2010
Heckle
Initial tests pass. Let's rumble.
************************************************************************* AidApp#property_names loaded with 4 possible mutations**********************************************************************
4 mutations remaining...Replacing AidApp#property_names with:
--- original+++ mutation def property_names- (meta_policy and meta_policy.property_names_for(:aid_app))+ (nil and meta_policy.property_names_for(:aid_app)) end
Sunday 30 May 2010
cyclomatic complexity
size
code complexity code similarities
test coverage
antipatterns
rake stats
Flay
ReekRoodi
Flog
Saikuro
Heckle Rcov
RAILS_BEST_PRACTICES
Sunday 30 May 2010
metric_fu
Sunday 30 May 2010
OrbitalJoe on flickr
check out the good stuff
Sunday 30 May 2010
what these metrics don’t tell you
Bugs
Sunday 30 May 2010
what these metrics don’t tell you
code performanceRuby Profiling
Sunday 30 May 2010
Reads like a book
Sunday 30 May 2010
http://railroad.rubyforge.org/http://www.igvita.com/2008/12/11/ruby-ast-for-fun-and-profit/http://ruby.sadi.st/Ruby_Sadist.htmlhttp://goruco2008.confreaks.com/04_davis.htmlhttp://cwd.dhemery.com/2009/11/wmaat/http://c2.com/cgi/wiki?AbcMetrichttp://hissa.nist.gov/HHRFdata/Artifacts/ITLdoc/235/title.htmhttp://blog.rubybestpractices.com/posts/judofyr/sexp-for-rubyists.html
Elise Huard @[email protected]://jabberwocky.euhttp://github.com/elisehuard
Sunday 30 May 2010