How to rate a
               Rails application

           Elise Huard @elise_huard
           http://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
                       end
                     end

                     resources :nodes, :only => [:show,:new,:create,:destroy] do
                       put :directions, :on => :collection
                     end

                     resources :walkers, :only => [:show,:new,:create,:destroy] do
                       get :select, :on => :collection
                     end
                     resources :itineraries, :only => [:show]

Sunday 30 May 2010     map.four_oh_four '*path' , :controller => 'four_oh_fours'
models
                railroad -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 tree
            RubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’)

           class Metrics                  s(:class,:Metrics, nil,
             def probe                      s(:scope,
                                               s(:defn,:probe,
               puts "good"                       s(:args),
             end                                 s(:scope,
           end                                     s(:block,
                                                      s(:call, nil, :puts,
                                                        s(:arglist,
                                                          s(:str, "good"))))))))


                                          Symbolic Expression (Sexp)




Sunday 30 May 2010
RubyParser and
                          Parsetree
                             Abstract syntax tree
            RubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’)

           class Metrics                   s(:class,:Metrics, nil,
             def probe                       s(:scope,
                                                s(:defn,:probe,
               puts "good"                        s(:args),
             end                                  s(:scope,
           end                                      s(:block,
                                                       s(:call, nil, :puts,
                                                         s(:arglist,
                                                           s(:str, "good"))))))))


                                           Symbolic Expression (Sexp)

                               Ruby2Ruby


Sunday 30 May 2010
RubyParser

                        pure ruby

                       Parsetree
                     ruby and inline c
                          < 1.9



Sunday 30 May 2010
rake stats
                 size
        Flog                          Flay
           code complexity             code similarities

     Roodi
                                              R_B_P
      Saikuro     Reek
       cyclomatic antipatterns                  Rails
       complexity
     Heckle                          Rcov

                     test coverage

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


                        Methods
                     Very good: < 20
                     All Right: < 50




Sunday 30 May 2010
FLAY
                     code similarities




Sunday 30 May 2010
FLAY
                     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


Sunday 30 May 2010
FLAY
          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 = similar
          if same node = identical




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 calculate
   if, 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


                     RubyParser
                     visitor pattern
                      visitor: checker (Configuration)
                      visitable: parsed nodes
                     = extensible




Sunday 30 May 2010
Reek




Sunday 30 May 2010
Reek

                     more OO-specific checks
                     Control Couple
                     Data Clump
                     Feature Envy
                     Large Class
                     Long Method
                     Long Parameter List
                     Simulated Polymorphism
                     Uncommunicative 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



                              RubyParser
                         extends parsed nodes
                            traverses nodes
                     returns code after Ruby2Ruby




Sunday 30 May 2010
rails_best_practices




                                   kamoda on Flickr

Sunday 30 May 2010
rails_best_practices
  ./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




Sunday 30 May 2010
rails_best_practices


                           Visitor pattern
                     visitor : checking_visitor
                      visitable: 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, svn
       Locates 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 test
               keeps track of the executed lines
                Using 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 + Ruby2Ruby
                                mutate

                     time-consuming: combinatorials
                     more 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
rake stats
                 size
        Flog                          Flay
           code complexity             code similarities

     Roodi
      Saikuro     Reek
       cyclomatic antipatterns               RAILS_BEST
       complexity                            _PRACTICES

     Heckle                          Rcov

                     test coverage

Sunday 30 May 2010
metric_fu




Sunday 30 May 2010
check out the good stuff




                                           OrbitalJoe on flickr
Sunday 30 May 2010
what these metrics don’t
                             tell you



                              Bugs




Sunday 30 May 2010
what these metrics don’t
                             tell you



                         code performance
                          Ruby 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.html
   http://goruco2008.confreaks.com/04_davis.html
   http://cwd.dhemery.com/2009/11/wmaat/
   http://c2.com/cgi/wiki?AbcMetric
   http://hissa.nist.gov/HHRFdata/Artifacts/ITLdoc/235/title.htm
   http://blog.rubybestpractices.com/posts/judofyr/sexp-for-
   rubyists.html


   Elise Huard @elise_huard
   elise@elisehuard.be
   http://jabberwocky.eu
   http://github.com/elisehuard

Sunday 30 May 2010

how to rate a Rails application

  • 1.
    How to ratea Rails application Elise Huard @elise_huard http://jabberwocky.eu Sunday 30 May 2010
  • 2.
  • 3.
  • 4.
    Where to Start? Sunday 30 May 2010
  • 5.
    Try the app Björn Söderqvist on Flickr Sunday 30 May 2010
  • 6.
  • 7.
    plugins and gems Photo Lex on Flickr Sunday 30 May 2010
  • 8.
  • 9.
  • 10.
  • 11.
    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 end end resources :nodes, :only => [:show,:new,:create,:destroy] do put :directions, :on => :collection end resources :walkers, :only => [:show,:new,:create,:destroy] do get :select, :on => :collection end resources :itineraries, :only => [:show] Sunday 30 May 2010 map.four_oh_four '*path' , :controller => 'four_oh_fours'
  • 12.
    models railroad -M | dot Tpng > models.png rubymine ctrl-alt-D Sunday 30 May 2010
  • 13.
    names “There are only two hard things in Computer Science: cache invalidation and naming things” Phil Karlton Sunday 30 May 2010
  • 14.
    Metrics: Know thine Tools docman on flickr Sunday 30 May 2010
  • 15.
    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
  • 16.
    RubyParser and Parsetree Ryan Davis and Eric Hodel (‘Ruby Sadists’) Sunday 30 May 2010
  • 17.
    RubyParser and Parsetree Abstract syntax tree RubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’) class Metrics s(:class,:Metrics, nil, def probe s(:scope, s(:defn,:probe, puts "good" s(:args), end s(:scope, end s(:block, s(:call, nil, :puts, s(:arglist, s(:str, "good")))))))) Symbolic Expression (Sexp) Sunday 30 May 2010
  • 18.
    RubyParser and Parsetree Abstract syntax tree RubyParser.new.parse(File.read(‘metrics.rb’),‘metric.rb’) class Metrics s(:class,:Metrics, nil, def probe s(:scope, s(:defn,:probe, puts "good" s(:args), end s(:scope, end s(:block, s(:call, nil, :puts, s(:arglist, s(:str, "good")))))))) Symbolic Expression (Sexp) Ruby2Ruby Sunday 30 May 2010
  • 19.
    RubyParser pure ruby Parsetree ruby and inline c < 1.9 Sunday 30 May 2010
  • 20.
    rake stats size Flog Flay code complexity code similarities Roodi R_B_P Saikuro Reek cyclomatic antipatterns Rails complexity Heckle Rcov test coverage Sunday 30 May 2010
  • 21.
    flog ‘the pain your code is in’ Sunday 30 May 2010
  • 22.
    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
  • 23.
    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
  • 24.
    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
  • 25.
    FLOG Methods Very good: < 20 All Right: < 50 Sunday 30 May 2010
  • 26.
    FLAY code similarities Sunday 30 May 2010
  • 27.
    FLAY 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 Sunday 30 May 2010
  • 28.
    FLAY 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 = similar if same node = identical Sunday 30 May 2010
  • 29.
    Saikuro cyclomatic complexity Sunday 30 May 2010
  • 30.
  • 31.
    Saikuro ruby-lex every keyword is interpreted into ‘state’ state used to calculate if, unless, while, until, for, elsif, when, rescue (blocks) Recursively Sunday 30 May 2010
  • 32.
    Saikuro Good: methods < 5 Sunday 30 May 2010
  • 33.
    Roodi ‘Ruby Object Oriented Design Inferometer’ nutmeg66 on flickr Sunday 30 May 2010
  • 34.
    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
  • 35.
    Roodi RubyParser visitor pattern visitor: checker (Configuration) visitable: parsed nodes = extensible Sunday 30 May 2010
  • 36.
  • 37.
    Reek more OO-specific checks Control Couple Data Clump Feature Envy Large Class Long Method Long Parameter List Simulated Polymorphism Uncommunicative Name Sunday 30 May 2010
  • 38.
    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
  • 39.
    Reek RubyParser extends parsed nodes traverses nodes returns code after Ruby2Ruby Sunday 30 May 2010
  • 40.
    rails_best_practices kamoda on Flickr Sunday 30 May 2010
  • 41.
    rails_best_practices ./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 Sunday 30 May 2010
  • 42.
    rails_best_practices Visitor pattern visitor : checking_visitor visitable: visitable sexp Sunday 30 May 2010
  • 43.
  • 44.
    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
  • 45.
    Churn Not only classes but also methods (RubyParser) Version control: git, Hg, svn Locates changes in source using logs (as in git log) Sunday 30 May 2010
  • 46.
    Churn common sense ... mostly useful in maintenance phase Sunday 30 May 2010
  • 47.
  • 48.
    Rcov Total coverage: comments included Sunday 30 May 2010
  • 49.
    Rcov Executes test keeps track of the executed lines Using C extension when possible to hook into MRI (experimental for 1.9) Sunday 30 May 2010
  • 50.
    Rcov good: 100% coverage Sunday 30 May 2010
  • 51.
  • 52.
    Heckle ParseTree + Ruby2Ruby mutate time-consuming: combinatorials more for small programs (gems, scripts) doesn’t work for ruby 1.9 (ParseTree) Sunday 30 May 2010
  • 53.
    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
  • 54.
    rake stats size Flog Flay code complexity code similarities Roodi Saikuro Reek cyclomatic antipatterns RAILS_BEST complexity _PRACTICES Heckle Rcov test coverage Sunday 30 May 2010
  • 55.
  • 56.
    check out thegood stuff OrbitalJoe on flickr Sunday 30 May 2010
  • 57.
    what these metricsdon’t tell you Bugs Sunday 30 May 2010
  • 58.
    what these metricsdon’t tell you code performance Ruby Profiling Sunday 30 May 2010
  • 59.
    Reads like abook Sunday 30 May 2010
  • 60.
    http://railroad.rubyforge.org/ http://www.igvita.com/2008/12/11/ruby-ast-for-fun-and-profit/ http://ruby.sadi.st/Ruby_Sadist.html http://goruco2008.confreaks.com/04_davis.html http://cwd.dhemery.com/2009/11/wmaat/ http://c2.com/cgi/wiki?AbcMetric http://hissa.nist.gov/HHRFdata/Artifacts/ITLdoc/235/title.htm http://blog.rubybestpractices.com/posts/judofyr/sexp-for- rubyists.html Elise Huard @elise_huard elise@elisehuard.be http://jabberwocky.eu http://github.com/elisehuard Sunday 30 May 2010