静的コード解析ツールの MetricFu は何を見ているのか

Rubyの静的コード解析ツールに [MetricFu](https://github.com/metricfu/metric_fu) があります。

静的コード解析とは、ソースコードを静的に解析して、特定のパターンに合致している箇所や、違反している箇所などさまざまな視点から定量的に評価する解析手法の一つです。

実装時の早い段階からローカルやJenkins等のCIサーバで解析することにより、コードの可読性や複雑さ、コーディングスタイルの違反など、ある程度改善することが可能になり、ソフトウェアの品質向上に繋がります。

Railsプロダクトであれば、他に [RuboCop](https://github.com/bbatsov/rubocop)(Ruby向けのコーディングスタイルチェッカー) や [Brakeman](http://brakemanscanner.org/)(Rails向けのセキュリティスキャナ) と併せて使うのが一般的だと思います。

今回はこの MetricFu が指摘してくれる項目を紹介しようと思います。

## Flog
コードの複雑度をチェックします。1メソッド毎に特定の演算子や処理がどのくらい含まれているかをチェックし、合計値がより多いメソッドほど複雑であるという判定になります。複雑なメソッドは可読性や保守性に優れないので可能であればリファクタリングしましょう。

http://ruby.sadi.st/Flog.html

```ruby
class Test
  def blah
    a = eval "1+1"
    if a == 2 then
      puts "yay"
    end
  end
end
```

```ruby
class Test
  def blah         # 11.2 =
    a = eval "1+1" # 1.2 + 6.0 +
    if a == 2 then # 1.2 + 1.2 + 0.4 +
      puts "yay"   # 1.2
    end
  end
end
```

```
Test#blah: (11.2)
     6.0: eval
     1.2: branch
     1.2: ==
     1.2: puts
     1.2: assignment
     0.4: lit_fixnum
```

## Rails Best Practices
Railsのベストプラクティスに従っているかどうかをチェックします。
あくまで個人？のベストプラクティスらしいので、本当にベストプラクティスなのかを検討する必要が若干ありますが、大半は有用なものです。

http://rails-bestpractices.com/

### サンプルアプリケーションに対する指摘の例

適当なRailsアプリケーションに対してRails Best Practicesのチェックを行うと、以下のような結果になりました。上位をピックアップして詳細を説明しようと思います。

| ベストプラクティス | 件数 |
|:---|:---|
| [Replace instance variable with local variable](http://rails-bestpractices.com/posts/2010/07/24/replace-instance-variable-with-local-variable/) | 1330 |
| [Simplify render in controllers](http://rails-bestpractices.com/posts/2010/12/12/simplify-render-in-controllers/) | 105 |
| [Simplify render in views](http://rails-bestpractices.com/posts/2010/12/04/simplify-render-in-views/) | 101 |
| remove unused methods | 70 |
| [Move Model Logic into the Model](http://rails-bestpractices.com/posts/2010/07/21/move-model-logic-into-the-model/) | 49 |
| [remove trailing whitespace](http://rails-bestpractices.com/posts/2010/12/02/remove-trailing-whitespace/) | 41 |
| [Restrict auto-generated routes.](http://rails-bestpractices.com/posts/2011/08/19/restrict-auto-generated-routes/) | 22 |
| [Use multipart/alternative as content_type of email](http://rails-bestpractices.com/posts/2010/08/05/use-multipart-alternative-as-content_type-of-email/) | 13 |
| [Overuse route customizations](http://rails-bestpractices.com/posts/2010/07/22/overuse-route-customizations/) | 9 |
| [Remove empty helpers](http://rails-bestpractices.com/posts/2011/04/09/remove-empty-helpers/) | 8 |
| [Use Observer](http://rails-bestpractices.com/posts/2010/07/24/use-observer/) | 5 |
| [Use query attribute](http://rails-bestpractices.com/posts/2010/10/03/use-query-attribute/) | 4 |
| [Always add DB index](http://rails-bestpractices.com/posts/2010/07/24/always-add-db-index/) | 4 |
| [Move code into model](http://rails-bestpractices.com/posts/2010/07/24/move-code-into-model/) | 2 |
| [Don't rescue Exception, rescue StandardError](http://rails-bestpractices.com/posts/2012/11/01/don-t-rescue-exception-rescue-standarderror/) | 2 |
| [Replace Complex Creation with Factory Method](http://rails-bestpractices.com/posts/2010/07/21/replace-complex-creation-with-factory-method/) | 2 |
| [Use model association](http://rails-bestpractices.com/posts/2010/07/19/use-model-association/) | 2 |

### 指摘数1位 : Replace instance variable with local variable

Partial View でも、インスタンス変数（@variable）を使えますが、partial を含んでいる View との依存関係が強まり、他のところで使いまわしにくくなります。
インスタンス変数ではなくローカル変数として、render で呼び出す際に明示的に渡した方がコードの見渡しが良くなります。

* ダメな例

```ruby
class PostsController < ApplicationController
  def show
    @post = Post.find(params[:id])
  end
end

<%= render :partial => "sidebar" %>
```

* ベストプラクティス

```ruby
<%= render :partial => "sidebar", :locals => { :post => @post } %>
```

もしくは

```ruby
<%= render "sidebar", :post => @post %>
```

### 指摘数2位 : Simplify render in controllers
Controller内でのrender指定時にactionのみ指定する場合は、明示的にハッシュのキーを指定しなくても
よいので記述を簡潔にできるよっていう指摘。他にも省略可能。

* Before

```ruby
render :action => :edit
render :action => 'edit'
```

* After

```
render :edit
render 'edit'
```

#### 別コントローラから

* Before

```ruby
render :template => 'books/edit'
```

* After

```ruby
render 'books/edit'
```

#### ファイル指定の場合

* Before

```
render :file => '/path/to/rails/app/views/books/edit'
```

* After

```
render '/path/to/rails/app/views/books/edit'
```


### 指摘数3位 : Simplify render in views
partialのrenderを指定する際に、:partialの部分や、optionの:localsの部分は省略可能っていう指摘。

* Before

```ruby
<%= render :partial => 'sidebar' %>
<%= render :partial => 'shared/sidebar' %>
```

* After

```ruby
<%= render 'sidebar' %>
<%= render 'shared/sidebar' %>
```

### その他のプラクティス一覧

他にたくさんのベストプラクティスがあります。
時間があれば一通り目を通してみることをオススメします。

* [Move finder to named_scope](http://rails-bestpractices.com/posts/2010/07/14/move-finder-to-named_scope/)
* [Use scope access](http://rails-bestpractices.com/posts/2010/07/20/use-scope-access/)
* [Add model virtual attribute](http://rails-bestpractices.com/posts/2010/07/21/add-model-virtual-attribute/)
* [Use model callback](http://rails-bestpractices.com/posts/2010/07/21/use-model-callback/)
* [model.collection_model_ids (many-to-many)](http://rails-bestpractices.com/posts/2010/07/22/model-collection_model_ids-many-to-many/)
* [Needless deep nesting](http://rails-bestpractices.com/posts/2010/07/22/needless-deep-nesting/)
* [Nested Model Forms](http://rails-bestpractices.com/posts/2010/07/22/nested-model-forms/)
* [Not use default route if you use RESTful design](http://rails-bestpractices.com/posts/2010/07/22/not-use-default-route-if-you-use-restful-design/)
* [Keep Finders on Their Own Model](http://rails-bestpractices.com/posts/2010/07/23/keep-finders-on-their-own-model/)
* [Love named_scope](http://rails-bestpractices.com/posts/2010/07/23/love-named_scope/)
* [DRY Controller (and debate)](http://rails-bestpractices.com/posts/2010/07/24/dry-controller-and-debate/)
* [DRY Metaprogramming](http://rails-bestpractices.com/posts/2010/07/24/dry-metaprogramming/)
* [Extract into Module](http://rails-bestpractices.com/posts/2010/07/24/extract-into-module/)
* [Extract to composed class](http://rails-bestpractices.com/posts/2010/07/24/extract-to-composed-class/)
* [Isolating Seed Data](http://rails-bestpractices.com/posts/2010/07/24/isolating-seed-data/)
* [Move code into controller](http://rails-bestpractices.com/posts/2010/07/24/move-code-into-controller/)
* [Move code into helper](http://rails-bestpractices.com/posts/2010/07/24/move-code-into-helper/)
* [the Law of Demeter](http://rails-bestpractices.com/posts/2010/07/24/the-law-of-demeter/)
* [Use before_filter](http://rails-bestpractices.com/posts/2010/07/24/use-before_filter/)
* [Fix N+1 Queries](http://rails-bestpractices.com/posts/2010/07/25/fix-n-1-queries/)
* [Use caching !](http://rails-bestpractices.com/posts/2010/07/25/use-caching/)
* [Double-check your migrations](http://rails-bestpractices.com/posts/2010/07/26/double-check-your-migrations/)
* [Write ur own spec macros](http://rails-bestpractices.com/posts/2010/07/26/write-ur-own-spec-macros/)
* [Writing specs for 3rd party declaratives](http://rails-bestpractices.com/posts/2010/07/26/writing-specs-for-3rd-party-declaratives/)
* [Use 'Background' to consolidate common steps in a feature](http://rails-bestpractices.com/posts/2010/07/27/use-background-to-consolidate-common-steps-in-a-feature/)
* [Use nested step to improve readability of ur scenario](http://rails-bestpractices.com/posts/2010/07/27/use-nested-step-to-improve-readability-of-ur-scenario/)
* [DRY your database.yml](http://rails-bestpractices.com/posts/2010/07/28/dry-your-database-yml/)
* [Use pickle & ur choice factory for data setup in ur cucumber features (revised)](http://rails-bestpractices.com/posts/2010/07/28/use-pickle-ur-choice-factory-for-data-setup-in-ur-cucumber-features-revised/)
* [Use render :collection Rails 3 when possible](http://rails-bestpractices.com/posts/2010/07/29/use-render-collection-rails-3-when-possible/)
* [abuse content_for](http://rails-bestpractices.com/posts/2010/07/30/abuse-content_for/)
* [Use asset_host for production server](http://rails-bestpractices.com/posts/2010/07/31/use-asset_host-for-production-server/)
* [Use I18n.localize for date/time formating](http://rails-bestpractices.com/posts/2010/08/06/use-i18n-localize-for-date-time-formating/)
* [Use css sprite automatically](http://rails-bestpractices.com/posts/2010/08/15/use-css-sprite-automatically/)
* [to_s/to_s(:short)](http://rails-bestpractices.com/posts/2010/08/16/to_s-to_s-short/)
* [Use STI and polymorphic model for multiple uploads](http://rails-bestpractices.com/posts/2010/08/18/use-sti-and-polymorphic-model-for-multiple-uploads/)
* [Use say and say_with_time in migrations to make a useful migration log](http://rails-bestpractices.com/posts/2010/08/19/use-say-and-say_with_time-in-migrations-to-make-a-useful-migration-log/)
* [Fetch current user in models](http://rails-bestpractices.com/posts/2010/08/23/fetch-current-user-in-models/)
* [use OpenStruct when advance search](http://rails-bestpractices.com/posts/2010/08/25/use-openstruct-when-advance-search/)
* [Put scripts at the bottom](http://rails-bestpractices.com/posts/2010/08/30/put-scripts-at-the-bottom/)
* [DRY bundler in capistrano](http://rails-bestpractices.com/posts/2010/09/02/dry-bundler-in-capistrano/)
* [Use batched finder for large data query](http://rails-bestpractices.com/posts/2010/09/09/use-batched-finder-for-large-data-query/)
* [Check if external gem-dependent classes are defined](http://rails-bestpractices.com/posts/2010/09/13/check-if-external-gem-dependent-classes-are-defined/)
* [Prevent SQL Injections by using the ? in queries](http://rails-bestpractices.com/posts/2010/09/17/prevent-sql-injections-by-using-the-in-queries/)
* [Generate polymorphic url](http://rails-bestpractices.com/posts/2010/09/23/generate-polymorphic-url/)
* [Substituting before_filter :load_object](http://rails-bestpractices.com/posts/2010/10/12/substituting-before_filter-load_object/)
* [Select specific fields for performance](http://rails-bestpractices.com/posts/2010/10/19/select-specific-fields-for-performance/)
* [Use memoization](http://rails-bestpractices.com/posts/2010/11/22/use-memoization/)
* [comment your magic codes](http://rails-bestpractices.com/posts/2010/12/19/comment-your-magic-codes/)
* [defer expensive job](http://rails-bestpractices.com/posts/2011/01/12/defer-expensive-job/)
* [Annotate your models](http://rails-bestpractices.com/posts/2011/01/23/annotate-your-models/)
* [split route namespaces into different files](http://rails-bestpractices.com/posts/2011/05/04/split-route-namespaces-into-different-files/)
* [Keep code struture in models consistent](http://rails-bestpractices.com/posts/2011/05/31/keep-code-struture-in-models-consistent/)
* [Namespaced models](http://rails-bestpractices.com/posts/2011/05/31/namespaced-models/)
* [Create base conroller](http://rails-bestpractices.com/posts/2011/06/02/create-base-conroller/)
* [Active Record Query Interface Optimization](http://rails-bestpractices.com/posts/2011/06/23/active-record-query-interface-optimization/)
* [Remove tab](http://rails-bestpractices.com/posts/2011/07/04/remove-tab/)
* [Optimize db migration](http://rails-bestpractices.com/posts/2011/10/30/optimize-db-migration/)
* [Use cells to abstract view widgets](http://rails-bestpractices.com/posts/2011/11/06/use-cells-to-abstract-view-widgets/)
* [Name your model methods after their behavior, not implementation.](http://rails-bestpractices.com/posts/2011/12/05/name-your-model-methods-after-their-behavior-not-implementation/)
* [Not use time_ago_in_words](http://rails-bestpractices.com/posts/2012/02/10/not-use-time_ago_in_words/)
* [Protect mass assignment](http://rails-bestpractices.com/posts/2012/03/06/protect-mass-assignment/)
* [use after_commit](http://rails-bestpractices.com/posts/2012/05/02/use-after_commit/)
* [rolling out with feature flags](http://rails-bestpractices.com/posts/2012/09/02/rolling-out-with-feature-flags/)
* [split your cap tasks into different files](http://rails-bestpractices.com/posts/2012/09/10/split-your-cap-tasks-into-different-files/)
* [Tell, don't ask](http://rails-bestpractices.com/posts/2012/09/29/tell-don-t-ask/)
* [Check the return value of "save", otherwise use "save!"](http://rails-bestpractices.com/posts/2012/11/02/check-the-return-value-of-save-otherwise-use-save/)
* [speed up assets precompile with turbo-sprockets-rails3](http://rails-bestpractices.com/posts/2012/11/23/speed-up-assets-precompile-with-turbo-sprockets-rails3/)
* [Pay more attentions on security](http://rails-bestpractices.com/posts/2013/03/22/pay-more-attentions-on-security/)
* [monitor your backend services](http://rails-bestpractices.com/posts/2013/03/28/monitor-your-backend-services/)
* [Clever enums in rails](http://rails-bestpractices.com/posts/2013/04/18/clever-enums-in-rails/)
* [default_scope is evil](http://rails-bestpractices.com/posts/2013/06/15/default_scope-is-evil/)
* [Don't modify the params hash](http://rails-bestpractices.com/posts/2013/09/18/don-t-modify-the-params-hash/)
* [Use Time.zone.now instead of Time.now](http://rails-bestpractices.com/posts/2014/10/22/use-time-zone-now-instead-of-time-now/)

## Reek
コードの"[不吉な匂い](http://objectclub.jp/technicaldoc/refactoring/refact-smell)"をチェックしてくれます。これも可能であれば指摘された項目はリファクタリングしましょう。

https://github.com/troessner/reek/blob/master/docs/Code-Smells.md

### サンプルアプリケーションに対する指摘の例

| 不吉な匂い | 件数 |
|:---|:---|
| [Duplicate Method Call](https://github.com/troessner/reek/blob/master/docs/Duplicate-Method-Call.md) | 884 |
| [Too Many Statements](https://github.com/troessner/reek/blob/master/docs/Too-Many-Statements.md) | 348 |
| [Irresponsible Module](https://github.com/troessner/reek/blob/master/docs/Irresponsible-Module.md) | 344 |
| [Uncommunicative Variable Name](https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Variable-Name.md) | 197 |
| [Utility Function](https://github.com/troessner/reek/blob/master/docs/Utility-Function.md) | 154 |
| [Feature Envy](https://github.com/troessner/reek/blob/master/docs/Feature-Envy.md) | 118 |
| [Nested Iterators](https://github.com/troessner/reek/blob/master/docs/Nested-Iterators.md) | 69 |
| [Control Parameter](https://github.com/troessner/reek/blob/master/docs/Control-Parameter.md) | 68 |
| [Unused Parameters](https://github.com/troessner/reek/blob/master/docs/Unused-Parameters.md) | 49 |
| [Nil Check](https://github.com/troessner/reek/blob/master/docs/Nil-Check.md) | 35 |
| [Prima-Donna-Method](https://github.com/troessner/reek/blob/master/docs/Prima-Donna-Method.md) | 32 |
| [Uncommunicative Parameter Name](https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Parameter-Name.md) | 23 |
| [Repeated Conditional](https://github.com/troessner/reek/blob/master/docs/Repeated-Conditional.md) | 22 |
| [Long Parameter List](https://github.com/troessner/reek/blob/master/docs/Long-Parameter-List.md) | 19 |
| [Boolean Parameter](https://github.com/troessner/reek/blob/master/docs/Boolean-Parameter.md) | 18 |
| [Uncommunicative Method Name](https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Method-Name.md) | 17 |
| [Data Clump](https://github.com/troessner/reek/blob/master/docs/Data-Clump.md) | 14 |
| [Too Many Instance Variables](https://github.com/troessner/reek/blob/master/docs/Too-Many-Instance-Variables.md) | 5 |
| [Too Many Methods](https://github.com/troessner/reek/blob/master/docs/Too-Many-Methods.md) | 4 |
| [Class Variable](https://github.com/troessner/reek/blob/master/docs/Class-Variable.md) | 2 |
| [Module Initialize](https://github.com/troessner/reek/blob/master/docs/Module-Initialize.md) | 2 |
| [Uncommunicative Module Name](https://github.com/troessner/reek/blob/master/docs/Uncommunicative-Module-Name.md) | 1 |

### その他の不吉な匂い一覧

* [Attribute](https://github.com/troessner/reek/blob/master/docs/Attribute.md) (disabled by default)

## Churn
バージョン管理による変更回数の多いソースは問題が多いのでは？という観点からチェックされます。必ずしも "変更回数が多い = 問題がある" わけではないですが、設計が曖昧であったり、当該ファイルの役目がきちんと定まっていない場合、変更回数は多くなりがちです。

https://github.com/danmayer/churn


## Roodi
メソッド行数や空処理のブロック、命名規則などが一定のルールに沿っているかをチェックします。

https://github.com/roodi/roodi

## Saikuro
分岐、行数等からコードの複雑度をチェックをします。

http://saikuro.rubyforge.org/

## Cane
1行あたりの文字数や、スペーシング、クラスコメントの有無などをチェックします。

https://github.com/square/cane

## Stats
コードのクラス数、行数、メソッド数の統計およびテストコードの行数とプロダクトコード：テストコード比などをチェックします。

## Flay
コードの重複部(いわゆるコピペコード)をチェックします。

http://ruby.sadi.st/Flay.html


## Hotspots
各分析結果から、悪しきプログラムを順に表示します。ここで上位にきているファイルは見直したほうが良いでしょう。

## まとめ
MetricFu は様々なツールを用いてソフトウェアのメトリクス解析を行ってくれます。ツールで被っているチェック項目などありますが、大半がソフトウェアの品質向上につながる指摘です。場合によっては基準をクリアしない場合、本番環境にデプロイさせないような運用を行うのもよいでしょう。まだ使ってないという人や、ソフトウェアの品質チェックはどういった点を確認するのか興味ある人は MetricFu をチェックしてみください。