查看: 3093|回复: 7

[讨论] 尝试了一下彻底解决alias黑洞

[复制链接]

遠航の猫咪

梦石
3
星屑
26947
在线时间
2423 小时
注册时间
2005-10-15
回帖
987

开拓者

发表于 2021-3-7 00:24:22 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 SailCat 于 2021-3-7 14:25 编辑

解决alias黑洞的办法就是不要用alias

解决了传参问题和参数不一样个数(可多可少)的问题

还有以下问题
1. 可选参数(ruby 1.81的可选参数运行时定义就是个天坑) 通过将原方法接口暴露变通解决
2. singleton方法(Module的def self.xxx) 已解决

基本算是可以涵盖大部分情况的解决方案了

调用方法如:
Module X/Class X
  define_alias_method(原方法名, 重定义方式, [增补参数], 挂接方法名)
或者
  define_alias_method(原方法名, 重定义方式, [增补参数]) {|参数接口| 块}
end

参数接口如果等于多于原方法参数个数,不需要增补参数
参数接口如果少于原方法参数个数,需要按顺序增补缺少的必要参数
新方法需要用到可选参数的情况下,只能另行定义并用前面的调用方法(ruby 1.81不能在块里面用可选参数)
重定义方式目前有以下9种:
:before 将新方法挂在原方法之前
:after 将新方法挂在原方法之后,并返回原方法的返回值
:alter 将新方法挂在原方法之后,并返回新方法的返回值
:around 将新方法包含在原方法周围,原方法会成为块中的第一个参数(Method对象),可以改变参数自主调用原方法
:inherit 将新方法包含在原方法周围,但可以在块中利用原方法的返回值做一些邪恶的事情
:if 如果原方法执行成功,则执行新方法
:unless 如果原方法执行不成功,则执行新方法
:and 如果新方法执行成功,则执行原方法
:or 如果新方法执行不成功,则执行原方法

[pre lang="ruby"]module SailCat
  Object.send :include, self
  Module.send :include, self
  def singleton_class
    class << self; self; end
  end
   
  def define_alias_method(symbol, manner = :alter, *arg, &block)
    in_class = self.is_a?(Class)
    attr_sym = in_class ? :instance_method : :method
    def_sym = in_class ? :class_eval : :eval
    old = send(attr_sym, symbol)
    old_n = old.arity
    old_n = ~old_n if old_n < 0
    if block_given?
      (in_class ? self : singleton_class).send :define_method, symbol, &block
      new = send(attr_sym, symbol)
    else
      new = send(attr_sym, arg[-1])
      arg = arg[0..-2]
    end
    s_new = "new#{in_class ? '.bind(self)' : ''}.call(*args)"
    s_old = "old#{in_class ? '.bind(self)' : ''}.call(*old_args)"
    case manner
    when :before
      method_eval = "#{s_new}; #{s_old}"
    when :after
      method_eval = "v = #{s_old}; #{s_new}; v"
    when :alter
      method_eval = "#{s_old}; #{s_new}"
    when :around
      method_eval = s_new.sub(/\*args/) {s_old[0..-17] + ", *args"}
    when :inherit
      method_eval = s_new.sub(/\*args/) {s_old + ", *args"}
    when :if
      method_eval = "#{s_new} if v = #{s_old}; v"
    when :unless
      method_eval = "#{s_new} unless v = #{s_old}; v"
    when :and
      method_eval = "#{s_new} and #{s_old}"
    when :or
      method_eval = "#{s_new} or #{s_old}"
    end
    context = in_class ? "self" : "singleton_class"
    send def_sym, %(#{context}.send :define_method, symbol do |*args|
      old_args = args[0, old_n] + arg; #{method_eval}
    end)
  end
end

module A
  def self.b(a, b = 6)
    a + b
  end
  def self.b2
    0
  end
  define_alias_method(:b, :around) {|b, x| p x, b.call(x, 9);  }
  define_alias_method(:b, :before, 4) { p 1 }
  define_alias_method(:b, :alter, :b2)
  define_alias_method(:b, :inherit) {|b, x| p b - x}
end

A.b(5)     #=> 1/4, 13/-5
[/pre]

ruby 1.81真的是各种戴脚镣,但凡有个define_singleton_method我也不至于这么麻烦:(

评分

参与人数 1赞 +1 收起 理由
KB.Driver + 1 精品文章

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
梦石
1
星屑
11993
在线时间
4534 小时
注册时间
2005-10-22
回帖
7091

开拓者贵宾

发表于 2021-3-7 01:36:10 | 显示全部楼层
开个全局变量把那些alias后的方法名丢里面
再做一堆 ||= 怎么样呢?

实在不行强行升到1.9吧

点评

那1.9会变成什么  发表于 2021-3-16 14:16
对哒~这就是P社风格的解决方案 (✺ω✺)  发表于 2021-3-7 19:50
为了解决一个bug,引发十个新bug……  发表于 2021-3-7 19:46
重定义字符串(✺ω✺)  发表于 2021-3-7 18:01
1.9会有其他问题……最典型的就是字符串不是字节数组了  发表于 2021-3-7 04:47
回复

使用道具 举报

梦石
0
星屑
782
在线时间
71 小时
注册时间
2017-12-2
回帖
12
发表于 2021-3-15 14:34:20 | 显示全部楼层
我过年时重搞的 1.9 方案 https://rpg.blue/forum.php?mod=viewthread&tid=484700 ,只写了after和around,其它那些都可以用类似手法简单实现。没拼字符串,走的instance_exec,手里没有1.8的环境,不过看1.8.7的文档是有instance_exec的,可以参考下。
java.lang.NullPointerException
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部