• 2007-07-11

    第一章节 - [阅读]

    Tag:Ruby 读书
    先从一段Ruby程序开始,即欧几里德最大公约数的算法实现:
    测试用例当然要先,
    require 'test/unit'
    require './one/Apple'

    class TestApple < Test::Unit::TestCase

      def test_cal
        ys = Apple.new
        assert_equal(5,ys.gcd(35,25))
        assert_equal(50,ys.gcd(100,50))
        assert_equal(9,ys.gcd(27,18))
        assert_equal(5,ys.gcd(5,10))
        assert_equal(5,ys.gcd(5,0))
        assert_equal(-5,ys.gcd(-5,0))
        assert_raise(ArgumentError){ys.gcd(0,0)}
        assert_equal(23,ys.gcd(0,23))
      end
    end
    然后就是算法的实现代码了:
    class Apple

      def gcd(arg1=0, arg2=0)
        if((arg1==0)&&(arg2==0))
          raise ArgumentError, "arg1 and arg2 are 0.", caller
        else
          gcdImpl(arg1,arg2)
        end    
      end
     
      def gcdImpl(arg1=0, arg2=0)
        if(arg2 == 0)
          return arg1
        elsif
          temp=arg1%arg2
          if(temp == 0)
            return arg2
          else
            gcdImpl(arg2,temp)
          end
        end
      end

    end

    从一个算法的描述来实现一个算法,仅仅是编写一段程序而已。而对于算法这种问题的程序化解决方案而言,需要如何一回事情呢?比方说,最大公约数到底是怎么一回事,5和0的最大公约数怎么可能是5呢?所以要学习算法到底是怎么一回事,需要探讨更多的问题?
    对于问题的理解、性能、选择、数据结构、如何设计、算法的描述、正确性证明、分析、代码实现等等一系列单元。当程序实现仅仅是第一反应的时候,问题理解、分析、设计、验证就变得异常重要了。
    算法所拥有的通用问题类型:排序、查找、字符串处理、图问题、组合问题、几何问题、数值问题;都是一些已经相当成熟的问题类型。很多使我们常常听说或者涉及的,也是算法设计和分析频繁的地方,算是很多的“肩膀”吧,算法都是都是在这里站立起来的。
  • 简单开始
    下载并且安装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的规范中要涉及到的一些东西。