対象読者
- 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
このように、ドキュメントには
- デフォルトで 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
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
そして、ソースコードの中のコメントに、ドキュメントと同じものが書かれています。
ですので、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
コメントが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
このようにテストを見ることで、どのようなコードに対して 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
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