Junichi Ito
January 13, 2018
50k

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

https://rubykansai.doorkeeper.jp/events/69011

January 13, 2018

## Transcript

2. ### About Me • ҏ౻३Ұ • RailsϓϩάϥϚ@SonicGarden • ϦϞʔτϫʔΧʔ • Ϊλʔ޷͖

• Blog: give IT a try • Twitter: @jnchito

6. ### ࠓ೔͓࿩͢͠Δ͜ͱ • ྫ֎ͱྫ֎ॲཧʹ͍͓ͭͯ͞Β͍ • ຊ౰ʹ͋ͬͨා͍࿩ • ྫ֎ॲཧͷόουϓϥΫςΟεˍϕετϓϥΫςΟε • ྫ֎ॲཧʢओʹઃܭʣʹؔ͢Δߴ౓ͳτϐοΫ •

ۀ຿ΤϥʔͱγεςϜΤϥʔͷҧ͍ • ྫ֎ॲཧͱϩʔϧόοΫͷؔ܎ • ߈Ίͷྫ֎ʢҙਤతʹྫ֎Λൃੜͤ͞Δʣ

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

ൃੜͨ͠ྫ֎Λద੾ʹॲཧ͢Δ͜ͱ • ΤϥʔͱແԑͰ͍ΒΕΔϓϩάϥϜ͸ͳ͍ • Αͬͯਖ਼͍͠ྫ֎ॲཧΛֶͿ͜ͱ͸ඇৗʹॏཁ
9. ### 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
10. ### ͨͩ͠ɺ͜ΕͰऴΘΔͱ΍͹͍ • ʮϋϯϚʔΛ࣋ͭਓʹ͸͢΂͕ͯఝʹݟ͑Δʯ • ྫ֎ॲཧΛ֮͑ͨਓ͸ྫ֎ॲཧΛ࢖͍ͨ͘ͳΔ • Τϥʔ͕ى͖ͨΒrescue͢Ε͹͍͍ͱצҧ͍͢Δ • ͋ͬͪͬͪ͜ʹbegin/rescueΛຒΊࠐΉ •

ʮΤϥʔʹڧ͍ϓϩάϥϜ͕Ͱ͖ͨYOʂʘ(^O^)ʗʯ • Ͱ͸͋Γ·ͤΜʂʂ!

12. ### ຊ౰ʹ͋ͬͨා͍࿩ʢલ৬Ͱͷମݧஊʣ • ͱ͋ΔWebγεςϜʢࣾ಺γεςϜʣΛड͚ܧ͍ͩ • ͦͷγεςϜ͸໰୊ͳ͘ಈ͍͍ͯͨʢΑ͏ʹݟ͑ͨʣ • γεςϜվमͷͨΊݱ৔ͷਓͱ࿩͢ػձΛ΋ͬͨ • ݱ৔ͷਓͷ࢖͍ํΛݟͤͯ΋Βͬͨ •

อଘϘλϯΛΫϦοΫˠʮγεςϜΤϥʔ: -1ʯ • ๻ʮ͑ͬɺԿ͜Εʁʯ • ݱ৔ͷਓʮ͜Ε͕ग़ͨΒϘλϯΛ࠶ΫϦοΫ͠·͢ʯ
13. ### Πϝʔδతʹ͸͜͏ʢ࣮ࡍ͸ASP.NETʣ • Τϥʔ͕ൃੜͨ͠ΒΤϥʔίʔυΛදࣔͯ͠ऴΘΓ • ௨஌΋͞Εͳ͍ɺϩάʹ΋·ͬͨ͘৘ใ͕࢒Βͳ͍ • ݱ৔͸ʮΤϥʔ͕ग़ͨΒ࠶ΫϦοΫʯΛϚχϡΞϧԽ • ͦͷޙͷௐࠪˠઃܭϛεͰσουϩοΫ͕සൃͯͨ͠ def

create @post = Post.new(post_params) @post.save! redirect_to post_path(@post) rescue => e flash.now[:alert] = "γεςϜΤϥʔ: #{e.error_code}" render action: :new end
14. ### ଞʹ΋͜Μͳͷͱ͔ʢ࣮ࡍ͸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 ☠
15. ### ͜ͷ݅ͷڭ܇ͱྫ֎ॲཧͷجຊతͳߟ͑ํ • ྫ֎ॲཧ͸࢖͍ํΛؒҧ͑Δͱɺ֐ͷํ͕େ͖͘ͳΔ • Ͱ͖Δ͜ͱͱɺ΍͍͍ͬͯ͜ͱ͸ҟͳΔ • ྫ֎ॲཧͷجຊతͳߟ͑ํ • ૉਓ͸rescue͢Μͳʂʂ •

Τϥʔ͕ग़ͨΒͦ͜ͰʓͶʂʂ • ྫ֎ॲཧ͸ϑϨʔϜϫʔΫͷڞ௨ॲཧʹ·͔ͤΖʂʂ
16. ### ྫ֎ॲཧΛ࢖ͬͯ΋ڐ͞ΕΔέʔεɾͦͷ1 • ଞͷ஥ؒΛಓͮΕʹͨ͘͠ͳ͍৔߹ # ෳ਺ͷϢʔβʔʹҰׅͰϝʔϧΛૹ৴͢Δ users.each do |user| begin #

ଞͷϢʔβʔΛಓͮΕʹ͠ͳ͍Α͏ɺΤϥʔ͕ى͖ͯ΋ଓߦ͢Δ send_mail_to(user) rescue => e puts "#{e.class}: #{e.message}" puts e.backtrace end end
17. ### ྫ֎ॲཧΛ࢖ͬͯ΋ڐ͞ΕΔέʔεɾͦͷ2 • ྫ֎ͷ༗ແͰ͔͠ཁ݅Λຬͨͤͳ͍৔߹ require 'json' def valid_json_format?(text) # ࣮ࡍʹύʔεͯ͠Τϥʔ͕ൃੜ͢Δ͔Ͳ͏͔νΣοΫ͢Δ͔͠ͳ͍ JSON.parse(text)

true rescue JSON::ParserError false end valid_json_format?('{ "a": 1 }') #=> true valid_json_format?('{ "a": 1') #=> false

19. ### 1. ྫ֎ͷѲΓͭͿ͠☠ • ୭΋Τϥʔʹؾ͔ͮͳ͍ɺ͋ͱͰௐࠪ΋Ͱ͖ͳ͍ • ্ͷྫ͸ۃ୺͕ͩɺ݁Ռతʹແࢹ͞ΕΔ৔߹΋͋Δ • ໭Γ஋͕ແࢹ͞Εͨ৔߹ͳͲ user =

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

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

ԿΒ͔ͷΤϥʔॲཧ end # ྫ֎ΫϥεΛࢦఆ͠ͳ͚Ε͹StandardErrorͱͦͷαϒΫϥε͕rescue͞ΕΔ begin save(user) rescue => e # ԿΒ͔ͷΤϥʔॲཧ end • ্ͷ2ͭ͸΍͍ͬͯΔ͜ͱ͸ಉ͡ • ͳͷͰɺԼͷΑ͏ʹStandardError͸লུ͢Ε͹ྑ͍
22. ### 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 } {
23. ### 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
24. ### 4. ྫ֎ॲཧΛςετ͠ͳ͍☠ • ؊৺ͷຊ൪؀ڥͰॳΊͯྫ֎ॲཧͷόάʹؾͮ͘ • ΋ͱ΋ͱൃੜ͍ͯͨ͠ΤϥʔͷݪҼ͕ࣦΘΕͯ͠·͏ • લ৬Ͱܦݧ͋ΓʢҾ͖ܧ͍ͩγεςϜͰɾɾɾʣ def some_method

1 / 0 rescue => e # messageͱॻͭ͘΋Γ͕mesageͱॻ͍ͯ͠·ͬͨ puts "Τϥʔ͕ൃੜ͠·ͨ͠: #{e.mesage}" puts e.backtrace end

27. ### 1. ݪଇͱͯ͠rescue͠ͳ͍ • ྫ֎ॲཧͷجຊతͳߟ͑ํʢ࠶ܝʣ • ૉਓ͸rescue͢Μͳʂʂ • Τϥʔ͕ग़ͨΒͦ͜ͰʓͶʂʂ • ྫ֎ॲཧ͸ϑϨʔϜϫʔΫͷڞ௨ॲཧʹ·͔ͤΖʂʂ

• Ҏ߱͸rescue͕Ͳ͏ͯ͠΋ඞཁͳঢ়گΛ૝ఆ
28. ### 2. ྫ֎ͷ৘ใΛϩάʹ࢒͢ˍ௨஌͢Δ • ྫ֎Ϋϥε໊ɺϝοηʔδɺόοΫτϨʔεΛϩάग़ྗ • Bugsnag΍RollbarɺErrbitͳͲͰ։ൃऀʹ௨஌͢Δ • Τϥʔʹ͙͢ؾ͖ͮɺ͙͢ௐࠪͰ͖ΔΑ͏ʹ͢Δ begin save(user)

rescue => e # ϩάʹ࢒͢ logger.error "#{e.class} / #{e.message}" logger.error e.backtrace.join("\n") # ௨஌͢Δ Bugsnag.notify(e) end
29. ### 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
30. ### ࢀߟ • ྫ֎ॲཧΛ࢖ΘͣʹࡁΉํ๏͕͋Ε͹ͦΕΛ࢖͏΂͖ 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
31. ### 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

34. ### Τϥʔʹ͸2छྨͷΤϥʔ͕͋Δ • ۀ຿ΤϥʔʢϏδωεྫ֎ʣ • ೖྗϑΥʔϜʹ͓͚ΔϢʔβʔͷೖྗϛε • ݖݶҧ൓ʢݖݶͷͳ͍ϖʔδ΁ͷΞΫηε౳ʣ • γεςϜΤϥʔʢٕज़తྫ֎ʣ •

ϓϩάϥϜͷϛεʢ͍ΘΏΔόάʣ • ωοτϫʔΫΤϥʔɺσʔλϕʔεͷμ΢ϯ౳ • ։ൃऀ͸͜ͷ۠ผΛ෇͚ͳͯ͘͸ͳΒͳ͍

36. ### ۀ຿Τϥʔ΁ͷରॲʢ಺෦ઃܭɾͦͷ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

42. ### ྫ֎ॲཧͱϩʔϧόοΫͷؔ܎ • ʮΞτϛοΫૢ࡞͸Կ͔ʯΛৗʹҙࣝ͢Δ • ΞτϛοΫૢ࡞ʢෆՄ෼ૢ࡞ʣͱ͸ • શͯͷมߋ͕੒ޭͨ͠ͱ͖ͷΈɺมߋΛ֬ఆ • 1ͭͰ΋ࣦഊ͢Ε͹શ෦ΩϟϯηϧʢϩʔϧόοΫʣ •

ྫ • ෳ਺ͷϨίʔυΛಉ࣌ʹߋ৽͢Δ৔߹ • σʔλϕʔεߋ৽ޙʹ՝ܾۚࡁAPIΛݺͼग़͢৔߹

45. ### ߈Ίͷྫ֎ ͦͷ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
46. ### ࢀߟ: ࢮ͵͸ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ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
47. ### ࢀߟ: ࢮ͵͸ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ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"