Kernel#evel()方法

和Object#instance_evel()、Module#class_evel()方法类似,evel()方法也是一个内核方法,Object#instance_evel()方法可以使调用对象为self,当前类为#self(当前对象的eigenclass),并且传递一个代码块访问self;Module#class_evel()方法则可以使调用者成为当前类,并在当前类中执行传入的块(self此时是一个class的实例对象),从而修改当前类的方法和属性。Kernel#evel()方法则是调用一串代码字符串,并且执行这个字符串中的代码。

*evel()方法都可以传入一个Binding类,Binding类是一个只包含作用域的对象,通过传入Binding类,可以使*evel()方法在Binding类所携带的作用域中执行代码。

Binding类可以通过Kernel#binding()方法创建:

class MyClass
    def my_method
        @a = 1
        binding
    end
end

当执行 b = MyClass.new.my_method时, b就代表了一个作用域,通过 eval “puts @a” ,b 传入该绑定,获得@a。

使用evel方法可以很方便地实现一些功能,如计算器应用的编写等,但是在能力越大的同时,也有更大的危险。Kernel#evel方法执行时不会检查字符串代码的语法,并且会带来一些安全问题:代码注入。当evel方法面向很多其他用户的时候,就可以通过传入一些命令来获得你的私有信息,所以用evel方法时必须非常谨慎,Ruby中,也会有一些安全措施:

Ruby会把不安全的对象标记为被污染的(尤其是从外部传入的对象),通过tainted?()方法可以判断该字符串是否被污染,为了避免检查每个对象的污染情况,Ruby内置了安全级别来默认处理这些危险操作。通过$SAFE全局变量可以改变当前的安全级别,默认为0——不受任何约束;一共有5个安全级别,任何大于0的安全级别都不能执行污染的字符串。

为了安全地使用evel,可以为evel()方法创造一个沙盒,并且在沙盒中运行该字符串:

proc{
    $SAFE = @safe_level
    evel “cmd”,b
}.call

evel()方法的替代

为了避免evel()带来的安全问题,可以使用其他的方法来替代evel():通过动态派发:send()方法来调用方法;通过class_evel()来进入类,并为类添加方法或者实例变量,并且用define_method()方法来动态添加方法;使用Object#instance_variable_set()和Object#instance_variable_get()方法来设置或者访问实例变量。

钩子方法

通过改写Class#inherited()方法、Module#included()方法、Module#method_added()方法等等,可以在相应的事件发生时执行所需要的代码。如在包含一个模块的时候打印提示:

module M
    def self.included(othermod)
        puts “M was mixed into #(othermod)”
    end
end
class C
    include M
end

在C类中包含M模块时,会打印:M was mixed into C 提示字符串。

钩子方法默认实现时只是捕获一个事件,并不会执行其他的动作, 可以通过改写Module#include()方法来完成上述功能:

class C
    def self.include(*modules)
         puts “#(modules) is included in C”
         super
    end
    include M
end 

上述方法直接修改了include方法,由于include()方法除了捕获到事件之外,还有其他的事情要做,所以需要通过super来进行原始的工作,其中super的作用是在父类(当前类是C,父类是Module)中调用同名的函数,并且将本函数的所有参数传入到同名函数中。还有一个方法super()带括号,则表示调用父类中的同名函数,但是不传入任何参数。*表示传入多个参数打包为一个数组,并且在方法调用时解开数组使每个元素成为一个独立参数,通过*modules可以一次包含多个模块。

通过环绕别名来实现钩子方法:

class C
    Class.class_eval {
        alias :real_include :include 
        def include (mod)
            real_include mod
            puts "#{mod} was included!"
        end
    }
    include M
end

类扩展混入

当一个类包含模块时,只会获得一组实例方法,而不会获得任何类方法,通过在eigenclass中包含一个模块来实现类扩展(或者使用extend()方法),如果一个模块期望被包含时一直可以作为类扩展,则可以通过在模块中添加钩子方法来实现:

module M
    def self.included(base)
        base.extend(ExtendMethods)
    end
   
    module ExtendMethods
        def my_method()
            #…
        end
    end
end

此时,如果在类中include M,则会调用钩子方法M.included(),将M中的子模块ExtendMethods(纯净室)作为类扩展,将Methods中的方法添加到包含类的eigenclass中。

上述将类扩展和钩子方法结合的技术叫做类扩展混入。而且,如果在M中又不需要被扩展的方法,则可以放到ExtendMethods外部定义一些额外的方法,这些方法不会被扩展为包含着的类方法。如果M中所有的方法都需要被扩展为类方法,则把所有的方法都定义在M本身即可。 

Ruby学习之元编程的更多相关文章

  1. Ruby学习: 类的定义和实例变量

    ruby是完全面向对象的,所有的数据都是对象,没有独立在类外的方法,所有的方法都在类中定义的. 一.类的定义语法 类的定义以 class 关键字开头,后面跟类名,以 end标识符结尾. 类中的方法以 ...

  2. ES6中的元编程-Proxy & Reflect

    前言 ES6已经出来好久了,但是工作中比较常用的只有let const声明,通过箭头函数改this指向,使用promise + async 解决异步编程,还有些数据类型方法...所以单独写一篇文章学习 ...

  3. C++模板元编程----选择排序

    目录 目录 前言 代码详解 数据的结构 数据的操作 分割向量 合并向量 寻找最大值 排序 总结 前言 模板在C++一直是比较神秘的存在.STL和Boost中都有大量运用模板,但是对于普通的程序员来说, ...

  4. 3-20 标准库:find库; 学习编程语言3节课(大多是旧识,全*栈)3-21 面向对象. Percent Strings; 元编程和Rails的相互理解

    Find The Find module supports the top-down traversal of a set of file paths.(一系列文件的路径的遍历) find(*path ...

  5. 3-11 《Ruby元编程》第4章block块 3-12

    第4章代码块blocks 基础知识 作用域:用代码块携带variables through scopes 通过传递block给instance_eval方法来控制作用域. 把block转换为Proc, ...

  6. 3-8《Ruby元编程》第二章对象模型

    <Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...

  7. Ruby元编程:动态添加类属性及其实际应用

    上个星期测试道的Monkey老师和我聊到测试用例参数过多的问题,其实这样的问题在我这里也同样经历过.比如我的测试用例必须面对不同的测试环境,每个环境有无数的参数,开发的最初阶段,因为参数少,所以就放在 ...

  8. Ruby元编程:单元测试框架如何找到测试用例

    前几天看了Google Testing Blog上的一篇文章讲到C++因为没有反射机制,所以如何注册测试用例就成了一件需要各显神通的事情.从我的经验来看,无论是Google的GTest还是微软的LTM ...

  9. 201707《Ruby元编程》

    元编程不过是编程--经典必读 作用域(绑定) 打破作用域门的方式 对象模型图 七条规则 法术手册 作用域(绑定) 改变作用域的关键字, 分别是module,class和def.我们称为作用域的门(sc ...

随机推荐

  1. PHP大文件分割上传(分片上传)

    服务端为什么不能直接传大文件?跟php.ini里面的几个配置有关 upload_max_filesize = 2M //PHP最大能接受的文件大小 post_max_size = 8M //PHP能收 ...

  2. 配置scrapy-splash+python爬取医院信息(利用了scrapy-splash)

    北京艾丽斯妇科医院(http://fuke.fuke120.com/) 首先先说一下配置splash 1.利用pip安装scrapy-splash库 pip install scrapy-splash ...

  3. SpringBoot集成Redis实现缓存处理(Spring AOP实现)

    第一章 需求分析 计划在Team的开源项目里加入Redis实现缓存处理,因为业务功能已经实现了一部分,通过写Redis工具类,然后引用,改动量较大,而且不可以实现解耦合,所以想到了Spring框架的A ...

  4. PHP获取一周的日期

    /** * 获取一周日期 * @param $time 时间戳 * @param $format 转换格式 */ function get_week($time, $format = "Y- ...

  5. webrtc视频数据render流程

  6. BFS求最短路 Abbottt's Revenge UVa 816

    本题的题意是输入起点,朝向和终点,求一条最短路径(多解时任意输出一个即可) 本题的主要代码是bfs求解,就是以下代码中的slove的主要部分,通过起点按照路径的长度来寻找最短路径,输出最先到终点的一系 ...

  7. CCFlow最近在山东济南总部举行组团培训活动,有參加的能够报名

    最近.CCFlow将会组织培训,由总设计师--周朋先生亲自授课. 行程                        培训内容 第一天                     ccflow的概述功能简 ...

  8. Java中File的使用

    File 代表文件或者目录的类 构造函数 File(File parent,String child)---代表了指定父目录下的指定的子文件或者子目录 File(String pathname)--- ...

  9. mysql查询进程、导入数据包大小设置

    mysql查询进程.导入数据包大小设置 zoerywzhou@163.com http://www.cnblogs.com/swje/ 作者:Zhouwan 2017-12-27 查询正在执行的进程: ...

  10. 解决[babel] note:the code generator has deoptimised the styling...

    在使用webpack的babel-loader编译es6的js文件时,出现了如题的提示. 解决方法如下: loaders: [ { test: /\.js$/, loader: 'babel', qu ...