Upgrade to Pro — share decks privately, control downloads, hide ads and more …

プロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13

プロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13

第80回 Ruby関西 勉強会の発表資料です。
https://rubykansai.doorkeeper.jp/events/69011

Avatar for Junichi Ito

Junichi Ito

January 13, 2018
Tweet

More Decks by Junichi Ito

Other Decks in Technology

Transcript

  1. ྫ֎ͱྫ֎ॲཧʹ͍͓ͭͯ͞Β͍ • ྫ֎(Exception)ͱ͸ • ͦΕҎ্ϓϩάϥϜΛଓߦͰ͖ͳ͍ྫ֎తͳঢ়گ • ࡶʹݴ͑͹ϓϩάϥϜͷΤϥʔ • ྫ֎ॲཧͱ͸ •

    ൃੜͨ͠ྫ֎Λద੾ʹॲཧ͢Δ͜ͱ • ΤϥʔͱແԑͰ͍ΒΕΔϓϩάϥϜ͸ͳ͍ • Αͬͯਖ਼͍͠ྫ֎ॲཧΛֶͿ͜ͱ͸ඇৗʹॏཁ
  2. Rubyʹ͓͚Δྫ֎ॲཧͷجຊߏจ begin # Τϥʔ͕ൃੜ͢Δ͔΋͠Εͳ͍ॲཧ a = 1 / 0 rescue

    => e # Τϥʔ͕ى͖ͨ৔߹ͷॲཧ puts "Τϥʔ͕ൃੜ͠·ͨ͠: #{e.message}” end # ϝιουશମ͕ྫ֎ॲཧͷର৅ʹͳΔ৔߹͸begin/endΛলུͰ͖Δ def some_method 1 / 0 rescue ZeroDivisionError => e puts "ZeroDivisionError͕ൃੜ͠·ͨ͠: #{e.message}" end # ্ͷίʔυͱԼͷίʔυ͸ಉ͡ def some_method begin 1 / 0 rescue ZeroDivisionError => e puts "ZeroDivisionError͕ൃੜ͠·ͨ͠: #{e.message}" end end
  3. ଞʹ΋͜Μͳͷͱ͔ʢ࣮ࡍ͸C#ʣ class PostService def find_data(id) # औಘͨ͠σʔλ͸ͳ͔ͥΠϯελϯεม਺ʹηοτ͞ΕΔ @data = Post.find(id)

    # ੒ޭ·ͨ͸ࣦഊΛ਺஋Ͱฦ͢ return 1 rescue => e # Τϥʔ͕ى͖ͨΒϝοηʔδ͚ͩ֨ೲͯ͠0Λฦ͢ @error_message = e.message return 0 end def save_data(data) # ΄͔ͷϝιου΋ಉ͡Α͏ͳྫ֎ॲཧ͕ॻ͔Ε͍ͯΔ end end # ҰԠ໭Γ஋͸ड͚औΔ͕ɺ1͔0ͷνΣοΫ͸͠ͳ͍ ret = post_service.find_data(id) # Կ΋ߟ͑ͣʹσʔλΛऔͬͯ͘Δ post = post_service.data # औಘʹࣦഊͨ͠Βpost͸nilͳͷͰɺ͜͜ͰΤϥʔ͕ى͖Δ title = post.title ☠
  4. ྫ֎ॲཧΛ࢖ͬͯ΋ڐ͞ΕΔέʔεɾͦͷ1 • ଞͷ஥ؒΛಓͮΕʹͨ͘͠ͳ͍৔߹ # ෳ਺ͷϢʔβʔʹҰׅͰϝʔϧΛૹ৴͢Δ users.each do |user| begin #

    ଞͷϢʔβʔΛಓͮΕʹ͠ͳ͍Α͏ɺΤϥʔ͕ى͖ͯ΋ଓߦ͢Δ send_mail_to(user) rescue => e puts "#{e.class}: #{e.message}" puts e.backtrace end end
  5. 1. ྫ֎ͷѲΓͭͿ͠☠ • ୭΋Τϥʔʹؾ͔ͮͳ͍ɺ͋ͱͰௐࠪ΋Ͱ͖ͳ͍ • ্ͷྫ͸ۃ୺͕ͩɺ݁Ռతʹແࢹ͞ΕΔ৔߹΋͋Δ • ໭Γ஋͕ແࢹ͞Εͨ৔߹ͳͲ user =

    build_user(data) # ൃੜͨ͠Τϥʔ͸׬શʹແࢹ͞ΕΔ begin save(user) rescue end # Կࣄ΋ͳ͔͔ͬͨͷΑ͏ʹॲཧΛଓߦ send_mail_to(user)
  6. 2. ExceptionΫϥεΛrescue͍ͯ͠Δ☠ • StandardErrorҎԼΛัଊ͢Δͷ͕ਖ਼ղ begin save(user) rescue Exception => e

    # ԿΒ͔ͷΤϥʔॲཧ end ϓϩΛ໨ࢦ͢ਓͷͨΊͷRubyೖ໳
 ୈ9ষʹొ৔͢Δਤ
  7. ࢀߟ # ໌ࣔతʹStandardErrorΛࢦఆ͢Δ begin save(user) rescue StandardError => e #

    ԿΒ͔ͷΤϥʔॲཧ end # ྫ֎ΫϥεΛࢦఆ͠ͳ͚Ε͹StandardErrorͱͦͷαϒΫϥε͕rescue͞ΕΔ begin save(user) rescue => e # ԿΒ͔ͷΤϥʔॲཧ end • ্ͷ2ͭ͸΍͍ͬͯΔ͜ͱ͸ಉ͡ • ͳͷͰɺԼͷΑ͏ʹStandardError͸লུ͢Ε͹ྑ͍
  8. 3-1. begin͔Βrescue·Ͱͷൣғ͕ແବʹ޿͍☠ • Ͳ͜Ͱൃੜ͢ΔΤϥʔΛัଊ͍ͨ͠ͷ͔Θ͔Βͳ͍ # ฏ੒ͷ೔෇จࣈྻΛDateΦϒδΣΫτʹม׵͢Δ def convert_heisei_to_date(heisei_text) begin m

    = heisei_text.match(/ฏ੒(?<jp_year>\d+)೥(?<month>\d+)݄(?<day>\d+)೔/) year = m[:jp_year].to_i + 1988 month = m[:month].to_i day = m[:day].to_i Date.new(year, month, day) rescue # ྫ֎͕ى͖ͨΒʢʹແޮͳ೔෇͕౉͞ΕͨΒʣnilΛฦ͍ͨ͠ nil end end } {
  9. 3-2. rescueઅʹྫ֎ΫϥεΛԿ΋ࢦఆ͠ͳ͍☠ • λΠϓϛε͕ݪҼͰൃੜͨ͠ྫ֎΋ัଊ͞Εͯ͠·͏ # ฏ੒ͷ೔෇จࣈྻΛDateΦϒδΣΫτʹม׵͢Δ def convert_heisei_to_date(heisei_text) begin m

    = heisei_text.match(/ฏ੒(?<jp_year>\d+)೥(?<month>\d+)݄(?<day>\d+)೔/) year = m[:jp_year].to_i + 1988 month = m[:month].to_i day = m[:day].to_i Date.new(year, month, day) rescue # ྫ֎͕ى͖ͨΒʢʹແޮͳ೔෇͕౉͞ΕͨΒʣnilΛฦ͍ͨ͠ nil end end
  10. 3. ྫ֎ॲཧͷର৅ൣғͱର৅ΫϥεΛߜΓࠐΉ • ัଊ͍ͨ͠Τϥʔ͚ͩΛ࣮֬ʹૂ͍ܸͪ͢Δ • ͦΕҎ֎ͷΤϥʔ͕ग़ͨΒଈɺҟৗऴྃͤ͞Δ def convert_heisei_to_date(heisei_text) m =

    heisei_text.match(/ฏ੒(?<jp_year>\d+)೥(?<month>\d+)݄(?<day>\d+)೔/) year = m[:jp_year].to_i + 1988 month = m[:month].to_i day = m[:day].to_i # ྫ֎ॲཧͷൣғΛڱΊɺัଊ͢Δྫ֎ΫϥεΛݶఆ͢Δ begin Date.new(year, month, day) rescue ArgumentError # ແޮͳ೔෇Ͱ͋Ε͹nilΛฦ͢ nil end end
  11. ࢀߟ • ྫ֎ॲཧΛ࢖ΘͣʹࡁΉํ๏͕͋Ε͹ͦΕΛ࢖͏΂͖ def convert_heisei_to_date(heisei_text) m = heisei_text.match(/ฏ੒(?<jp_year>\d+)೥(?<month>\d+)݄(?<day>\d+)೔/) year =

    m[:jp_year].to_i + 1988 month = m[:month].to_i day = m[:day].to_i # ྫ֎ॲཧͰ͸ͳ͘ɺ৚݅෼ذΛ࢖͏ if Date.valid_date?(year, month, day) Date.new(year, month, day) end end
  12. 4. ྫ֎ॲཧ΋ςετ͢Δ • ྫ֎ॲཧͷڍಈΛ֬ೝͰ͖ΔςετίʔυΛॻ͘ • ࠶ݱ͕೉͍͠৔߹͸ϞοΫΛ׆༻͢Δ def some_method 1 /

    0 rescue => e puts "Τϥʔ͕ൃੜ͠·ͨ͠: #{e.message}" puts e.backtrace end class SampleTest < Minitest::Test def test_some_method assert_output /Τϥʔ͕ൃੜ͠·ͨ͠/ do some_method end end end
  13. ۀ຿Τϥʔ΁ͷରॲʢ಺෦ઃܭɾͦͷ1ʣ • ྫ֎ॲཧ͸ݪଇͱͯ͠࢖Θͳ͍ • ੒ޭ or ࣦഊ͸ϝιουͷ໭Γ஋Ͱදݱ͢Δ • ݺͼग़͠ଆ͸ඞͣ໭Γ஋Λݕূ͢Δ def

    create @event = Event.new(event_params) # saveϝιουͷ໭Γ஋ΛνΣοΫ if @event.save # ໭Γ஋͕trueͳͷͰ੒ޭ redirect_to @event, notice: 'Event was successfully created.' else # ໭Γ஋͕falseͳͷͰࣦഊ render :new end end
  14. ۀ຿Τϥʔ΁ͷରॲʢ಺෦ઃܭɾͦͷ2ʣ • ෆඞཁʹྫ֎ॲཧΛ࢖Θͳ͍͜ͱ • ྫ֎ॲཧΛͳͯ͘͠΋ۀ຿ΤϥʔʹରॲͰ͖Δ͔ʁ • NOͳΒॲཧϑϩʔͷ੍ޚʹྫ֎ॲཧΛ࢖͍ͬͯΔ # ϓϩδΣΫτͷશؔ܎ऀʹରͯ͠కΊ੾ΓͷมߋΛࢼΈΔϝιου def

    extend_deadline_for_all_stakeholders(project, new_deadline) project.developers.each do |developer| developer.update!(deadline: new_deadline) end project.customer.update!(deadline: new_deadline) project.manager.update!(deadline: new_deadline) true rescue ActiveRecord::RecordInvalid # ۀ຿Τϥʔͳͷʹྫ֎ॲཧΛ࢖͍ͬͯΔʢNGʣ false end
  15. ࢀߟ • ྫ֎ॲཧΛ࢖Θͣʹ࣮૷͢Δ৔߹ͷίʔυྫ def extend_deadline_for_all_stakeholders(project, new_deadline) all_success = true project.developers.each

    do |developer| all_success &= developer.update(deadline: new_deadline) end all_success &= project.customer.update(deadline: new_deadline) all_success &= project.manager.update(deadline: new_deadline) # ্ͷॲཧͰ1ͭͰ΋false͕ฦΕ͹ɺall_success͸falseʹมΘΔ # ʢຊདྷ͸σʔλϕʔετϥϯβΫγϣϯͷϩʔϧόοΫ΋ߟྀ͢΂͖ɻৄࡉ͸ޙड़ʣ all_success end
  16. RailsͰෳ਺ͷϨίʔυΛಉ࣌ʹߋ৽͢Δ৔߹ • transactionϝιουͱRollbackྫ֎Λ׆༻͢Δ • γεςϜΤϥʔ͕ى͖ͨΒͦ͜ͰϩʔϧόοΫ • ۀ຿ΤϥʔͳΒRollbackྫ֎Ͱ໌ࣔతʹϩʔϧόοΫ def extend_deadline_for_all_stakeholders(project, new_deadline)

    all_success = true transaction do project.developers.each do |developer| all_success &= developer.update(deadline: new_deadline) end all_success &= project.customer.update(deadline: new_deadline) all_success &= project.manager.update(deadline: new_deadline) unless all_success # ໌ࣔతʹϩʔϧόοΫͤ͞Δʢtransactionϝιου͕rescue͢ΔͷͰॲཧ͸ଓߦ͞ΕΔʣ raise ActiveRecord::Rollback end end all_success end ] [
  17. ߈Ίͷྫ֎ ͦͷ1: ༧ظͤ͵৚݅෼ذΛ͢ • ༧ظͤ͵৚݅෼ذʹೖͬͨΒྫ֎Λൃੜͤ͞Δ • ࢮ͵͸ͣͷϓϩάϥϜΛແཧʹੜ͔ͯ͠͸͍͚ͳ͍ # elseʹೖͬͨΒྫ֎Λൃੜͤ͞Δύλʔϯʢྑ͍ྫʣ def

    currency_of(country) case country when :japan 'yen' when :us 'dollar' when :india 'rupee' else raise ArgumentError, "ແޮͳࠃ໊Ͱ͢ɻ#{country}" end end # ྫ֎͕ൃੜ͢Δ currency_of(:italy) #=> ArgumentError: ແޮͳࠃ໊Ͱ͢ɻitaly
  18. ࢀߟ: ࢮ͵͸ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ1 • else͕ͳ͍ → nil͕ฦͬͯ͠·͏ # elseΛ༻ҙ͠ͳ͍ύλʔϯʢྑ͘ͳ͍ྫʣ def currency_of(country)

    case country when :japan 'yen' when :us 'dollar' when :india 'rupee' end end # ૝ఆ֎ͷࠃ໊Λ౉͢ͱnil͕ฦΔ currency = currency_of(:italy) #=> nil # ༧ظͤ͵λΠϛϯάͰΤϥʔ͕ൃੜ͢Δ currency.upcase #=> NoMethodError: undefined method `upcase' for nil:NilClass
  19. ࢀߟ: ࢮ͵͸ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ2 • elseͰಛఆͷ஋Λฦ͢ → ໃ६ͨ͠஋͕ฦͬͯ͠·͏ # elseΛ:indiaͱͯ͠ѻ͏ύλʔϯʢྑ͘ͳ͍ྫʣ def currency_of(country)

    case country when :japan 'yen' when :us 'dollar' else 'rupee' end end # ໃ६ͨ͠஋͕ฦ͖ͬͯͯ͠·͏ country = :italy currency = currency_of(country) #=> “rupee” price = 100 "#{country}: #{price}#{currency}" #=> "italy: 100rupee"
  20. ߈Ίͷྫ֎ ͦͷ2: ͋Γಘͳ͍Τϥʔʹ͸ڧؾͰ • ۀ຿Τϥʔ͢ΒγεςϜΤϥʔͱݟͳͤΔ৔߹΋͋Δ • ͦ͏͍͏έʔεͰ͸save!΍update!Λ࢖͏ • ͨͩ͠ɺΞτϛοΫૢ࡞͚ͩ͸ҡ࣋͢ΔΑ͏ʹ def

    extend_deadline_for_all_stakeholders(project, new_deadline) # ສҰͷࣄଶʹ͸උ͓͑ͯ͘ʢσʔλϕʔε΁ͷมߋΛΞτϛοΫૢ࡞ʹ͢Δʣ transaction do # ୭Ұਓͱͯ͠కΊ੾ΓͷԆ௕ʹ͸จ۟ΛݴΘͳ͍͸ͣͩʂʢจ۟ΛݴΘΕͨΒγεςϜΤϥʔʣ project.developers.each do |developer| developer.update!(deadline: new_deadline) end project.customer.update!(deadline: new_deadline) project.manager.update!(deadline: new_deadline) end end
  21. ࢀߟจݙ • .NETͷྫ֎ॲཧ Part.1ʢ@nakama00ɺ2008೥ʣ • ྫ֎ઃܭʹ͓͚Δେࡑʢ@t_wadaɺ2012೥ʣ • RailsΞϓϦέʔγϣϯʹ͓͚ΔΤϥʔॲཧʢྫ֎ઃܭʣͷߟ͑ํʢ@jnchitoʣ • give

    IT a tryʢ@jnchitoʣ • Nasty design can blast your brain!ʢ2010೥3݄31೔ʣ • ٕज़ॻ௥Ճͦͷ2ʢ2010೥4݄10೔ʣ • and…