一、心得体会
1、今天完成了什么?
- 看了6个小时的镐头书
- 18章、21章、22章,我想快速把书看一遍,然后看看哪些是需要自己重点看的,哪些是不需要看的
- 10个controller:货位(shelves)、门店(shops)、来源(sources)、标签(tags)、teams(客服小组 )、工艺(technics)、TestingPhotos、testing、工单、运输
2、今天收获什么?
- 一个心得:下次再看一本新书的时候,一定要用最快的速度浏览全书一遍,然后选择重点部分看。
- 今天用了最快的速度看了3章,差不多有100页,就是因为我发现一些知识点现在用不上
- 写Ruby扩展的步骤
- Rub的一些基础知识:什么是字面量、符号、逻辑运算、正则表达式、常量和变量、基本类型
3、犯了哪些错误?
4、明天还需要哪些工作?
- 明天继续看镐头书剩余的部分,以最快的速度看,争取4个小时看完,然后2个小时看rails-guide
5、今天状态不错,感觉找到了看书的方法。
二、读书笔记
18.2 会话(session)
require "cgi"
require "cgi/session"
cgi = CGI.new("html3")
sess = CGI::Session.new(cgi, "session_key" => "rubyweb", "prefix" => "web-session.")
if sess['lastaccess']
msg = "You were last here #{sess['lastaccess']}."
else
msg = "Looks like you haven't been here for a while"
end
count = (sess["accesscount"] || 0).to_i
count += 1
msg << "<p>Number of visit: #{count}"
sess["accesscount"] = count
sess["lastaccess"] = Time.now.to_s
sess.close
cgi.out {
cgi.html {
cgi.body {
msg
}
}
}
18.2 提升性能(Improving Performance)
你可以使用Ruby编写Web的CGI程序,但是,与大部分的CGI程序一样,默认的配置是每访问cig-bin的一个页面,就启动一个新的Ruby程序拷贝,这对机器的利用而言是很昂贵的。
一般来说,这些模块是动态加载的。
第21章 扩展Ruby(Extending Ruby)
你的第一个扩展(Your first Extension)
为了介绍编写扩展,这个扩展只是为了测试一个过程——它并没有做什么无法单纯用RUby完成的事情。
class MyTest
def initialize
@arr = Array.new
end
def add(obj)
@arr.push(obj)
end
end
换言之,我们要用c编写一个扩展,让它和上面的Ruby类做到插入兼容(plug-compatible)。等价的C代码看起来和下面的类似。
#include "ruby.h"
static int id_push;
static VALUE t_init(VALUE self)
{
VALUE arr;
arr = rb_ary_new();
rb_iv_set(self, "@arr", arr)
return self;
}
static VALUE t_add(VALUE self, VALUE obj)
{
VALUE arr;
arr = rb_iv_get(self, "@arr");
rb_funcall(arr, id_push, 1, obj)
return arr;
}
VALUE cTest;
void Init_my_test() {
cTest = rb_define_class("MyTest", rb_cobject);
rb_define_method(cTest, "initialize", t_init, 0);
id_push = rb_intern("push");
}
我们仔细查看这个实例,发现它阐明了本章许多重要的概念,首先,我们需要包含头文件ruby.h获得所需的ruby定义。
现在查看最后的一个方法Init_my_test。每个扩展都定义一个名为Init_name的全局函数,这个函数在解释器第一次加载时(或者静态链接)名为name的扩展时被调用。它被用来初始化扩展,并加入到Ruby的环境中。
我们定义一个新类MyTest,是Object的子类。
接下来,我们将add和initialize设置为MyTest的两个实例方法,调用rb_define_method会在Ruby方法名和实现它的C函数间建立一个绑定,如果RUby代码调用对象实例的add方法,解释器会调用C函数t_add,并传入一个参数。
类似地,当调用该类的new方法时,Ruby会构造一个基本的对象,然后调用initialize,这里我们定义了它要调用的c函数t_init,然而没有参数。
21.1.1 构建我们的扩展 (Building our Extension)
1、在C源文件My_test.c的同目录下,创建一个名为extconf.rb的文件,extconf.rb应该包括以下两行。
require 'mkmf'
create_makefile("my_test")
2、运行extconf.rb。这会生成一个Makefile。
ruby extconf.rb
creating Makefile
3、 使用make来构建扩展,这是在Mac OS X系统中产生的结果。
构建这个扩展的所有结果,会被组装成一个共享库。
21.1.2 运行我们的扩展(Running Our Extension)
我们可以简单地通过在运行时动态require来使用它(适用于大部分系统)。我们可以将它放到一个测试中,来验证它如我们预期的那样工作。
require 'my_test'
require 'test/unit'
class TestTest < Test::Unit::TestCase
def test_test
t = MyTest.new
assert_equal(Object, MyTest.superclass)
assert_equal(MyTest, t.class)
t.add(1)
t.add(2)
assert_equal([1,2], t.instance_eval("@arr"))
end
end
21.2 C中的Ruby对象(Ruby Objects in C)
21.3 Jukebox扩展(The Jukebox Extension)
21.3.1 包装C结构(Wrapping C Structures)
我们已经得到了供应商控制音乐CD点唱机单元的库,并已经准备好将它连接到Ruby中,供应商的头文件类似于:
typedef struct _cdjb {
int statusf;
int request;
void *data;
char pengding;
int unit_id;
void *status;
}
这是供应商提供的全部操作,虽然他们可能不承认,代码是用面向对象的方式编写的额,我们并不知道CDJukeBox接口中所有成员的含义,但这不碍事——我们可以将它看作是不透明的一大推数据。
供应商的代码如何操作它,我们只需要将这些数据携带进去。
任何时候,你想要如Ruby对象那样处理一个C结构,你需要将它包装(wrap)成一个特殊的Ruby内部类,称为DATA(类型为T_DATA)。有两个宏完成这一包装,且其中一个宏会再次返回你的结构。
21.3.2 API:C数据类型的包装(API:C Data Type Wrapping)
第22章 Ruby语言(The Ruby Language)
22.1 源代码编排(Source Layout)
Ruby程序是7位ASCII、Kanji(使用EUC或者SJIS)或者UTF-8来表示的。如果要使用非7位ASCII的其他字符集,那么必须适当地设置KCODE选项。
Ruby是基于行的语言,Ruby的表达式和语句都以行尾结束,除非解释器能够确定语句是不完整的——比如行最后一个符号是操作符或者逗号,分号可以区分一行中的多个表达式,你也可以在行尾加一个反斜线表示延续到下一行。注释以#开始,到物理行结束为止,在语法分析时将被忽略。
举个例子:
a = 1 + 2 + \
3
返回:
6
Ruby会忽略以=begin开头和以=end开头的行之间的物理行,它们可以用来注释掉一段代码或者用在源代码块中嵌入文档。
Ruby一次性读入程序,所以你可以用管道将程序传递给Ruby解释器的标准输入流。
echo 'puts "Hello"' | ruby
在源代码的任意位置,如果Ruby遇到只含有“END”且前后没有空格的行,那么Ruby将认为该行是程序的结束行——后面的行都不会被当做程序码。
不过,使用全局的IO对象DATA,可以将这些行读入到运行的程序中。
22.1.1 BEGIN和END Blocks
Ruby的每个源代码文件都可以生命当自己 被装载时要执行的代码block(BEGIN block)和程序运行结束后要执行的block(END block)。
一个程序可以包含多个BEGIN和END block。????? 啥意思
BEGIN {
}
END {
}
BEGIN block的执行顺序和它出现的顺序相同,END block的执行顺序与此相反。
22.1.2 常规分隔输入(General Delimited Input)
除了常用的引用(quoting)机制外,还可以用其他形式来表示字符串字面量(literal)、数组、正则表达式和shell命令,这就是常规分隔语法,所有这些字面量都是以一个百分号开头,后跟一个指明字面量类型的字符的。
类型字符后面的是分隔符,分隔字符可以是任意非字母或非多字节的字符,如果分隔符是(、[、{或<其中之一,那么从它到对应的闭合分隔符之间的字符,属字面量所有。
%q/this is string/
%q-string-
%q(a (nested) string)
22.2 基本类型(The Basic Types)
Ruby的基本类型包括数字、字符串、数组、散列表(hash)、区间(range)、符号和正则表达式。
22.3.1 整数和浮点数(Integer and Floating-Point Numbers)
Ruby的整数是Fixnum类或Bignum类的对象。
Fixnum对象可以容纳比本机字长少一位的整数。
当一个Fixnum超过这个范围时,它将会自动转换成Bignum对象。
整数由一个可选的符号标记、一个可选的进制指示符(0代表八进制,0d代表十进制,0x代表十六进制,0b代表二进制)和一个相应进制的字符串组成。
123455
0d12345
123_343
-43
你可以在一个ASCII字符前加一个问号,来获得其对应的整数值,Ctrl组合键字符可以由来产生。
一个带有小数点和指数的数字字面量被认为是Float对象,Fload对象和本机上double数据类型大小一样。
小数点后必须跟一个数字。
12.34
-0.1234e2
字符串
Ruby提供了多种机制来生成字面量字符串,每种机制都产生String类型的对象,不同机制的区别在于如何分割字符串,以及字面量内容中会进行哪些替换。
单引号引起来的字符串字面量执行的替换最少,这两者都会将\序列转换为单个反斜线,单引号的形式。
当字符串字面量用于赋值语句或者参数时,一个新的String对象将被创建。
3.times do
print 'hello'.object_id, " "
end
返回结果:
70250930457120 70250930456960 70250930456840 => 3
'hello'的对象id被打印3次
22.2.2 区间(Ranges)
除了条件表达式中,还能构成Range对象,两个点的形式是闭合区间,而三点的形式是半开半闭的。
22.2.3 数组(Arrays)
数组类的字面量是在方括号间由逗号分隔的一连串对象引用组成的,尾部的逗号被忽略。
arr = [ fred, 10, 3.14, "This is a string", barney("pebbles")]
数组亦可以用简写形式%w和%W来构成,小写字母的形式将空格隔开的token提取为连续的数组元素。在单个字符串内不执行替换。
大写字母的形式虽然也把单词列表转换成数组,但是对每个词执行和双引号一样的替换规则,词之间的空格可以用反斜线转义。
arr = %w( fred wilma barney betty great)
arr = %W( fred wilma barney betty great)
22.2.4 散列表(Hashes)
Ruby的Hash字面量可以由花括号中的键/值对列表构成。
对散列表键的要求
散列表键必须能够响应hash消息并返回一个散列码(hash code),且某个键对应的散列码不能改变,散列表中使用的键也必须能用eql?来比较。
如果eql?在比较两个键时返回真,那么这两个键必定具有相同的散列码,这意味着某些类(例如数组和散列表)不适合做键,因为它们的hash值可能会随其内容而发生变化。
如果你保存了键对象的一个外部引用,并使用该引用修改了对象,那么这也将修改它的散列码,进而基于该键的查询将无效。
因为字符串是最常用的键,且字符串内容会经常变化,所以Ruby会对字符串键进行特别处理,如果你使用String对象作为hash键,则hash将在内部赋值该字符串,并使用该拷贝作为键。
如果你实现了自定义的类,并用该类的对象实例作为hash键,那么你需要确保(a)一旦对象被创建,它的散列码就不再改变,或者每当键的散列码发生变化时,都调用hash#rehash方法重新对散列表进行索引。
22.2.5 符号(Symbols)
Ruby符号是一个对应字符串(通常是一个名字)的标识符,你可以通过在名字前加一个冒号来构成改名字的符号,也可以在任意字符串字面量前加一个冒号来创建该字符串的符号,在双引号字符串中会发生替换,不管程序如何使用名字,一个具体的名字或者字符串总是产生同样的符号。
:Object
:my_variable
:"Ruby rules"
a = "cat"
其他语言称这个过程为interning,而称符号为原子。
22.2.6 (Regular Expressions)
正则表达式的字面量(literal)是Regexp类型的对象,可以通过调用Regexp.new构造函数显式创建正则表达式,也可以使用字面量形式/pattern/和%r{pattern}隐式创建。
%r构造体(contruct)是常规分隔输入的一种形式。
22.3.1 变量/方法二义性(Variable/Method Ambiguity)
在表达式中,当Ruby看到像a这样的名字时,它需要判断a是一个局部变量引用还是对没有参数的方法a的调用,Ruby使用一种启发式的方法来判断这种情况。
当Ruby解析源代码文件时,它会记录所有已经被赋值的符号。
当解析的时候,Ruby看到第一个print语句使用了a,并且由于还没有遇到对a的任意赋值语句,所以把它当做方法调用,但是当解析到第二个print语句时,由于Ruby遇到了对a的一个赋值语句,所以把它当做变量。
注意赋值语句不一定被执行——只要Ruby看到它了就可以,下面的程序不会导致错误。
a = 1 if false; a
22.4 变量和常量(Variables and Constants)
Ruby的变量和常量含有对象的引用,变量本身没有内在的类型,变量的类型仅仅由变量引用的对象所能响应的消息决定。
Ruby常量也是对对象的引用,常量在它第一次被赋值的时候创建(通常是在类或者模块的定义中)。在不灵活的语言不通,Ruby允许你改变常量的值。
22.4.1 常量和变量的作用域(Scope of Constants and Variables)
在类或者模块内的任意位置都可以直接访问此类或模块中定义的常量,在类或者模块之外,可以通过在域作用符::前面加上一个返回适当类或者模块对象的表达式来访问。
不属于任意类或者模块的常量,可以直接访问或者使用不带前缀的域作用符来访问,常量可以在方法体外定义,通过在常量名之前使用类或者模块名和域作用符,还可以将常量从外面添加到已存在的类或模块中。
OUTER_CONST = 99
class Const
def get_const
CONST
end
CONST = OUTER_CONST + 1
end
Const.new.get_const
Const::CONST
::OUTER_CONST
Const::NEW_CONST = 123
全局变量是贯穿整个程序的变量,每次对某个特定全局名字的引用总是返回同一个对象,引用一个未被初始化的全局变量将会返回nil。
类变量是贯穿类或者模块体的变量,它必须在使用之前初始化,一个类变量被类的所有实例共享,且只在类中可以使用。
class Song
@@count = 0
def intialize
@@count += 1
end
def Song.get_count
@@count
end
end
类变量属于包含该变量的最内层的类或模块, 在顶层使用的类变量在object内中定义,这种类变量类似于全局变量,在singleton方法中定义的类变量属于顶层类。
22.5 表达式(Expressions)
表达式中用的术语可以是下面任意一个:
字面量 Ruby字面量可以是数字、字符串、数组、散列表、区间、符号和正则表达式
Shell命令 封装在反引号之间字符串或以%x开头的常规字符串。字符串的值是在宿主操作系统的标准shell上运行字符串表示的命令标准输出。执行后也会将命令退出状态保存在$?变量中。
filter = "*.c"
files = `ls #{filter}`
files = %x{ls #{filter}}
- 符号产生器。符号对象是通过在操作符、字符串、变量、常量、方法、类或模块名字前加一个冒号生成的。
- 对应不同名字的符号对象是唯一的,但是它并不是指向名字的具体实例。
- 变量或常量引用
- 方法调用
22.5.1 操作符表达式(Operator Expressions)
表达式可以用操作符来组合。
22.5.2 关于赋值的更多内容(more on Assignment)
如果左值是一个变量名或者常量名,那么该变量或者常量获得相应左值的引用。
a = /reg/
obj[] = "one" 其实是:obj.[] = ("one")
obj[1] = "one" 其实是:obj.[] = (1, "one")
并行赋值
赋值表达式可以有一个或者多个左值,也可以有一个或多个右值,本节将阐述Ruby是如何处理具有不同参数组合的赋值的。
22.5.3 Block表达式(Block Expression)
22.5.4 布尔表达式(Boolean Expressions)
Ruby预定义了全局的false和nil。在布尔表达式上下文中,这两个都被当做假,所有其他值都被当做真,当你需要使用显式地“真”值时,可以使用常量true。
与、或、非和Defined
22.5.5 if和unless表达式(if and unless Expressions)
22.6 方法定义(Method definition)
方法定义可能不包含类或模块定义,它们可以含有嵌套的实例或者单例方法定义。当执行外部的方法时,内部的方法被定义。
在被嵌套的上下文中,内部方法不是一个闭包(closure) ——它是自包含的。
22.6.1 方法参数(Method Arguments)
方法定义可以含有零或多个普通参数,一个可选的数组参数和可选的block参数,参数之间用逗号分隔,而且参数列表可以用括号括起来。
普通参数是一个局部变量,后可跟一个等于号和作为默认值的表达式,表达式在方法被调用时被求解,表达式是从左向右求解的。
def options(a = 99, b = a + 1)
[a, b]
end