21
7

More than 5 years have passed since last update.

RuboCop のドキュメントに example を追加してみる

Last updated at Posted at 2017-10-05

対象読者

  • OSS に Pull-Request を投げてみたい人

なにこれ

OSS にPull-Requestを送ってみたいけど、どうやったらいいのかわからなくて躊躇している人も多いのではないでしょうか。
この記事では、RuboCopのドキュメントを更新するPull-Requestを作成する方法について解説します。

10月はHacktoverfestが開催されることもあり、Pull-Requestを送る絶好の機会です。
Pull-Request にチャンレジしてみてはいかがでしょうか。

RuboCop?

RuboCop とは、Ruby の静的解析ツールです。
例えば、以下のようなスタイルの崩れたコードへ警告を出して自動的に修正したり、

def foo
     bar ( baz)
  end

以下のような危険なコードに対して警告を出したりします。

def foo
  bar
  return
  baz # 絶対に実行されない!
end

詳しくは、レポジトリのREADMEをご覧ください。

example?

では、タイトルの「ドキュメントにexampleを追加してみる」というのはどのようなことでしょうか?

RuboCop では、ひとつひとつのCop(検出する項目の単位)毎に、ドキュメントが書かれています。
ドキュメントは、GitHub レポジトリのmanual/ディレクトリ下か、 http://rubocop.readthedocs.io/en/latest/ で見ることができます。今回は、リビジョンを指定することができるGitHubレポジトリの方を見ます。
例えば、先程の"絶対に到達できないコード"のドキュメントを見てみましょう。

Lint/UnreachableCode

Enabled by default Supports autocorrection
Enabled No

This cop checks for unreachable code.
The check are based on the presence of flow of control
statement in non-final position in begin(implicit) blocks.

Example

# bad

def some_method
  return
  do_something
end

# bad

def some_method
  if cond
    return
  else
    return
  end
  do_something
end
# good

def some_method
  do_something
end

https://github.com/bbatsov/rubocop/blob/08877fe14c6a3af12a10691f38b63d1d9e70ecf2/manual/cops_lint.md#lintunreachablecode

このように、ドキュメントには

  • デフォルトで enabled か、auto-correction をサポートしているか
  • Cop の簡単な説明
  • 良いコード、悪いコードの実例

が書かれています。

ですが、中にはコード例が書かれていないCopもあります。例えば、Layout/SpaceInsideParens cop はドキュメントに example が書かれていません。

Layout/SpaceInsideParens

Enabled by default Supports autocorrection
Enabled Yes

Checks for spaces inside ordinary round parentheses.

References

https://github.com/bbatsov/rubocop/blob/08877fe14c6a3af12a10691f38b63d1d9e70ecf2/manual/cops_layout.md#layoutspaceinsideparens

example のないドキュメントは、パット見で何をするCopなのかがわからないため、とても不便です。
そのため、このようなドキュメントにexampleを追加していきたいと思っています。
このLayout/SpaceInsideParensを例に、どうやったらexampleを追加できるのかを解説します。

ドキュメントはどこに書く?

実は先程のmanual/cops_layout.mdなどは、自動生成されたファイルです。
そのためドキュメントを書く際には、manual/ディレクトリ下のファイルを直接編集するのではなく、オリジナルとなるファイルを編集する必要があります。

ドキュメントのオリジナルは、Copのソースコード内にコメントとして書かれています。
例えば、先程のLint/UnreachableCodeのソースコードは、lib/rubocop/cop/lint/unreachable_code.rbに存在します。

# frozen_string_literal: true

module RuboCop
  module Cop
    module Lint
      # This cop checks for unreachable code.
      # The check are based on the presence of flow of control
      # statement in non-final position in *begin*(implicit) blocks.
      #
      # @example
      #
      #   # bad
      #
      #   def some_method
      #     return
      #     do_something
      #   end
      #
      #   # bad
      #
      #   def some_method
      #     if cond
      #       return
      #     else
      #       return
      #     end
      #     do_something
      #   end
      #
      # @example
      #
      #   # good
      #
      #   def some_method
      #     do_something
      #   end
      class UnreachableCode < Cop

https://github.com/bbatsov/rubocop/blob/70bc212b7da7175628b04f65d68c30686911a60a/lib/rubocop/cop/lint/unreachable_code.rb#L1-L37

そして、ソースコードの中のコメントに、ドキュメントと同じものが書かれています。
ですので、example を追加する際はこのコードを編集していくことになります。

example の追加の仕方

では、example が存在しなかったLayout/SpaceInsideParens Cop に example を追加してみましょう。
この Cop のコメントは、以下のようになっています。

# frozen_string_literal: true

module RuboCop
  module Cop
    module Layout
      # Checks for spaces inside ordinary round parentheses.
      class SpaceInsideParens < Cop

https://github.com/bbatsov/rubocop/blob/70bc212b7da7175628b04f65d68c30686911a60a/lib/rubocop/cop/layout/space_inside_parens.rb#L1-L7

コメントが1文しかありませんね。ここに example を追加しましょう。

どうやって Cop の動きを知る?

example を書くためには、その Cop の動きを知る必要があります。
コメントに書いてある文面だけからは動きが推測しづらい場合もあると思います。
そのような場合は、その Cop のテストを見ると良いでしょう。

Layout/SpaceInsideParensのテストは、spec/rubocop/cop/layout/space_inside_parens_spec.rbに存在します。ソースコードと対応した名前になっているので、探しやすいと思います。
そのうちの一部を抜粋します。

describe RuboCop::Cop::Layout::SpaceInsideParens do
  subject(:cop) { described_class.new }

  it 'registers an offense for spaces inside parens' do
    expect_offense(<<-RUBY.strip_indent)
      f( 3)
        ^ Space inside parentheses detected.
      g = (a + 3 )
                ^ Space inside parentheses detected.
    RUBY
  end
  # ...
end

https://github.com/bbatsov/rubocop/blob/08877fe14c6a3af12a10691f38b63d1d9e70ecf2/spec/rubocop/cop/layout/space_inside_parens_spec.rb#L1-L14

このようにテストを見ることで、どのようなコードに対して RuboCop が警告を出すのかを把握することが出来ます。
今回はこのテストケースをそのまま example として使用しましょう。

またテストだけでは物足りない場合は、実際に RuboCop を実行してみることをおすすめします。

example の書式

example は、YARD の書式に沿って書きます。
@example というブロックの中に、Rubyのコード例を書いていきます。
またコード例には "bad" な例と "good" な例の両方を記述すると良いでしょう。

実際に書くと、こんな感じになるでしょう。

module RuboCop
  module Cop
    module Layout
      # Checks for spaces inside ordinary round parentheses.
      #
      # @example
      #   # bad
      #   f( 3)
      #   g = (a + 3 )
      #
      #   # good
      #   f(3)
      #   g = (a + 3)

インデントの数に注意してください。@example#から数えてスペースがひとつ、コードの部分はスペースが3つ必要です。

また、設定項目がある Cop の場合にはそれぞれの設定項目における動作例が書かれていると良いでしょう。
例えば、Style/Aliasのドキュメントは以下のようになっています。

# This cop enforces the use of either `#alias` or `#alias_method`
# depending on configuration.
# It also flags uses of `alias :symbol` rather than `alias bareword`.
#
# @example
#
#   # EnforcedStyle: prefer_alias
#
#   # good
#   alias bar foo
#
#   # bad
#   alias_method :bar, :foo
#   alias :bar :foo
#
# @example
#
#   # EnforcedStyle: prefer_alias_method
#
#   # good
#   alias_method :bar, :foo
#
#   # bad
#   alias bar foo

https://github.com/bbatsov/rubocop/blob/f54c3c35db70f26a399fc9bad413d72c19b310a1/lib/rubocop/cop/style/alias.rb#L6-L29

Cop に設定項目があるかどうかは、config/default.yml を見ることで判断できます。
例えば、Style/Aliasは設定項目があるので、config/default.ymlに記載があります。

Style/Alias:
  EnforcedStyle: prefer_alias
  SupportedStyles:
    - prefer_alias
    - prefer_alias_method

https://github.com/bbatsov/rubocop/blob/master/config/default.yml#L654-L658

ファイルの自動生成

このままではmanual/ディレクトリ下のファイルが同期されていません。
そのため、ソースコードの変更をmanual/ディレクトリ下にも反映する必要があります。

RuboCop ではそれを行うための rake task を用意しています。

$ bundle exec rake generate_cops_documentation

上記のタスクを実行することで、manual/下にも変更が反映されます。

テストの実行

殆どの場合、ドキュメントを変更しただけでコードが壊れることがないでしょう。
そのため、Pull-Request を投げる前にわざわざテストを実行する必要はほとんどありません(心配な人は、bundle exec rakeしてください)。

ですが、コメントの変更だけでも CI が落ちる可能性はあります。
何故ならば、RuboCop は自身のコードに対しても RuboCop を実行しているためです。例えば、Metrics/LineLengthのような Cop はコメントの行のみの変更でも警告が発生しうる可能性があります。

そのため、念の為に RuboCop を実行しておくと良いでしょう。RuboCop プロジェクトに対する RuboCop の実行は、bundle exec rake internal_investigationで行うことが出来ます。

Pull-Request を送る

あとは、変更をコミットして、forkして、Pull-Request を送るだけです。

実際にLayout/SpaceInsideParensの example を追加する Pull-Request はこちらになります。
https://github.com/bbatsov/rubocop/pull/4827

まとめ

RuboCop のドキュメントに example を追加する方法について紹介しました。
簡単に OSS に貢献が出来るので、ぜひ取り組んでみてはいかがでしょうか?
何かわからないことがあれば、この記事のコメントや私のTwitterまで気軽に質問をしてください。

また、Rubyの日本語ドキュメントである「るりま」でも、コードの example を追加するプロジェクトが走っています。
るりまサンプルコード追加プロジェクト月次報告 #ruby_polisher
もし興味があればそちらもご覧ください。

Document がない Cop

最後に、現時点(2017/10/05)でドキュメントがない Cop の一覧を添付しておきます。
このリストは雑なワンライナーを書いて取得したので、もしかしたら間違いがあるかも知れません。その場合はご容赦ください。

# リストを生成するワンライナー
$ ruby -ractive_support -ractive_support/core_ext -e 'cops=`rubocop --show-cops`.scan(/^\w+\/\w+/); examples = `git grep @example`.scan(%r!\w+/\w+\.rb!).map{|e|e[%r!^(.+)\.rb!, 1].camelize.gsub("::",?/)}; puts cops-examples'
  • Layout/CommentIndentation
  • Layout/EmptyLines
  • Layout/EndOfLine
  • Layout/IndentArray
  • Layout/IndentHash
  • Layout/InitialIndentation
  • Layout/SpaceAfterSemicolon
  • Layout/SpaceAroundEqualsInParameterDefault
  • Layout/SpaceAroundOperators
  • Layout/SpaceBeforeComma
  • Layout/SpaceInsideBlockBraces
  • Layout/SpaceInsideBrackets
  • Layout/SpaceInsideHashLiteralBraces
  • Layout/Tab
  • Layout/TrailingBlankLines
  • Layout/TrailingWhitespace
  • Lint/ScriptPermission
  • Lint/Syntax
  • Lint/UnneededDisable
  • Metrics/AbcSize
  • Metrics/BlockLength
  • Metrics/BlockNesting
  • Metrics/ClassLength
  • Metrics/CyclomaticComplexity
  • Metrics/LineLength
  • Metrics/MethodLength
  • Metrics/ModuleLength
  • Metrics/ParameterLists
  • Naming/AsciiIdentifiers
  • Naming/ClassAndModuleCamelCase
  • Naming/ConstantName
  • Naming/FileName
  • Naming/MethodName
  • Naming/VariableName
  • Performance/FixedSize
  • Performance/RangeInclude
  • Rails/ActionFilter
  • Rails/Exit
  • Rails/HasAndBelongsToMany
  • Rails/Output
  • Rails/Validation
  • Security/JSONLoad
  • Security/YAMLLoad
  • Style/ArrayJoin
  • Style/AsciiComments
  • Style/Attr
  • Style/BarePercentLiterals
  • Style/BeginBlock
  • Style/BlockComments
  • Style/BlockDelimiters
  • Style/CaseEquality
  • Style/CharacterLiteral
  • Style/ClassAndModuleChildren
  • Style/ClassCheck
  • Style/ClassVars
  • Style/CollectionMethods
  • Style/ColonMethodCall
  • Style/CommentAnnotation
  • Style/Copyright
  • Style/DefWithParentheses
  • Style/Documentation
  • Style/EmptyLiteral
  • Style/Encoding
  • Style/EndBlock
  • Style/FlipFlop
  • Style/For
  • Style/FormatString
  • Style/FrozenStringLiteralComment
  • Style/GlobalVars
  • Style/IfUnlessModifier
  • Style/IfWithSemicolon
  • Style/MethodDefParentheses
  • Style/MultilineTernaryOperator
  • Style/NegatedWhile
  • Style/NestedTernaryOperator
  • Style/Not
  • Style/NumericLiteralPrefix
  • Style/OneLineConditional
  • Style/ParenthesesAroundCondition
  • Style/PercentQLiterals
  • Style/PerlBackrefs
  • Style/Proc
  • Style/RedundantSelf
  • Style/RescueModifier
  • Style/Semicolon
  • Style/Send
  • Style/SignalException
  • Style/SingleLineBlockParams
  • Style/SingleLineMethods
  • Style/SpecialGlobalVars
  • Style/StringLiterals
  • Style/StringMethods
  • Style/TrivialAccessors
  • Style/UnlessElse
  • Style/UnneededCapitalW
  • Style/UnneededPercentQ
  • Style/VariableInterpolation
  • Style/WhenThen
  • Style/WhileUntilDo
  • Style/WhileUntilModifier
21
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
7