Meta Ruby-ruby元编程
Moday: The Ojbect Model
- Monday with Bill
- Open Classes
- Inside Class Definitions
- The Problem with Open Classes
- The Truth About Classes
- What's in an Object
- Classes Revisited
- Constants
- Objects and Classes Wrap-up
- Another Learning Opportunity
- Quiz: Missing Lines
- Quiz Solution
- What Happens When You Call a Method?
- Method Lookup
- Kernel Module
- Method Execution
- Quiz: Trangle of Modules
- Quiz Solution
- Object Model Wrap-up
Tuesday: Methods
- A duplication Problem
- The Legacy System
- Double, Treble... Trouble
- Dynamic Methods 我的导师告诉我,当你调用一个方法时,实际上是给一个对象发送了一条消息。
- Calling Methods Dynamically
- obj.my_method(3) 这个 . 就是调用方法的标记,使用Object#send方法可以替换这个点, obj.send(:my_method, 3) 第一个参数可以是符号或者字符串,为什么要用send?因为可以将方法名作为一个参数,在代码运行的最后阶段才决定调用哪个方法。这种技术称为:Dynamic Dispatch 动态派发。
- x = :this_is_a_symbol,符号和字符串的区别?大多数情况下,符号表示事物的名字,尤其是和元编程相关的名字,比如方法名: 1.send(:+, 2), String.to_sym or String#intern(), 反向调用是Symbol#to_s or Symbol#id2name();
- OpenStruct
- 动态派发技术给每个 key 赋值。 YAML.load_file(conf.rc).each {|k, v| conf.send("#{k}=", v)}
- Test::Unit 里的动态派发判断哪些方法是测试方法: method_names = public_instance_methods(true); tests=method_names.delete_if {|method_name| method_name !~ /^test./} 这种方法优势成为: Pattern Dispatch 模式派发。
- 动态定义方法: Module#define_method(); define_method :my_method {|my_arg| my_arg *3};类的实例方法,,动态方法=Dynamic Method; Dynamic Class Method
- Refactoring the Computer Class: 提炼公共的代码为一个方法.
- introspection: data_source.methods.grep(/^get_(. )_info$/) { Computer.define_component $1 }
- Calling Methods Dynamically
- method_missing() 这个非常特殊的方法,Ghost Method, 它存在kernel#method_missing, nick.send :method_missing, :my_method ,这句code是ruby解释器找不到方法是做的事。
- Overriding method_missing() 每个来找method_missing()方法的都带着方法和参数过来的。
- def method_missing(method, *args)
- puts "You called: #{method} (#{args.join(', ')} )"
- puts "(You also passed it a block)" if block_given?
- end
- Ghost Methods 幽灵方法,例子Gem包:ruport
- 如果别人问你一个你不知道的问题,你就问:你为什么要问这个问题? 。。。接受者并没有相对应的方法,这就是个Ghost Method.
- return as($1.to_sym, *args, &block) if id.to_s=~ /^to_(.*)/); return rows_with($1.to_sym => args[0]) if id.to_s =~ /^rows_with_(.*)/
- rows_with_country("France")虽然不过是个语法糖,但也有实际好处:如果定义新的输出格式,就会自动获得相对应的方法。
- attribute = name.to_s; if attribute =~ /=$/ { @attributes[attribute.chop] = args[0] }
- Dynamic Proxies 动态代理
- http://api.flickr.com/services/rest/?method=flickr.people.findByUsername&username=ducandavidson&api_key=your API key here
- method=flickr.tags.getListUser() 把Flickr API方法中的点号.变成下划线_. 这样就可以变成一个合法的Ruby方法名。 Flickr#tags_getListUser()都是幽灵方法。
- request(method_id.id2name.gsub(/_/, '.'), params[0])
- delegate; require 'delegate'; 助手被DelegateClass(助手类); 管理类 < DelegateClass(助手类), 管理处理不了的方法,自动调用助手类的方法处理。
- method_missing() 会把方法转发给别的对象的
- Refactoring the Computer Class(Again)
- @data_source.respond_to?("method") ; @data_source.send("method", args[0]);
- respond_to? 方法,判断不了Ghost方法method_missing()...里面的方法无法判断,这是需要overload respond_to?方法。
- const_missing()
- Quiz: Bug Hunt
- method_missing方法使用要小心,谨慎。
- More method_missing()
- When Methods Clash冲突时候,如果存在真实方法,则不会调用method_missing方法。
- 这里要用到 undef_method方法,取消继承链条里面的别的方法。
- undef_method m unless m.to_s =~ /method_missing|respond_to?/ 还有个Object中的__id__ 和 __send__不能取消, may cause serious problem.
- instance_methods.each do |m|
- undef_method m unless m.to_s =~ /^__|method_missing|respond_to?/ 这些特征的方法保留,其它的方法 取消。
- end
- Wrapping It Up
- p BasicObject.instance_methods => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec, :__send__] ,从BasicObject继承来的类自动成为白板类 BlankClass
- 发现方法之间的重复性
第三章 代码库
Wednesday: Blocks
block控制作用域的手段。作用域表示:哪些代码可以看到哪些变量和方法。 可调用对象: block, proc, lambda. 存储一个块,供后续使用。
块是继承自“函数式编程语言 functional programming language” ,对象、方法、块。
- How to Handle Hump Day 如何读过驼峰日
- Today‘s Road map
- Block块 => Scope作用域 => Closure闭包 => instance_eval() => block to Proc and lambda be callable object 块变可调用的对象。
- Back to the Basics
- 只有调用一个方法时,才能定义一个块: a_method(1,2) {|x, y| (x +y )*3}, 前面是调用方法,后面紧跟着代码块:可以是{...},或者 do...end
- Kernel#block_given?
- return yield if block_given?
- Quiz:Ruby#
- using 关键字,里面的代码执行完后会自动释放。
- The Challenge and Quiz Solution: begin ... rescue....rescue ...rescue.... else ... ensure ...
- module Kernel
- def using(resource)
- begin
- yield
- ensure
- resource.dispose
- end
- end
- end
- Today‘s Road map
- Closures 闭包
- 闭包也是块,代码块中的变量是定义的时候绑定的当时环境的变量,在被调用的方面里面的变量,这个代码块看不见,也就称为闭包closure.
- 所有绑定寄居的场所--作用域-scope, 绑定是对象的局部变量,实例变量,self。
- Scope作用域
- 站在一个有实例变量和方法的self对象中,脚下一堆局部变量,远处一棵挂满常量的树,更远处还有一组全局变量。
- Scope Gate: class, module, def ; class and module定义是会被执行,def执行时才会。
- 全局变量: $开头的
- 顶级实例变量: @开头的
- Flattening the Scope 扁平化作用域。
- nested lexical scopes 嵌套文法作用域 [ˈleksɪkl] 词汇的
- flattening the scope 扁平化作用域
- Flat Scope 扁平作用域
- class =》 Class.new; def => define_method; ,通过这种替换,穿越作用域门。
- 共享作用域
- 在方法里面,通过Kernel.send :define_method, :my_method {} 来定义方法。 这些动态派发(Kernel.send)方法,共享变量的技巧称为 共享作用域。
- Scope Wrap-Up 作用域小结, 穿越作用域
- class =》 Class.new
- module => Module.new
- def => Module#define_method()
- instance_eval()
- Object#instance_eval()
- instance_eval(), eval 是评估的意思,实例评估,评估就要进入实例的内部,,进入内部去探测,这里就有个新名词:探针,谁是探针?instance_eval()带的代码块就是探针,因为这段代码就像探针一样进入到了instance的内部。, instance_eval待的这段代码块被称为: 上下文探针 Context Probe,
- instance_exec() 是升级版, 可以给进入对象内执行的块,带上参数。
- Breaking Encapsulations 打破封装
- 这个很有趣,也就是给对象里面增加实例变量,而又不用重新去定义类, someobject.instance.eval {@options = something } 这个太厉害了,算是动态定义了实例变量了吗?
- 这个上下文探针的应用感觉已经超出了名字的范围,不仅仅是探针,毕竟探针只是探测是只读的,这个是修改了。。
- Clean Room 洁净室
- Clean Room就是干净的房间,进来就是为了执行代码块Block。
- 这又是一个有趣的应用,洁净室里面只有一些方法,就好比是一个没有署名的工具,都可以用,
- 这个 Clean Room 是不是像 云函数的概念呢? 把代码提交上到云上执行, 提交的代码不会更改云函数。
- 代码换一种写法,让这个代码更具有层次感了。
- 代码块就是闭包,它在调用方法的时候被定义,通过block_given?判断是否存在,通过yield(&block)调用,它可以去当探针,也可以用于洁净室。
- 可调用对象 Callable Object
- 代码块有很多个称呼了,如探针,闭包。这里还可以转换成一个对象:proc,lambda
- Proc.new {|x| x+1}
- lambda {|x| x -1}
- Proc {|x| x -1}
- 块就像是方法的额外的匿名参数。yield运行一个块。
- 如何把块传给另外一个方法? 需要给这个匿名参数赋予一个&开头的名字,且只能放在最后。
- def teach_mach(a, b, &operation) &operation 就表示一个Proc对象。这也是&操作符的真正含义。
- &操作符的真正含义:这是一个Proc对象,我想把它当做一个块来使用。
- 简单的去掉&操作符,就能再次得到一个Proc对象。
- Proc对象就可以用 Proc.call()来执行。 通过call来调用,这种技术称为:延迟执行: Defered Evaluation
- def my_method(greeting)
- puts "#{greeting}, #{yield}"
- end
- my_proc = Proc {"Bill"}
- my_method("Hello", &my_proc) => 将my_proc当做块来使用&my_proc
- HighLine例子
- name = h1.ask("Name?", lambda {|x| x.capitalize })
- puts "Hello, #{name}"
- 先把Proc传给Question类的一个对象,并把这个Proc当成一个实例变量存储起来,在收集完用户输入后,这个Question对象会把用户的输入传递给这个Proc对象。这也是延迟执行 Defered Evaluation
- Proc 和Lambda的区别
- proc更像一段程序,return返回的是定义它的作用域。 传递给他的参数也数量也是可以变化的,就像你是否定义了几个变量。
- Lambda 像一个对象, return返回的是lambda本身, 传递给他的参数也是固定不可变的。定义的是2个参数,那就传2个参数。
- 鉴于此,大家爱用lambda多一点,他还有种简写的方式:
- p = ->(x) {x + 1} 代替: p = lambda {|x| x+ 1}
- Methods Revisited重访方法。
- lambda就是个方法,
- 普通的类的对象的方法,可以通过 p = obj.method :my_method 的方式将这个my_method变成lambda 。 p.call() = object.my_method(). 这里不是完全相等的,lambda是闭包,没有obj所在的环境下的装备(绑定),那么可以将这个目前为lambda的 p卸掉装备,unbound = p.unbind, 卸掉装备的这个p变成了真正的lambda,但是他又不是完整的lambda,是一个没有灵魂的专属残片。纯正的lambda是有标准接口的一个手臂,可以用来接入到设备上,从对象过来的p是从设备上扣下来的还连着控制线手臂,还能够被obj使用,现在通过unbind,断开了和obj的链接,成了个孤件,再给他接到同类的上面,又可以用了。p = unbound.bind(another_object); p.call
- Method#to_proc
- Object#method :my_method => lambda
- define_method 把块转换成方法
- Callable Objects Wrap-Up可调用对象小结
- 块
- proc
- lambda
- 方法
- 领域专属语言 Writing a Domain-Specific Language
- def event(name)
- puts "ALERT: #{name}" if yield
- end
- Dir.glob('*events.rb').each {|file| load file}
- test_events.rb
- event “。。。” do
- true
- end
- def event(name)
- 共享event
lambda { setups = {} events = {} Kernel.send :define_method, :event do |name, &block| events[name] = block end Kernel.send :define_method, :setup do |&block| setups << block end Kernel.send :define_method, :each_event do |&block| events.each_pair do |name, event| block.call name, event end end Kernel.send :define_method, :each_setup do |&block| setups.each do |setup| block.call setup end end }.call Dir.glob('*events.rb').each do |file| load file each_event do |name, event| env = Object.new each_setup do |setup| env.instance_eval &setup end puts "ALERT #{name}" if env.instance_eval &event end end
Kernel.send : 动态派发
:define_method, 块变方法
lambda {...}.call 延迟执行。
env.instance_eval &setup 执行环境绑定
instance_eval 进入实例内执行。
类是一个加强的模块
Class Definitions Demystified 类定义揭秘
Inside Class Definitions 深入类定义
类也可以是self, 当前类 The Current Class
怎么样才能在不知道类的名字的情况下打开一个类呢?需要一种新的方式,它不需要使用class关键字就能修改当前类。答案就是class_eval()
Module#class_eval()方法会在一个已经存在的类的上下文中执行一个块:
def add_method_to(a_class) a_class.class_eval do def m; 'Hello!'; end end end
add_method_to String
"abc".m #=> "Hello!"
当前类
类里面的方法1里面再定义方法2,方法2的self不是类,但是还是有当前类。
在顶级作用域中定义方法的时候,这个方法会成为Object类的实例方法。
instance_eval()和class_eval()区别很大
instance_eval仅仅会修改self
class_eval()会同时修改self和当前类,实际上是打开了该类,就像class关键字做的一样。
阅读量: 733
发布于:
修改于:
发布于:
修改于: