Skip to content

[原创]Rails中用AASM实现工作流与多步骤向导

ruby有一个act as state machine的插件,可以实现工作流或者多步骤向导的功能。
state machine,是计算机科学里面一种模型,这个插件主要实现的是finite state machine,既有限状态自动机。

首先,AASM的安装:

config.gem "rubyist-aasm", :source => "http://gems.github.com", :lib => 'aasm'

安装好之后需要在你的Model里面加入

include AASM

下面是一个简单的例子。来介绍AASM的使用。
加入我们有一个person的model,一个person可以处于三种状态,sleeping, working 还有eating.
初始状态是sleeping,
从sleeping只可以通过eat事件,转到eating,不可以转到working.(吃了饭才能工作)。
从working只可以通过eat事件,转到eating,不可以转到sleeping. (不能空肚子睡觉)。
然后从eating可以通过work事件转到working,或者sleep转到sleeping.

这些可以转化成下面的代码

class Person < ActiveRecord::Base
  include AASM
 
  aasm_column :current_state #定义那个字段来储存当前状态
  aasm_initial_state :sleeping #定义初始状态
 
  #定义三个不同的状态
  aasm_state :sleeping
  aasm_state :eating
  aasm_state :working

  #定义每个event及event发生时引发的状态转换
  aasm_event :eat do
    transitions :from => [:sleeping,:working], :to => :eating
  end
 
  aasm_event :work do
    transitions :from => :eating, :to =>:working
  end
 
  aasm_event :sleep do
    transitions :from => :eating, :to => :sleeping
  end
end

这样基本上就已经建好了state machine了。可以在console里面实验一下:

ruby-1.8.7-p249 > p=Person.create
 => #<Person id: 10, name: nil, current_state: "sleeping", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_events_for_current_state
 => [:eat]
ruby-1.8.7-p249 > p.eat
 => true
ruby-1.8.7-p249 > p
 => #<Person id: 10, name: nil, current_state: "eating", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_events_for_current_state
 => [:sleep, :work]
ruby-1.8.7-p249 > p.work
 => true
ruby-1.8.7-p249 > p
 => #<Person id: 10, name: nil, current_state: "working", created_at: "2010-05-09 06:32:41", updated_at: "2010-05-09 06:32:41">
ruby-1.8.7-p249 > p.aasm_current_state
 => :working

可以看到,可以直接用 p.event的形式,来触发事件。
aasm_current_state来获得当前状态,aasm_events_for_current_state 来获得当前状态可用的event.

结合到view的时候也很简单。把aasm_events_for_current_state获得的集合交给用户选择。
返回到server之后的param经过验证之后。可以用p.send(param[:event])的形式触发。

这里的例子是一个工作流的例子。多步骤向导也很类似,这里不再赘述。

除此之外,AASM还提供了很多有用的callback, 感兴趣的朋友可以深入研究一下。
github列出了下面几个:
oldstate:exit*
event:before
__find transition, if possible__
transition:on_transition*
oldstate:before_exit
newstate:before_enter
newstate:enter*
__update state__
event:success*
oldstate:after_exit
newstate:after_enter
event:after
obj:aasm_event_fired*

MacPorts迁移到homebrew

一直用MacPorts但是不喜欢,像不喜欢cygwin一样不喜欢macports,因为他们喜欢在你的系统内倔强的自成一体,不看环境就装很多额外的依赖库。
看了很多人介绍homebrew之后决定试一下。

准备工作,安装好了Xcode, 没有MacPorts的可以跳过第一步。

第一步,删除MacPorts
sudo port -f uninstall installed

sudo rm -rf \
/opt/local \
/Applications/DarwinPorts \
/Applications/MacPorts \
/Library/LaunchDaemons/org.macports.* \
/Library/Receipts/DarwinPorts*.pkg \
/Library/Receipts/MacPorts*.pkg \
/Library/StartupItems/DarwinPortsStartup \
/Library/Tcl/darwinports1.0 \
/Library/Tcl/macports1.0 \
~/.macports

第二步:
无git安装 homebrew
curl -L http://github.com/mxcl/homebrew/tarball/master | tar xz –strip 1 -C /usr/local

第三步:
装好了,第三步就是用了
brew install wget
brew install git
需要升级的话,就
brew update

参考文章:http://www.engineyard.com/blog/2010/homebrew-os-xs-missing-package-manager/

Ruby 1.8.7 和1.9.1 Windows手动安装

Windows的ruby installer给出各种各样的怪异错误,实在是受不了了,于是手动装了,其实很简单,就是解压一个文件夹,但是很多库需要装一下.


第一步:安装所需的lib

包都是ruby主页推荐的,我不用tk装,列表可以在此找到:http://jarp.does.notwork.org/win32/
zlib
里面的dll
openssl
-找到“libeay32.dll”, 复制到 ruby\bin. .
-找到 “libssl32.dll”, 复制到 ruby\bin 然后改名为ssleay32.dll
readline
复制readline.dll
iconv
里面的dll
pdcurses
里面的dll
gdbm
里面的dll

不想这么麻烦,可以下载我已经单独取出来的dll文件,全部放倒ruby/bin
文件:rubylib

第二步:将ruby/bin加入PATH
第三步:安装gem
- 下载rubygem
- 解压缩
- 到里面的目录,运行ruby setup.rb

完成

Zoom Zoom

Zoom Zoom是Mazda的理念.
翻开Mazda用户手册,扉页上写着:

Zoom Zoom
秋千,自行车,还记得小时候的快乐么.
孩子们本能上就能体验到的快乐.
成长,更多的责任,让许多成人渐渐遗忘了Zoom Zoom.
一个独特的汽车公司,帮你找回童年的乐趣.
用大人的话说,这种乐趣就是速度与动感带来的愉悦和自由.
用孩子的话说,就是Zoom Zoom.

刚来加拿大的时候,主在Homestay, 也没有那么多钱装宽带。
闲下来的时间,上网少,看电视多,看各种广告,练习听力。
让我印象最深刻的就是马自达的广告,Zoom~ Zoom~ Zoom~的音乐伴随着每一款车的广告。

转眼9年,要挑选自己第一辆全新的车(之前的都是旧车)。
虽然思考过很多车型,网上查看了各种技术参数。
但其实心中回响的,还是Zoom Zoom的音乐。
而且我一直认为,汽车外观设计上,Mazda都走在其他品牌前面。
2010 新款Mazda 3外观更是超前于时代。

终于,趁我的心还年轻,我也有了自己的Zoom Zoom,等着以后沉稳了,可能就要选Venza了。



203 Routing in Rails 3

203: Routing in Rails 3
(From asciicasts.com and Railscast )
Translated by Yinghai. Please contact zyinghai at gmail.com regarding this translation.
翻译的比较急,如有问题,请联系zyinghai at gmail.com

这一集我们继续研究Rails的新特性, 这次我们看一下Rails 3的路由. Rails 3提供了一些新的API和新的功能来定义路由. 为了更好的演示,我们先建立一个detour应用。

rails detour

建立好应用之后,我们可以打开config/routes.rb文件看一下. 默认的文件包含了一些演示新API的文档和例子,当你有空的时候,这些都值得一看。 我们先删掉自带的文档,换上老的格式,再来对比一下Rails 3新的用法。

/config/routes.rb

Detour::Application.routes.draw do |map|
  map.resources :products, :member => { :detailed => :get }
  map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums|
     forums.resources :topics
  end

  map.root :controller => "home", :action => "index"

  map.about "/about", :controller => "info", :action => "about"
end

以上是需要转化的老格式。

我们先从这一个开始:

map.resources :products, :member = { :detailed =>:get }

这个路由有一个product资源,和一个叫做detailed的附加action,可以通过Get方法请求。

Rails 3第一个值得注意的改变,就是我们不再使用map对象,取而代之的是在routes.draw块里面直接调用resources。resources包括的:member和:collection 动作,可以通过一个块来定义, 上述的路由可以新格式是这样:

resources :products do
  get :detailed, :on => :member
end

下面,我们来看以下更复杂的路由:

map.resources :forums, :collection => { :sortable => :get, :sort => :put } do |forums|
  forums.resources :topics
end

这个路由,在forum资源里面,我们有两个额外的:collection动作,还有一个嵌套的Topic资源。新的API中,可以写成这样:

resources :forums do
  collection do
    get :sortable
    put :sort
  end
  resources :topics
end

以上代码在一次展示了取消map.resources,直接用resources块。我们有两个collection动作,尽管我们可以像第一个例子中detailed动作一样使用:on,但我们这里用collection块达到了同样的效果 (member动作也可以使用member块)。任何在这个块里面定义的路由,都会作用在forums的集合上。在我们的例子中,我们定义了两个,一个是GET方法的sortable和PUT方法的sort。

至于嵌套的topics资源,我们可以在forums的块里再次调用resources,起到嵌套的效果。

下一个路由,我们看一下怎么定义root的控制器和动作。

map.root :controller => "home", :action => "index"

这里,我们可以直接调用root,用:to来定义要转到的动作,控制器跟动作之间,可以用#来区隔。

root :to => "home#index"

用一个#分隔的字符串来定义控制器跟动作,是Rails 3的新特性之一。我们可以用类似的格式来定义named路由。

map.about "/about", :controller => "info", :action => "about"

Rails 3中改写成这样:

match "/about" => "info#about", :as => :about

没有:as参数,这个路由就是单纯的转向”/about”, 加了:as 之后,在我们的应用里面可以使用about_path或者about_url。

新功能

从上面的例子可以看出,只需要微小的改动,就可以从老的API转化到新的API,但是真正吸引人的是新API提供的一些新功能,下面我们来看其中的一部分。

可选参数

前一个版本就已经有对可选参数的支持,但是用法比较麻烦,下面我们来看一下Rails 3中怎么使用。

我们建立一个info控制器和about动作来展示可选参数。Rails 3支持用rails g来作为rails generate的快捷方式。

rails g controller info about

我们可以用rails s快捷方式来启动服务器

rails s

现在我们打开http://localhost:3000/about, 我们会转到之前已经在路由配制文件里面定义的info#about动作。

假设我们下一步想要添加对pdf格式的支持,可是现在打开http://localhost:3000/about.pdf的话,我们会看到一个路由错误,提示没有定义路由。
在路由文件里,我们可以添加一个format参数。

match "/about.:format" => "info#about", :as => :about

如果我们刷新页面,这次会得到应用找不到template错误。

我们已经解决了之前的问题,但是format参数不是可选参数,如果我们返回之前没有.pdf后缀的页面,我们会遇到路由错误,我们可以做简单的改动,只需用括号括起来,就可以让format变为可选参数。

match "/about(.:format)" => "info#about", :as => :about

然后我们可以正常的打开http://localhost:3000/about 或者 http://localhost:3000/about.pdf。

下面,我们来看一下怎么使用更为复杂的可选参数,例如,我们现在有一个博客应用,我们想要在网址中,用可选的年月日来过滤一段特定时间的贴子。
我们可以用下面的路由达到这个效果,主义括号可以嵌套起来定义多个可选参数。

match "/:year(/:month(/:day))" =&gt; "info#about"

在view中,我们添加一些调试信息,来检查传入的参数:

/app/views/info/about.html.erb

<h1>Info#about</h1>
Find me in app/views/info/about.html.erb
<%= debug params %>

下面,如果我们传入year参数,about动作会收到我们传入的值,month和day参数也是如此。

这个路由是比较宽泛的,如果我们传入,例如http://localhost:3000/foo/bar,这样的参数也会被传到控制器,很显然,想要符合日期规范的参数,我们可以用constrains来达到这样的目的。

Constraints
Constraints 就是Rails 2中的requirements. :constraints可以传入一个hash来限定匹配。以下的例子用四个数字来限定年,两个数字来限定月和日。

match "/:year(/:month(/:day))" =&gt; "info#about", :constraints =&gt; { :year =&gt; /\d{4}/, :month =&gt; /\d{2}/, :day =&gt; /\d{2}/ }

添加了这个constraint之后,我们再看一下/foo/bar路径,这次我们得到了一个无法匹配的错误。

我们现在做的,Rails 2的requirements都可以做到。但是Rails 3的Constraint更加强大。
例如,我们可以用user_agent参数来限制一些浏览器的访问,以Firefox为例。

match "/secret" => "info#about", :constraints => { :user_agent => /Firefox/ }

如果我们用Safari, Chrome或者Opera访问页面,我们会看到一个匹配错误。但是Firefox可以正常浏览。

我们也可以添加一些更有用处的constraint比如主机名。

match "/secret" => "info#about", :constraints => { :host => /localhost/ }

使用这个constraint,我们可以通过http://localhost:3000/secret来浏览网页,但是用ip地址将会返回错误( http://127.0.0.1/secret ),其实两个地址本质上都是一样的。这个功能可以用来限制某些二级域名的访问,目前这一部分还在开发中,在以后的版本中,constraint可以传入:subdomain参数。

如果我们有多个路由同时使用一个constraint,会造成很多代码的重复。我们可以用constraints方法把这些路由放到一个块中来降低代码重复。

constraints :host => /localhost/ do
  match "/secret" =>; "info#about"
  match "/topsecret" => "info#about"
end

还有很多constraint的高级功能,就不在这里一一赘述了。

Rack路由

因为时间的关系,我们没办法涵盖所有Rails3的路由改进,我们会在未来的视频里慢慢接触。 我今天想介绍的最后一个功能,是Rails 3路由与Rack的集成。
通常我们将地址匹配到一个控制器和动作,但是我们也可以匹配给一个Rack应用,这是一个非常强大的功能,我用一个简单的Rack应用来展示这个功能。

match "/hello" => proc { |env| [200, {}, "Hello Rack!"] }

如果你不是很熟悉Rack,可以看一下第151集视频,我们上面代码就是返回HTTP成功代码,和很简单的内容。

我们访问 /hello 就可以看到“Hello Rack!,以此我们可以看出Rack工作正常并响应请求.你可以用这个强大的功能将路由转到不同的应用。比如可以将路由匹配到Sinatra应用。

Rails 3的路由功能带来了很多令人振奋的可能性,尽管今天我们只是大体地介绍了一下,以后我们会详细的介绍一些具体用法。

如果你需要更多Rails 3的路由信息,可以参考Yehuda Katz的博客和RailsGuides.

Please contact zyinghai at gmail.com regarding this translation.
翻译的比较急,如有问题,请联系zyinghai at gmail.com

Dreamhost部署 Ruby on Rails

建好数据库,用git在dreamhost账号里面clone一个项目目录。
然后添加域名,目录指向public子目录。
钩上ROR选项后,点fully host domain。

不幸的是,启动总有问题。
于是我做了下面几件事情。

1 把rails 2.3.5 冷冻进项目目录
rake rails:freeze:gems

2 设置数据库
database.yml
production:
adapter: mysql
encoding: utf8
database: waimai_hailizi
host: mysql.*******.net
username: ********
password: ************

3 建立数据库表
rake –trace db:migrate RAILS_ENV=production

4 配制环境 config/environment.rb
RAILS_GEM_VERSION = ’2.3.5′ unless defined? RAILS_GEM_VERSION
ENV['RAILS_ENV']=’production’

if ENV['RAILS_ENV'] == ‘production’ # don’t bother on dev
ENV['GEM_PATH'] = ‘/home/$HOME/.gems’ + ‘:/usr/lib/ruby/gems/1.8′
end

5 设置gem的安装目录,以及
export PATH=”$HOME/.packages/bin:$HOME/.gems/bin:${PATH}”
export GEM_HOME=$HOME/.gems
export GEM_PATH=”$GEM_HOME:/usr/lib/ruby/gems/1.8″

参考这里

6 安装所需要的gem
gem install formtastic

7将gem也冷冻到项目目录里
rake gems:unpack

8 重启服务
touch tmp/restart.txt

然后竟然神奇可以用了。。YAY~~

Ubuntu 9.10

D-Link DWA 140:
Add blacklist rt2800usb to /etc/modprobe.d/blacklist.conf

Compiz:
apt-get install compizconfig-settings-manager

Terminus:
apt-get install xfonts-terminus

Extras:
sudo apt-get install ubuntu-restricted-extras

英文环境中文配置:
cd /etc/fonts/conf.d/
sudo ln -s /etc/fonts/conf.avail/69-language-selector-zh-cn.conf
sudo ln -s /etc/fonts/conf.avail/99-language-selector-zh.conf

两年多

自从07年不写东西到现在,已经过了两年多了:
两年前,我可以四舍到20来岁的,两年后,我只能五入到30来岁。
两年前,我还是无忧无虑的大学生,两年后,我成了终日碌碌的工薪族。
两年前,我有加拿大的暂住证,两年后,我拿了加拿大的户口本。

两年前,是独行,两年后,是人夫。

我开的不是Yinghai.Org, 是寂寞

“哥们,你抽得不是烟,是寂寞”

不知道从什么时候,网络开始流行这句话.山寨版应声而起, “你X的不是XX,你X的是寂寞”

我也山寨一下流行的话吧。

Yinghai.Org其实4,5年前就被我买了,用WordPress, 断断续续的码字,也写了不少东西。

那个时候想写一个自己的blog程序,于是关了网站,自己闷头闭门造车。

东拉西扯的学了不少新东西,各种语言,技巧,构架,库。。。

从头开始做了一个,觉得不好,有用Liferay构建了一个,又觉得受制于平台。

两年过去了,自己还没用上自己写的blog。

原因很简单,就算我是诸葛亮,也比不过WordPress得千千万万个臭屁匠。

何况我自己本身是臭屁匠,开源社区人人都是诸葛亮。

虽然在这个过程中,学到了不少东西,

可是回头一看,WP已经长足发展。

于是长叹,我写得不是程序,是寂寞。。。

Yinghai.Org重新开了,转了一大圈,还是用的WP.

希望新朋友老朋友还能常来看看。

注:当然,我不寂寞,老婆一直闹着让我写她,一点都不寂寞。

Tagged

20年最强对话

2007,史蒂夫乔布斯和比尔盖茨的对话。
Continue reading ›