ข้อดีของวิธีการแตะในทับทิม


116

ฉันเพิ่งอ่านบทความในบล็อกและสังเกตเห็นว่าผู้เขียนใช้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"

คำตอบ:


103

เมื่อผู้อ่านพบ:

user = User.new
user.username = "foobar"
user.save!

userพวกเขาจะต้องทำตามทั้งสามเส้นแล้วรู้ว่ามันเป็นเพียงแค่การสร้างอินสแตนซ์ที่มีชื่อว่า

ถ้าเป็น:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

สิ่งนั้นจะชัดเจนทันที ผู้อ่านไม่จำเป็นต้องอ่านสิ่งที่อยู่ในบล็อกเพื่อให้ทราบว่ามีการuserสร้างอินสแตนซ์


3
@Matt: และทิ้งนิยามตัวแปรใด ๆ ที่ทำในกระบวนการเมื่อบล็อกทำงานเสร็จแล้ว และควรมีเพียงวิธีเดียวที่เรียกว่าวัตถุคุณสามารถเขียนได้User.new.tap &:foobar
Boris Stitnicky

28
ฉันไม่พบว่าการใช้นี้น่าสนใจมาก - เนื้อหาไม่สามารถอ่านได้อีกต่อไปนั่นคือเหตุผลที่อยู่ในหน้านี้ หากไม่มีข้อโต้แย้งในการอ่านที่ชัดเจนฉันจึงเปรียบเทียบความเร็ว การทดสอบของฉันระบุรันไทม์เพิ่มเติม 45% สำหรับการใช้งานอย่างง่ายข้างต้นลดน้อยลงเมื่อจำนวนเซ็ตเตอร์บนอ็อบเจ็กต์เพิ่มขึ้น - ประมาณ 10 ตัวขึ้นไปและความแตกต่างของรันไทม์มีค่าเล็กน้อย (YMMV) 'การแตะ' เป็นห่วงโซ่ของวิธีการในระหว่างการแก้ไขจุดบกพร่องดูเหมือนจะชนะไม่เช่นนั้นฉันต้องการมากกว่านี้เพื่อชักชวนฉัน
dinman2022

7
ฉันคิดว่าบางอย่างuser = User.create!(username: 'foobar')จะชัดเจนและสั้นที่สุดในกรณีนี้ :) - ตัวอย่างสุดท้ายจากคำถาม
ลี

4
คำตอบนี้ขัดแย้งในตัวเองและไม่สมเหตุสมผล เกิดขึ้นมากกว่า "การสร้างอินสแตนซ์ที่มีชื่อuser" นอกจากนี้ข้อโต้แย้งที่ว่า "ผู้อ่านจะไม่ต้องอ่านสิ่งที่อยู่ในบล็อกเพื่อให้ทราบว่ามีการuserสร้างอินสแตนซ์" ไม่มีน้ำหนักเนื่องจากในบล็อกโค้ดแรกผู้อ่านต้องอ่านเพียงบรรทัดแรกเท่านั้น "เพื่อให้ทราบว่ามีการuserสร้างอินสแตนซ์"
Jackson

5
ทำไมฉันถึงมาที่นี่? เหตุใดเราทุกคนจึงค้นหาสิ่งที่แตะอยู่ที่นี่
Eddie

37

อีกกรณีหนึ่งที่จะใช้การแตะคือการจัดการกับวัตถุก่อนที่จะส่งคืน

แทนสิ่งนี้:

def some_method
  ...
  some_object.serialize
  some_object
end

เราสามารถบันทึกบรรทัดพิเศษ:

def some_method
  ...
  some_object.tap{ |o| o.serialize }
end

ในบางสถานการณ์เทคนิคนี้สามารถบันทึกได้มากกว่าหนึ่งบรรทัดและทำให้โค้ดมีขนาดกะทัดรัดมากขึ้น


24
ฉันจะยิ่งรุนแรงมากขึ้น:some_object.tap(&:serialize)
amencarini

28

การใช้การแตะเหมือนที่บล็อกเกอร์ทำนั้นเป็นเพียงวิธีการที่สะดวกสบาย ในตัวอย่างของคุณอาจใช้งานมากเกินไป แต่ในกรณีที่คุณต้องการทำหลาย ๆ อย่างร่วมกับผู้ใช้การแตะสามารถให้อินเทอร์เฟซที่ดูสะอาดตาได้ ดังนั้นบางทีอาจจะดีกว่าในตัวอย่างดังนี้:

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

อีกครั้งนี่เป็นที่ถกเถียงกัน - แต่ในกรณีนี้สามารถทำให้เวอร์ชันที่สองดูยุ่งเหยิงกว่าเล็กน้อยและใช้เวลาในการแยกวิเคราะห์ของมนุษย์มากขึ้นเล็กน้อยเพื่อดูว่าวิธีการทั้งหมดถูกเรียกใช้บนวัตถุเดียวกัน


2
นี่เป็นเพียงตัวอย่างอีกต่อไปของสิ่งที่ OP ตั้งคำถามไว้แล้วคุณยังสามารถทำทุกข้อข้างต้นได้ด้วยuser = User.new, user.do_something, user.do_another_thing... คุณช่วยขยายความได้ไหมว่าทำไมถึงทำเช่นนี้
แมตต์

แม้ว่าตัวอย่างจะเหมือนกัน แต่เมื่อแสดงในรูปแบบที่ยาวขึ้น แต่เราสามารถดูได้ว่าการใช้การแตะอาจดึงดูดความสวยงามสำหรับกรณีนี้ได้อย่างไร ฉันจะเพิ่มการแก้ไขเพื่อช่วยสาธิต
Rebitzele

ไม่เห็นเหมือนกัน การใช้tapไม่เคยเพิ่มประโยชน์ใด ๆ ในประสบการณ์ของฉัน การสร้างและใช้งานuserตัวแปรโลคัลนั้นสะอาดกว่ามากและอ่านได้ในความคิดของฉัน
gylaz

สองคนนั้นไม่เทียบเท่ากัน หากคุณทำu = user = User.newแล้วใช้uสำหรับการเรียกการตั้งค่าจะเป็นไปตามตัวอย่างแรกมากกว่า
Gerry

26

สิ่งนี้จะเป็นประโยชน์กับการดีบักชุด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

14

แสดงภาพตัวอย่างของคุณภายในฟังก์ชัน

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

1
ไม่มีความแตกต่างอย่างแน่นอนระหว่างสองตัวอย่างของคุณ คุณหมายถึงจะกลับมาuser?
Bryan Ash

1
นั่นคือประเด็นของเขา: ตัวอย่างเหมือนกันทุกประการมีเพียงเรื่องเดียวที่ชัดเจนเกี่ยวกับผลตอบแทน ประเด็นของเขาคือสิ่งนี้สามารถหลีกเลี่ยงได้โดยใช้การแตะ:User.new.tap{ |u| u.username = name; u.save! }
Obversity

14

หากคุณต้องการส่งคืนผู้ใช้หลังจากตั้งชื่อผู้ใช้คุณจะต้องดำเนินการ

user = User.new
user.username = 'foobar'
user

ด้วยtapคุณสามารถบันทึกผลตอบแทนที่น่าอึดอัดนั้นได้

User.new.tap do |user|
  user.username = 'foobar'
end

1
นี่เป็นกรณีการใช้งานที่พบบ่อยที่สุดObject#tapสำหรับฉัน
Lyndsy Simon

1
คุณได้บันทึกโค้ดเป็นศูนย์แล้วและตอนนี้เมื่อดูที่ส่วนท้ายของวิธีการว่าจะส่งคืนอะไรฉันต้องสแกนสำรองเพื่อดูว่าบล็อกนั้นเป็นบล็อก # แท็ป ไม่แน่ใจว่าเป็นการชนะประเภทใด
Irongaze.com

บางที แต่นี่อาจเป็น 1 ซับได้อย่างง่ายดาย user = User.new.tap {|u| u.username = 'foobar' }
lacostenycoder

11

ส่งผลให้โค้ดมีความยุ่งเหยิงน้อยลงเนื่องจากขอบเขตของตัวแปรถูก จำกัด ไว้เฉพาะส่วนที่จำเป็นจริงๆ นอกจากนี้การเยื้องภายในบล็อกยังทำให้โค้ดอ่านง่ายขึ้นโดยการรวมโค้ดที่เกี่ยวข้องไว้ด้วยกัน

คำอธิบายของtapพูดว่า :

ให้ตัวเองไปยังบล็อกแล้วส่งคืนตนเอง จุดประสงค์หลักของวิธีนี้คือการ "เจาะเข้า" ห่วงโซ่วิธีการเพื่อดำเนินการกับผลลัพธ์ระดับกลางภายในห่วงโซ่

หากเราค้นหาซอร์สโค้ดรางสำหรับtapการใช้งานเราจะพบการใช้งานที่น่าสนใจบางอย่าง ด้านล่างนี้เป็นรายการบางส่วน (ไม่ใช่รายการที่ละเอียดถี่ถ้วน) ที่จะให้แนวคิดบางประการเกี่ยวกับวิธีใช้:

  1. ผนวกองค์ประกอบเข้ากับอาร์เรย์ตามเงื่อนไขบางประการ

    %w(
    annotations
    ...
    routes
    tmp
    ).tap { |arr|
      arr << 'statistics' if Rake.application.current_scope.empty?
    }.each do |task|
      ...
    end
  2. การเริ่มต้นอาร์เรย์และส่งคืน

    [].tap do |msg|
      msg << "EXPLAIN for: #{sql}"
      ...
      msg << connection.explain(sql, bind)
    end.join("\n")
  3. ในฐานะที่เป็นน้ำตาลเชิงไวยากรณ์เพื่อทำให้โค้ดอ่านง่ายขึ้น - เราสามารถพูดได้ว่าในตัวอย่างด้านล่างนี้คือการใช้ตัวแปรhashและserverทำให้เจตนาของโค้ดชัดเจนขึ้น

    def select(*args, &block)
        dup.tap { |hash| hash.select!(*args, &block) }
    end
  4. เริ่มต้น / เรียกใช้วิธีการบนวัตถุที่สร้างขึ้นใหม่

    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
  5. เพื่อดำเนินการกับผลลัพธ์ของการyieldโทรโดยไม่ต้องใช้ตัวแปรชั่วคราว

    yield.tap do |rendered_partial|
      collection_cache.write(key, rendered_partial, cache_options)
    end

9

รูปแบบของคำตอบของ @sawa:

ตามที่ระบุไว้แล้วการใช้tapช่วยในการหาจุดประสงค์ของรหัสของคุณ (ในขณะที่ไม่จำเป็นต้องทำให้กะทัดรัดมากขึ้น)

สองฟังก์ชั่นต่อไปนี้ยาวเท่า ๆ กัน แต่ในฟังก์ชั่นแรกคุณต้องอ่านจนจบเพื่อหาสาเหตุที่ฉันเริ่มต้น Hash ว่างในตอนเริ่มต้น

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

แอปพลิเคชั่นนี้tapสำหรับอาร์กิวเมนต์ที่น่าสนใจยิ่งขึ้น ฉันเห็นด้วยกับคนอื่น ๆ ว่าเมื่อคุณเห็นuser = User.newเจตนาก็ชัดเจนอยู่แล้ว อย่างไรก็ตามโครงสร้างข้อมูลที่ไม่ระบุตัวตนสามารถใช้กับอะไรก็ได้และtapอย่างน้อยวิธีนี้ก็ทำให้ชัดเจนว่าโครงสร้างข้อมูลเป็นจุดสำคัญของวิธีการ
volx757

ไม่แน่ใจว่าตัวอย่างนี้ดีกว่าและการเปรียบเทียบเทียบกับการdef tapping1; {one: 1, two: 2}; endแสดงโดยใช้.tapจะช้ากว่าประมาณ 50% ในกรณีนี้
lacostenycoder

9

เป็นตัวช่วยสำหรับการผูกมัดการโทร ส่งผ่านวัตถุไปยังบล็อกที่กำหนดและหลังจากบล็อกเสร็จสิ้นจะส่งคืนวัตถุ:

an_object.tap do |o|
  # do stuff with an_object, which is in o #
end  ===> an_object

ข้อดีคือการแตะจะส่งคืนวัตถุที่เรียกใช้เสมอแม้ว่าบล็อกจะส่งคืนผลลัพธ์อื่น ๆ ก็ตาม ดังนั้นคุณสามารถแทรกบล็อกการแตะลงตรงกลางของท่อวิธีการที่มีอยู่ได้โดยไม่ทำลายการไหล


8

tapฉันจะบอกว่าไม่เป็นประโยชน์อะไรกับการใช้ ประโยชน์ที่เป็นไปได้เพียงอย่างเดียวดังที่@sawa ชี้ให้เห็นคือและฉันอ้างว่า: "ผู้อ่านจะไม่ต้องอ่านสิ่งที่อยู่ในบล็อกเพื่อให้ทราบว่ามีการสร้างผู้ใช้อินสแตนซ์" อย่างไรก็ตาม ณ จุดนั้นสามารถทำให้เกิดข้อโต้แย้งได้ว่าหากคุณใช้ตรรกะในการสร้างเรกคอร์ดที่ไม่ซับซ้อนความตั้งใจของคุณจะสื่อสารได้ดีขึ้นโดยแยกตรรกะนั้นเป็นวิธีการของตัวเอง

ฉันถือจะเห็นว่าtapเป็นภาระที่ไม่จำเป็นในการอ่านรหัสและสามารถทำได้โดยไม่ต้องหรือทดแทนด้วยเทคนิคที่ดีกว่าเช่นสารสกัดจากวิธี

แม้ว่าtapจะเป็นวิธีที่สะดวกสบาย แต่ก็เป็นความชอบส่วนบุคคลเช่นกัน ให้tapลอง จากนั้นเขียนโค้ดโดยไม่ใช้การแตะดูว่าคุณชอบแบบใดแบบหนึ่ง


4

อาจมีจำนวนการใช้งานและสถานที่ที่เราสามารถtapใช้ได้ จนถึงตอนนี้ฉันพบการใช้งานเพียง 2 tapครั้งเท่านั้น

1) จุดประสงค์หลักของวิธีนี้คือการเชื่อมโยงเข้าไปในห่วงโซ่วิธีการเพื่อดำเนินการกับผลลัพธ์ระดับกลางภายในห่วงโซ่ กล่าวคือ

(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}" }

2) คุณเคยพบว่าตัวเองเรียกใช้เมธอดบนวัตถุบางอย่างและค่าที่ส่งคืนไม่ใช่สิ่งที่คุณต้องการหรือไม่? บางทีคุณอาจต้องการเพิ่มค่าโดยพลการให้กับชุดพารามิเตอร์ที่เก็บไว้ในแฮช คุณอัปเดตด้วยHash []แต่คุณได้รับแถบย้อนกลับแทนที่จะเป็นแฮช params ดังนั้นคุณต้องส่งคืนอย่างชัดเจน กล่าวคือ

def update_params(params)
  params[:foo] = 'bar'
  params
end

เพื่อเอาชนะสถานการณ์นี้ที่นี่tapวิธีการเข้ามามีบทบาท เพียงแค่เรียกมันบนวัตถุจากนั้นแตะบล็อกที่มีรหัสที่คุณต้องการเรียกใช้ วัตถุจะถูกส่งกลับไปยังบล็อกจากนั้นจะถูกส่งกลับ กล่าวคือ

def update_params(params)
  params.tap {|p| p[:foo] = 'bar' }
end

ยังมีกรณีการใช้งานอื่น ๆ อีกมากมายให้ลองค้นหาด้วยตัวคุณเอง :)

ที่มา:
1) API Dock Object แตะ
2) ห้าทับทิมวิธีที่คุณควรจะใช้


3

คุณพูดถูก: การใช้tapในตัวอย่างของคุณนั้นไม่มีจุดหมายและอาจจะสะอาดน้อยกว่าทางเลือกอื่นของคุณ

ตามบันทึกของ Rebitzele tapเป็นเพียงวิธีอำนวยความสะดวกซึ่งมักใช้เพื่อสร้างการอ้างอิงที่สั้นกว่าไปยังวัตถุปัจจุบัน

กรณีการใช้งานที่ดีอย่างหนึ่งtapคือสำหรับการดีบัก: คุณสามารถแก้ไขอ็อบเจ็กต์พิมพ์สถานะปัจจุบันจากนั้นแก้ไขอ็อบเจ็กต์ต่อไปในบล็อกเดียวกัน ดูที่นี่สำหรับตัวอย่างเช่น: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions

บางครั้งฉันชอบใช้tapวิธีการภายในเพื่อส่งคืนตามเงื่อนไขก่อนเวลาในขณะที่ส่งคืนวัตถุปัจจุบันเป็นอย่างอื่น


นี่เป็นแอปพลิเคชั่นที่กล่าวถึงในเอกสาร: ruby-doc.org/core-2.1.3/Object.html#method-i-tap
Ciro Santilli 郝海东冠状病六四事件法轮功

3

มีเครื่องมือที่เรียกว่า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!

1

คุณสามารถทำให้รหัสของคุณเป็นโมดูลาร์ได้มากขึ้นโดยใช้การแตะและสามารถจัดการตัวแปรท้องถิ่นได้ดีขึ้น ตัวอย่างเช่นในโค้ดต่อไปนี้คุณไม่จำเป็นต้องกำหนดตัวแปรโลคัลให้กับอ็อบเจ็กต์ที่สร้างขึ้นใหม่ในขอบเขตของวิธีการ สังเกตว่าตัวแปรบล็อกuถูกกำหนดขอบเขตภายในบล็อก เป็นหนึ่งในความสวยงามของทับทิมรหัส

def a_method
  ...
  name = "foobar"
  ...
  return User.new.tap do |u|
    u.username = name
    u.save!
  end
end


1

ฉันจะยกตัวอย่างอื่นที่ฉันใช้ ฉันมีวิธี 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

1

ในโลกที่รูปแบบการเขียนโปรแกรมเชิงฟังก์ชันกลายเป็นแนวทางปฏิบัติที่ดีที่สุด ( 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หลายครั้งที่นี่


0

อะไรคือความแตกต่าง?

ความแตกต่างในแง่ของการอ่านรหัสนั้นเป็นโวหารเท่านั้น

รหัสเดินผ่าน:

user = User.new.tap do |u|
  u.username = "foobar"
  u.save!
end

ประเด็นสำคัญ:

  • สังเกตว่าไฟล์ uตอนนี้ใช้ตัวแปรเป็นพารามิเตอร์บล็อกอย่างไร?
  • หลังจากบล็อกเสร็จแล้วไฟล์ 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.