Reekメモ

Reek 6.0.3で確認。

以下のGemをインストールする。

group :development, :test do
  gem 'reek'
end

bundle exec reek 対象で実行。-t.reek.ymlファイルが作成され現状のコードの問題をTODO化して後回しにできる。.reek.ymlはreekの設定ファイル。

RSpec matchersも用意されていて、動的に生成されたコードのチェックも行える。

Railsでは以下の設定を追加するのが良い(参照:Reek - Working with Rails)。

directories:
  "app/controllers":
    IrresponsibleModule:
      enabled: false
    NestedIterators:
      max_allowed_nesting: 2
    UnusedPrivateMethod:
      enabled: false
    InstanceVariableAssumption:
      enabled: false
  "app/helpers":
    IrresponsibleModule:
      enabled: false
    UtilityFunction:
      enabled: false
  "app/mailers":
    InstanceVariableAssumption:
      enabled: false
  "app/models":
    InstanceVariableAssumption:
      enabled: false

共通の設定項目は、Reek - Basic Smell Optionsを参照。

自分の考える推奨設定

directories:
  "db":
    FeatureEnvy:
      enabled: false
  "app/controllers":
    IrresponsibleModule:
      enabled: false
    NestedIterators:
      max_allowed_nesting: 2
    UnusedPrivateMethod:
      enabled: false
    InstanceVariableAssumption:
      enabled: false
  "app/helpers":
    IrresponsibleModule:
      enabled: false
    UtilityFunction:
      enabled: false
  "app/mailers":
    InstanceVariableAssumption:
      enabled: false
  "app/models":
    InstanceVariableAssumption:
      enabled: false

detectors:
  # publicメソッドにのみ適用
  UtilityFunction:
    public_methods_only: true
  # ブロックで1文字変数が使えなくなるため無効化
  # キャメルケースチェックはRuboCopでもできる
  UncommunicativeVariableName:
    enabled: false
  # メソッドの行数チェック。RuboCopと重複するので無効化
  TooManyStatements:
    enabled: false
  # モジュールやクラスコメントの有無チェック。RuboCopと重複するので無効化
  IrresponsibleModule:
    enabled: false
  # Serviceクラスでは他のオブジェクトのメソッド呼び出しが多いので無効化する
  # app/servicesに対して適用してもよいがドメインサービスも除外するため正規表現で指定する
  FeatureEnvy:
    exclude:
    - "/.*Service#.*/"
  # foo_model.idは複数回呼び出すことを許可する
  DuplicateMethodCall:
    allow_calls:
    - id$
  # 自己カプセル化が必要になったときのためにセッターは用意しておきたい
  # かといって煩雑な記述はしたくない
  # そもそも、セッターを外部から利用するような使い方をするのが悪いし
  # そういうメンバがいることを想定しないので無効化する
  Attribute:
    enabled: false

# 除外パス
exclude_paths:
  - vendor/bundle

警告の一覧と簡単な説明

公式の一覧はReek - Code Smellsを参照。

  • Attribute
    • publicなセッターがある場合に警告する
    • attr_accessorattr_writerがpublicな場合に警告される。def foo=(foo)のような=付きメソッドはpublicでも警告されない。
  • ClassVariable
    • クラス変数を使っていると警告する
  • ControlCouple
    • メソッド全体がif param...else...endになっていて引数paramで処理を振り分けている場合に警告する
    • BooleanParameter
      • 引数のデフォルト値がtruefalseで設定されている場合
    • ControlParameter
      • BooleanParameter以外の場合
  • DataClump
    • 同じ組み合わせの引数が複数のメソッドに現れたり、インスタンス変数に同じ部分文字列が使われているものが複数ある場合に警告する
  • DuplicateMethodCall
    • 1つのメソッドの中で同じメソッドを複数回呼び出している場合に警告する
    • return val[index] if val[index].present?のようなケースでval[index]が二回実行されていると警告される
  • InstanceVariableAssumption
    • クラス定義の外でインスタンス変数が設定されると仮定しているコードがある場合に警告する
    • メモ化と相性が悪いが、メモを保存するインスタンス変数にコンストラクタでnilを代入しておけば警告は避けられる
  • IrresponsibleModule
    • モジュールやクラスコメントがない場合に警告
    • RuboCopと重複するので無効化して良い
  • LargeClass
    • TooManyConstants
    • TooManyInstanceVariables
    • TooManyMethods
  • LongParameterList
  • LongYieldList
  • LowCohesion
    • FeatureEnvy
      • 自分のインスタンス変数やメソッドよりも、他のオブジェクトのメソッド呼び出しが多いような場合に警告する
      • Serviceクラスなどの場合は無効化した方が良いだろう
    • UtilityFunction
      • 自分のインスタンス変数やメソッドを使っておらず、他のオブジェクトを呼び出しているメソッドを警告
  • ModuleInitialize
  • NestedIterators
  • MissingSafeMethod
    • !付きのメソッドがあるが、同名の!なしメソッドがない場合に警告
    • 以前はPrima Donna Methodと呼ばれていた
  • SimulatedPolymorphism
    • ManualDispatch
    • NilCheck
      • x.nil?x == nilなどnilチェックをしている場合に警告
      • x ||= valx&.methodif xなどは警告されない
    • RepeatedConditional
  • SubclassedFromCoreClass
  • TooManyStatements
    • メソッドのコード行数が長すぎる場合に警告
    • RuboCopでできるので無効化して良い
  • UncommunicativeName
    • UncommunicativeMethodName
    • UncommunicativeModuleName
    • UncommunicativeParameterName
    • UncommunicativeVariableName
      • 1文字変数、数値で終わる変数名、キャメルケースの変数名を警告
      • list.map {|x| ...}xも警告されるので考えもの
      • キャメルケースはRuboCopでもチェックするため無効化しても良いのではないか
  • UnusedParameters
  • UnusedPrivateMethod