Magic in ruby

1,351 views

Published on

Method Swizzling
Method Decorator
Refinement

Published in: Technology, Education
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,351
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Magic in ruby

  1. 1. Magic in Ruby David Lin davll.xc@gmail.com <beer> Oct. 18, 2013 </beer>
  2. 2. Suppose You Know 1. Ruby Programming Language - class, module, method - block - mixin (include, extend) 2. Metaprogramming - class_eval - define_method 3. Monkey Patching
  3. 3. Outline 1. Smalltalk-style method calling 2. Method swizzling 3. Decorator 4. Refinement
  4. 4. Ruby is Smalltalk-style! Objective-C, too.
  5. 5. 丟訊息 給物件 看看物件有什麼反應
  6. 6. obj.hello(“world”)
  7. 7. obj.hello(“world”) 這是 message
  8. 8. obj.hello(“world”) 這是 receiver
  9. 9. obj.hello(“world”)
  10. 10. obj.send(:hello, “world”) Yes, it sends a message!
  11. 11. obj 接收了message 內容為 hello(“world”)
  12. 12. 現在 obj 就要
  13. 13. 暴走 同步率400%
  14. 14. obj 找一個對應的 method body 根據 message name :hello
  15. 15. 然後... 引爆 invoke 呼叫這個 method body
  16. 16. DEFINE A METHOD 事實上是定義一個 method body 然後連到對應的 message name
  17. 17. def hello(s) define a method named ‘hello’ define a method for the name ‘hello’ a method body :hello
  18. 18. method body & message name 沒有綁死在一起 而是可以改變的
  19. 19. HACK IT! Let’s change behaviour
  20. 20. Method Swizzling 把 method body “抽換”掉
  21. 21. Example.rb class MyObj def hello(s) “Hello #{s}” end end method body A :hello
  22. 22. Hack1.rb (part 1) class MyObj alias_method :hello_original, :hello end :hello_original method body A :hello
  23. 23. Hack1.rb (part 2) class MyObj def hello_with_bracket(s) “{” + hello_original(s) + ”}” end end method body B :hello_with_bracket send message :hello_original
  24. 24. Hack1.rb (part 3) class MyObj alias_method :hello, :hello_with_bracket end method body B method body A :hello_with_bracket :hello
  25. 25. Hack1.rb (final) :hello method body B :hello_with_bracket send message method body A :hello_original
  26. 26. 可不可以 更簡潔些? 太多沒必要的 message names
  27. 27. Hack2.rb (to expect) :hello method body B call directly method body A :hello_with_bracket send message :hello_original
  28. 28. Hack2.rb (to expect) :hello method body B call directly method body A
  29. 29. Hack2.rb (yes, that’s all) class MyObj m = instance_method(:hello) define_method(:hello) do |s| “{” + m.bind(self).call(s) + “}” end end
  30. 30. Hack2.rb class MyObj m = instance_method(:hello) define_method(:hello) do |s| “{” + m.bind(self).call(s) + “}” end end m (a local var) method body A :hello
  31. 31. Hack2.rb class MyObj m = instance_method(:hello) define_method(:hello) do |s| “{” + m.bind(self).call(s) + “}” end end method body B method body A :hello
  32. 32. Hack2.rb (Ah, ha!) class MyObj m = instance_method(:hello) define_method(:hello) do |s| “{” + m.bind(self).call(s) + “}” end end method body B call directly method body A :hello
  33. 33. Decorator Method Wrapping (it’s more complicant than Python, though)
  34. 34. Example (Unsafe!) def send_money(from, to, amount) from.balance -= amount to.balance += amount from.save! to.save! end
  35. 35. Example (Safer) def send_money(from, to, amount) ActiveRecord::Base.transcation do from.balance -= amount to.balance += amount from.save! to.save! end end
  36. 36. Example in Ruby def send_money(from, to, amount) ActiveRecord::Base.transcation do # do operations end end
  37. 37. Example in Python def send_money(from, to, amount): try: db.start_transcation() # do operations db.commit_transcation() except: db.rollback_transcation() raise
  38. 38. Decorating in Python Form 1: @transcational def send_money(from, to, amount): # do operations Form 2: def send_money(from, to, amount): # do operations send_money = transcational(send_money)
  39. 39. Decorator in Python def transcational(func): def func2(*args): try: db.start_transcation() func(*args) # call decoratee db.commit_transcation() except: db.rollback_transcation() raise return func2
  40. 40. Decorating in Ruby class Bank extend Transcational # include decorator def send_money(from, to, amount) # do operations end transcational :send_money # decorate! end
  41. 41. Decorator in Ruby module Transcational def transcational(mthd_name) mthd = instance_method(mthd_name) define_method(mthd_name) do |*args, &blk| ActiveRecord::Base.transcation { # call decoratee mthd.bind(self).call(*args, &blk) } end end end
  42. 42. 這只是 Method Swizzling since Ruby has methods but functions
  43. 43. Refinement For Ruby 2.0+
  44. 44. 當你要... Monkey Patching Modify existing classes of other libs
  45. 45. Instead of monkey patching class KlassOrModule # define instance methods... end module MyLibrary # using modified KlassOrModule... end
  46. 46. 住手! You’re gonna RUIN EVERYTHING!
  47. 47. Use refinement module MyLibrary refine KlassOrModule do # define instance methods... end # using modified KlassOrModule... end
  48. 48. Example module MyLibrary refine String do # modify String def hello “Hello #{self}” end end end
  49. 49. Example (cont.) # Outside MyLibrary puts “Kitty”.hello # => NoMethodError: hello
  50. 50. Example (cont.) # Outside MyLibrary using MyLibrary puts “Kitty”.hello # => Hello Kitty
  51. 51. Inside Refinement Actually, refinement creates an annoymous module to mixin a class. module MyLib refine String do puts self.is_a?(Module) # => true puts self # => #<refinement:String@MyLibrary> end end
  52. 52. Inside Refinement module MyLib refine String do module <refinement:String@MyLibrary> end end
  53. 53. It is ... Automatic Mixining 自動化MIXIN
  54. 54. When using MyLibrary ... every new instance of String is extended! using MyLibrary puts “x”.hello # “x” is extended puts String.new(“x”).hello
  55. 55. using is lexical in scope just like Java’s import
  56. 56. using is lexical 1. Refinements are activated only at top-level - Not inside class, module, and method scope 2. Scope of the effect is the whole source code
  57. 57. using is lexical Please DO NOT… class Foo using MyLib end module Moo using MyLib end def bar using MyLib end
  58. 58. using is lexical # refinement of MyLib is deactived # “World”.hello # => NoMethodError using MyLib # activate! # refinement of MyLib is activated def hello_world “World”.hello end # END OF FILE Scope of MyLib Refinement
  59. 59. Refinement in Ruby 1. Actually, it is based on Mixining 2. Avoid global scope corruption 3. Prefer lexical scope to runtime scope => easy to use, just like import in Java
  60. 60. Better Refinement than Monkey Patching please, get rid of monkey patching.
  61. 61. 魔法のRuby まほうのRuby
  62. 62. 本簡報 用高橋流製作 在此向たかはしさん致敬
  63. 63. <FIN>

×