Nirvana Studio
http://network.feedsky.com/nirvana

圈子简介
Nirvana Studio成员的BLOG聚合

圈子公告
Nirvana Studio成员的BLOG聚合
创建者:ShiningRay
分类: IT-互联网
成员数: 3
订阅数:2
Tag: 软件  开发 
创建时间:2007-12-21
作者:Nicholas Ding
Nicholas Ding
时间:2008-07-09
Still remember the great company Caucho? Yes, it’s still alive. Besides its flagship product Resin, it also provides some open source libraries. Actually, there are 2 remoting libraries you can use. One is Burlap, and another is Hessian. Hessian is the binary protocol implementation of Burlap. Burlap is xml based RPC. That’s quick easy to use [...]
作者:Nicholas Ding
Nicholas Ding
时间:2008-07-08
今天把大概一年前写的 Django 程序拿出来做一些改进,主要这一年来 Django 做了重大升级,虽然兼容性非常不错,但是以前代码里面扩展 manage.py 的部分已经可以重写了,因为 Django 提供了 API 来进行扩展。 因为 Django 根据字符串形式的 namespace 来加载模块,所以我见过不少修改 manage.py,通过重新定义全局变量来修改 Django 设置的代码。我虽然也非常喜欢 hack Django 这个框架,但是我一定不喜欢去改 Django 本身的代码,尽可能通过外部修改来达到自己想要的功能。 trunk 中的 Django 版本提供了扩展 manage.py 的 Command 的 API,只需要在相应的 module 中加入 management/commands 目录,在 commands 下面创建多个 py 就可以得到扩展自定义命令的功能了。譬如加入 management/commands/mycommand.py,就可以通过 manage.py mycommand 来执行,非常的方便。我早起的程序通过修改 manage.py 本身来实现,现在完全可以升级一下了。 但是没想到出问题了,因为 namespace 的问题,Django 在搜索 commands 的方式上是通过 relative import 形式。但是我习惯性把 INSTALLED_APPS 这个变量中的 [...]
作者:ShiningRay
ShiningRay的Blog
时间:2008-07-04

本文要讲的php shell并非是使用php来写shell脚本,而是讲一个php的REPL的交互式shell。所谓REPL,也就是read-eval-print-loop,也就是说,shell读入一个指令,计算,然后输出结果,常见的有Python、Ruby的IRB等。

对于PHP,我们常常会写一个test.php,然后放入一些自己的代码,再打开浏览器检验一下,如果不对,又要重新修改代码,非常麻烦。交互式shell的好处就可以体现出来了,输入指令之后可以立刻看到执行代码的结果,所以非常适合用来检验代码片段(snippets)的正确性以及进行一些试验。

PHP本身自带一个交互式的shell,在命令行中输入php -i,便可以交互式运行,但要先输入一个php脚本的起始标签,但是该shell遇到异常的时候会直接退出,非常不方便。那么我找到一个非常接近于REPL的交互式shell便是php-shell


1. 安装

php-shell要求php 5.0以上版本

下载PHP_Shell-0.3.1.tgz

运行pear install PHP_Shell-0.3.1.tgz

如果确保pear已经安装,并在你的PATH路径中

2. 使用方法

在命令行下输入php-shell(Linux为php-shell.sh)进入交互式命令行:


PHP-Shell - Version 0.3.1, with readline() support
(c) 2006, Jan Kneschke <jan@kneschke.de>

>> use '?' to open the inline help 

>>
 

这时候我们可以输入我们所需的表达式了,在php-shell中直接输入变量名,它也可以将内容直接打印出来:

>> $a = new ArrayObject( )
ArrayObject::__set_state(array(
))
>> $a->
__construct(
append(
count(
exchangeArray(
getArrayCopy(
getFlags(
getIterator(
getIteratorClass(
offsetExists(
offsetGet(
offsetSet(
offsetUnset(
setFlags(
setIteratorClass(
>> $a->append( 'a' )

>> $a->count( )
1
>> $a->offsetGet( 0 )
'a'

当出现异常时,php-shell也会打印出异常的内容。
你可以使用exit退出。

3. Tab补全

php-shell一个强大的功能是支持补全,以下内容可以被补全:

  • new 类名()
  • 类名::方法名()
  • 类名::常量名
  • $变量名
  • 函数名()
  • $object->方法名()

请先输入第一个字符然后按TAB按钮。如果只有一种匹配,它便会直接补全名称,否则,再按一次TAB会列出所有的可能性。

如果你想查看所有已声明的类,你可以使用 getdeclaredclasses():

>> get_<TAB>
>> get_<TAB><TAB>
get_browser(
...
get_declared_classes(
...
get_resource_type(
>> get_dec<TAB>
>> get_declared_<TAB><TAB>
get_declared_classes(
get_declared_interfaces(
get_defined_constants(
get_defined_functions(
get_defined_vars(
>> get_declared_classes( )

4. 总结

如果是php新手,希望php-shell能帮你快速掌握php。老手也可以用其来检验代码段。

除此之外,也可以尝试一下基于Web页面的php shell,如http://phpshell.sf.net/


作者:Nicholas Ding
Nicholas Ding
时间:2008-07-03
昨天开始看 Flex 相关的东西,打算把一个现有的 Python Web 应用前端逐渐转换成 Flex。向 咖啡屋的鼠标 了解了下 Flex 如何与后端应用通讯,知道了一个新的协议叫做 AMF,是 Flash/Flex 原生支持的 RPC 协议,而且用起来也要比 XML 方式更加简单。 正好又一个 Python 的实现叫做 PyAMF 可以做协议的解析,很不错!
作者:Nicholas Ding
Nicholas Ding
时间:2008-07-03
昨天开始看 Flex 相关的东西,打算把一个现有的 Python Web 应用前端逐渐转换成 Flex。向 咖啡屋的鼠标 了解了下 Flex 如何与后端应用通讯,知道了一个新的协议叫做 AMF,是 Flash/Flex 原生支持的 RPC 协议,而且用起来也要比 XML 方式更加简单。 正好又一个 Python 的实现叫做 PyAMF 可以做协议的解析,很不错!
作者:ShiningRay
ShiningRay的Blog
时间:2008-07-01

BANGB0901BBE0645D046B037EBEEXIANGUO

鲜果


作者:ShiningRay
ShiningRay的Blog
时间:2008-06-30

有问题或评价,请联系: socialface@gmail.com

程序截图: http://www.socialface.com/slapp/screenshot.jpg

简介

欢迎来到Slapp的教程。本文的主要目标是通过构建一个简易的聊天墙应用来介绍一下Merb微框架的主要组件。

本文其次的目标是成为最好的Merb开放教程并能不断更新。同时,我们希望本教程可以逐渐变得丰富来展现Merb框架的所有方面和开发方式。

许可

This tutorial is Copyright 2008 Social Corp. and is licensed under a Creative
Commons Attribution-Noncommercial 3.0 United States License, available at:

相关的源代码以MIT形式的开发源代码许可:

直接浏览代码:

也可以直接下载:

参与人员名单

本教程最初由来自#merb的Slurry撰写。Slurry在撰写本文时主要参照了Merb的官方文档[^a]以及#merb IRC频道的内容。

在学习Merb的RSpec的过程中,还咨询了John Hornbeck的Blerb[^b],参考了Tim Connor的“Isolate controller and view testing in merb”一文[^c]——虽然帮助很大,真正掌握Merb/Rspec还是得益于来自#merb的benburkert。

中文版由ShiningRay翻译

前期决定

设计上来说,Merb是一个ORM无关的框架。当然,这仅仅是表示有不同的模型层可以选择。对于Merb v0.9.2来说,包括:DataMapper、Sequel和ActiveRecord(来自Rails)。

尽管DataMapper和Sequel都是很好的选择,但现在关注Merb的主流人群基本上都是来自Rails背景;因此,我们在本教程中会继续使用ActiveRecord。

1.) 创建模型

在像Merb这样快速成长的社区中,一个问题可能只需更新或者重装Merb相关的包(gem)就能简单解决,所以让我们先花点时间这样准备一下:

# gem sources -a http://merbivore.com
# gem install merb activerecord merb_activerecord merb_helpers rspec merb_rspec

$ merb-gen app slapp
$ cd slapp

完成了前两行命令后,Merb、ActiveRecord以及Rspec应该已经安装好或已经更新了,同时后两行命令则应该创建了一个初始的空Merb应用。

从这里开始,我们就要告诉Merb:我们要怎样用ActiveRecord,要使用哪个测试框架,以及需要载入哪些确切的merb_helpers包(如表单相关的东西)

编辑:slapp/config/init.rb 并取消第2742的注释,同时将:dependency "merb_helpers" 添加在
Merb::BootLoader.after_app_loads do 这一行之前:

use_orm :activerecord
...
use_test :rspec
...
dependency "merb_helpers"
...
Merb::BootLoader.after_app_loads do
  ### Add dependencies here that must load after the application loads:

  # dependency "magic_admin" # this gem uses the app's model classes
end

现在我们已经告诉Merb要使用ActiveRecord: use_orm :activerecord, 让我们来生成第一个模型:

$ merb-gen model Post

靠,居然没效。不过等等,你应该还有一个全新的slapp/config/database.yml.sample 文件等待配置,对吧?[^1]

好,现在你要做的就是将这个文件名字改成database.yml并在其中插入合适的数据库链接信息,像这样:

:development: &defaults
  :adapter: mysql
  :database: slapp_development
  :username: slapp
  :password: SL@rrYin08
  :host: localhost
  :socket: /tmp/mysql.sock
  :encoding: utf8

:test:
  <<: *defaults
  :database: slapp_test

让我们重新运行上面的merb-gen命令,不过这次我们还要再加上一些Post的默认属性:

$ merb-gen model Post body:string created_at:datetime

如果一切正常,那么第一个模型类就有了。让我们使用rake对数据库进行迁移,来加上表:

$ rake db:migrate

现在,Slapp应该有一个有效可用的Post模型了。为了以防万一,我们将介绍RSpec[^2].

2.) RSpec

RSpec——你可能已经知道了——是Test::Unit的另一种替代测试方案。与RSpec交互主要通过rake任务和spec命令:

$ rake spec

或者,更加详细的:

$ spec spec/* --format specdoc -c

(重要:这里使用的rake任务来自Merb前沿代码,应该很快会发布为标准包。在那之前,可以使用以下同等任务:
rake specs 以及 rake spec TASK=controllers/pages)

两个任务会运行slapp/spec/*中的任何代码,简洁起见,我们这里会使用rake。

让我们再次运行测试:

$ rake spec

如你所见,Merb已经为我们创建了一个默认的测试,但是他还不能通过:

Post
- should have specs (PENDING: Not Yet Implemented)
...

打开: slapp/spec/models/post_spec.rb 看看这个测试在哪里:

describe Post do

  it "should have specs"

end

将该spec改成实用的代码,比如:

describe Post do

  it "should be valid when new" do
      post = Post.new
      post.should be_valid
  end

end

该测试看上去好像不多,但是它确实可以验证Merb、RSpec、ActiveRecord和我们的数据库都已经安装成功并工作正常。

$ rake spec

同时,如果我们在此运行新的spec,数据库又出现一个问题。确切地说,我们忘记了创建slapp_test 数据库并将slapp_development的结构复制过去。

$ rake db:create:all
$ rake db:test:clone

重新运行spec:

$ rake spec
...
Post
- should be valid
...
1 example, 0 failures

这时成功在向我们问候:1 example, 0 failures,这一行表示所有的spec都通过了。更重要的是,我们遇到并克服了实际使用RSpec的第一个问题。

3.) 控制器

虽然现在从技术上说我们没有控制器也能启动Merb,但是基本上作不了什么。事实上就是什么都作不了,我们还是先来创建一个控制器:

$ merb-gen controller Posts

如你所见,Merb的控制器的命名方式是模型名(或者资源等)的名字复数化,且不使用Controller后缀。

看一下slapp/app/controllers/posts.rb,你应该看到我们新的Posts控制器里有一个默认的#index动作。另外,Merb还应该创建了一个新的spec: slapp/spec/controllers/posts_spec.rb ,里面的内容应该类似于:

describe Posts, "index action" do
  before(:each) do
      dispatch_to(Posts, :index)
  end
end

让我们编辑:slapp/spec/controllers/posts_spec.rb,更改内容为:

require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')

describe Posts, "#index" do

  it "should respond correctly" do
      dispatch_to(Posts, :index).should respond_successfully
  end

end

指定控制器是比较直观的,在上面的例子中,我们描述了Posts控制器(从dispatch_to中返回的)有一个#index动作并且它能被外部世界成功调用(即,它返回一个HTTP 20x 代码)[^3].

我们想再次运行spec,但是由于我们没有添加任何模型或视图测试,所以让我们执行更加明确的“仅控制器”的rake任务:

$ rake spec:controller

成功的测试结果:

Posts index action
- should respond correctly
...
1 example, 0 failures

漂亮。现在我们已经有了一个可以工作的控制器和一个有效的模型——我们只缺一些好的视图了。

4.) The View from Above

前面当我们创建Posts控制器时,Merb同时也为#index动作创建了一个草图。位于:slapp/app/views/posts/index.html.erb,你也许会看看里面有什么,然而,让我们先暂时忽略这个视图,并回到控制器。

slapp/app/controllers/posts.rb 中,我们执行一个简单的 ActiveRecord #find
并在#index动作中将结果存储为一个实例变量:

class Posts < Application

  def index
      @posts = Post.find(:all, :order => "created_at DESC")
      render
  end

end

然后我们确认该动作仍然是可以调用的:

$ rake spec:controller
...
1 example, 0 failures

就和Rails(或者其他Web框架)中一样,在控制器中创建的实例变量可以在对应的视图中调用。

在本案中,我们可以回到:slapp/app/views/posts/index.html.erb 并将临时文本替换成显示@posts变量内容的代码。

为了能保持模块化,我们将使用Merb #partial[^4] 功能来达到这个目的。

slapp/app/views/posts/index.html.erb 的内容替换成:

<h1>Welcome to Slapp</h1>
<h2>A simple chat wall</h2>

<p>Recent Posts:</p>
<div id="posts" class="container">
    <%= partial("/shared/post", :with => @posts) %>
</div>

不看HTML,调用#partial应该还是比较容易理解的——
我们想多次渲染slapp/app/views/shared/_post.html.erb 视图来呈现@posts的内容。

现在创建: shared/ 目录以及: _post.html.erb 文件:

$ mkdir app/views/shared
$ touch app/views/shared/_post.html.erb

并编辑 slapp/app/views/shared/_post.html.erb 的内容为:

<div id="post-<%= post.id %>" class="post">
    <p class="body"><%= h(post.body) %></p>
    <p class="created"><%= relative_date(post.created_at) %></p>
</div>

上面调用: partial("/shared/post", :with => @posts) 会反复传递一个单个post对象给视图_post.html.erb并渲染。

5.) 启动Merb

现在我们有了模型、控制器、以及一个视图,让我们启动Merb:

$ merb
$ curl http://localhost:4000/

你应该看到了一个普通的欢迎页,再转到:

$ curl http://localhost:4000/posts/index

这时你应该看到slapp/app/views/posts/index.html.erb的内容了。当然,由于我们还未创建任何帖子,所以应该看不到任何东西。

让我们使用交互Merb会话(其实就是一个在Merb应用的内容中启动的IRB)修正上面的问题:

$ merb -i
~ Loaded DEVELOPMENT Environment...
...
>> Post.create(:body => "Memp went down")

使用典型的 ActiveRecord #create 方法,我们现在创建了一个Post。重新载入
Posts#index 页面:

$ curl http://localhost:4000/posts/index

我们应该成功地看到了新的帖子。

6.) 特殊的视图

现在我们已经实现了视图,也许他们不会经常更改,让我们先描述他们。

首先,创建目录和spec文件:

$ mkdir -p spec/views/posts/
$ touch spec/views/posts/index_spec.rb

然后将一下代码放入slapp/spec/views/posts/index_spec.rb

require File.join(File.dirname(__FILE__), "..", "..", "spec_helper.rb")

describe "posts/index" do

  before(:each) do
      @controller = Posts.new(fake_request)
      @posts = [Post.create(:body => "Merb", :created_at => Time.now), Post.create(:body => "Rocks!", :created_at => Time.now)]
      @controller.instance_variable_set(:@posts, @posts)
      @body = @controller.render(:index)
  end

  it "should have a containing div for the posts" do
      @body.should have_selector("div#posts.container")
  end

  it "should have a div for each individual post" do
      @posts.each do |post|
        @body.should have_selector("div#posts.container div#post-#{ post.id }.post")
      end
  end

  it "should have the contents of each post inside a div with an id and class" do
      @posts.each do |post|
        @body.should match_tag(:div, :id => "post-#{ post.id }", :class => "post", :content => post.body)
      end
  end

  after(:each) do
      Post.destroy_all
  end

end

当使用RSpec描述对象时,常常需要在运行测试的前后维护测试特定的内容。毫不奇怪,RSpec向我们提供了#before#after块来实现这个目的。

回到代码中:我们在#before块中首先做的是从我们的 Posts 创建@controller实例。下面我们使用fake_request助手[^5]来模拟HTTP请求。

回想一下我们的视图:

...
<p>Recent Posts:</p>
<div id="posts" class="container">
    <%= partial("/shared/post", :with => @posts) %>
</div>

我们知道我们还需要一组帖子来进行测试,碰巧,这就是#before块的第二和第三行所做的事情:

...
@posts = [Post.create(:body => "Merb", :created_at => Time.now), Post.create(:body => "Rocks!", :created_at => Time.now)]

@controller.instancevariableset(:@posts, @posts)

这里,我们仅仅将插入了一组记录并保存为@controller@posts实例变量。记住,调用Posts控制器的#index动作和我们之前所做的没有什么区别,除了我们使用了一个ActiveRecord的#find,而不是手工使用#new创建帖子:

前面的Posts控制器Our Posts controller from earlier:

class Posts < Application

  def index
      @posts = Post.find(:all, :order => "created_at DESC")
      render
  end

end

最后,#before的第四和最后一行渲染了视图并将响应的主体(本案中是HTML)放入了@body 实例变量。

...
@body = @controller.render(:index)
...

(本质上来说,我们是使用fake_request来“查看”Posts#index动作。)

现在,我们的控制器已经设置好了,同时视图也渲染了,我们就开始列出我们对视图中应该有什么的预期。[^6]

首先,我们描述HTML里应该最外面有一个div来放每一个单独的帖子的div:

...
it "should have a containing div for the posts" do
    @body.should have_selector("div#posts.container")
end
...

下面,我们断定容器div确实包含着帖子的div:

...
it "should have a div for each individual post" do
  @posts.each do |post|
      @body.should have_selector("div#posts.container div#post-#{ post.id }.post")
  end
end
...

然后,我们进入每个帖子的div来验证内容准确地匹配对应的Post:

...
it "should have the contents of each post inside a div with an id and class" do
  @posts.each do |post|
      @body.should match_tag(:div, :id => "post-#{ post.id }", :class => "post", :content => post.body)
  end
end
...

最后,我们使用#after块来删除在#before块中创建的帖子:

...
after(:each) do
  Post.destroy_all
end
...

尽管我们还没真正完成,我们先来验证一下整个应用:

$ rake spec
...
Posts#index
- should respond correctly

Post
- should be valid

posts/index.html.erb
- should have a containing div for the posts
- should have a div with an id and class for each individual post
- should have the contents of each post inside a div with an id and class

Finished in 0.226266 seconds

5 examples, 0 failures

7.) 表单创建

有了浏览帖子的能力之后,现在就可以实现同样重要的创建帖子的功能了。

打开slapp/app/views/posts/index.html.erb并在帖子列表下面添加该表单[^7]:

...
<p>Post Something:</p>
<% form_tag(:action => url( :controller => "posts", :action => "create") ) do %>
    <%= text_field(:name => "body", :size => 40) %>
    <%= submit_button("Post Message!") %>
<% end %>

几乎不言自明,我们是要构建一个简单的表单,有一个文本输入框和一个提交按钮。

你应该已经注意到我们已经将表单设置为递交到Posts控制器的#create动作。我们需要实现这个动作,不过在我们继续之前,我们先快速描述一下这个表单。

编辑: slapp/spec/views/posts/index_spec.rb并在#after 块上面添加一下内容:

...
it "should have a form to create new posts with a single input and submit button" do
  @body.should match_selector("form[@action=/posts/create]")
  @body.should match_selector("form[@action=/posts/create] input[@name=body]")
  @body.should match_selector("form[@action=/posts/create] button[@type=submit]")
end

和前面一样,我们使用#match_selector 来断言表单、正文输入框以及提交按钮的存在。唯一不同的是我们使用了一个基于HTML属性的选择器[^8]form[@action=/posts/create]

Run the specs:

$ rake spec:view
...
4 examples, 0 failures

我们可以再次启动Merb来亲眼检验一下新的表单了。不过,因为我们已经使用了RSpec,这步不是非常必须的,我们可以立刻继续往下。

说道RSpec,这次,当我们在要去完成#create动作的时候,我们应该在写代码之前先写出该步骤的spec。

将以下内容复制到 slapp/spec/controllers/posts_spec.rb

require File.join(File.dirname(__FILE__), "..", 'spec_helper.rb')

describe Posts, "#index" do

  it "should respond correctly" do
      dispatch_to(Posts, :index).should respond_successfully
  end

end

describe Posts, "#create" do

  before(:each) do
      @params = { :body => "It was a good game though" }
  end

  it "should redirect to #index after successfully creating a Post" do
      lambda {
        dispatch_to(Posts, :create, @params).should redirect_to("/posts/index")
      }.should change(Post, :count)
  end

end

这里,在#before块中,我们准备了一个只有一个:body键的@params表以便开始描述#create动作 。下面,我们列出了第一个预期:create动作在它成功创建一个帖子之后应该重定向到#index动作。

(这就是如果一个浏览器通过表单提交了某些信息,在我们的应用中能看到的情况。)

我们用一个lambda表达式检验一个帖子是不是被创建了,其中RSpec会调用两次:先执行一次,然后等相关的{...}块执行完之后再执行另一次。

这两次中,RSpec都会调用Post.count,如果两次调用返回不同的值,那么我们就能确信Post被创建了,那么这个块(该动作)就是成功的。

因为我们还没有编码#create,所以spec显然会失败:

$ rake spec:controller
...
Posts#create
- should redirect to #index after successfully creating a Post (FAILED - 1)

1)
'Posts#create should redirect to #index after successfully creating a Post' FAILED
count should have changed, but is still 0
...

切换到: slapp/app/controllers/posts.rb并再次复制以下内容:

class Posts < Application

  def index
      @posts = Post.find(:all, :order => "created_at DESC")
      render
  end

  def create
      Post.create!(:body => params[:body])
      redirect url(:action => "index")
  end

end

现在我们定义了#create,现在回到spec文件,应该就可以通过了:

$ rake spec:controller
...
2 examples, 0 failures

通过了这些spec,我们就有了一个可以正常工作的聊天墙了。

听起来是个好消息,我们也几乎就要完成了。我们还需要做得就是检验创建一个新的帖子需要一定的文本,否则则会出现一个异常。

8.) 收尾

现在,任何都可以点击 “Post Message!” 然后创建一个新的帖子。因为我们并不想让一堆空白的帖子占据聊天墙,所以我们应该在创建新帖子之前校验至少有一些文本被提交了。

因为本文是一个教程,我们不打算将所有东西都仔细进行合适的处理,所以这里我们直接使用ActiveRecord的validates_length_of过滤器。 ;-)

打开: slapp/spec/models/post_spec.rb 并观察我们现有的spec:

...
it "should be valid when new" do
  post = Post.new
  post.should be_valid
end

因为我们要校验正文文本的存在,这个spe已经不再有效,我们需要如下的内容来替代:

describe Post do

  it "should NOT be valid when new" do
      post = Post.new
      post.should_not be_valid
  end

  it "should require at least two body characters to be valid" do
      post = Post.new
      post.should_not be_valid
      post.errors.on(:body).should include("is too short (minimum is 2 characters)")
  end

end

和平时一样,我们首先运行失败的spec来建立我们对于特定行为的预期:

...
Post
- should NOT be valid when new (FAILED - 1)
- should require at least two body characters to be valid (FAILED - 2)

1)
'Post should NOT be valid when new' FAILED
expected valid? to return false, got true
./spec/models/post_spec.rb:7:

2)
'Post should require at least two body characters to be valid' FAILED
expected valid? to return false, got true
./spec/models/post_spec.rb:12:
...

然后实现上面提出的修正,在本案中,则是在slapp/app/models/post.rb中加入一样:

class Post < ActiveRecord::Base

  validates_length_of :body, :minimum => 2

end

再次运行spec来检验我们的修正是否有效:

$ rake spec:model
...
2 examples, 0 failures

这时我们的模型完成了。让我们继续给Posts控制器添加一个spec——
我们需要描写当提交一个帖子没有包含正文的时候,我们没有真正去处理这个错误而已直接返回由我们的ORM抛出的异常。

编辑slapp/spec/controllers/posts_spec.rb并加入下面这个spec:

...
it "should raise an exception when insufficient body text is submitted" do
  lambda {
      dispatch_to(Posts, :create).should redirect_to("/posts/index")
  }.should raise_error(ActiveRecord::RecordInvalid)
end

如你所见,我们仅仅是不带@params表来调用#create。这创建一个没有正文的空Post,这样就会导致ActiveRecord的校验失败,并抛出我们预期的RecordInvalid异常。

有了这个,我们的第一个聊天墙的版本就完成了。就和前面一样,你可以通过启动Merb并浏览Posts#index动作来试试程序:

$ merb
$ curl http://localhost:4000/posts/index

最后的思考

从这里开始,你可能还有很多东西想添加,例如:分页、动态发布/更新、SPAM过滤器、文本格式化等等。

这些对于任何优秀的聊天程序都是很基本的,你都可以利用Merb来实现。

同时,别忘了浏览官方的项目首页看看有没有最新的更新、看看别人的版本甚至创建属于你自己的:

有任何问题/评价,请致电socialface@gmail.com或者在#merb找 Slurry

脚注

[^1]: The observant may have also just found their first “Merb” bug.. the
generator claimed to have made a “database.sample.yml” file, although the file
is really named “database.yml.sample”. :-)

[^2]: RSpec links in order of approximate handyness to the beginner:

[^3]: Merb RSpec controller matchers:

[^4]: Merb partials:

[^5]: Merb fake_request helper:

[^6]: Merb RSpec view matchers:

[^7]: Merb Form Helpers:

[^8]: Hpricot CSS Selectors:


作者:ShiningRay
ShiningRay的Blog
时间:2008-06-26

也算中国开源界的一个进步。

http://wiki.woodpecker.org.cn:9081/classes/corepython/

Nicholas:

这本书的翻译可以说历时 3 个月左右吧,判随严格的审校制度,质量得到了保证。在啄木鸟的页面 审校计划 里面可以看到所有的参与者以及参与审校的朋友们,大家付出了很多的心血保证这次翻译的完成,在这份邮件里面,可以看到项目在 12 月出总算胜利结束了。

但是随着翻译结束,关于此书的消息就一直没有下文了,没人知道这本书什么时候会出版,这本书也没有作为 PDF 在网上流传。但目前这本书却已经在市面上开始销售了,并且译者也不再是 CPUG 所熟悉的名字,我大概看了一下 CSDN 在网上提供的示例章节,并且对比 OpenBookProject 中此书的翻译,我的直觉告诉我,这些翻译就是来自 CPUG 的朋友们,没有他们的努力,这本书不可能完成。

但是还是这个译者的问题,列表中的 CorePy 就是宋吉广,为什么他当初匿名参与,不愿意透露自己的身份,再者,为什么出版的时候他就以自己的名字作为译者,将广大参与翻译的 Pythoner 作为了贡献者。虽然我没有参与这次翻译,但是就算作为一个旁观者,我觉得也有必要出来说一下。

我们都是技术工作者,因为喜爱 Python 这门语言互相认识,并且希望能够推广 Python,大家一腔热情翻译的书最后却以别人的名字出版了,难道不觉得很难受吗。更多的还有出版涉及的利益问题,大家花了大力气翻译,但最后出版社和宋吉广倒是占了不少便宜,反而大家什么都没有得到。如果大家不是很在乎这一块,我觉得至少也应该交给 CPUG,作为建设费用,日后可以添置服务器,设备之用。


作者:Nicholas Ding
Nicholas Ding
时间:2008-06-20
看了 Robbin 写的 我为什么鼓吹facebook,为什么唱衰OpenSocial? 这篇文章之后感触良多。这半年来做的网站也就如 Robbin 提及的一样,我们从 Facebook 带来了主要的流量。 在接触 Facebook 之前,我就听说过 OpenSocial,那时候对 SNS 不是很感兴趣,于是没有深入研究过 OpenSocial。半年前我的合伙人应该看到了 Facebook 平台的成功,于是制定了新的开发计划。先立足与 Facebook,开发几个 Facebook 小程序来吸引用户,随后把这些用户引导到我们网站上来。 谈到 Facebook 的小程序,不得不说的 Viral Marketing 如果做 Facebook 小程序,那么无庸置疑,取得用户是最关键的,所以我们需要看一下 Facebook 平台提供什么样的方式让我们能接触到用户。这里不得不提到 Viral Marketing,光是用户喜欢还不行,我们需要让用户把他喜欢的东西告诉他的朋友,通过这样实现流量的提升。那么具体有大概几种方式呢? 1. 发送邀请 发送邀请成为了一个非常简单的功能,Facebook 提供的 FBML 有预定义的标签,很方便的做这个事情,通过两个标签,并且不需要任何编码,就可以实现下面的邀请页面,让你很方便的将这个程序推广给你的好友们。 2. Newsfeed Newsfeed 让你知道你周围发生的事情,譬如我的一个朋友同时也做了 “IQ 测试”,并且分数不错,那么显然她希望别人知道或者了解这样一件事情。那么简单的很,Facebook 让你发送 Newsfeed,这样作为她的朋友,我的首页上面就会出现我的朋友进行了 IQ 测试这么一个 Newsfeed,如果我感兴趣的话,我也可以去试一试。 Newsfeed 可以使用图片,一张设计精美的图片和有趣的文字会让人对这个小程序更感兴趣。因为 Newsfeed 同时存在与 Profile 页面,所以有时候浏览朋友的页面,顺便看到有趣的 Feed 也会让人有试一试的冲动。 3. Notification 通过邀请和 Newsfeed 我们已经可以有一定的用户基础了,但是玩过这个小程序之后可能用户就放着了。某天如果我们更新了小程序,加了新功能,或者是我们希望在今后与用户互动,我们该怎么做呢?虽然 Facebook 处于安全考虑,不会提供用户的 [...]
作者:Nicholas Ding
Nicholas Ding
时间:2008-06-20
看了 Robbin 写的 我为什么鼓吹facebook,为什么唱衰OpenSocial? 这篇文章之后感触良多。这半年来做的网站也就如 Robbin 提及的一样,我们从 Facebook 带来了主要的流量。 在接触 Facebook 之前,我就听说过 OpenSocial,那时候对 SNS 不是很感兴趣,于是没有深入研究过 OpenSocial。半年前我的合伙人应该看到了 Facebook 平台的成功,于是制定了新的开发计划。先立足与 Facebook,开发几个 Facebook 小程序来吸引用户,随后把这些用户引导到我们网站上来。 谈到 Facebook 的小程序,不得不说的 Viral Marketing 如果做 Facebook 小程序,那么无庸置疑,取得用户是最关键的,所以我们需要看一下 Facebook 平台提供什么样的方式让我们能接触到用户。这里不得不提到 Viral Marketing,光是用户喜欢还不行,我们需要让用户把他喜欢的东西告诉他的朋友,通过这样实现流量的提升。那么具体有大概几种方式呢? 1. 发送邀请 发送邀请成为了一个非常简单的功能,Facebook 提供的 FBML 有预定义的标签,很方便的做这个事情,通过两个标签,并且不需要任何编码,就可以实现下面的邀请页面,让你很方便的将这个程序推广给你的好友们。 2. Newsfeed Newsfeed 让你知道你周围发生的事情,譬如我的一个朋友同时也做了 “IQ 测试”,并且分数不错,那么显然她希望别人知道或者了解这样一件事情。那么简单的很,Facebook 让你发送 Newsfeed,这样作为她的朋友,我的首页上面就会出现我的朋友进行了 IQ 测试这么一个 Newsfeed,如果我感兴趣的话,我也可以去试一试。 Newsfeed 可以使用图片,一张设计精美的图片和有趣的文字会让人对这个小程序更感兴趣。因为 Newsfeed 同时存在与 Profile 页面,所以有时候浏览朋友的页面,顺便看到有趣的 Feed 也会让人有试一试的冲动。 3. Notification 通过邀请和 Newsfeed 我们已经可以有一定的用户基础了,但是玩过这个小程序之后可能用户就放着了。某天如果我们更新了小程序,加了新功能,或者是我们希望在今后与用户互动,我们该怎么做呢?虽然 Facebook 处于安全考虑,不会提供用户的 [...]

享受Rss订阅统计和发行管理服务, 马上使用Feedsky。
图文版权归原作者所有,模版版权归Feedsky所有