61
61

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GitHub Styleguide を訳してみた(Ruby編)

Last updated at Posted at 2014-10-26

GitHubによるコーディングスタイルガイドのうちRuby編の翻訳です。原文はこちらにあります。

HTML編もありますので、こちらもあわせてどうぞ。


#Ruby

このスタイルガイドの多くはhttps://github.com/bbatsov/ruby-style-guideから来ています。GitHub従業員は、社内で受理されたコーディングパターンやスタイルがあれば、pull requestを出してフィードバックを要求してください。

コーディングスタイル

  • スペース2つをインデントとするソフト・タブを使うこと。
  • 1行の長さは80文字以下にすること。
  • 行末の空白スペースを残さないこと。
  • ファイルの最終行には空行を入れること。
  • 演算子・カンマ・コロンやセミコロンのあとや{}の前後にはスペースを入れること。
sum = 1 + 2
a, b = 1, 2
1 > 2 ? true : false; puts "Hi"
[1, 2, 3].each { |e| puts e }
  • ([)]の前後にはスペースを入れないこと。
some(arg).other
[1, 2, 3].length
  • !の後にスペースを入れないこと。
!array.include?(element)
  • whencaseと同じインデントの深さにすること。
case 
when song.name == "Misty"
  puts "Not again!"
when song.duration > 120
  puts "Too long!"
when Time.now.hour > 21
  puts "It's too late"
else 
  song.play
end

kind = case year
       when 1850..1889 then "Blues"
       when 1890..1909 then "Ragtime"
       when 1910..1929 then "New Orleans Jazz"
       when 1930..1939 then "Swing"
       when 1940..1950 then "Bebop"
       else "Jazz"
       end
  • defの間や、メソッド内のロジックの段落の間に改行を入れること。
def some_method
  data = initialize(options)
  
  data.manipulate!
  
  data.result
end

def some_method
  result
end

ドキュメンテーション

出来るかぎりTomDocを使うこと。

# Public: Duplicate some text an arbitrary number of times.
#
# text  - The String to be duplicated.
# count - The Integer number of times to duplicate the text.
#
# Examples
#
#   multiplex("Tom", 4)
#   # => "TomTomTomTom"
#
# Returns the duplicated String.
def multiplex(text, count)
  text * count
end

シンタックス

  • defに引数がつく場合は括弧を使うこと。メソッドが引数を受け取らない場合は、括弧は省くこと。
def some_method
  # method body
end

def some_method_with_arguments(arg1, arg2)
  # method body
end
  • 完全にそうすべき理由がないかぎり、forは使わないこと。多くの場合、代わりにイテレーションをすることの方が適切である。foreachから少し違った形で実装されているので、結果的に遠回りをしてしまうことになる。つまり、foreachと違って新しいスコープを用意しないので、ブロック内で定義された変数は外から参照できてしまう。
arr = [1, 2, 3]

# bad
for elem in arr do
  puts elem
end

# good
arr.each { |elem| puts elem }
  • 複数行にわたるif/unlessにはthenを使わないこと。
# bad
if some_condition then
  # condition body
end

# good
if some_condition
  # condition body
end
  • 分岐条件が細かすぎるとき以外は、三項演算子?:の使用は控えること。ただし、if/then/else/end構文を1行の条件式にする目的には三項演算子?:を使うこと。
# bad
result = if some_condition then something else something_else end

# good
result = some_condition ? something : something_else
  • 三項演算子による条件分岐ではそれぞれ1つの式を使うこと。つまり、三項演算子はネストしないこと。そういった場合はif/else構文の方が好ましい。
# bad
some_condition ? (nested_condition ? nested_something : nested_something_else) : something_else

# good
if some_condition
  nested_condition ? nested_something : nested_something_else
else
  something_else
end
  • andorといったキーワードは禁止とする。とくにそうする価値がないから。常に&&||を使うこと。

  • 複数行にわたる三項演算子?:は避け、代わりにif/elseを使うこと。

  • 条件の中身が1行の場合は、if/unlessを1行で書くこと。

# bad
if some_condition
  do_something
end

# good
do_something if some_condition
  • unlesselseは一緒に使わないこと。こういったものは、肯定文が先にくるように書き直すこと。
# bad
unless success?
  puts "failure"
else
  puts "success"
end

# good
if success?
  puts "success"
else
  puts "failure"
end
  • if/unless/whileによる条件式には括弧を使わないこと。
# bad
if (x  > 10)
  # condition body
end

# good
if x > 10
  # condition body
end
  • 1行で書ける場合はdo...endよりも{...}を使うこと。また、複数行にわたるブロックでは{...}を使わないこと(チェーンが複数行になると見難くなる)。フロー制御やメソッド定義においてはdo...endを使うこと(Rakefileや特定のDSLなど)。チェーンで書くときはdo...end を使わないこと。
names = ["Bozhirdar", "steve", "Sarah"]

# good
names.each { |name| puts name }

# bad
names.each do |name|
  puts name
end

# good 
names.select { |name| name.start_with?("S") }.map { |name| name.upcase }

# bad
names.select do |name|
  name.start_with?("S")
end.map { |name| name.upcase }

複数行にわたるコードで{...}を使うことは特に見た目上問題はないという意見もあるかもしれないが、本当にリーダブルであるか?このブロックはいい感じの他のメソッドに切り分けることはできないか?を自問するべきである。

  • 不要な箇所でのreturnは使わないこと。
# bad
def some_method(some_arr)
  return some_arr.size
end

# good
def some_method(some_arr)
  some_arr.size
end
  • パラメータ引数に代入するときは、=演算子の前後にスペースを入れること。
# bad
def some_method(arg1=:default, arg2=nil, arg3=[])
  # do something...
end

# good
def some_method(arg1 = :default, arg2 = nil, arg3 = [])
  # do something...
end
  • =演算子(代入)の返り値は使っても良い。
# bad
if (v = array.grep(/foo/)) ...

# good
if v = array.grep(/foo/) ...

# 正しい順序になっていればこれも可
if (v = next_value) == "hello" ...
  • 変数を初期化するときには||=を自由に使って良い。
# set name to Bozhidar, only if it's nil or false
name ||= "Bozhidar"
  • ブール値を初期化する際には||=を使わないこと。(変数の現在の値がfalseだったときのことを考えてみれば分かる)
# bad - would set enabled to true even if it was false
enabled ||= true

# good
enabled = true if enabled.nil?
  • $0-9$といったような、Perlスタイルな特殊変数を使わないこと。こういった変数は謎を増やすので、ワンライナーのスクリプト以外では使用を控えること。$PROGRAM_NAMEといったような長いバージョンの方が望ましい。

  • メソッドの名前と括弧の間にはスペースを入れないこと。

# bad
f (3 + 2) + 1

# good
f(3 + 2) + 1
  • メソッドの1つめの引数が括弧開きから始まる場合、メソッドを使うときには括弧を使うこと。例:f((3 + 2) + 1)

  • ブロック内の未使用の引数には_を使うこと。

# bad
result = hash.map { |k, v| v + 1 }

# good
result = hash.map { |_, v| v + 1 } 
  • 型チェックにおいては、===を使わないこと。===はRubyにおけるcaseなどの実装サポートなので、可換とは限らない。たとえば、String === "hi"はtrueであるが"hi" === Stringはfalseになる。代わりに、必要なときはis_a?kind_of?を使うこと。

リファクタリングのほうがなお良い。明示的に型チェックを行うコードは、詳しく検討してみるべきである。

命名

  • メソッド名や変数名にはスネークケース(snake_case)を使うこと。
  • クラス名やモジュール名にはキャメルケース(CamelCase)を使うこと。(HTTP、RFC、XMLなどの頭字語は大文字のままで良い)
  • そのほかの定数はすべて大文字(SCREAMING_CAMEL_CASE)を使うこと。
  • 推定を行うメソッド(返り値がブール値であるメソッド)には、最後にクエスチョンマークをつけること(例:Array#empty?
  • 危険な挙動をともなう可能性のあるメソッド(例:selfや引数を書き換えるもの、exit!など)には、最後にエクスクラメーションマークをつけること。バングメソッド(エクスクラメーションマークがついたメソッド)は、エクスクラメーションマークがついていないメソッドがある場合のみ作ること。より詳しく見る

クラス 

  • 継承に関連して変わった挙動をすることがあるので、クラス変数@@は使わないこと。
class Parent
  @@class_var = "parent"
  
  def self.print_class_var
    puts @@class_var
  end
end

class Child < Parent
  @@class_var = "child"
end

Parent.print_class_var # => "child"と出力される

上を見るとわかるように、クラスにおけるヒエラルキーでは1つのクラス変数を共有してしまう。クラス変数よりも、インスタンス変数のほうが好ましい。

  • リファクタリングや変更に強くするために、シングルトンメソッドにはdef self.methodを使うこと。
class TestClass
  # bad
  def TestClass.some_method
    # method body
  end
  
  # good
  def self.some_other_method
    # method body
  end
  • 必要なとき(アクセサや属性のエイリアスの設定)以外はclass << selfを使わないこと。
class TestClass
  # bad 
  class << self
    def first_method
      # method body
    end
    
    def second_method
      # method body
    end
  end
    
  # good
  class << self
    attr_accessor :per_page
    alias_method :nwo, :find_by_name_with_owner
  end
    
  def self.first_method
    # method body
  end
    
  def self.second_method
    # method body
  end
end
  • publicprotectedprivateは、メソッド定義と同じインデントの深さにすること。また、それぞれの上には空行を入れること。
class SomeClass
  def public_method
    # ...
  end
  
  private
  def private_method
    # ...
  end
end
  • 内部的なクラス/インスタンスメソッドのやりとりにおいて、変数に隠れたメソッドを特定する以外には、明示的にselfをレシーバとしないこと。
class SomeClass
  attr_accessor :message

 def greeting(name)
   message = "Hi #{name}" # Rubyではattribute writeではなくローカル変数
   self.message = message
 end
end

例外

  • フロー制御のために例外を使わないこと。
# bad
begin 
  n / d
rescue ZeroDevisionError
  puts "Cannot devide by 0!"
end

# good
if d.zero?
  puts "Cannot devide by 0!"
else
  n / d
end
  • Exceptionクラスとして例外を拾わないこと。
# bad
begin
  # ここで例外が発生する
rescue
  # 例外処理
end

# これもダメ
begin
  # ここで例外が発生する 
rescue Exception
  # 例外処理
end

コレクション

  • 文字列の配列が必要なときは、配列リテラルよりも%wが好ましい。
# bad
STATES = ["draft", "open", "closed"]

# good
STATES = %w(draft open closed)
  • ユニークな要素を扱うときは、ArrayではなくSetを使うこと。Setは非順序で重複のないコレクションとして持つ。これはArrayの直感的な操作性や機能と、Hashの参照速度のどちらもあわせ持つ。

  • ハッシュのキーには文字列ではなくシンボルを使うこと。

# bad
hash = { "one" => 1, "two" => 2, "three" => 3 }

# good
hash = { :one => 1, :two => 2, :three => 3 }

文字列

  • 文字列は、結合するよりも文字列内で展開するほうが好ましい。
# bad
email_with_name = user.name + " <" + user.email + ">"

# good
email_with_name = "#{user.name} <#{user.email}>"
  • 文字列にはダブルクオテーションを使うこと。文字列内展開やエスケープ記号は区切り記号が変わろうと動作し、また文字列リテラルにおいては'よりも"の方がはるかに一般的である。
# bad
name = 'Bozhidar'

# good
name = "Bozhidar"
  • 大きなデータのかたまりを作るときには、String#+の使用は避けること。代わりにString#<<を使うこと。文字列の結合は文字列インスタンスを適切に変異させ、また文字列オブジェクトをたくさん生成するString#+よりも速い。
# good and also fast
html = ""
html << "<h1>Page title</h1>"

paragraphs.each do |paragraph|
  html << "<p>#{paragraph}</p>"
end

正規表現

  • $1-9は何を内包しているかが把握しづらいため使わないこと。代わりに名前のついたグループを使うこと。
# bad
/(regexp)/ =~ string
...
process $1

# good
/(?<meaningful_var>regexp>)/ =~ string
...
process meaningful_var
  • ^$は行の始まりと終わりと示すものであり、文字列の始めと終わりを指さないことに注意する。文字列全体とのマッチを行うには、\A\zを使うこと。
string = "some injection\nusername"
string[/^username$/] # マッチする
string[/\Ausername\z/] # マッチしない
  • 複雑な正規表現では、xオプション使うこと。これにより、可読性が向上し、コメントをつけることができる。ただし、スペースが無視されることに注意する。
regexp = %r{
  start     # テキスト
  \s        # スペースと文字
  (group)   # 最初のグループ
  (?:alt1|alt2) # 代替要素
  end
}x

パーセント(%)リテラル

  • %wは自由に使ってよい。
STATES = %w(draft open closed)
  • 文字列の式展開やダブルクオテーションの埋め込みを必要とする文字列には、%()を使うこと。複数行にわたる場合は、ヒアドキュメントの方が好ましい。
# bad (式展開がない)
%(<div class="text">Some text</div>)
# ok
"<div class=\"text\">Some text</div>"

# bad (ダブルクオテーションがない)
%(This is #{quality} style)
# ok 
"This is #{quality} style"

# bad (複数行)
%(<div>\n<span class="big">#{exclamation}</span>\n</div>)
# ヒアドキュメントが望ましい

# good (式展開やクオテーションがあり1行である)
%(<tr><td class="name">#{name}</td>)
  • 1つ以上の/とマッチさせるときのみ%rを使うこと。
# bad
%r(\s+)

# これもダメ
%r(^/(.*)$)
# 修正後: /^\/(.*)$/

# good
%r(^/blog/2011/(.*)$)

ハッシュ

ハッシュリテラルには、1.9で導入されたJSONスタイルのシンタックスではなく、ハッシュロケットを使うこと。

# bad
user = {
  login: "defunkt",
  name: "Chris Wanstrath"
}

# bad
user = {
  login: "defunkt",
  name: "Chris Wanstrath",
  "followers-count" => 52390235
}

# good
user = {
  :login => "defunkt",
  :name => "Chris Wanstrath",
  "followers-count" => 52390235
}

キーワード引数

キーワード引数の使用は推奨されているものの、メソッドの引数がともすれば不明瞭になるような場合には必須ではない。また、pre-2.0 ruby以降では、「擬似引数としてのハッシュ」よりもキーワード引数の方が望ましい。

これの代わりには

def remove_member(user, skip_membership_check=false)
  # ...
end

# メソッド定義以外の場所: ここでのtrueとはどういう意味だろうか?
remove_member(user, true)

もっと明示的になるよう、こうするべきである。

def remove_member(user, skip_membership_check: false)
  # ...
end

# Elsewhere, now with more clarity:
remove_member user, skip_membership_check: true

上記の何よりも

自らの:heart:に従うこと。


ところで

Repro株式会社では一緒に切磋琢磨できる仲間を募集しています。 ぜひこちらをごらんください!

61
61
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
61
61

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?