fork download
  1. eval DATA.read
  2.  
  3. class Foo
  4. extend Ova
  5.  
  6. def bar a, b = String, c = [Numeric], d = :to_h | Hash | :keys, e
  7. 'This is just silly.'
  8. end
  9.  
  10. def bar a = String
  11. bar a.to_i
  12. end
  13.  
  14. def bar a = Numeric
  15. a * 2
  16. end
  17.  
  18. def bar a = [Numeric]
  19. a.map(&:to_s)
  20. end
  21. end
  22.  
  23. class Bar
  24. def keys
  25. end
  26. end
  27.  
  28. foo = Foo.new
  29. p foo.bar 21
  30. p foo.bar '21'
  31. p foo.bar [1, 2, 3]
  32. p foo.bar -> {}, '?!', [1, 2, 3], Bar.new, ObjectSpace
  33. p foo.bar :x, 'y'
  34.  
  35. __END__
  36. module Ova
  37. # autovivifying map from [class][method][signature] to Method
  38. @@Ova = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
  39.  
  40. # Wrap #respond_to? for parity with Class#===.
  41. Responder = Struct.new(:method) do
  42. def === obj
  43. obj.respond_to?(method)
  44. end
  45. end
  46.  
  47. Sum = Struct.new(:types) do
  48. def === obj
  49. types.any? { |t| t === obj }
  50. end
  51. end
  52.  
  53. def self.signature imeth
  54. iseq = RubyVM::InstructionSequence.of(imeth).to_a
  55. insns = iseq.last
  56. info = iseq.grep(Hash).reduce(:merge)
  57.  
  58. [Object] * info[:lead_num].to_i +
  59.  
  60. info[:opt].to_a.each_cons(2).map do |a, b|
  61. from, to = insns.index(a), insns.index(b)
  62. insns[from...to].grep(Array).reduce([]) do |stack, (op, arg)|
  63. stack << case op
  64. when :getconstant # classes
  65. const_get(arg)
  66. when :putobject # symbols
  67. Responder.new(arg)
  68. when :newarray
  69. [stack.pop]
  70. when :duparray # single-symbol arrays (?)
  71. [Responder.new(arg[0])]
  72. when :opt_send_without_block # alternation (|)
  73. Sum.new(stack.pop(2))
  74. else # "no-op"
  75. stack.pop
  76. end
  77. end.pop
  78. end +
  79.  
  80. [Object] * info[:post_num].to_i
  81. end
  82.  
  83. def self.dispatch mname
  84. proc do |*args, &blk|
  85. matched, imeth = @@Ova[self.class][mname].find do |sig, _|
  86. sig.size == args.size && sig.zip(args).all? do |match, obj|
  87. obj.all? { |o| match[0] === o } rescue match === obj
  88. end
  89. end
  90.  
  91. if matched
  92. imeth.bind(self).call(*args, &blk)
  93. else
  94. raise ArgumentError,
  95. "#{self.class}##{mname} of " +
  96. "(#{args.map(&:class) * ', '})" +
  97. " not implemented",
  98. caller
  99. end
  100. end
  101. end
  102.  
  103. def method_added mname
  104. return if caller[1]['method_added']
  105.  
  106. imeth = instance_method(mname)
  107. @@Ova[self][mname][Ova.signature(imeth)] = imeth
  108.  
  109. return if @@Ova[self][mname].size == 1
  110.  
  111. define_method(mname, Ova.dispatch(mname))
  112. end
  113. end
Runtime error #stdin #stdout #stderr 0.01s 6528KB
stdin
Standard input is empty
stdout
42
42
["1", "2", "3"]
"This is just silly."
stderr
prog.rb:33:in `<main>': Foo#bar of (Symbol, String) not implemented (ArgumentError)