RubyOnRails实战

简单开始
下载并且安装

Ruby 1.8.4;
RubyGems;

通过命令行安装所有的Rails和其依赖:

gem install rails --include-dependencies

在用户工作目录运行命令行,即创建第一个rails的模板:

rails depot
cd depot
ruby script/server

在浏览器打开 http://localhost:3000 即可发现第一个rails的web应用已经开始run起来了。

然后我参考书籍《Agile Web Development with Rails》2nd Edition的第一个例子一步一步的动手写了第一个ROR程序。(具体的过程不详细记录了.)
从数据库准备、数据库的配置、创建表、migrate、创建controller和maintenance、add missing column、添加validation功能、使用scaffold、定制html、装填unit test数据。整个例子的思路比较清晰,从数据库、数据访问、业务、controller、展示;给我留下深刻的影响的是:自动生成功能、内嵌web server、支持数据库配置,支持应用的模板、支持mvc模板等,所以让人似乎感觉到一些DSL的色彩。

做一个复杂一些的例子
数据库准备
支持数据库Mysql,从 http://dev.mysql.com/downloads/mysql/5.0.html#downloads 下载数据库Mysql安装,并且下载Mysql Administrator作为数据库管理工具。
创建Schema为:

depot_dev

在文件./config/database.yml中对数据库进行配置:

development:
  adapter: mysql
  database: depot_dev
  username: depot
  password: depot
  host: localhost

测试配置:

rake db:migrate

如果配置不正确在控制台会出现错误的日志。
创建Products的Model和Table如下:

D:\IDE\rails-exampe\depot>ruby script/generate model product

输出:

exists  app/models/
exists  test/unit/
exists  test/fixtures/
create  app/models/product.rb
create  test/unit/product_test.rb
create  test/fixtures/products.yml
create  db/migrate
create  db/migrate/001_create_products.rb

在./db/migrate/001_create_products.rb用类似DDL的形式表达表的结构:

class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column :title,       :string
      t.column :description, :text
      t.column :image_url,   :string
    end
  end

  def self.down
    drop_table :products
  end
end

Okey,那就Migrate吧:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== CreateProducts: migrating ==================================================
-- create_table(:products)
   -> 0.0780s
== CreateProducts: migrated (0.0780s) =========================================

数据库一切搞定,下一步开始应用层了,首先创建Controller:

D:\IDE\rails-exampe\depot>ruby script/generate controller admin

输出:

exists  app/controllers/
exists  app/helpers/
create  app/views/admin
exists  test/functional/
create  app/controllers/admin_controller.rb
create  test/functional/admin_controller_test.rb
create  app/helpers/admin_helper.rb

创建Maintenance应用:
编辑app/controller/admin_controller.rb

class AdminController < ApplicationController
  scaffold :product
end

启动ruby script/server,通过浏览器就可以浏览这个Rail scaffolds的应用了。
迭代2:Add a missing column

D:\IDE\rails-exampe\depot>ruby script/generate migration add_price
      exists  db/migrate
      create  db/migrate/002_add_price.rb

编辑db/migrate/002_add_price.rb

class AddPrice < ActiveRecord::Migration
  def self.up
    add_column :products, :price, :decimal, :precision => 8, :scale => 2, :default => 0
  end

  def self.down
    remove_column :products, :price
  end
end

执行命令:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== AddPrice: migrating ========================================================
-- add_column(:products, :price, :decimal, {:default=>0, :precision=>8, :scale=>2})
-> 0.2650s
== AddPrice: migrated (0.2650s) ===============================================

一切Okey,第二个迭代也已经结束:)
第三个迭代:Validate!
编辑app/model/product.rb文件:

class Product < ActiveRecord::Base
  validates_presence_of :title, :description, :image_url
  validates_numericality_of :price
  validates_uniqueness_of :title
  validates_format_of :image_url, 
                      :with    => %r{\.(gif|jpg|png)$}i,
                      :message => "must be a URL for a GIF, JPG, or PNG image"

  protected
  def validate
    errors.add(:price, "should be at least 0.01") if price.nil? ||  price < 0.01
  end
end

第四个迭代:Prettier Listings
创建静态的scaffold如下:

D:\IDE\rails-exampe\depot>ruby script/generate scaffold product admin
      exists  app/controllers/
      exists  app/helpers/
      exists  app/views/admin
      exists  app/views/layouts/
      exists  test/functional/
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
        skip    app/models/product.rb
   identical    test/unit/product_test.rb
   identical    test/fixtures/products.yml
      create  app/views/admin/_form.rhtml
      create  app/views/admin/list.rhtml
      create  app/views/admin/show.rhtml
      create  app/views/admin/new.rhtml
      create  app/views/admin/edit.rhtml
overwrite app/controllers/admin_controller.rb? [Ynaqd] y
       force  app/controllers/admin_controller.rb
overwrite test/functional/admin_controller_test.rb? [Ynaqd] y
       force  test/functional/admin_controller_test.rb
   identical  app/helpers/admin_helper.rb
overwrite app/views/layouts/admin.rhtml? [Ynaqd] y
       force  app/views/layouts/admin.rhtml
      create  public/stylesheets/scaffold.css

定制list.rhtml:

<div id="product-list">
  <h1>Product Listing</h1>

  <table cellpadding="5" cellspacing="0">
  <% for product in @products %>
    <tr valign="top" class="<%= cycle('list-line-odd', 'list-line-even') %>">

      <td>
        <img class="list-image" src="<%= product.image_url %>"/>
      </td>

      <td width="60%">
        <span class="list-title"><%= h(product.title) %></span><br />
        <%= h(truncate(product.description, 80)) %>
      </td>

      <td class="list-actions">
        <%= link_to 'Show', :action => 'show', :id => product %><br/>
        <%= link_to 'Edit', :action => 'edit', :id => product %><br/>
        <%= link_to 'Destroy', { :action  => 'destroy', :id => product },
                                 :confirm => "Are you sure?",
                                 :method  => :post %>
      </td>
    </tr>
  <% end %>
  </table>
</div>

<%=  if @product_pages.current.previous 
       link_to("Previous page", { :page => @product_pages.current.previous })
     end
%>
<%= if @product_pages.current.next 
      link_to("Next page", { :page => @product_pages.current.next })
    end
%>

<br />

<%= link_to 'New product', :action => 'new' %>

增加测试数据:

D:\IDE\rails-exampe\depot>ruby script/generate migration add_test_data
      exists  db/migrate
      create  db/migrate/003_add_test_data.rb

编辑db/migrate/003_add_test_data.rb:

class AddTestData < ActiveRecord::Migration
  def self.up
    Product.delete_all
    Product.create(:title => 'Pragmatic Version Control',
      :description =>
      %{<p>
        This book is a recipe-based approach to using Subversion that will
        get you up and running quickly--and correctly. All projects need
    version control: it's a foundational piece of any project's
    infrastructure. Yet half of all project teams in the U.S. don't use
    any version control at all. Many others don't use it well, and end
    up experiencing time-consuming problems.
      </p>},
    :image_url => '/images/svn.jpg',
    :price => 28.50)
  end

  def self.down
    Product.delete_all
  end
end

安装测试数据:

D:\IDE\rails-exampe\depot>rake db:migrate

输出:

D:0:Warning: require_gem is obsolete.  Use gem instead.
(in D:/IDE/rails-exampe/depot)
== AddTestData: migrating =====================================================
== AddTestData: migrated (0.2030s) ============================================

编辑Layouts:app/views/layouts/admin.rhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <title>Admin: <%= controller.action_name %></title>
  <%= stylesheet_link_tag 'scaffold', 'depot' %>
</head>
<body>
<p style="color: green"><%= flash[:notice] %></p>
<%= yield :layout %>
</body>
</html>

编辑:app/views/admin/list.rhtml

<div id="product-list">
  <h1>Product Listing</h1>

  <table cellpadding="5" cellspacing="0">
  <% for product in @products %>
    <tr valign="top" class="<%= cycle('list-line-odd', 'list-line-even') %>">

      <td>
        <img class="list-image" src="<%= product.image_url %>"/>
      </td>

      <td width="60%">
        <span class="list-title"><%= h(product.title) %></span><br />
        <%= h(truncate(product.description, 80)) %>
      </td>

      <td class="list-actions">
        <%= link_to 'Show', :action => 'show', :id => product %><br/>
        <%= link_to 'Edit', :action => 'edit', :id => product %><br/>
        <%= link_to 'Destroy', { :action  => 'destroy', :id => product },
                                 :confirm => "Are you sure?",
                                 :method  => :post %>
      </td>
    </tr>
  <% end %>
  </table>
</div>

<%=  if @product_pages.current.previous 
       link_to("Previous page", { :page => @product_pages.current.previous })
     end
%>
<%= if @product_pages.current.next 
      link_to("Next page", { :page => @product_pages.current.next })
    end
%>

<br />

<%= link_to 'New product', :action => 'new' %>

参考:
http://www.rubyonrails.org/down
书籍《Agile Web Development with Rails》2nd Edition

另,JSR 315: Java Servlet 3.0 Specification under review at JCP:http://jcp.org/en/jsr/detail?id=315可以看到Servlet 3.0的规范中要涉及到的一些东西。