layout | title | permalink |
---|---|---|
base |
루비 스타일 가이드 |
/ |
Ruby는 Shopify의 주요 언어입니다. 저희는 주로 Ruby를 쓰는 회사이고 이 가운데서 대기업 중 하나일 것입니다. 또한 Ruby는 새로운 웹 프로젝트 및 스크립트 작성 시 즐겨 사용되는 언어입니다.
저희는 Shopify의 모든 개발자들이 최소한 Ruby에 대한 기본적인 이해는 있다고 생각합니다. 정말 대단한 언어에요. 이 언어는 여러분이 매일 어떤 일을 하든 더 나은 개발자로 만들어 줄겁니다. 아래 내용들은 Ruby로 개발하면서 따라야 할 느슨한 코딩 스타일입니다.
이 스타일 가이드는 10여 년간 Shopify의 Ruby 개발에 대한 결과물입니다. 가이드 내용의 대부분은 Bozhidar Batsov의 루비 스타일 가이드에 기반을 두고 있고, Shopify의 많은 컨트리뷰터들에 의해 각색되어 있습니다.
저희는 여러분의 Ruby 프로젝트들에서 이 스타일 가이드를 적용할 수 있도록 RuboCop을 사용하는 걸 추천합니다. RuboCop 설치 및 사용 방법은 RuboCop 공식 문서를 참고해주세요.
저희는 이 스타일 가이드로 적용 및 동기화할 수 있는 기본 RuboCop 구성을 제공합니다.
이를 사용하려면 Gemfile
에 아래 코드를 추가해 주세요.
gem "rubocop-shopify", require: false
그리고 프로젝트의 RuboCop 설정 파일 최상단에 아래 코드를 추가해 주세요.
inherit_gem:
rubocop-shopify: rubocop.yml
그럼 이 스타일 가이드의 Include
, Exclude
설정값이 RuboCop 기본 구성에 적용됩니다.
설정 적용에 대한 더 많은 정보는 RuboCop 문서를 참고해 주세요.
- 일반(General)
- 레이아웃(Layout)
- 구문(Syntax)
- 네이밍(Naming)
- 주석(Comments)
- 클래스 및 모듈(Classes and Modules)
- 예외(Exceptions)
- 컬렉션(Collections)
- 문자열(Strings)
- 정규 표현식(Regular Expressions)
- 퍼센트 리터럴(Percent Literals)
- 테스트(Testing)
-
메서드의 모든 줄이 동일한 추상화 단계에서 동작하게 하세요. (Single Level of Abstraction 원리)
-
함수형 방식으로 코딩하세요. 가능한 한 뮤테이션(사이드 이펙트)을 피하세요.
-
과한 방어적 프로그래밍은 전혀 발생하지 않을 에러로부터 보호해 불필요한 런타임 및 유지 보수 비용을 발생시킬 수 있습니다.
-
메서드 내에서 인자값의 변화를 피하세요.
-
몽키패치 방식을 피하세요.
-
긴 메서드가 되지 않게 피하세요.
-
많은 매개변수를 갖지 않게 피하세요.
-
필요 없는 메타 프로그래밍을 피하세요.
-
private
/protected
가 우회되지 않게send
보다public_send
사용을 권장합니다. -
ruby -w
를 통해 안전한 코드를 작성하세요. -
3개보다 많은 블록 중첩을 피하세요.
-
소스 파일 인코딩은
UTF-8
로 사용하세요. -
들여쓰기 시 탭이 아닌 공백 2칸을 사용하세요.
-
Unix 스타일의 줄바꿈을 사용하세요.
-
명령문 및 표현식을 구분하기 위해
;
을 사용하는 걸 피하세요. 각 줄마다 1개의 표현식을 쓰세요. -
연산자 앞뒤,
,
,:
,;
뒤,{
앞뒤,}
앞에 공백을 사용하세요. -
(
,[
이후나]
,)
이전의 공백 사용을 피하세요. -
!
연산자 이후에 공백 사용을 피하세요. -
범위식(예시:
1..5
)에서 공백 사용을 피하세요. -
메서드 콜 앞뒤에 공백 사용을 피하세요.
# 나쁜 예 foo . bar # 좋은 예 foo.bar
-
람다 표현식 내에서 공백 사용을 피하세요.
# 나쁜 예 a = -> (x, y) { x + y } # 좋은 예 a = ->(x, y) { x + y }
-
when
은case
와 같은 깊이로 들여쓰기를 하세요. -
조건문 결과를 변수값으로 할당 시, 분기(
if
,else
, ...)를 변수에 맞추세요.# 나쁜 예 result = if some_cond # ... # ... calc_something else calc_something_else end # 좋은 예 result = if some_cond # ... # ... calc_something else calc_something_else end
-
begin
블록 결과를 변수값으로 할당 시,rescue
/ensure
/end
를 시작 줄에 맞추세요.# 나쁜 예 host = begin URI.parse(value).host rescue URI::Error nil end # 좋은 예 host = begin URI.parse(value).host rescue URI::Error nil end
-
메서드 정의 사이마다, 그리고 메서드 내부적으로 논리적 단락마다 빈 줄을 사용하세요.
-
메서드 파라미터에 기본값을 할당할 때
=
연산자 주위에 공백을 사용하세요. -
불필요하게
\
를 사용한 줄바꿈을 피하세요. -
만약 메서드 호출 시 파라미터로 인해 1줄이 넘어갈 때, 파라미터를 메서드 호출 줄보다 1탭 더 들여쓰세요.
# 시작 상태 (줄이 너무 김) def send_mail(source) Mailer.deliver(to: "bob@example.com", from: "us@example.com", subject: "Important message", body: source.text) end # 나쁜 예 (2번 들여쓰기) def send_mail(source) Mailer.deliver( to: "bob@example.com", from: "us@example.com", subject: "Important message", body: source.text) end # 좋은 예 def send_mail(source) Mailer.deliver( to: "bob@example.com", from: "us@example.com", subject: "Important message", body: source.text, ) end
-
여러 줄에 걸쳐 메서드 체인을 할 때, 1탭 더 들여써서 메서드를 호출하세요.
# 나쁜 예 (이전 메서드 호출에 맞게 들여쓰기) User.pluck(:name) .sort(&:casecmp) .chunk { |n| n[0] } # 좋은 예 User .pluck(:name) .sort(&:casecmp) .chunk { |n| n[0] }
-
여러 줄에 걸친 배열 원소들을 정렬하세요.
-
한 줄에 최대 120자로 제한하세요.
-
줄 마지막에 공백을 피하세요.
-
정렬 목적을 제외한 추가 공백을 피하세요.
-
모든 파일을 줄바꿈으로 끝내세요.
-
블록 주석을 피하세요.
# 나쁜 예 =begin comment line another comment line =end # 좋은 예 # comment line # another comment line
-
메서드 호출 시 여는 괄호가 첫 번째 인자와 다른 줄에 있을 때, 닫는 괄호를 마지막 인자 뒷 줄에 사용하세요.
# 나쁜 예 method( arg_1, arg_2) # 좋은 예 method( arg_1, arg_2, )
-
매직 코멘트와 코드 및 설명 주석을 빈 줄로 분리하세요.
# 좋은 예 # frozen_string_literal: true # Some documentation for Person class Person # Some code end # 나쁜 예 # frozen_string_literal: true # Some documentation for Person class Person # Some code end
-
접근 제어자(attribute accessor) 주변에 빈 줄을 사용하세요.
# 나쁜 예 class Foo attr_reader :foo def foo # do something... end end # 좋은 예 class Foo attr_reader :foo def foo # do something... end end
-
메서드, 클래스, 모듈, 블록 간에 빈 줄 사용을 피하세요.
# 나쁜 예 class Foo def foo begin do_something do something end rescue something end true end end # 좋은 예 class Foo def foo begin do_something do something end rescue something end end end
-
오직 상수(클래스, 모듈 포함)와 생성자(예시:
Array()
,Nokogiri::HTML()
)를 참조할 때만::
를 사용하세요. 일반 메서드 호출에서는::
사용을 피하세요. -
상수를 찾아갈 때 부모 클래스/모듈을 탐색하지 않으므로 클래스와 모듈을 정의나 상속할 때
::
사용을 피하세요.# 나쁜 예 module A FOO = "test" end class A::B puts FOO # NameError exception을 발생시킵니다. end # 좋은 예 module A FOO = "test" class B puts FOO end end
-
메서드에 파라미터가 있을 때 괄호와 함께
def
를 사용하세요. 어느 파라미터도 들어가지 않을 때만 괄호를 생략하세요. -
for
사용을 피하세요. -
then
사용을 피하세요. -
if
/then
/else
/end
구조보다 삼항 연산자(?:
) 사용을 권장합니다.# 나쁜 예 result = if some_condition then something else something_else end # 좋은 예 result = some_condition ? something : something_else
-
삼항 연산자에서 분기당 하나의 표현식을 사용하세요. 즉, 삼항 연산자는 반드시 중첩되지 않아야 합니다. 이런 경우에는
if
/else
구조를 사용하세요. -
여러 줄의
?:
(삼항 연산자) 사용을 피하세요. 대신if
/unless
를 사용하세요. -
1줄 조건일 때
when x then ...
을 사용하세요. -
not
대신!
을 사용하세요. -
and
/or
보단&&
/||
사용을 권장합니다. -
부정 조건문에서
if
보단unless
사용을 권장합니다. -
else
을 포함한unless
사용을 피하세요. 긍정 조건문이 먼저 앞에 오도록 다시 작성하세요. -
메서드 호출 시 인자들을 괄호로 감싸주세요. 단, 인자를 받지 않는 메서드의 경우엔 괄호를 생략하세요. 또는 메서드 호출 시 단일 줄이면서 아래 조건일 때는 괄호를 생략하세요.
- 내부 수신자(implicit receiver)를 포함한 클래스 메서드 호출일 때
- 신택틱 슈거(syntactic sugar)를 통한 호출일 때 (예시:
1 + 1
은+
메서드를 호출하고,foo[bar]
는[]
메서드를 호출합니다.)
# 나쁜 예 class User include(Bar) has_many(:posts) end # 좋은 예 class User include Bar has_many :posts SomeClass.some_method(:foo) end
- 아래 메서드들 중 하나일 때
require
require_relative
require_dependency
yield
raise
puts
-
메서드 호출 시 내부 옵션 해시의 중괄호를 생략하세요.
-
블록 내 메서드 호출이 유일한 연산일 때 proc 호출 단축 방법을 사용하세요.
# 나쁜 예 names.map { |name| name.upcase } # 좋은 예 names.map(&:upcase)
-
단일 줄 블록에서
do...end
보단{...}
사용을 권장합니다. -
여러 줄 블록에서
{...}
보단do...end
사용을 권장합니다. -
가능하다면
return
을 생략하세요. -
가능하다면
self
를 생략하세요.# 나쁜 예 self.my_method # 좋은 예 my_method # 또한 좋은 예 attr_writer :name def my_method self.name = "Rafael" # `self`는 접근 제어자를 참조할 때 필요합니다. end
-
조건문에서 해당 결과값을 사용할 때 괄호로 그 식을 감싸주세요.
if (value = /foo/.match(string))
-
아직 초기화되지 않은 변수를 초기화할 때만
||=
를 사용하세요. -
boolean 변수를 초기화할 때
||=
사용을 피하세요.# 나쁜 예 - 값이 false더라도 true로 설정됩니다. @enabled ||= true # 좋은 예 @enabled = true if @enabled.nil? # 또한 유효한 예 - defined?를 사용한 회피 방법 @enabled = true unless defined?(@enabled)
-
메서드 이름과 여는 괄호 사이에 공백을 피하세요.
-
lambda
키워드보단 람다 표현식 사용을 권장합니다.# 나쁜 예 l = lambda { |a, b| a + b } l.call(1, 2) l = lambda do |a, b| tmp = a * 7 tmp * b / 50 end # 좋은 예 l = ->(a, b) { a + b } l.call(1, 2) l = ->(a, b) do tmp = a * 7 tmp * b / 50 end
-
Proc.new
보단proc
사용을 권장합니다. -
호출하지 않는 블록 파라미터 이름 앞에
_
를 붙이세요. 또는_
로만 이름을 지어도 괜찮습니다. -
유효하지 않은 데이터가 생길 수 있을 때 보호 구문(guard clause) 사용을 권장합니다. 보호 구문은 함수 맨 위에 가능한 한 빨리 빠져나올 수 있는 조건문을 배치하는 걸 말합니다.
# 나쁜 예 def compute_thing(thing) if thing[:foo] update_with_bar(thing) if thing[:foo][:bar] partial_compute(thing) else re_compute(thing) end end end # 좋은 예 def compute_thing(thing) return unless thing[:foo] update_with_bar(thing[:foo]) return re_compute(thing) unless thing[:foo][:bar] partial_compute(thing) end
-
옵션 해시보단 키워드 인자 사용을 권장합니다.
-
collect
보단map
,detect
보단find
,find_all
보단select
,length
보단size
사용을 권장합니다. -
DateTime
보단Time
사용을 권장합니다. -
"2018-03-20T11:16:39-04:00"
과 같이 ISO08601 포맷의 문자열을 받을 때Time.parse(foo)
대신Time.iso8601(f00)
사용을 권장합니다.
-
심볼, 메서드, 변수에 대해선
snake_case
를 사용하세요. -
클래스, 모듈에 대해선
CamelCase
를 사용하세요. 단, HTTP, RFC, XML과 같은 두문자어에 대해선 그대로 대문자로 유지하세요. -
파일 및 디렉토리 네이밍에 대해선
snake_case
를 사용하세요. (예시:hello_world.rb
) -
소스 파일 당 1개의 클래스 또는 모듈을 정의하세요. 단, 클래스/모듈의 이름을
CamelCase
에서snake_case
로 바꿔서 파일 이름을 지으세요. -
상수에 대해선
SCREAMING_SNAKE_CASE
를 사용하세요. -
짧은 블록을 넣을 때, 실제 인자값으로 들어갈 것에 따라 해당 인자의 이름을 지으세요. (예시:
|hash, e|
는 hash, element로 유추 가능) -
이항 연산자(예시:
+
,==
)를 정의할 때, 파라미터 이름을other
로 지으세요. (단,<<
와[]
는 의미가 달라지므로 이 규칙에선 제외합니다.) -
서술형 메서드(predicate method)는
?
와 함께 이름을 지으세요. 서술형 메서드는 boolean 값을 반환하는 메서드입니다. -
만약 메서드가 boolean 값을 반환하지 않는다면 메서드 이름 끝에
?
를 피하세요. -
메서드 이름 앞에
is_
를 붙이는 걸 피하세요.# 나쁜 예 def is_empty? end # 좋은 예 def empty? end
-
메서드 이름 앞에
get_
을 붙이는 걸 피하세요. -
bang 타입이 아닌 메서드가 없을 때 메서드 이름 끝에
!
을 붙이는 걸 피하세요. bang은 기존 메서드보다 더 위험한 버전의 메서드를 의미합니다. 예를 들어save
는 ActiveRecord에서 boolean 값을 반환하지만,save!
는 실패 시 예외를 발생시킵니다. -
매직 넘버 사용을 피하세요. 상수를 사용하고 그 상수에 의미 있는 이름을 지어주세요.
-
차별적 어원을 가진 (혹은 그렇게 해석될 수 있는) 명명법 사용을 피하세요.
-
읽는 사람이 놓치지 않게 주석에 관련된 문맥을 포함하세요.
-
주석과 코드를 계속 동기화하세요.
-
적절한 대문자 쓰기 및 구두점을 사용해 주석을 작성하세요. (역주: 주석은 항상 영어로 작성한다고 가정합니다.)
-
불필요한 주석 작성을 피하세요. 만약 코드 작동 방식히 명확하지 않다면 어떻게 코드가 돌아가는지가 아닌 왜 이렇게 코드를 작성했는지에 집중하세요.
-
클래스 메서드로만 이뤄져 있을 경우 클래스보단 모듈 사용을 권장합니다. 클래스는 해당 클래스로부터 인스턴스를 생성하는게 적절할 때만 사용해야 합니다.
-
module_function
보단extend self
사용을 권장합니다.# 나쁜 예 module SomeModule module_function def some_method end def some_other_method end end # 좋은 예 module SomeModule extend self def some_method end def some_other_method end end
-
클래스 메서드를 선언할 때
def self.
보단class << self
블록을 사용하세요. 그리고 한 블록 안에 클래스 메서드를 모두 모으세요.# 나쁜 예 class SomeClass def self.method1 end def method2 end private def method3 end def self.method4 # 이건 실제로 private 메서드로 동작하지 않습니다. end end # 좋은 예 class SomeClass class << self def method1 end private def method4 end end def method2 end private def method3 end end
-
클래스 계층 구조를 설계할 때 리스코프 치환 원칙을 준수하세요.
-
간단한 접근자와 설정자를 정의할 때
attr_accessor
,attr_reader
,attr_writer
를 사용하세요.# 나쁜 예 class Person def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end def first_name @first_name end def last_name @last_name end end # 좋은 예 class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end end
-
attr
보단attr_reader
와attr_accessor
사용을 권장합니다. -
클래스(
@@
) 변수 사용을 피하세요. (역주: 상속 계층에 있는 모든 클래스는 하나의 클래스 변수를 공유합니다.) -
public
,protect
,private
는 메서드 정의와 같은 깊이만큼 들여쓰세요. 가시성을 위해 위아래로 빈 줄을 띄어두세요.class SomeClass def public_method # ... end private def private_method # ... end def another_private_method # ... end end
-
alias
보다alias_method
사용을 권장합니다.
-
raise
를 사용해서 예외를 발생시키세요. -
2개 인자를 받는
raise
에서는RuntimeError
를 생략하세요.# 나쁜 예 raise RuntimeError, "message" # 좋은 예 - 기본 에러로 RuntimeError를 발생시킵니다. raise "message"
-
예외 인스턴스 생성해
raise
를 하는 것보다raise
인자값에 예외 클래스와 메시지를 넣어 사용하는 걸 권장합니다.# 나쁜 예 raise SomeException.new("message") # `raise SomeException.new("message"), backtrace`와 같이 사용할 수 없습니다. # 좋은 예 raise SomeException, "message" # `raise SomeException, "message", backtrace`로 사용할 수 있습니다.
-
ensure
블록에서 반환(return)하는 걸 피하세요. 만약 메서드 내ensure
블록에서 명시적으로 반환한다면 어떠한 예외들이 발생하더라도 에러가 처리되지 않은 채 반환하고, 전혀 예외가 발생하지 않았더라도 그 반환값을 도출합니다. 사실상 예외 발생 부분은 무시됩니다.# 나쁜 예 def foo raise ensure return "very bad idea" end
-
가능하다면 명시적으로
begin
블록을 사용하지 마세요.# 나쁜 예 def foo begin # main logic goes here rescue # failure handling goes here end end # 좋은 예 def foo # main logic goes here rescue # failure handling goes here end
-
아무것도 하지 않는
rescue
구문 사용을 피하세요.# 나쁜 예 begin # 에러 발생 rescue SomeError # rescue 절에서 아무것도 하지 않음 end # 나쁜 예 - `rescue nil`은 구문 에러를 포함한 모든 에러들을 내포해서 에러 추적을 어렵게 만듭니다. do_something rescue nil
-
예외 발생 시점에서 바로
rescue
사용을 피하세요.# 나쁜 예 - StandardError 클래스와 그 하위 클래스들의 예외들을 잡습니다. read_file rescue handle_error($!) # 좋은 예 - 오직 Errno::ENOENT 클래스와 그 하위 클래스들의 예외들을 잡습니다. def foo read_file rescue Errno::ENOENT => error handle_error(error) end
-
Exception
클래스를 사용한 예외 처리를 피하세요.# 나쁜 예 begin # exit를 호출하면 kill 시그널이 잡힙니다. (단, kill -9는 제외) exit rescue Exception puts "you didn't really want to exit, right?" # exception handling end # 좋은 예 begin # 아무것도 없는 rescue는 `Exception`이 아닌 `StandardError`를 기준으로 예외 처리를 합니다. rescue => error # exception handling end
-
새 예외 클래스를 선언하기보단 표준 라이브러리에 있는 예외 사용을 권장합니다.
-
예외 변수로 의미 있는 이름을 사용하세요.
# 나쁜 예 begin # an exception occurs here rescue => e # exception handling end # 좋은 예 begin # an exception occurs here rescue => error # exception handling end
-
배열 및 해시를 생성할 때 파라미터를 넘겨주지 않는다면 배열/해시 표기법을 사용하세요.
# 나쁜 예 arr = Array.new hash = Hash.new # 좋은 예 arr = [] hash = {}
-
%w
나%i
보단 배열 구문 그대로 사용하는 걸 권장합니다.# 나쁜 예 STATES = %w(draft open closed) # 좋은 예 STATES = ["draft", "open", "closed"]
-
여러 줄의 컬렉션에서는 마지막에 콤마를 붙이세요.
# 나쁜 예 { foo: :bar, baz: :toto } # 좋은 예 { foo: :bar, baz: :toto, }
-
배열에서 처음이나 마지막 원소를 접근할 때
[0]
,[-1]
보단first
,last
사용을 권장합니다. -
해시 키에 값이 바뀔 수 있는(mutable) 객체 사용을 피하세요.
-
해시의 모든 키가 심볼일 때 단축 구문을 사용하세요.
# 나쁜 예 { :a => 1, :b => 2 } # 좋은 예 { a: 1, b: 2 }
-
해시의 모든 키가 심볼이 아닐 때 단축 구문보단 로켓(
=>
) 구문 사용을 권장합니다.# 나쁜 예 { a: 1, "b" => 2 } # 좋은 예 { :a => 1, "b" => 2 }
-
Hash#has_key?
보단Hash#key?
사용을 권장합니다. -
Hash#has_value?
보단Hash#value?
사용을 권장합니다. -
반드시 존재해야 하는 해시 키를 다룰 때
Hash#fetch
를 사용하세요.heroes = { batman: "Bruce Wayne", superman: "Clark Kent" } # 나쁜 예 - 만약 실수하게 된다면 문제를 바로 알아차리지 못할 수 있습니다. heroes[:batman] # => "Bruce Wayne" heroes[:supermann] # => nil # 좋은 예 - fetch는 문제를 명확하게 하기 위해 KeyError를 발생시킵니다. heroes.fetch(:supermann)
-
해시 키에 기본값을 설정할 때 커스텀 로직보단
Hash#fetch
를 사용하세요.batman = { name: "Bruce Wayne", is_evil: false } # 나쁜 예 - 만약 거짓 값(nil, false)과 함께 || 연산자를 사용한다면 예상치 못한 결과를 얻을 수 있습니다. batman[:is_evil] || true # => true # 좋은 예 - fetch는 거짓 값에서 올바르게 동작합니다. batman.fetch(:is_evil, true) # => false
-
여는 괄호가 첫 번째 원소 줄과 다른 줄에 있을 때
]
과}
를 마지막 원소 뒷 줄에 배치하세요.# 나쁜 예 [ 1, 2] { a: 1, b: 2} # 좋은 예 [ 1, 2, ] { a: 1, b: 2, }
-
문자열 연결(concatenation)보단 문자열 보간(interpolation)과 문자열 형식화(formatting)를 권장합니다.
# 나쁜 예 email_with_name = user.name + " <" + user.email + ">" # 좋은 예 email_with_name = "#{user.name} <#{user.email}>" # 좋은 예 email_with_name = format("%s <%s>", user.name, user.email)
-
문자열 보간식 괄호 안에 공백 사용을 피하세요.
# 나쁜 예 "From: #{ user.first_name }, #{ user.last_name }" # 좋은 예 "From: #{user.first_name}, #{user.last_name}"
-
큰따옴표 문자열을 사용하세요.
# 나쁜 예 'Just some text' 'No special chars or interpolation' # 좋은 예 "Just some text" "No special chars or interpolation" "Every string in #{project} uses double_quotes"
-
?x
와 같은 단일 문자 구문 사용을 피하세요. -
문자열에 보간된 인스턴스 및 전역 변수 주위에
{}
를 사용하세요.class Person attr_reader :first_name, :last_name def initialize(first_name, last_name) @first_name = first_name @last_name = last_name end # 나쁜 예 - 유효하지만 어색합니다. def to_s "#@first_name #@last_name" end # 좋은 예 def to_s "#{@first_name} #{@last_name}" end end $global = 0 # 나쁜 예 puts "$global = #$global" # 괜찮지만 전역 변수를 쓰지 마세요. puts "$global = #{$global}"
-
보간한 객체에서
Object#to_s
사용을 피하세요.# 나쁜 예 message = "This is the #{result.to_s}." # 좋은 예 - 암시적으로 `result.to_s`가 호출됩니다. message = "This is the #{result}."
-
더 빠르고 전문화된 대안이 있는 경우
String#gsub
사용을 피하세요.url = "http://example.com" str = "lisp-case-rules" # 나쁜 예 url.gsub("http://", "https://") str.gsub("-", "_") str.gsub(/[aeiou]/, "") # 좋은 예 url.sub("http://", "https://") str.tr("-", "_") str.delete("aeiou")
-
여러 줄에 걸친 히어독(heredoc)을 사용할 때 공백 문자가 유지되는 걸 기억하세요. 과도한 공백 문자를 줄이기 위해 약간의 마진을 사용하는 건 좋은 방법입니다.
code = <<-END.gsub(/^\s+\|/, "") |def test | some_method | other_method |end END # => "def test\n some_method\n other_method\nend\n" # Rails에서 `#strip_heredoc`을 통해 위와 같은 결과를 얻을 수 있습니다. code = <<-END.strip_heredoc def test some_method other_method end END # => "def test\n some_method\n other_method\nend\n"
-
Ruby 2.3 이상에서는 "물결표 히어독" 구문 사용을 권장합니다. 이건 Rails에서의
strip_heredoc
과 동일하게 동작합니다.code = <<~END def test some_method other_method end END # => "def test\n some_method\n other_method\nend\n"
-
히어독 내용과 끝을 시작 지점과 맞추세요.
# 나쁜 예 class Foo def bar <<~SQL 'Hi' SQL end end # 좋은 예 class Foo def bar <<~SQL 'Hi' SQL end end # 나쁜 예 # 히어독 내용이 히어독 끝맺음보다 앞 쪽에 있습니다. foo arg, <<~EOS Hi EOS # 좋은 예 foo arg, <<~EOS Hi EOS # 좋은 예 foo arg, <<~EOS Hi EOS
-
문자열 내 단순 텍스트 찾기일 경우 정규 표현식을 쓰지 않는 걸 권장합니다.
string["text"]
-
캡쳐한 결과를 사용하지 않을 때 non-capturing 그룹을 사용하세요.
# 나쁜 예 /(first|second)/ # 좋은 예 /(?:first|second)/
-
그룹 내 값을 캡쳐할 때 Perl 기반의 레거시 변수보단
Regexp#match
사용을 권장합니다.# 나쁜 예 /(regexp)/ =~ string process $1 # 좋은 예 /(regexp)/.match(string)[1]
-
숫자 형식의 그룹보단 이름 기반의 그룹 사용을 권장합니다.
# 나쁜 예 /(regexp)/ =~ string ... process Regexp.last_match(1) # 좋은 예 /(?<meaningful_var>regexp)/ =~ string ... process meaningful_var
-
문자열의 처음부터 끝까지 찾을 때는
^
,$
보단\A
,\z
사용을 권장합니다.string = "some injection\nusername" string[/^username$/] # `^`, `$`는 줄의 시작부터 끝까지 찾습니다. string[/\Ausername\z/] # `\A`, `\z`는 문자열의 시작부터 끝까지 찾습니다.
-
문자열 보간과 큰따옴표가 필요한 단일 줄의 문자열에서만
%()
를 사용하세요. 여러 줄의 경우, 히어독 사용을 권장합니다. -
문자열에
'
와"
가 모두 있지 않다면%q
사용을 피하세요. 일반 문자열 리터럴이 더 읽기 쉬우며 문자열 내 이스케이프 할 단어가 많지 않다면 문자열 그대로 쓰는 것이 더 낫습니다. -
정규 표현식에 적어도 1개 이상의
/
가 있을 때만%r
을 사용하세요.# 나쁜 예 %r{\s+} # 좋은 예 %r{^/(.*)$} %r{^/blog/2011/(.*)$}
-
%s
사용을 피하세요. 공백이 있는 심볼을 만들기 위해선:"some string"
처럼 사용하세요. -
괄호가 포함된 정규 표현식만 제외하고 모든
%
리터럴 구분 괄호로()
사용을 권장합니다.()
,{}
,[]
,<>
순으로 리터럴 안에 포함되지 않은 괄호를 사용하세요.
-
여러분이 쓴 여타 다른 코드들처럼 테스트 코드를 취급하세요. 이 말은 즉슨 가독성, 유지관리성, 복잡성 등을 계속 염두에 두어야 한다는 걸 말합니다.
-
테스트 프레임워크로 Minitest를 권장합니다.
-
코드 각기의 스펙을 확인할 수 있게 테스트 케이스를 제한해서 구성하세요.
-
테스트 케이스의 시작(setup), 동작(action), 어서션(assertion) 구역을 빈 줄로 구분된 단락으로 구성하세요.
test "sending a password reset email clears the password hash and set a reset token" do user = User.create!(email: "bob@example.com") user.mark_as_verified user.send_password_reset_email assert_nil user.password_hash refute_nil user.reset_token end
-
복합적인 테스트 케이스는 단독으로 기능을 테스트하는 여러 개의 간단한 테스트들로 분리하세요.
-
테스트 케이스를 정의할 때
def test_foo
보단test "foo"
스타일의 구문 사용을 권장합니다. -
좀 더 설명적인 에러 메시지를 도출하는 어서션 메서드 사용을 권장합니다.
# 나쁜 예 assert user.valid? assert user.name == "tobi" # 좋은 예 assert_predicate user, :valid? assert_equal "tobi", user.name
-
assert_nothing_raised
사용을 피하세요. 대신 긍정 어서션을 사용하세요. -
기대문(expectation)보단 어서션 사용을 권장합니다. 기대문은 특히 싱글톤 객체의 조합에서 더 불확실한 테스트로 이끕니다.
# 나쁜 예 StatsD.expects(:increment).with("metric") do_something # 좋은 예 assert_statsd_increment("metric") do do_something end