一、心得体会
1、今天完成了什么?
- 看了Ruby核心、Duck typing、类与对象、RUby安全
- 看10个controller
2、今天收获了什么?
- 类对象的类型不是类
- 什么是module
- 什么是代理类
- 10个controller:工单日志(Ticketschanges)、工单(tickets)、加急原因、登记、仓库(warehouses)、话术(words)、师傅(workers)、小组规则、案例标签(case_tags)、案例(cases)
3、今天犯了哪些错误?
4、今天的状态如何? - 今天状态爆表,看完了镐头书,还把rails-guide快速浏览了一遍,觉得比镐头书要容易些,两天内看完。
5、明天还需要做什么? - 明天好好计划怎么在两天之内把rails书看完
- 计划看一半
二、读书笔记
22.6 方法参数
22.7 调用方法(Invoking a Method)
开头的形参将被赋值给方法的实际参数,这些形参后面可以有一个key=>value配对的列表,这些key=>value对被将收集到一个新的Hash对象中,并作为一个形参传入方法。
这些形参后面可以是一个前面带星号的参数,如果这个形参是一个数组,那么Ruby将用相应的数组元素,将它替换为零或多个形参。
def regular(a, b, *c)
end
block可以关联一个方法调用,该调用可以使用字面量形式的block(它必须和调用的最后一行在同一行上),也可以关联一个参数,该参数包含带有&符号的Proc或Method对象的引用,不管block参数存在与否,Ruby使用全局函数Kernel.block_given?的值来判断是否存在与本调用相关联的block。
a_proc = lambda{ 99 }
an_array = [ 98, 97, 96]
def block
yield
end
block { }
block do
end
block(&a_proc)
def all(a, b, c, *d, &e)
puts "a = #{a.inspect}"
puts "b = #{b.inspect}"
puts "c = #{c.inspect}"
puts "d = #{d.inspect}"
puts "block = #{yield(e).inspect}"
end
all('test', 1 => 'cat', 2 => 'dog', *an_array)
Foo.bar() #方法调用
Foo.bar #方法调用
Foo::bar() #方法调用
Foo::bar #访问常量
方法的返回值是执行的最后一个表达式的值。
return []
return表达式会立即退出方法,如果不带参数调用return,则return的返回值为nil,如果带一个参数,则返回该参数的值,如果参数多于一个,则返回一个包含所有参数值的数组。
22.7.1 super
在方法体内,调用super就像调用原方法一样,不过是在含有原方法的对象的超类中搜索方法体,如果没有参数(且未加括号)传递给super,则原方法的参数将作为super的参数,否则,super的参数将被传递。
22.7.2 操作符方法(Operator Methods)
如果操作符表达式中的操作符是一个可重新定义的方法,Ruby像调用如下表达式那样执行操作符表达式。
22.7.3 属性赋值(Attribute Assignment)
当receiver.attrname作为左值出现时,Ruby将调用接收者中名为attrname=方法,并以右值作为其唯一参数,这种赋值语句的返回值是rvalue的值——attrname=的返回值将被抛弃。
如果你想访问返回值(多半情况下不是rvalue的值),那么向方法发送一个显式的消息
class Demo
attr_reader :attr
def attr=(val)
@attr = val
"return value"
end
end
d = Demo.new
22.7.4 元素引用操作符(Element Reference Operator)
receiver[expr[,expr]...]
receiver[expr[,expr]...] = rvalue
当用作右值时,元素引用调用接收者的[]方法,并以方括号中的表达式作为参数传递。## ????不懂
22.8 别名(aliasing)
alias new_name old_name
将创建一个引用已有的方法、操作符、全局变量或正则表达式向后引用的新名字
当为方法起别名字,新的名字将指向原方法体的一个拷贝,即使后来方法被重新定义了,别名仍旧会调用原来的方法实现代码。
def meth
"original method"
end
alias origi meth
def meth
"new and improved"
end
meth
origi
22.9 类定义(Class Definition)
Ruby的类定义通过执行类代码体创建或者扩展类Class的对象。第一种形式中,一个命名类将被创建或扩展。生成的对象将被赋给名为classname的常量。
这个名字应该以一个大写字母开头,在第二种形式中,一个匿名类会和指定的对象相关联。
如果superexpr存在,那么它应当是一个以Class对象为结果的表达式,而且它也将是被定义的类的超类,如果省略了superexpr,则默认为类Object。
在方法体内,随着各种定义代码的读入,大多数Ruby表达式将被执行。然而:
- 方法定义将在类的对象的一个表中注册该方法。
- 嵌套的类和模块定义将被存储在类的常量中,而不是全局常量中。
module NameSpace
class Example
CONST = 123
end
end
obj = NameSpace::Example.new
a = NameSpace::Example::CONST
方法Module#include将把命名的模块添作被定义的类的匿名超类。
使用域作用符(::)可以为类定义中的classname前置一个已存在的类或模块名。这种语法会将新的定义插入到前面已定义的模块或类的名字空间,但不是在这些外部类的作用域中解释此定义。
在下面的例子中,类C被插入到模块A的作用域,但并不是A的上下文中进行解释。结果,对CONST的引用被解释成该名字对应的顶层常量而不是A的常量,而且我们必须使用单例方法的全名,因为在A::C的上下文,C本身不是一个已知的常量。
CONST = "outer"
module A
CONST = "inner"
end
module A
class begin
def B.get_const
CONST
end
end
end
A::B.get_const
class A::C
def (A::C).get_const
CONST
end
end
A::C.get_const
22.9.1 从类中创建对象(Creating Objects from Classes)
类class定义的实例方法class#new将被创建接收者对应的类的对象。这是通过调用classexpr.allocate来完成的,你可以重载此方法,但是你的实现必须返回正确的类的对象,然后它调用新创建的对象的initialize,并将传递给new的参数传递给initialize。
如果类定义中重载了类方法new,并且new没有调用super,那么将无法创建该类的对象,并且调用new将返回nil。
和其他方法一样,initialize应该调用super,以保证父类被适当的初始化,如果父类是Object,则不需要这么做,因为Object类不做任何实例相关的初始化。
22.9.2 类属性声明(Class Attribute Declarations)
类属性声明不是Ruby语法的一部分:它们不过是定义在类Module中的方法,该方法会自动创建访问类属性的方法。
class name
attr attribute
attr_reader attribute
attr_writer attribute
attr_accessor attribute
end
22.10 模块定义(Module Definitions)
module name
body
end
模块基本上是一个不能被实例化的类,和类一样,在定义过程中模块体将被执行,生成的模块Module对象被存储在一个常量中,模块中可以含有类方法和实例方法。也可以定义常量和类变量。和类一样,通过使用Module对象作为接收者调用模块方法,通过使用“::”域作用符来访问常量。
CONST = "outer"
module Mod
CONST = 1
def Mod.method1
CONST + 1
end
end
module Mod::Inner
def (Mod::Inner).method2
CONST + " scope"
end
end
Mod::CONST
Mod.method1
Mod::Inner::method2
22.10.1 Mixins——混入模块(Mixins——including Modules)
class | module name
include expr
end
使用include方法可以将一个模块包含到另一个模块或者类的定义中,含有include的模块或类定义可以访问它所包含模块的常量、类变量和实例方法。
如果一个模块被包括到一个类定义中,那么模块的常量、类变量和实例方法实际上被绑定到该类的一个匿名(且不可访问)超类中,类的对象会响应发送给模块实例方法的消息。
模块函数
尽管include在提供mixin功能时很有用,它也可以把模块的常量、类变量和实例方法带入到另一个名字空间中,然而,实例方法定义的功能不能通过模块方法的实现。
module Math
def sin(x)
end
end
方法Module#module_function通过拷贝一个或多个模块实例方法的定义来创建相应的模块方法来解决问题。
module Math
def sin(x)
end
end
实例方法和模块方法是两个不同的方法:方法定义被module_function拷贝出来而不是建立别名。
访问控制
22.12 Blocks,Closure和Proc对象
代码block是closure;它能记住其被定义时的上下文,并在被调用时使用该上下文。上下文中包含self的值、常量、类常量、局部变量和任意被截获的block。
class Holder
CONST = 100
def call_block
a= 101
@a = 102
@@q = 103
yield
end
end
class Creator
CONST = 0
def create_block
a = 1
@a = 2
@@a = 3
proc do
puts "a = #{a}"
puts "@a = #@a"
puts "@@a = #@@a"
puts yield
end
end
end
block = Creator.new.create_block { "original" }
Holder.new.call_block(&block)
返回结果:
a = 1
@a = 2
@@a = 3
original
WHY?????
22.12.1 Proc对象,break和next
什么是block?
是和方法相关联的一堆代码,并在它们被定义的上下文运作。
Block不是对象,但是它们能被转换成类proc的对象。
有3中方式将block转换成proc对象。
- 通过传递block给一个方法,该方法的最后一个参数前有一个地址符&。该参数会接受block作为Proc对象。
def meth1(p1, p2, &block)
puts block.inspect
end
meth1(1,2) {"a block"}
meth1(3,4)
- 通过调用Proc.new,再将它和block关联。
block = Proc.new { "a block" }
block
- 通过调用方法Kernel.lambda关联block到方法调用
block = lambda{ "a block" }
block
前面两种风格的Proc对象在使用时是相同的。我们称这些对象为raw procs。第三种风格,由lambda生成,为了Proc对象添加一些额外的功能,我们很快就能看到,我们称这种对象为lambdas。
无论在哪种block内,next语句将退出block。block的值是传递给next的值,如果没有值传递给next,则为nil。
def meth
res = yield
"The block returns #{res}"
end
meth {next 99}
pr = Proc.new {next 99}
pr.call
pr = lambda {next 99}
pr.call
在raw proc中,break语句可以终止调用block犯法,方法的返回值为传递给break的参数。
22.12.2
return和block
处于作用域block内的return和该作用域的return一样,block内原上下文不再有效的return会引发异常。
22.13 引发异常(Raising EXceptions)
raise
raise string
raise thing
第一种形式重新引发$!中存储异常,如果$!是nil,则引发一个新的RuntimeError。
第二种形式创建一个新的RuntimeError异常,并设置其消息为给定的字符串。
第三种形式通过在其第一个参数上调用Exception方法创建一个异常对象,然后设置异常消息和调用栈为第二个和第三个参数。
类Exception和其对象含有一个称为exception的工厂方法,所以异常类的名字和实例可以用作raise的第一个参数。
当异常发生时,Ruby会将异常对象的引用存储在全局变量$!中。
22.13.2 处理异常(HandingExceptions)
rescue expr
Rescue语句修饰符
一条语句可以含有一个可选的rescue修饰符,该修饰符后跟另一条语句。
22.14 Catch和Throw
方法Kernel.catch将执行与之相关联的block。
catch (symbol | string) do
block
end
方法Kernel.throw将中断语句的正常执行。
throw(symbol | string[, obj])
当throw执行时,Ruby在调用栈中向上搜索直到找到匹配符号或字符串的第一个catch block。
如果找到,则搜索结束,并从被捕获的block后面继续执行,如果throw的第二个参数存在,那么它的值将作为catch的值返回。
第23章 Duck Typing
你应该注意到:Ruby中没有声明变量和方法的类型。
23.1 Classes aren't types
duck typing哲学:对象的类型是根据它能做什么而不是根据它的类决定的。
23.3 标准协议和强制转换
Ruby提供了转换协议(conversion protocals)的概念——对象可以选择把自己转换成另一个类的对象。
Ruby有三种标准的方式实现它。
我们已经遇到了第一种:to_i和to_s方法分别把他们的接收者转换成字符串和整数。
这些转换方法不是严格的:比如,如果对象有某种得体的字符串表示,它可能会有to_s方法。
为了返回数字的字符串表示,我们的Roman类可能会实现to_s方法。
第二种形式的转换函数使用名字如to_str何to_int的方法。
class OneTwo
def to_ary
[1, 2]
end
end
ot = OneTwo.new
a, b = ot
puts "a = #{a}, b = #{b}"
printf("%d--%d\n", *ot)
to_hash
to_int
to_in
to_proc
23.3.1 数字强制转换
coerce
23.4 该做的做,该说的说(Walk the walk, Talk the Talk)
第24章 类与对象
类与对象无疑是Ruby的中心,但是第一眼看上去可能让人有点困惑,
一个RUby对象有三个部分:一组标志、一些实例变量、以及相关联的类。
24.1 类与对象是如何工作的
所有的类/对象的交互,都是使用上面给出的简单模型解释的:对象引用类,而类引用零个或多个类,不过,实现细节可能有些诡异。
一个基本对象、以及它的类和超类
class Guitar
def paly()
end
end
lu = Guitar.new
24.1.1 你的基本的、日常对象(Your Basic, Everyday Object)
24.1.2 什么是Meta(What's the Meta?)
24.1.3 特定于Object的类(Object-Specific Classes)
Ruby允许你创建一个和特定对象绑定的类。
24.1.4 Mixin
24.1.3 扩展对象(extending objects)
24.4 继承与可见性(Inheritance and Visibility)
关于类继承最后一个诡秘的地方,十分含糊。
在一个类定义中,你可以修改祖先类中方法的可见性,例如,你可以像下面这样:
class Base
def aMethod
puts "Got here"
end
private :aMethod
end
class Derived1 < Base
public :aMethod
end
class Derived2 < Base
end
在这个例子中,我们可以调用D1类实例中的aMethod,但不能通过Base或D2的实例来调用它。
那么Ruby是如何辗转让一个方法有两个不同的可见性呢?
如果一个子类改变了父类中某个方法的可见性,Ruby实际上在子类中插入了一个隐藏的代理方法,使用了super调用原来的方法,然后,它将这个代理的可见性设置为你所需要的。这意味着,下面的代码:
class Derived1 < Base
public :aMethod
end
实际上等同于:
class Derived < Base
def aMethod(*arg)
super
end
public :aMethod
end
super调用可以访问父类中的方法,而不管其可见性,所以重写方法可以让子类覆写父类中的可见性规则。
24.5 冻结对象(freezing Objects)
有时候你费尽力气才让你的对象完全正确,如果允许其他人可以改变它。
第25章 Ruby安全
Walter Webcoder是一个非常棒的想法:
require 'benchmark'
include Benchmark
test = "Stormy Weather"
m = test.method(:length)
n = 100000
bm(12) { |x|
x.report("call") {n.times {m.call}}
x.report("send") {n.times {test.send(:length)}}
x.report("eval") {n.times {eval "test.length"}}
}
26.4 系统钩子(System Hooks)
钩子是一个跟踪(trap)一些Ruby事件的技术,比如跟踪对象创建,在Ruby中,最简单的hook技术是截取程序执行的系统命令,可能你想记录程序执行的所有操作系统命令。
只要重命名Kernel.system方法,用你自己的Kernel.system替换它就可以了。
module Kernel
alias_method :old_system, :system
def system(*arg)
result = old_system(*arg)
puts "system(#{args.join(', ')}) returned #{result}"
result
end
end
system("date")
system("kan", "happ")
26.6 列集和分布式Ruby
Java提供了序列化(serialize)对象的特性,可以在某处保存对象,并且需要的话可以重建(reconstitute)它们,比如可以使用这个功能保存一个对象树,这些对象树表示某部分程序的状态——一份文档、一小段音乐
Ruby把这种类型的序列化成为列集(marshaling)。可以使用Marshal.dump方法来保存一个对象和它的部分或所有组成对象(component)。
26.6.3分布式Ruby(Distributed Ruby)
使用drd的Ruby进程可能会作为服务器、客户机或者两者都是,drd服务器是对象的源,而客户机是对象的使用者,对客户机来说。
26.7 编译时?运行时?任何时?(Compile、Runtime、Anytime)
使用Ruby要记住的一件重要事情是:在“编译时”和“运行时”之间没有太大的差别,它们都是相同的,可以向一个正运行的进程添加代码。
方法
- sort! 对数组进行排序,在排序的过程中arr将被冻结
a = ['a', 'b', 'e', 'c']
a.sort! -> ["a", "b", "c", "e"]
a -> ["a", "b", "c", "e"]
如果去掉!,a的状态不变
a = ['a', 'b', 'e', 'c']
a.sort -> ["a", "b", "c", "e"]
a -> ['a', 'b', 'e', 'c']
Rails-guide
- 5分钟快速浏览