Computer >> 컴퓨터 >  >> 프로그래밍 >> Ruby

Rails 5.1에서 놓치기 쉬운 숨은 기능들

지난주 RailsConf 2017에서 드디어 Rails 5.1이 출시되었습니다.

발표 소식을 쫓아오셨다면 이미 큰 변화들을 접하셨을 겁니다. 최신 JavaScript와의 향상된 통합, 암호화된 시크릿(encrypted secrets) 지원, 그리고 시스템 테스트 도입이 대표적입니다. 개인적으로 가장 반가웠던 점은 드디어 form_forform_tag라는 애매한 조합을 정리하고 하나로 통합한 form_with가 등장했다는 것입니다. 빨리 사용해 보고 싶네요.

하지만 제가 Rails를 사랑하는 이유는 화려한 신기능 때문만은 아닙니다. 바로 꾸준히 쌓여가는 작고 섬세한 개선들 때문입니다. 일상적인 개발 경험을 한층 편안하게 만들어 주는 이런 디테일 덕분에 Rails 앱을 작성할 때마다 기분이 좋아집니다. 그리고 Rails 5.1에는 이런 작은 개선들이 가득합니다.

더 일관성 있는 태그 헬퍼

Rails의 태그 헬퍼인 tagcontent_tag를 사용해 보신 적이 있나요?

<%= content_tag :p, @user.name, class: "name" %>

Rails 5.1에는 새로운 태그 헬퍼 문법이 추가되었습니다.

이제 tag.divtag.br처럼 호출하면 됩니다. 인자 순서를 외우거나 서로 다른 두 메서드를 번갈아 쓰느라 고민할 필요가 없어졌죠.

<%= tag.p @user.name, class: "name" %>
<%= tag.br %>

새로운 태그 헬퍼는 기본적으로 HTML5를 지원하며, 심지어 직접 만든 커스텀 엘리먼트도 생성할 수 있습니다:

<%= tag.pea @user.name, class: "name" %>
<!-- <pea class="name">Justin Weiss</pea> 로 렌더링됩니다 -->

단순한 차이 그 이상을 검증하세요

assert_difference는 정말 유용한 메서드입니다. 이 메서드가 등장하기 전에는 테스트 코드에서 지역 변수를 여기저기 옮겨 담느라 너무 많은 시간을 낭비했습니다:

old_score = @user.score
@user.answer_question!(...)
assert_equal old_score + 10, @user.score

assert_difference를 사용하면 테스트의 의도가 훨씬 명확해집니다:

assert_difference "@user.score", 10 do
  @user.answer_question!(...)
end

Rails 5.1의 assert_changes는 여기서 한 걸음 더 나아갑니다.

assert_difference는 오직 값의 증감만 확인할 수 있습니다. 반면 assert_changes는 두 문자열 사이의 변경, nil과 특정 값 사이의 전환처럼 숫자가 아닌 변화도 검증할 수 있습니다:

assert_changes "users(:justin).name", from: "Justin", to: "Bob" do
  @user.update_attributes(name: "Bob")
end

문자열 대신 람다(lambda)를 넘길 수도 있습니다:

assert_changes -> { users(:justin).name }, from: "Justin", to: "Bob" do
  @user.update_attributes(name: "Bob")
end

to: 옵션에는 === 연산자로 비교 가능한 무엇이든 들어갈 수 있습니다. 값을 구체적으로 알지 못하더라도 어떤 '종류'인지만 알면 되므로 아주 유용합니다:

assert_changes -> { users(:justin).updated_at }, to: ActiveSupport::TimeWithZone do
  @user.update_attributes(name: "Bob")
end

모든 것을 위임하세요

Rails 코드를 살펴보면 delegate 메서드를 종종 발견하게 됩니다. 위임(delegation)은 상속 없이 다른 클래스의 동작 위에 새로운 동작을 얹고 싶을 때 유용합니다:

class Player
  delegate :id, :name, to: :@user

  def initialize(user)
    @user = user
  end

  def points
    Game.points_for_user(@user.id)
  end
end

하지만 때로는 감싼 클래스에 모든 것을 그대로 전달하고 싶은 경우도 있습니다.

Ruby에서는 method_missing이나 SimpleDelegator 등으로 이를 구현할 수 있습니다. 하지만 기존의 delegate 메서드와 더 자연스럽게 어울리도록, Rails 5.1에는 delegate_missing_to가 추가되었습니다. 이름 그대로 동작합니다:

class Player
  delegate_missing_to :@user

  def initialize(user)
    @user = user
  end

  def points
    Game.points_for_user(@user.id)
  end
end

이제 Player 클래스에 정의되지 않은 메서드 호출은 모두 @user 객체에서 대신 찾게 됩니다.

보너스: alias_method_chain이 사라졌습니다!

제가 좋아하는 Ruby 2 기능 중 하나는 Module#prepend입니다. 너무 마음에 들어서 관련 글도 쓸 정도였는데, 특히 Module#prepend가 결국 alias_method_chain을 대체하기를 바란다는 내용이었습니다.

그리고 Rails 5.1부터 alias_method_chain은 공식적으로 제거되었고, prepend로 완전히 대체되었습니다.

Rails의 새 버전은 언제나 설레게 만듭니다. 하지만 Rails의 진짜 아름다움은 디테일에서 나옵니다. 매일 작성하는 코드를 더 즐겁게 만들어 주는 작은 변화들이죠.

그렇다면 이런 변화들은 어떻게 찾을 수 있을까요? 체인지로그(changelog)를 샅샅이 읽어보세요. 흥미로운 풀 리퀘스트를 살펴보세요. 5.1의 새로운 작은 기능 중 어떤 것이 여러분의 개발 생활을 한결 편하게 해 줄지 직접 찾아보세요.

그리고 멋진 발견을 하셨다면 혼자 간직하지 마세요. 이곳에 공유해 주세요. 우리 모두 새로운 것을 배울 수 있으니까요!