Share
Embed
プロを目指す人のための例外処理(再)入門 / #rubykansai 2018-01-13
第80回 Ruby関西 勉強会の発表資料です。
https://rubykansai.doorkeeper.jp/events/69011
Junichi Ito
January 13, 2018
Transcript
53 slides
-
About Me • ҏ౻३Ұ • RailsϓϩάϥϚ@SonicGarden • ϦϞʔτϫʔΧʔ • Ϊλʔ͖
• Blog: give IT a try • Twitter: @jnchito -
ࠓ͓͢͠Δ͜ͱ • ྫ֎ͱྫ֎ॲཧʹ͍͓ͭͯ͞Β͍ • ຊʹ͋ͬͨා͍ • ྫ֎ॲཧͷόουϓϥΫςΟεˍϕετϓϥΫςΟε • ྫ֎ॲཧʢओʹઃܭʣʹؔ͢ΔߴͳτϐοΫ •
ۀΤϥʔͱγεςϜΤϥʔͷҧ͍ • ྫ֎ॲཧͱϩʔϧόοΫͷؔ • ߈Ίͷྫ֎ʢҙਤతʹྫ֎Λൃੜͤ͞Δʣ -
ྫ֎ͱྫ֎ॲཧʹ͍͓ͭͯ͞Β͍ • ྫ֎(Exception)ͱ • ͦΕҎ্ϓϩάϥϜΛଓߦͰ͖ͳ͍ྫ֎తͳঢ়گ • ࡶʹݴ͑ϓϩάϥϜͷΤϥʔ • ྫ֎ॲཧͱ •
ൃੜͨ͠ྫ֎Λదʹॲཧ͢Δ͜ͱ • ΤϥʔͱແԑͰ͍ΒΕΔϓϩάϥϜͳ͍ • Αͬͯਖ਼͍͠ྫ֎ॲཧΛֶͿ͜ͱඇৗʹॏཁ -
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 -
ͨͩ͠ɺ͜ΕͰऴΘΔͱ͍ • ʮϋϯϚʔΛ࣋ͭਓʹ͕ͯ͢ఝʹݟ͑Δʯ • ྫ֎ॲཧΛ֮͑ͨਓྫ֎ॲཧΛ͍ͨ͘ͳΔ • Τϥʔ͕ى͖ͨΒrescue͢Ε͍͍ͱצҧ͍͢Δ • ͋ͬͪͬͪ͜ʹbegin/rescueΛຒΊࠐΉ •
ʮΤϥʔʹڧ͍ϓϩάϥϜ͕Ͱ͖ͨYOʂʘ(^O^)ʗʯ • Ͱ͋Γ·ͤΜʂʂ! -
ຊʹ͋ͬͨා͍ʢલ৬Ͱͷମݧஊʣ • ͱ͋ΔWebγεςϜʢࣾγεςϜʣΛड͚ܧ͍ͩ • ͦͷγεςϜͳ͘ಈ͍͍ͯͨʢΑ͏ʹݟ͑ͨʣ • γεςϜվमͷͨΊݱͷਓͱ͢ػձΛͬͨ • ݱͷਓͷ͍ํΛݟͤͯΒͬͨ •
อଘϘλϯΛΫϦοΫˠʮγεςϜΤϥʔ: -1ʯ • ʮ͑ͬɺԿ͜Εʁʯ • ݱͷਓʮ͜Ε͕ग़ͨΒϘλϯΛ࠶ΫϦοΫ͠·͢ʯ -
Πϝʔδతʹ͜͏ʢ࣮ࡍ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 -
ଞʹ͜Μͳͷͱ͔ʢ࣮ࡍ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 # औಘʹࣦഊͨ͠ΒpostnilͳͷͰɺ͜͜ͰΤϥʔ͕ى͖Δ title = post.title ☠ -
͜ͷ݅ͷڭ܇ͱྫ֎ॲཧͷجຊతͳߟ͑ํ • ྫ֎ॲཧ͍ํΛؒҧ͑Δͱɺͷํ͕େ͖͘ͳΔ • Ͱ͖Δ͜ͱͱɺ͍͍ͬͯ͜ͱҟͳΔ • ྫ֎ॲཧͷجຊతͳߟ͑ํ • ૉਓrescue͢Μͳʂʂ •
Τϥʔ͕ग़ͨΒͦ͜ͰʓͶʂʂ • ྫ֎ॲཧϑϨʔϜϫʔΫͷڞ௨ॲཧʹ·͔ͤΖʂʂ -
ྫ֎ॲཧΛͬͯڐ͞ΕΔέʔεɾͦͷ1 • ଞͷؒΛಓͮΕʹͨ͘͠ͳ͍߹ # ෳͷϢʔβʔʹҰׅͰϝʔϧΛૹ৴͢Δ users.each do |user| begin #
ଞͷϢʔβʔΛಓͮΕʹ͠ͳ͍Α͏ɺΤϥʔ͕ى͖ͯଓߦ͢Δ send_mail_to(user) rescue => e puts "#{e.class}: #{e.message}" puts e.backtrace end end -
ྫ֎ॲཧΛͬͯڐ͞ΕΔέʔεɾͦͷ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 -
1. ྫ֎ͷѲΓͭͿ͠☠ • ୭Τϥʔʹؾ͔ͮͳ͍ɺ͋ͱͰௐࠪͰ͖ͳ͍ • ্ͷྫۃ͕ͩɺ݁Ռతʹແࢹ͞ΕΔ߹͋Δ • Γ͕ແࢹ͞Εͨ߹ͳͲ user =
build_user(data) # ൃੜͨ͠Τϥʔશʹແࢹ͞ΕΔ begin save(user) rescue end # Կࣄͳ͔͔ͬͨͷΑ͏ʹॲཧΛଓߦ send_mail_to(user) -
2. ExceptionΫϥεΛrescue͍ͯ͠Δ☠ • StandardErrorҎԼΛัଊ͢Δͷ͕ਖ਼ղ begin save(user) rescue Exception => e
# ԿΒ͔ͷΤϥʔॲཧ end ϓϩΛࢦ͢ਓͷͨΊͷRubyೖ ୈ9ষʹొ͢Δਤ -
ࢀߟ # ໌ࣔతʹStandardErrorΛࢦఆ͢Δ begin save(user) rescue StandardError => e #
ԿΒ͔ͷΤϥʔॲཧ end # ྫ֎ΫϥεΛࢦఆ͠ͳ͚ΕStandardErrorͱͦͷαϒΫϥε͕rescue͞ΕΔ begin save(user) rescue => e # ԿΒ͔ͷΤϥʔॲཧ end • ্ͷ2͍ͭͬͯΔ͜ͱಉ͡ • ͳͷͰɺԼͷΑ͏ʹStandardErrorলུ͢Εྑ͍ -
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 } { -
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 -
4. ྫ֎ॲཧΛςετ͠ͳ͍☠ • ؊৺ͷຊ൪ڥͰॳΊͯྫ֎ॲཧͷόάʹؾͮ͘ • ͱͱൃੜ͍ͯͨ͠ΤϥʔͷݪҼ͕ࣦΘΕͯ͠·͏ • લ৬Ͱܦݧ͋ΓʢҾ͖ܧ͍ͩγεςϜͰɾɾɾʣ def some_method
1 / 0 rescue => e # messageͱॻͭ͘Γ͕mesageͱॻ͍ͯ͠·ͬͨ puts "Τϥʔ͕ൃੜ͠·ͨ͠: #{e.mesage}" puts e.backtrace end -
1. ݪଇͱͯ͠rescue͠ͳ͍ • ྫ֎ॲཧͷجຊతͳߟ͑ํʢ࠶ܝʣ • ૉਓrescue͢Μͳʂʂ • Τϥʔ͕ग़ͨΒͦ͜ͰʓͶʂʂ • ྫ֎ॲཧϑϨʔϜϫʔΫͷڞ௨ॲཧʹ·͔ͤΖʂʂ
• Ҏ߱rescue͕Ͳ͏ͯ͠ඞཁͳঢ়گΛఆ -
2. ྫ֎ͷใΛϩάʹ͢ˍ௨͢Δ • ྫ֎Ϋϥε໊ɺϝοηʔδɺόοΫτϨʔεΛϩάग़ྗ • BugsnagRollbarɺErrbitͳͲͰ։ൃऀʹ௨͢Δ • Τϥʔʹ͙͢ؾ͖ͮɺ͙͢ௐࠪͰ͖ΔΑ͏ʹ͢Δ begin save(user)
rescue => e # ϩάʹ͢ logger.error "#{e.class} / #{e.message}" logger.error e.backtrace.join("\n") # ௨͢Δ Bugsnag.notify(e) end -
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 -
ࢀߟ • ྫ֎ॲཧΛΘͣʹࡁΉํ๏͕͋ΕͦΕΛ͏͖ 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 -
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 -
Τϥʔʹ2छྨͷΤϥʔ͕͋Δ • ۀΤϥʔʢϏδωεྫ֎ʣ • ೖྗϑΥʔϜʹ͓͚ΔϢʔβʔͷೖྗϛε • ݖݶҧʢݖݶͷͳ͍ϖʔδͷΞΫηεʣ • γεςϜΤϥʔʢٕज़తྫ֎ʣ •
ϓϩάϥϜͷϛεʢ͍ΘΏΔόάʣ • ωοτϫʔΫΤϥʔɺσʔλϕʔεͷμϯ • ։ൃऀ͜ͷ۠ผΛ͚ͳͯ͘ͳΒͳ͍ -
ۀΤϥʔͷରॲʢ෦ઃܭɾͦͷ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 -
ۀΤϥʔͷରॲʢ෦ઃܭɾͦͷ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 -
ࢀߟ • ྫ֎ॲཧΛΘͣʹ࣮͢Δ߹ͷίʔυྫ 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_successfalseʹมΘΔ # ʢຊདྷσʔλϕʔετϥϯβΫγϣϯͷϩʔϧόοΫߟྀ͖͢ɻৄࡉޙड़ʣ all_success end -
ྫ֎ॲཧͱϩʔϧόοΫͷؔ • ʮΞτϛοΫૢ࡞Կ͔ʯΛৗʹҙࣝ͢Δ • ΞτϛοΫૢ࡞ʢෆՄૢ࡞ʣͱ • શͯͷมߋ͕ޭͨ͠ͱ͖ͷΈɺมߋΛ֬ఆ • 1ͭͰࣦഊ͢Εશ෦ΩϟϯηϧʢϩʔϧόοΫʣ •
ྫ • ෳͷϨίʔυΛಉ࣌ʹߋ৽͢Δ߹ • σʔλϕʔεߋ৽ޙʹ՝ܾۚࡁAPIΛݺͼग़͢߹ -
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 ] [ -
߈Ίͷྫ֎ ͦͷ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 -
ࢀߟ: ࢮ͵ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ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 -
ࢀߟ: ࢮ͵ͣͳͷʹɺࢮͳͳ͍έʔεɾͦͷ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" -
߈Ίͷྫ֎ ͦͷ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 -
·ͱΊ: ਖ਼͍͠ྫ֎ॲཧͰͤͳਓੜΛ̇ • ྫ֎ͱྫ֎ॲཧʹ͍͓ͭͯ͞Β͍ • ຊʹ͋ͬͨා͍ • ྫ֎ॲཧͷόουϓϥΫςΟεˍϕετϓϥΫςΟε • ྫ֎ॲཧʢओʹઃܭʣʹؔ͢ΔߴͳτϐοΫ
• ۀΤϥʔͱγεςϜΤϥʔͷҧ͍ • ྫ֎ॲཧͱϩʔϧόοΫͷؔ • ߈Ίͷྫ֎ʢҙਤతʹྫ֎Λൃੜͤ͞Δʣ -
ࢀߟจݙ • .NETͷྫ֎ॲཧ Part.1ʢ@nakama00ɺ2008ʣ • ྫ֎ઃܭʹ͓͚Δେࡑʢ@t_wadaɺ2012ʣ • RailsΞϓϦέʔγϣϯʹ͓͚ΔΤϥʔॲཧʢྫ֎ઃܭʣͷߟ͑ํʢ@jnchitoʣ • give
IT a tryʢ@jnchitoʣ • Nasty design can blast your brain!ʢ20103݄31ʣ • ٕज़ॻՃͦͷ2ʢ20104݄10ʣ • and…