루비 탭 방식의 장점
방금 블로그 기사를 읽다가 작가가 사용한 것을 발견했습니다.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 |