[ruby] 루비 탭 방식의 장점
방금 블로그 기사를 읽고 있는데 저자가 tap
다음과 같은 스 니펫에 사용하는 것을 발견했습니다 .
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
내 질문은 정확히 무엇을 사용하여 얻을 수있는 이점이나 이점 tap
입니까? 그냥 할 수는 없습니다.
user = User.new
user.username = "foobar"
user.save!
또는 더 나은 방법 :
user = User.create! username: "foobar"
답변
독자가 만날 때 :
user = User.new
user.username = "foobar"
user.save!
그들은 세 줄을 모두 따라야 할 것이며라는 인스턴스를 만드는 것임을 인식해야 할 것입니다 user
.
다음과 같은 경우 :
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
그러면 즉시 명확해질 것입니다. 독자는 인스턴스 user
가 생성 되었음을 알기 위해 블록 내부에있는 내용을 읽을 필요가 없습니다 .
답변
탭을 사용하는 또 다른 경우는 객체를 반환하기 전에 조작하는 것입니다.
그래서이 대신 :
def some_method
...
some_object.serialize
some_object
end
추가 줄을 절약 할 수 있습니다.
def some_method
...
some_object.tap{ |o| o.serialize }
end
어떤 상황에서이 기술은 한 줄 이상을 절약하고 코드를 더 간결하게 만들 수 있습니다.
답변
블로거처럼 탭을 사용하는 것은 단순히 편리한 방법입니다. 귀하의 예에서는 과도했을 수 있지만 사용자와 많은 작업을 수행하려는 경우 탭은 분명히 더 깨끗한 인터페이스를 제공 할 수 있습니다. 따라서 아마도 다음과 같은 예에서 더 나을 수 있습니다.
user = User.new.tap do |u|
u.build_profile
u.process_credit_card
u.ship_out_item
u.send_email_confirmation
u.blahblahyougetmypoint
end
위의 방법을 사용하면 모든 메서드가 동일한 개체 (이 예제의 사용자)를 참조한다는 점에서 이러한 모든 메서드가 함께 그룹화되어 있음을 쉽게 확인할 수 있습니다. 대안은 다음과 같습니다.
user = User.new
user.build_profile
user.process_credit_card
user.ship_out_item
user.send_email_confirmation
user.blahblahyougetmypoint
다시 말하지만, 이것은 논쟁의 여지가 있습니다. 그러나 두 번째 버전이 조금 더 복잡해 보이고 모든 메서드가 동일한 객체에서 호출되고 있는지 확인하기 위해 조금 더 사람이 파싱해야하는 경우가 있습니다.
답변
이것은 일련의 연결 범위 를 디버깅하는 데 유용 할 수 있습니다 .ActiveRecord
User
.active .tap { |users| puts "Users so far: #{users.size}" }
.non_admin .tap { |users| puts "Users so far: #{users.size}" }
.at_least_years_old(25) .tap { |users| puts "Users so far: #{users.size}" }
.residing_in('USA')
따라서 로컬 변수에 아무것도 저장하지 않고 원래 코드를 많이 변경하지 않고도 체인의 어느 지점에서나 디버그하기가 매우 쉽습니다.
마지막으로, 정상적인 코드 실행을 방해하지 않고 빠르고 눈에 잘 띄지 않는 디버깅 방법 으로 사용합니다 .
def rockwell_retro_encabulate
provide_inverse_reactive_current
synchronize_cardinal_graham_meters
@result.tap(&method(:puts))
# Will debug `@result` just before returning it.
end
답변
함수 내에서 예제 시각화
def make_user(name)
user = User.new
user.username = name
user.save!
end
이 접근 방식에는 기본적으로 암시 적 반환 값 이라는 큰 유지 관리 위험이 있습니다.
이 코드에서는 save!
저장된 사용자 를 반환해야합니다. 그러나 다른 오리를 사용하거나 현재의 오리를 사용하는 경우 완료 상태 보고서와 같은 다른 항목을 얻을 수 있습니다. 따라서 오리를 변경하면 코드가 깨질 수 있습니다 user
. 일반 또는 탭을 사용 하여 반환 값을 확인하면 발생하지 않는 일 입니다.
나는 이와 같은 사고를 꽤 자주 보았는데, 특히 하나의 어두운 버그가있는 모서리를 제외하고는 일반적으로 반환 값이 사용되지 않는 함수에서 그렇습니다.
암시 적 반환 값은 초보자가 효과를 알지 못한 채 마지막 줄 뒤에 새 코드를 추가하는 것을 깨뜨리는 경향이 있습니다. 그들은 위의 코드가 실제로 무엇을 의미하는지 알지 못합니다.
def make_user(name)
user = User.new
user.username = name
return user.save! # notice something different now?
end
답변
사용자 이름을 설정 한 후 사용자를 반환하려면 수행해야합니다.
user = User.new
user.username = 'foobar'
user
tap
그 어색한 귀환을 당신 과 함께
User.new.tap do |user|
user.username = 'foobar'
end
답변
변수의 범위가 실제로 필요한 부분으로 만 제한되기 때문에 코드가 덜 복잡해집니다. 또한 블록 내의 들여 쓰기는 관련 코드를 함께 유지하여 코드를 더 읽기 쉽게 만듭니다.
자신을 블록에 양보 한 다음 자신을 반환합니다. 이 방법의 주요 목적은 체인 내의 중간 결과에 대한 작업을 수행하기 위해 메서드 체인을 “탭”하는 것입니다.
rails 소스 코드에서 tap
usage를 검색 하면 흥미로운 사용법을 찾을 수 있습니다. 다음은 사용 방법에 대한 몇 가지 아이디어를 제공하는 몇 가지 항목 (전체 목록이 아님)입니다.
-
특정 조건에 따라 배열에 요소 추가
%w( annotations ... routes tmp ).tap { |arr| arr << 'statistics' if Rake.application.current_scope.empty? }.each do |task| ... end
-
배열 초기화 및 반환
[].tap do |msg| msg << "EXPLAIN for: #{sql}" ... msg << connection.explain(sql, bind) end.join("\n")
-
코드를 더 읽기 쉽게 만들기위한 구문 설탕-아래 예에서 변수
hash
를server
사용하고 코드의 의도를 더 명확하게 만들 수 있습니다.def select(*args, &block) dup.tap { |hash| hash.select!(*args, &block) } end
-
새로 생성 된 객체에서 메서드를 초기화 / 호출합니다.
Rails::Server.new.tap do |server| require APP_PATH Dir.chdir(Rails.application.root) server.start end
아래는 테스트 파일의 예입니다.
@pirate = Pirate.new.tap do |pirate| pirate.catchphrase = "Don't call me!" pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}] pirate.save! end
-
yield
임시 변수를 사용하지 않고 호출 결과에 따라 조치를 취 합니다.yield.tap do |rendered_partial| collection_cache.write(key, rendered_partial, cache_options) end