rEFINE in ruby




szRubyists Meetup 12th

by WinDy

ABOUT me


正宗Ruby玩家
就职于一家网络公司
三年经验
野路子

联系方式请在这里找: http://who.yafeilee.me

Try MONKEY-PATCH


class Fixnum
  alias old_plus +
  def +(o)
    old_plus(o).old_plus(o)
  end
end

puts 1+1 # output is 3


MONKEY-patch in REAL WORLD



    require 'rubygems'
    require 'active_support'
    require 'active_support/core_ext'
    
    puts [1,2].second # output is 2
    

PRoblem


一剑封喉
走火入魔
如履薄冰
信春哥,坑队友
......

Refine COMING


module R
  refine String do
    def camelize(o)
      "ABC"
    end
  end
end

class K
  using R
  puts "a".camelize # got ABC
end

puts "a".camelize # no method error

REFINE



so easy...

REFINE



I don't think so...

rEFINE


do you know module_eval ?

class Baz < Quux
  def up_and_add(str1, str2)
    str1.upcase + str2.upcase
  end
end
Really so simple ?

REFINE


module BadRefinement
  refine String do
    def upcase
      reverse
    end
  end
end
  
class Quux
  using BadRefinement
end

class Baz < Quux
  def up_and_add(str1, str2)
    str1.upcase + str2.upcase
  end
end

REFINE



real MAGIC!!!

REFINE



real "COOL" ?

REFINE



Oh no, BAD thing agian!

REFINE


封装性
歧义
行为一致性

REFINE



All Broken!!!

REFINE WITH 
MODULE_EVAL


def add_all(str_ary)
  str_ary.inject('') do |str, accum|
    accum + str
  end
end

仅仅是 "字符串连接吗" ? 
不要忘了 module_eval

REFINE WITH 
MODULE_EVAL


module WeirdPlus
  refine String do
    def +(other)
      "#{self} plus #{other}"
    end
  end
end

class MyArray
  def initialize
    @ary = ['foo', 'bar', 'baz']
  end

  def inject(accum, &block)
    @ary.each do |str|
      accum = WeirdPlus.module_exec(str, accum, &block)
    end
    accum
  end
end
    

实现的问题


  • 当前上下文(lexical scope)
  • 调用者上下文(inherit scope)

附: Method Search



METhoD SEARCH E.G.


class A
  def self.a
    puts 'a'
  end

  class << self
    a # wrong
  end
end

class B < A
  a # right
end 

constant lookup


module A
  module B; end
  module C
    module D
      B == A::B
    end
  end
end

surrounding lexical scope

CONSTANT LOOKUP


module A
  module C
    module D
      Module.nesting == [A::C::D, A::C, A]
    end
  end
end

CONSTANT LOOKUP TESTING


module M1
  CT = "ok"
end

class C1
  CK = "ck"
  include M1

  def self.method1
    puts self
    puts "#{CK} in method1"
    puts "#{CT} in method1"
  end

  class << self    
    def method2
      puts self
      puts "#{CK} in method1"
      puts "#{CT} in method2"
    end
  end
end

C1.method1 # here is ok ?
C1.method2 # what will happen ?

CONSTANT LOOKUP SUMMARY


  • Lookup lexical scope
  • if not, lookup ancestors scope


so easy, isn't it ? yes, but not really.

CLASS_EVAL


class A
  module B; end
end

class C
  module B; end
  puts A.class_eval { B } == A::B
end 
     true or false ?

CLASS_EVAL


class A
  module B; end
end

class C
  module B; end
  puts A.class_eval("B") == A::B
end    
     true or false ?

READ MORE




C语言足够好? 看这个:

解决之道


  • 去掉 module_eval 对 refine 的支持
  • 限制 using 的支持范围:

  • using 限定为关键字
  • using 只允许在类中使用

回顾


  • module_eval带来的问题
  • 限制using

当前的实现


Ruby2.0.0-preview1

当前的实现


Ruby2.0.0-preview2

IN the LATER

  • refinements are file scope
  • only top-level "using" is available
  • no module scope refinements
  • no refinements inheritance
  • module_eval do not introduce refinement

BUT IN the future



激烈讨论中:

总结


  • DON'T HARM.
  • 隔离作用域.
  • 理解背后的原理.
  • BUT...  please don't use it now.

QA


Any Question?






Thx.

THANKS



中译版: 

refine in ruby

By Li Yafei

refine in ruby

  • 6,896