Ruby学习之元编程
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学习之元编程的更多相关文章
- Ruby学习: 类的定义和实例变量
ruby是完全面向对象的,所有的数据都是对象,没有独立在类外的方法,所有的方法都在类中定义的. 一.类的定义语法 类的定义以 class 关键字开头,后面跟类名,以 end标识符结尾. 类中的方法以 ...
- ES6中的元编程-Proxy & Reflect
前言 ES6已经出来好久了,但是工作中比较常用的只有let const声明,通过箭头函数改this指向,使用promise + async 解决异步编程,还有些数据类型方法...所以单独写一篇文章学习 ...
- C++模板元编程----选择排序
目录 目录 前言 代码详解 数据的结构 数据的操作 分割向量 合并向量 寻找最大值 排序 总结 前言 模板在C++一直是比较神秘的存在.STL和Boost中都有大量运用模板,但是对于普通的程序员来说, ...
- 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 ...
- 3-11 《Ruby元编程》第4章block块 3-12
第4章代码块blocks 基础知识 作用域:用代码块携带variables through scopes 通过传递block给instance_eval方法来控制作用域. 把block转换为Proc, ...
- 3-8《Ruby元编程》第二章对象模型
<Ruby元编程> 第二章 对象模型 类定义揭秘inside class definitions: class关键字更像一个作用域操作符,核心作用是可以在里面随时定义方法. [].meth ...
- Ruby元编程:动态添加类属性及其实际应用
上个星期测试道的Monkey老师和我聊到测试用例参数过多的问题,其实这样的问题在我这里也同样经历过.比如我的测试用例必须面对不同的测试环境,每个环境有无数的参数,开发的最初阶段,因为参数少,所以就放在 ...
- Ruby元编程:单元测试框架如何找到测试用例
前几天看了Google Testing Blog上的一篇文章讲到C++因为没有反射机制,所以如何注册测试用例就成了一件需要各显神通的事情.从我的经验来看,无论是Google的GTest还是微软的LTM ...
- 201707《Ruby元编程》
元编程不过是编程--经典必读 作用域(绑定) 打破作用域门的方式 对象模型图 七条规则 法术手册 作用域(绑定) 改变作用域的关键字, 分别是module,class和def.我们称为作用域的门(sc ...
随机推荐
- mysql数据库误删除操作说明
在日常运维工作中,对于mysql数据库的备份是至关重要的!数据库对于网站的重要性使得我们对mysql数据的管理不容有失!然后,是人总难免会犯错误,说不定哪天大脑短路了来个误操作把数据库给删除了,怎么办 ...
- UWP 常用文件夹
①KnownFolders KnownFolders.PicturesLibrary 等等列举 ②ApplicationData.Current ApplicationData.Current.Loc ...
- C#、Java中的一些小功能点总结(持续更新......)
前言:在项目中,有时候一些小的功能点,总是容易让人忽略,但是这些功能加在项目中往往十分的有用,因此笔者在这里总结项目中遇到的一些实用的小功能点,以备用,并持续更新...... 1.禁用DataGrid ...
- springmvc关于前台日期作为实体类对象参数类型转换错误
页面报错: 后台错误: Field error in object 'user' on field 'birthday': rejected value [2013-06-24]; codes [ty ...
- Hadoop 少量map/reduce任务执行慢问题
最近在做报表统计,跑hadoop任务. 之前也跑过map/reduce但是数据量不大,遇到某些map/reduce执行时间特别长的问题. 执行时间长有几种可能性: 1. 单个map/reduce任务处 ...
- Effective Java 第三版——4. 使用私有构造方法执行非实例化
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 【JAVA零基础入门系列】Day7 Java输入与输出
[JAVA零基础入门系列](已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day ...
- MyBatis 批量操作、集合遍历-foreach
在使用mybatis操作数据库时,经常会使用到批量插入.IN条件查询的情况,这时就难免要使用到foreach元素.下面一段话摘自mybatis官网: foreach 元素的功能是非常强大的,它允许你指 ...
- 《代码大全(第二版)》【PDF】下载
<代码大全(第二版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382264 内容简介 <代码大全(第2版)>是著 ...
- Robotframework-Appium系列:登录操作
之前Appium的环境已经配置完成(参考Robotframework-Appium系列:安装配置),接下来就是如何使用Appium来完成我们的apk的测试工作. 一.环境准备 所需的软件列表如下 Ro ...