Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

ebisu.rb #19 超実践 super

220 views

Published on

ebisu.rb #19 で登壇したときの資料です。
https://ebisurb.connpass.com/event/103701/

Published in: Technology
  • Be the first to comment

  • Be the first to like this

ebisu.rb #19 超実践 super

  1. 1. 超実践 super super practice for `super` @kamina_zzz
  2. 2. 湊谷 海斗 @kamina_zzz 2 食べログ DevOps チーム 兼 データサイエンスチーム ● 各種バージョンアップやったり ● 内製のライブラリ書いたり ● 機械学習のシステム構築したり ● 日本酒飲んだり ● バンドやったり minatoya kaito
  3. 3. きほん 3 basic usages
  4. 4. super のきほん class Super def hello puts 'Super: Hello World!' end end class Sub < Super def hello super end end Sub.new.hello #=> Super: Hello World! 4 スーパークラスの 同名 method を探索 → 実行
  5. 5. super のきほん class Super def hello puts 'Super: Hello World!' end end module Mod def hello puts 'Mod: Hello World from mod.' end end class Sub < Super include Mod def hello super end end Sub.new.hello #=> Mod: Hello World from mod. 5 正しくは: 継承上位の 同名 method を探索 → 実行
  6. 6. super のきほん class Ultra def hello puts 'Ultra: Hello World from Ultra.' end end class Super < Ultra end class Sub < Super def hello super end end Sub.new.hello #=> Ultra: Hello World from Ultra. 6 正しくは: 継承上位の 同名 method を探索 → 実行
  7. 7. super class Super def hello(*args) puts "Super: Hello #{args.join(',')}!" end end class Sub < Super def hello(*args) super end end Sub.new.hello('a', 'b', 'c') #=> Super: Hello a,b,c! 7 引数の渡し方 ● そのまま ● 無し ● 独自に設定
  8. 8. super() class Super def hello(*args) puts "Super: Hello #{args.join(',')}!" end end class Sub < Super def hello(*args) super() end end Sub.new.hello('a', 'b', 'c') #=> Super: Hello ! 8 引数の渡し方 ● そのまま ● 無し ● 独自に設定
  9. 9. super(arg1, arg2) class Super def hello(*args) puts "Super: Hello #{args.join(',')}!" end end class Sub < Super def hello(*args) super('Japan', 'JP', 'ja') end end Sub.new.hello('a', 'b', 'c') #=> Super: Hello Japan,JP,ja! 9 引数の渡し方 ● そのまま ● 無し ● 独自に設定
  10. 10. パッチ 特定の method の 一部だけ変更したい まさか ruby -v < 2.0 ってことは ないですよね…? class Target def hello puts "Hello World!" end end module Japan def hello puts "Hello Japan!" super end end Target.prepend Japan Target.new.hello #=> Hello Japan! #=> Hello World! 10
  11. 11. 実践 11 practices
  12. 12. こんなときどうする? 12 # 動的に定義されるメソッドにパッチを当てたい ● クラスメソッドで指定した引数を元にメソッドを定義する機構があるとする ● そのメソッドの仕様を自分好みにカスタマイズしたい
  13. 13. 動的に定義されるメソッドに パッチを当てたい こんな状況があったとして この foo= にパッチを あてたい $attrs = [:foo, :bar] class MyClass attr_writer *$attrs end obj = MyClass.new obj.foo = 'test' 13
  14. 14. やってみる 方針1: 同名のmethod を define_method して、 その中で super を呼ぶ $attrs = [:foo, :bar] class MyClass attr_writer *$attrs end module MyPatch $attrs.each do |attr| define_method("#{attr}=") do |val| super(val) puts 'this is patched!' end end end MyClass.prepend MyPatch obj = MyClass.new obj.foo = 'test' 14
  15. 15. やってみる 方針2: module_eval して 同名 method を定義し、 その中で super を呼ぶ $attrs = [:foo, :bar] class MyClass attr_writer *$attrs end module MyPatch $attrs.each do |attr| module_eval <<~EVAL, __FILE__, __LINE__ + 1 def #{attr}=(val) super puts "this is patched!" end EVAL end end MyClass.prepend MyPatch obj = MyClass.new obj.foo = 'test' 15 ※ module_eval == class_eval
  16. 16. define_method vs module_eval 16
  17. 17. pros/cons 17 - define_method module_eval pros ● 行数がちょっと少なくて済む ● 例外時の見え方がわかりやすい ● 移植しやすい cons ● super に渡す全引数を書かなきゃい けない ● 人によっては(?)読みづらい
  18. 18. super の引数 `block (2 levels) in <module:MyPatch>': implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly. (RuntimeError) 18 module MyPatch $attrs.each do |attr| define_method("#{attr}=") do |val| super puts 'this is patched!' end end end パッチ部分をこのように書くと 怒られてしまう
  19. 19. super の引数 `block (2 levels) in <module:MyPatch>': implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly. (RuntimeError) 19 define_method で定義された method の中では super に暗黙的に引数を渡すことはサポートされてないよ。 だから明示的にすべての引数を書いて渡してね。 ko1 氏曰く「どーしても実装出来なかったからです.誰か,実装方法を教えてください. (原文ママ)」とのこと。 http://www.atdot.net/~ko1/diary/200806.html#d14
  20. 20. Exception の例 20 module MyPatch $attrs.each do |attr| define_method("#{attr}=") do |val| super(val) raise 'ERROR!!!' end end end define_method の場合 Traceback (most recent call last): 1: from tmp.rb:17:in `<main>' tmp.rb:10:in `block (2 levels) in <module:MyPatch>': ERROR!!! (RuntimeError)
  21. 21. Exception の例 21 module MyPatch $attrs.each do |attr| module_eval <<~EVAL, __FILE__, __LINE__ + 1 def #{attr}=(val) super raise 'ERROR!!!' end EVAL end end module_eval の場合 Traceback (most recent call last): 1: from tmp.rb:19:in `<main>' tmp.rb:11:in `foo=': ERROR!!! (RuntimeError)
  22. 22. Exception の例 22 Traceback (most recent call last): 1: from tmp.rb:19:in `<main>' tmp.rb:11:in `foo=': ERROR!!! (RuntimeError) Traceback (most recent call last): 1: from tmp.rb:17:in `<main>' tmp.rb:10:in `block (2 levels) in <module:MyPatch>': ERROR!!! (RuntimeError) eval 系 method の第2, 第3 引数をちゃんとセットしておけば 例外が起きた method が明確
  23. 23. まとめ ● super にもいろいろあるので正しく使っていこう ● パッチを作るときは super + eval + prepend が良い感じ 23
  24. 24. おしまい 24
  25. 25. We're hiring! 25 @kamina_zzz reply or DM =>

×