Ruby 元编程 第二版随笔(一)
目标:建立一个存放电影名和影评的数据库,因此决定创建一个简单的代码库,用于在数据库中实现对象的持久化。
第一次尝试:编写一个代码库,把数据库中的每个表映射到一个类中,同时把每条记录映射到对象中,每当创建一个对象或访问它的属性时,这个对象就会产生一条SQL语句并发送给数据库。所有的这些功能都封装在一个类里面。
classs Entity attr_reader :table, :ident def initialize(table, ident) @table = table @ident = ident Database.sql "INSERT INTO #{@table} (id) VALUES (#{@ident})" end def set(col, val) Database.sql "UPDATE #{@table} SET #{col}='#{val}' WHERE id=#{@ident}" end def get(col) Database.sql ("SELECT #{col} FROM #{@table} WHERE id=#{@ident}") [0][0] end end
在数据库中,每个表都有一个id字段,每个Entity 会保存这个字段的内容以及它 的表名。创建一个Entity 对象后, 该对象会把自己保存在数据库中,Entity#set 方法会创建SQL语句更新字段的值, 而Entity#get 方法创建SQL语句读取字段的值。database使用数组的数组作为返回的数据集。
我们可以继承Entity类来映射一个指定的表。 例如, 用Movie类映射一个名为movies 的表:
class Movie < Entity def initialize(ident) super "movies", ident end def title get "title" end def title=(value) set "title", value end def director get "director" end def director=(value) set "director", value end end
Movie 类 的每个属性有两个方法: 一个像Movie#title这样的reader方法和一个像Movie#title=这样的writer方法。只要在Ruby命令行解释器里边输入命令就可以把一部电影加载到数据库里。
movie = Movie.new(1) movie.title = "SuperMan" movie.director = "Stabley Kubrick"
但是Ruby 中有一个非常强大的类库 Active Record 可以吧对象映射到数据表中,
那么用Active Record 写出来的Movie类是什么样子呢?
Movie类:
class Movie < ActiveRecord::Base end
OK 就这么简单 ,我们只是从Active Record ::Base继承了一个子类,它不用指定用那个表来映射Movie对象,也不用title和director这些看起来差不多的方法。程序会照样工作。
movie = Movie.create movie.title = "SuperMan" movie.title # => "SuperMan"
上面代码创建了一个Movie对象,该对象包装了movies表中的一条记录。然后通过Movie#title和Movie#title=方法访问title字段。这是如何实现的呢, 这和Active Record 的工作原理有关。
Active Record 通过内省机制查看类的名字。因为类名是Movie,Active Record 会自动把它映射到movies 的表中。(它知道如何转换英文单词的单复数。)
那么,像title和title=这样的方法(简称为访问器)又是怎样处理的呢? 这就是元编程的妙用了。Active Record 会自动定义这些方法。Active Record ::Base在运行时读取数据库的表模式,找到movies表有两个名为title和director 的字段,然后自动定义两个同名的属性和响应的访问器。也就是说,Active Record 在程序运行时动态的创建了Movie#title和Movie#director=这样的方法。
Ruby 不但可以在运行时访问语言构件,还能够修改他们。是不是很神奇呢?
上一篇: Ruby 元编程 第二版随笔(三)
下一篇: Rails数据库迁移基本操作