在Ruby中用Lafcadio来进行 O/R mapping

在Ruby中用Lafcadio来进行 O/R mapping
Francis Hwang
http://lafcadio.rubyforge.org/
sera@fhwang.net

翻译整理:http://www.ruby-cn.org
转载请保留。


关于Lafcadio
  我开始写Lafcadio主要是为了自己用,在2003年8月,我在rubyforge建立了这个项目,它的主要作用是让你把精力集中在ruby代码上,而不是如何操作mysql上。这也使得单元测试更简单。目前已经有一些网站用它了,包括Rhizome.org。

User domain class
例如,我们可以写一个User 的domain类。

1. XML and Ruby class definition

<lafcadio_class_definition name="User">
    <field name="firstName" class="TextField"/>
    <field name="lastName" class="TextField"/>
    <field name="email" class="TextField"/>
    <field name="password" class="TextField"/>
    <field name="birthday" class="DateField"/>
</lafcadio_class_definition>


class User < DomainObject
end

2. 建立 MySQL 表结构。
% lafcadio_schema -c config.dat User.rb
create table users (
    objId int not null auto_increment,
    primary key (objId),
    firstName varchar(255) not null,
    lastName varchar(255) not null,
    email varchar(255) not null,
    password varchar(255) not null,
    birthday date not null
);

3. Create and commit an instance

twentyYearsAgo = Date.today - (365 * 20)
john = User.new({ 'firstName' => 'John',
            'lastName' => 'Doe',
            'email' => 'john.doe@email.com',
            'password' => 'my_password',
            'birthday' => twentyYearsAgo })
puts john.email
=> "john.doe@email.com"
puts john.objId
=> nil
john.commit
puts john.objId
=> 1
4. 选取和编辑

objectStore = ObjectStore.getObjectStore
john = objectStore.getUser(1)
john.birthday = john.birthday - (365 * 10)
john.email = 'john.doe@hotmail.com'
john.commit # or objectStore.commit( john )

5. 删除记录
objectStore = ObjectStore.getObjectStore
john = objectStore.getUser 1
john.delete = true
objectStore.commit john

6. 添加新方法
class User < DomainObject
    def name
        nonNilNames = []
        [ firstName, lastName ].each { |aName|
            nonNilNames << aName if aName
        }
        nonNilNames.join (' ')
    end
end
puts john.name
=> "John Doe"



domain对象的关联
下面我们将看到如何用Lafcadio来在两个domain之间建立关系。

1. 定义 Message domain 类

<lafcadio_class_definition name="Message">
    <field name="subject" class="TextField" />
    <field name="body" class="TextField" />
    <field name="author" class="LinkField"  linkedType="User" />
    <field name="recipient" class="LinkField" linkedType="User" />
    <field name="dateSent" class="DateField" />
</lafcadio_class_definition>


class Message < DomainObject
end

2.将Users链接到Message
objectStore = ObjectStore.getObjectStore
john = objectStore.getUser( 1 )
jane = objectStore.getUser( 2 )
messageBody = <<MESSAGE_BODY
Hey, Jane,
Let's go to the movies on Saturday!
MESSAGE_BODY
message = Message.new({ 'subject' => 'hey',
        'body' => messageBody,
        'author' => john,
        'recipient' => jane,
        'dateSent' => Date.today })

puts message.author.firstName
=> "John"
message.commit


覆盖表的默认属性
    Lafcadio中的很多表都用了默认得属性,我们可以很容易的覆盖这些属性。

class ExampleClass < DomainObject
    def ExampleClass.tableName
        'different_table'
    end
    def ExampleClass.sqlPrimaryKeyName
        'id'
    end
end

example = objectStore.getExampleClass( 10 )
puts example.objId
=> 10

选择集合
    如果你想选择很多记录,Lafcadio提供了一些不一样的方法。

allMessages = objectStore.getAll( Message )
messagesByJohn = objectStore.getMessages( john,'author' )
messagesToJohn = objectStore.getMessages( john,'recipient' )
hotmailCondition = Query::Like.new( 'email','hotmail.com', User)
puts hotmailCondition.toSql
=> "email like '%hotmail.com%'"
usersWithHotmailAddresses =objectStore.getSubset( hotmailCondition )


测试驱动的设计
一旦你把数据库都隐藏在ObjectStore之后,就可以把MockObjectStore当作数据库来进行测试了。

class TestSaveMessage < LafcadioTestCase
    def testSavesMessage
    ... # create John and Jane with User.new
        john.commit
        jane.commit
        body = "Let's go to the movies on Saturday!"
        cmd = SaveMessage.new( john, jane, "Hey",body )
        cmd.execute
        message = @mockObjectStore.getMessage( 1 )
        assert_equal( body, message.body )
        assert_equal( john, message.author )
    end
end

你可以从SQL简化多少?
    这样设计挑战之一是如何在速度和使用的方便程度山上进行平衡。实际上,SQL有很多特点,不一定能进行很好的包装从而简化开发。

这里我们比较两种方法来取出一些行,第一个例子用了Vapor (http://vapor.rubyforge.org/) 第二个是 Lafcadio的。

# sample Vapor code
cities = @mgr.query( City,"name = ? AND altitude = ?",[ name, altitude ] )

# sample Lafcadio code
nameCond = Query::Equals.new( 'name', name, City )
altCond = Query::Equals.new( 'altitude', altitude, City)
cond = Query::CompoundCondition.new( nameCond, altCond )
cities = objectStore.getSubset( cond )

另一种可能:Block-driven查询
users = objectStore.getUsers { |user|
        user.email =~ /rubyforge/.org/ &&
        user.birthday < Date.new( 1970, 1, 1 )
}

-->


select * from users
where email like '%rubyforge.org%'
and birthday < '1970-01-01';


将来发展
尽管Lafcadio对我来说感觉不错,但是对别人来说这个接口不一定好用,我会努力让它变得清晰,并且再多写一些文档出来。
最后,Lafcadio将会支持更多的数据库,并且提供更多的工具,比如用程序自动取得表的结构定义,然后给你生成一个xml文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值