yield_self를 사용하여 특정 조건에서만 where 절을 사용하도록 설정한 이야기

코딩하는 동안 특정 조건에서만 where 절을 활성화하고 싶습니다! 라고 생각하는 일이 있었습니다.
처음에는 단순히 if문으로 분기하고 있었습니다만, 너무 어색하다고 생각했기 때문에 분투해 보았습니다.

샘플 코드
if kind == 'hoge'
   User.where(kind: kind)
else
   User.all
end

ActiveRecord 확장



이 기사을 참고로 ActiveRecord를 확장해 보았습니다.
기사의 확장 코드는 Rails5에서 작동하지 않았기 때문에 Rails의 코드를 읽으면서 다음과 같이 확장했습니다.

확장 코드
ActiveRecord::QueryMethods::WhereChain.include(Module.new do
  def if(condition, opts, *rest)
    return @scope unless condition

    @scope.where_clause += @scope.send(:where_clause_factory).build(opts, rest)
    @scope
  end
end)

샘플 코드
kind = 'hoge'
User.where.if(kind == 'hoge', kind: kind).to_sql
=> "SELECT `users`.* FROM `users` WHERE `users`.`kind` = hoge"
kind = 'fuga'
User.where.if(kind == 'hoge', kind: kind).to_sql
=> "SELECT `users`.* FROM `users`"

좋은 느낌입니다

리뷰에서 거부됨



도야 얼굴로 풀릭을 보내는 것도 리뷰에서 아래의 지적을 받았습니다.
  • Rails 버전이 올라갈 때이 코드가 움직일 보장이 없습니다.
  • 현재 Rails4 확장 코드가 Rails5에서 작동하지 않았습니다

  • Rails 표준 기능이 아니기 때문에 처음으로이 코드를 본 사람이 당황하지 않을까
  • 놀람 최소의 원칙은 중요하다


  • 가장. . . 라고 밖에 말할 수 없었기 때문에 확장 코드의 채용은 배웅했습니다.

    yield_self 사용



    이제 if문으로 쓸까라고 포기하고 있었는데, yield_self를 사용해서는 어떨까라고 조언을 받았습니다.
    yield_self를 사용한 코드가 여기.

    샘플 코드
    kind = 'hoge'
    User.yield_self{|scope| kind == 'hoge' ? scope.where(kind: kind) : scope.all }.to_sql
    => "SELECT `users`.* FROM `users` WHERE `users`.`kind` = hoge"
    kind = 'fuga'
    User.yield_self{|scope| kind == 'hoge' ? scope.where(kind: kind) : scope.all }.to_sql
    => "SELECT `users`.* FROM `users`"
    

    다음과 같이 QueryMethod를 연결할 수도 있습니다.

    샘플 코드
    kind = 'hoge'
    User.yield_self{|scope| kind == 'hoge' ? scope.where(kind: kind) : scope }.where(age: 0..10).to_sql
    => "SELECT `users`.* FROM `users` WHERE `users`.`kind` = hoge AND (`users`.`age` BETWEEN 0 AND 10)"
    kind = 'fuga'
    User.yield_self{|scope| kind == 'hoge' ? scope.where(kind: kind) : scope }.where(age: 0..10).to_sql
    => "SELECT `users`.* FROM `users` WHERE (`users`.`age` BETWEEN 0 AND 10)"
    

    yield_self를 사용한 코드는 무사히 LGTM 받았습니다

    요약


  • yield_self 편리
  • 팀 개발에서 확장을 앞두는 것이 무난할지도
  • 좋은 웹페이지 즐겨찾기