루비 탭 방식의 장점
방금 블로그 기사를 읽다가 작가가 사용한 것을 발견했습니다.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생성됩니다.
이 기능은 일련의 연결된 범위를 디버깅할 때 유용합니다.
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 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
다시 말하지만, 이것은 논쟁의 여지가 있지만, 두 번째 버전이 조금 더 혼란스러워 보이고, 모든 방법이 동일한 개체에서 호출되는지 확인하는 데 약간 더 많은 사람의 구문 분석이 필요하다는 경우가 있습니다.
사용자 이름을 설정한 후 사용자를 반환하려면 다음 작업을 수행해야 합니다.
user = User.new
user.username = 'foobar'
user
와 함께tap은 그 어색한 답례를 할 수 .
User.new.tap do |user|
user.username = 'foobar'
end
변수의 범위가 실제로 필요한 부분으로만 제한되기 때문에 코드가 덜 복잡해집니다.또한 블록 내 들여쓰기는 관련 코드를 함께 보관함으로써 코드를 더 쉽게 읽을 수 있게 합니다.
설명:
블록에 자신을 양보한 다음 자신을 반환합니다.이 방법의 주요 목적은 메소드 체인을 "투입"하여 체인 내의 중간 결과에 대한 작업을 수행하는 것입니다.
레일 소스 코드에서 사용법을 검색하면 흥미로운 사용법을 찾을 수 있습니다.다음은 사용 방법에 대한 몇 가지 아이디어를 제공하는 몇 가지 항목(전체 목록이 아님)입니다.
특정 조건에 따라 배열에 요소 추가
%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
함수 내에서 예제 시각화
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
@sawa의 답변에 대한 변형:
언급한 와 같이, 이듯언했급사를 사용하여, 용미이를 사용합니다.tap코드의 의도를 파악하는 데 도움이 됩니다(코드를 더 압축할 필요는 없음).
다음 두 함수는 동일하게 길지만 첫 번째 함수에서는 처음에 빈 해시를 초기화한 이유를 끝까지 읽어야 합니다.
def tapping1
# setting up a hash
h = {}
# working on it
h[:one] = 1
h[:two] = 2
# returning the hash
h
end
반면, 여기서는 초기화되는 해시가 블록의 출력(이 경우 함수의 반환 값)이 된다는 것을 처음부터 알고 있습니다.
def tapping2
# a hash will be returned at the end of this block;
# all work will occur inside
Hash.new.tap do |h|
h[:one] = 1
h[:two] = 2
end
end
통화 연결 도우미입니다.지정된 블록으로 객체를 전달하고 블록이 완료된 후 객체를 반환합니다.
an_object.tap do |o|
# do stuff with an_object, which is in o #
end ===> an_object
이점은 블록이 다른 결과를 반환하더라도 탭이 호출된 개체를 항상 반환한다는 것입니다.따라서 흐름을 끊지 않고 기존 메소드 파이프라인의 중간에 탭 블록을 삽입할 수 있습니다.
사용하는 것에는 이점이 없다고 생각합니다.tap@sawa가 지적한 유일한 잠재적 이점은 다음과 같습니다. "인스턴스 사용자가 생성되었음을 알기 위해 블록 내부의 내용을 읽을 필요가 없습니다."하지만 그 시점에서 단순하지 않은 레코드 생성 논리를 수행하는 경우 해당 논리를 자체 방법으로 추출하여 의도를 더 잘 전달할 수 있다는 주장이 제기될 수 있습니다.
라는 의견을 가지고 있습니다.tap는 코드의 가독성에 불필요한 부담이 되며, Extract Method와 같은 더 나은 기술 없이 수행되거나 대체될 수 있습니다.
하는 동안에tap편리한 방법이고, 개인적인 선호이기도 합니다.줘를 tap시도해보세요. 그러면 탭을 사용하지 않고 코드를 작성하고, 어떤 방법이 다른 방법보다 마음에 드는지 확인하세요.
방법을 읽는 것이 얼마나 어려운지를 측정하는 flog라는 도구가 있습니다."점수가 높을수록 코드는 더 고통스럽습니다."
def with_tap
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
end
def without_tap
user = User.new
user.username = "foobar"
user.save!
end
def using_create
user = User.create! username: "foobar"
end
그리고 플로그의 결과에 따르면 방법은tap그것이 가장 읽기 어렵습니다(그리고 저는 그것에 동의합니다).
4.5: main#with_tap temp.rb:1-4
2.4: assignment
1.3: save!
1.3: new
1.1: branch
1.1: tap
3.1: main#without_tap temp.rb:8-11
2.2: assignment
1.1: new
1.1: save!
1.6: main#using_create temp.rb:14-16
1.1: assignment
1.1: create!
할 수 있는 장소와 할 수 수 .tap지금까지 저는 다음과 같은 두 가지 용도만 찾았습니다.tap.
이 방법의 주요 목적은 메소드 체인을 이용하여 체인 내의 중간 결과에 대한 작업을 수행하는 것입니다.
(1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
tap { |x| puts "array: #{x.inspect}" }.
select { |x| x%2 == 0 }.
tap { |x| puts "evens: #{x.inspect}" }.
map { |x| x*x }.
tap { |x| puts "squares: #{x.inspect}" }
어떤 개체에 대해 메소드를 호출하고 반환 값이 원하는 값이 아니라는 것을 발견한 적이 있습니까?해시에 저장된 매개 변수 집합에 임의 값을 추가하려고 했을 수 있습니다.Hash.[]로 업데이트하지만 params 해시 대신 backbar를 받기 때문에 명시적으로 반환해야 합니다.
def update_params(params)
params[:foo] = 'bar'
params
end
이상황극위해기복하을,위해,tap방법이 실행됩니다.객체에서 호출한 다음 실행할 코드가 있는 블록을 탭하면 됩니다.객체가 블록에 반환된 다음 반환됩니다.예
def update_params(params)
params.tap {|p| p[:foo] = 'bar' }
end
그 밖에도 수십 가지의 사용 사례가 있으니 직접 찾아보세요 :)
다음과 같습니다.
API 도킹 개체 탭
사용해야 할 다섯 가지 방법
맞요: 사법용의 .tap당신의 예는 무의미하고 아마도 당신의 대안들보다 덜 깨끗할 것입니다.
언급했듯이, Rebitzele 지적이듯했이,▁as,tap는 현재 개체에 대한 짧은 참조를 만드는 데 사용되는 편리한 방법일 뿐입니다.
다음을 위한 한 가지 유용한 사용 사례tap디버깅을 위한 것입니다. 개체를 수정하고 현재 상태를 인쇄한 다음 동일한 블록에서 개체를 계속 수정할 수 있습니다.예를 들어 http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions 을 참조하십시오.
나는 가끔 사용하는 것을 좋아합니다.tap내부 메서드는 조건부로 조기에 반환하고 그렇지 않으면 현재 개체를 반환합니다.
탭을 사용하여 코드를 보다 모듈화할 수 있으며 로컬 변수를 보다 효율적으로 관리할 수 있습니다.예를 들어, 다음 코드에서는 메소드의 범위에서 새로 만든 개체에 로컬 변수를 할당할 필요가 없습니다.블럭 변수 u는 블럭 내에서 범위가 지정됩니다.그것은 사실 루비 코드의 아름다움 중 하나입니다.
def a_method
...
name = "foobar"
...
return User.new.tap do |u|
u.username = name
u.save!
end
end
우리가 사용할 수 있는 레일에서tap매개 변수를 명시적으로 화이트리스트에 추가합니다.
def client_params
params.require(:client).permit(:name).tap do |whitelist|
whitelist[:name] = params[:client][:name]
end
end
제가 사용한 또 다른 예를 들어보겠습니다.사용자를 위해 저장하는 데 필요한 매개 변수를 반환하는 user_params 메서드가 있습니다(이것은 Rails 프로젝트입니다).
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
)
end
루비가 마지막 라인의 출력을 반환하는 것 외에는 아무것도 반환하지 않는 것을 볼 수 있습니다.
그런 다음 시간이 지나면 조건부로 새 속성을 추가해야 했습니다.그래서 다음과 같은 것으로 변경했습니다.
def user_params
u_params = params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
)
u_params[:time_zone] = address_timezone if u_params[:address_attributes]
u_params
end
여기서 탭을 사용하여 로컬 변수를 제거하고 반환을 제거할 수 있습니다.
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
).tap do |u_params|
u_params[:time_zone] = address_timezone if u_params[:address_attributes]
end
end
기능적 프로그래밍 패턴이 모범 사례가 되고 있는 세계에서 (https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming), 를 볼 수 있습니다.tap의 입장에서.map실제로 단일 값으로 변환 체인의 데이터를 수정할 수 있습니다.
transformed_array = array.map(&:first_transformation).map(&:second_transformation)
transformed_value = item.tap(&:first_transformation).tap(&:second_transformation)
을 선언할 필요가 .item여기서 여러 번
무엇이 다른가요?
코드 가독성 측면에서의 차이는 순전히 스타일적인 것입니다.
코드 워크스루:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
핵심 사항:
- 사용 방법에 주목하십시오.
u변수가 이제 블록 매개 변수로 사용됩니까? - 블이완료후된록후,▁the▁after된▁is▁block료,
user변수는 이제 사용자(사용자 이름: 'foobar' 및 저장된 사용자)를 가리켜야 합니다. - 그것은 그저 즐겁고 읽기 쉽습니다.
API 설명서
다음은 읽기 쉬운 소스 코드 버전입니다.
class Object
def tap
yield self
self
end
end
자세한 내용은 다음 링크를 참조하십시오.
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
위의 답변과는 별도로, 저는 Rspec을 작성하면서 스텁과 조롱에 탭을 사용했습니다.
例시:가 여러 해야 할 때,안 .여러 개의 인수를 사용하여 스텁하고 조롱해야 하는 복잡한 쿼리가 있을 때 이를 놓쳐서는 안 됩니다.여기서 대안은 다음을 사용하는 것입니다.receive_message_chain(그러나 세부 정보가 부족합니다.)
# Query
Product
.joins(:bill)
.where("products.availability = ?", 1)
.where("bills.status = ?", "paid")
.select("products.id", "bills.amount")
.first
# RSpecs
product_double = double('product')
expect(Product).to receive(:joins).with(:bill).and_return(product_double.tap do |product_scope|
expect(product_scope).to receive(:where).with("products.availability = ?", 1).and_return(product_scope)
expect(product_scope).to receive(:where).with("bills.status = ?", "paid").and_return(product_scope)
expect(product_scope).to receive(:select).with("products.id", "bills.amount").and_return(product_scope)
expect(product_scope).to receive(:first).and_return({ id: 1, amount: 100 })
end)
# Alternative way by using `receive_message_chain`
expect(Product).to receive_message_chain(:joins, :where, :where, :select).and_return({ id: 1, amount: 100 })
언급URL : https://stackoverflow.com/questions/17493080/advantage-of-tap-method-in-ruby
'programing' 카테고리의 다른 글
| web.xml과 같이 spring-boot servlet을 구성하는 방법은 무엇입니까? (0) | 2023.06.21 |
|---|---|
| (-) 기호를 사용하여 두 표의 전화 번호 비교 (0) | 2023.06.21 |
| 페이지 로드 후 Angular 4에서 (DOM) Element의 너비를 가져오는 방법 (0) | 2023.06.21 |
| 계산된 속성에서 반환된 어레이의 개체 편집 중 (0) | 2023.06.21 |
| 두 날짜 사이의 날짜? (0) | 2023.06.16 |