发布新日志

  • 我爱Ruby的37个理由

    2008-02-02 18:07:58

    我不打算浪费时间来谈论Ruby的历史,如果你没有听说过它,你可以去它的主页看看www.ruby-lang.org,或者去它的新闻组comp.lang.ruby。如果你知道Ruby,我将讲述我为什么会喜爱它。(你也可能去我的Ruby主页或者个人主页看看)

    1. 它是面向对象的。 这表示什么意义呢?如果问10个程序员,你也许会得到12种结果,你有你的看法,我不会试图去改变你的看法。但是有一点,Ruby提供了对数据和方法的封装,允许类的继承,对象的多态。不像其它语言(C++,Perl等),Ruby从设计的时候开始就是一种面向对象的语言。
    2. 它是纯面向对象的语言。难道是我多余?不是这样的,之所以这么说,因为Ruby中一切都是对象,包括原始数据类型(primitive data types),比如字符串,整型,都表示的是一个对象,而不需要Java那样提供包装类(wrapper classes)。另外,甚至是常量,也会被当作对象来处理,所以一个方法的接收者,可以是一个数字常量。
    3. 它是动态语言。对于只熟悉像C++,Java这样静态语言的人来说,这是一个重大的概念上的差别。动态意味着方法和变量可以在运行时候添加和重定义。它减少了像C语言那样的条件编译(#ifdef),而且容易实现反射API(reflection API)。动态性使得程序能自我感知(self-aware),比如运行时类型信息,检测丢失的方法,用来检测增加方法的钩子等。在这些方面Ruby和Lisp和Smalltalk都有一些关系。
    4. 它是一种解释执行的语言。这是一个负杂的问题,值得重点解释一下,也许这个特点会因为性能的原因而引起从优点变为缺点的争论。对于此,我有几点见解:1.第一:快速开发循环是一个巨大的好处,这要得意于Ruby的解释执行。2.多慢才叫慢呢?在说它慢之前先定一个慢的基准。3.也许有人要批评我了,但我还要说:处理器每年都在变得原来越快。4.如果你真的很在意你的速度,你可以用C开发一部分你的代码。5.最后,从某种意义上说,这是一个还在争论中的问题,没有一个语言天生就是解释型的,世界上没有哪个法律进制开发一个Ruby编译器出来。
    5. 它理解正则表达式。很多年之前,正则表达式只是用在UNIX的工具如grep或者sed中,或者在vi中进行一些一定的查找-替换等。Perl的出现解决了这些问题,而现在,Ruby同样也能做到这些。越来越多得人认识到了这种字符串和文本处理技术的难以置信的能力,如果你对此表示怀疑,那么请去看一下 Jeffrey Friedl的书Mastering Regular Expressions,然后,你就应该不会有什么怀疑了。
    6. 它是多平台的。 Ruby可以运行在Linux,UNIX,Windows,BeOS?,甚至MS-DOS。如果我没记错,甚至还有一个Amiga 版本的
    7. 它是派生来的。这是一件好事情吗?抛去书本上的知识,它是有用的。牛顿曾说过“我如果看得比别人远,那是因为我站在巨人的肩膀上”。Ruby同样也是站在巨人的肩膀上,它借鉴了Smalltalk, CLU, Lisp, C, C++, Perl, Kornshell等的优点。在我看来它的原则包括:1.不要重复制造轮子。2.不要修补没有损坏的东西。3.最后一个也是比较特别的,它能平衡(Leverage )你已有的知识。你了解UNIX的文件和管道,没关系,你可以在Ruby中继续用,你用了两年的时间学习了printf 指示符,不必担心,Ruby中你也可以使用printf。你知道Perl的正则表达式处理,那么你也就学会了Ruby中的正则表达式。
    8. 它是创新的。是不是觉得这个和第七条矛盾了?也许是有一部分矛盾,每个硬币都有两面。一些Ruby的特点都是创新的东西,比如非常有用的Mix-in,也许这个特点会被后来的语言借鉴。(注:一位读者指出Lisp早在1979年就有mix-in了,这是我的疏忽;我应该找个更好的例子,并且能确信它。)
    9. 它是非常高层次的语言。(Very High-Level Language :VHLL) 这是一个容易引起争论的话题,因为这个术语还没有广泛使用。而且它的意思比起OOP来说还是有讨论余地的。我这么说,指的是Ruby能支持复杂的结构和这些结构的负杂的操作,而需要的指令非常少,这与最小努力原则(Principle of Least Effort)一致。
    10. 它有一个灵巧的垃圾收集器。 像malloc和free 这样的例程已经是昨天的恶梦了,你不需要什么回收内存的操作,甚至是调用垃圾收集器。
    11. 它是脚本语言。不要因为此就认为它不够强大,它不是一个玩具。它是完全成熟的语言,用它能轻松的完成传统的脚本操作,比如运行外部程序,检查系统资源,使用管道,捕获输出等等。
    12. 它是通用的。 Kornshell做的东西它也可以做,C语言做的东西它也可以做的很好。你可以用它写一个只运行一次的只有10行的程序,或者对一些遗留程序进行包装,你想写个web server,或者一个CGI,都可以用Ruby来写。
    13. 它是多线程的。 Y你可以用一些简单的API来写多线程程序,甚至在MS-DOS上都可以。
    14. 它是open source的。你想看它的源代码吗?可以,你也可以提交补丁,参加广泛的社区,包括它的创造者。
    15. 它是值觉得。 Ruby的学习曲线比较低,而如果你翻过了一个坎,你开始“猜测”事情是怎么工作的,而且你的猜测很多时候都是正确的。Ruby坚持最小惊讶( Least Astonishment)的原则。
    16. 它有异常机制。 像Java和 C++一样, Ruby 中也有异常机制,这意味着你不必因为返回值而将代码弄得凌乱不堪,很少的嵌套if语句,很少的意大利面条似的逻辑,更好的错误处理。
    17. 它有一个高级的数组类:Array。 Ruby中数组都是动态的,你不必像pascal那样在声明它的大小,也不必像C,C++那样为它分配内存。它们是对象,所以你不必关心它们的长度,实际上你不能"走到末尾(walk off the end)"。这个类提供了各种方法,使得你能够根据索引,根据元素来访问数组内容,也可以反向处理数组。你也可以用数作作为set,队列,堆栈等。如果你想用查找表,可以用哈希结构。
    18. 它是可以扩展的。 你可以用C或者Ruby来编写外部库(external libraries),同样,你也可以修改已有的类和对象。
    19. 鼓励文档编程(literate programming)。你可以在Ruby程序中嵌入注释或者文档,这些文档可以用Ruby的文档工具提取和处理。(真正的文档编程者可能认为这是必须的基本东西吧)
    20. 创造性的使用标点符号和大写字母。比如一个方法返回一个boolean型(Ruby中并没有这种说法),那么一般这个方法最后都以问号结尾,如果一个方法要修改接收者本身,或者具有破坏性,则用一个感叹号结尾,简单,直觉。所有常量,包括类名,都以大写字母开头,所有对象属性以@符号开头。这有匈牙利命名法的实用性,但是没有视觉上的丑陋性。
    21. 没有保留字。只要语法分析器看不出歧义,就允许用所谓“保留字”作为标识符。呼吸一下新鲜空气。
    22. 支持迭代器。这使得你可以给一个数组,list,tree等对象传递一个块,然后对它们的每个元素进行block调用。这个技术值得深入学习。
    23. 它的安全性。 Ruby借鉴了Perl中基于$SAFE变量的分层控制机制。这对于CGI程序来说非常有用,可以防止人们攻击web服务器。
    24. Ruby中没有指针。 像 Java一样,和C++不同,Ruby中没有指针的概念,所以免除了关于指针语法和调试的头疼。当然,这也意味着最底层的程序开发将会很困难,比如访问一个设备的控制状态寄存器;但是,我们可以用一个C库来调用。(像C语言程序员有时候要使用汇编语言一样,Ruby程序员有时候也要使用C语言来完成一定的任务)
    25. 它使得人们专注于细节。 Ruby中有很多同义词和别名,你也许不记得字符串或数组的长度是size还是length,没关系,它们任何一个都可以工作。对于Range来说,你可以使用begin 和end 或者使用 first 和 last,它们也都工作。你想拼写indices,结果写成了indexes,没关系,这两个都一样。
    26. 非常灵活的语法。方法调用时候括号可以省略,参数之间只需用逗号分割。类似Perl风格的数组定义可以让你不用全部使用引号和逗号定义一个字符串的数组。关键字return可以生路。
    27. 丰富的库函数。 Ruby提供了线程,socket,有限对象持久化,CGI,服务器端可执行的,数据库等其它库函数,还有对Tk的支持等。还有很多其它的库函数。
    28. 本身自带调试器(debugger)。在完美的世界中,我们才不需要调试器,但是这个世界不是完美的。
    29. 交互式执行。 可以用Ruby像Kornshell那样执行。 (这可能是本页最具争论的一点,我不得不承认,Ruby真的不是一个很好的shell。但我仍然坚持,基于Ruby的shell是一个不错的主意。)
    30. 它是简明的。 不像Pascal那样要求if后面跟着then,while后面跟着do 。变量不需要声明,它们不需要类型。返回类型不必指定,关键字return 可以省略,它将返回最后一个表达式的值。另一方面,它也不像Perl或者C那样复杂难懂。
    31. 它是面向表达式的(expression-oriented)。 你可以轻易的使用 x = if a<0 then b else c  这样的表达式。
    32. 语法砂糖(syntax sugar)。 (像Mary Poppins解释:一勺语法的糖能使语义被接受) 。如果你想对数组x进行迭代,可以用for a in x。你也可以用a+=b代替a=a+b,这都行。很多操作符其实在Ruby中都是方法,这些方法的名字比较直观,短小,有着便利的语法。
    33. 它支持操作符重载。如果我没有记错的话,早在很久之前的SNOBOL就提供了这个功能,但是直到C++它才变得流行。虽然它可能乱用而出错,但是这仍是一个非常不错的优点。另外Ruby自动定义操作符的赋值版本,比如,如果你重定义了+,那么,你同时得到了一个+=操作符。
    34. 支持无限精度的数字。 有人会关心 short, int, long吗,只需要使用 Bignum就行了,你可以轻松的实现365的阶乘。
    35. 有幂操作符。在很久以前,我们在BASIC和FORTRAN中使用它,然而当我们学习Pascal和C之后,我们才认识到这个操作符有多差劲。(我们被告知自己连它是怎么工作的都不知道-它使用了对数,迭代了吗,效率如何?),但是,我们真的关系这些吗?如果是,我们可以重写这个方法,否则,Ruby有非常好的* *(星号之间无空格)操作符可以用。
    36. 强大的字符串处理。 If如果你想查找,判断,格式化,trim,定界(delimit),interpose,tokenize,你可以自己选择随便用哪一个来得到你想要的结果。
    37. 规则很少引起异常。 Ruby的语法和语义比其它语言有条理,每种语言都有独特的一面,每条规则都会有异常发生,但是Ruby规则引起的异常就少的多了。
  • Rails系列教程(10):Linking Pages Together

    2008-02-02 16:02:38

    在最前面,我们先要谈一个有关MVC的话题:

    MVC中的controller是一个协调者,他可以协调多个view,同样也可以协调多个model,这样的结果就是:

    多个Model可以对应一个controller,同样多个view也可以对应一个controller 

     

    理解这个结构对开发是有很重要的意义的,注意,这仅仅表示M,V,C的对应关系,而不表示流程

     

     

    超级连接:

    为了说明问题,我们先来建立一个页面goodbye,大家应该很熟悉何如建立了吧,先建立action method,然后写好相应的template,这里的template这样写:

    <html>
    <body>
    <h1>Goodbye!</h1>
    <p>
    It was nice having you here
    </p>

    <a href="/say/hello">Go to Hello</a>

    </body>
    </html>

     

    注意,我们这里使用了一个超级连接,这样就把2个页面连接到一起了,注意href="/xxx/xxx"第一个xxx表示controller name,第二个表示action name。我们使用这样的URL连接将有一定问题,因为我们建立这样的URL的前提是系统约定好了使用这样的格式,而这样的约定是可以改变的,假如约定发生变化,一切都会变化,那我们所有页面的连接都要重新改过,为此,Rails提供了另外一种实现方式,调用 link_to method,这个method是一个helper method,使用方式如下:

     

    <p>Time to say<%= link_to "GoodBye!", :action => "goodbye" %></p>

     

    link_to这个method,这里带上了2个parameters,第一个是用于显示连接的文本,第二个表示连接的内容,这个parameter提供了action的名字,这样,连接也就生成了

     

    我们还可以用link_to弹出一个confirm,做法如下:

    <%=link_to "delete", :action=>"delete", :confirm=>"你确定要删除吗"%>

  • Rails系列教程(9):Hello, Rails!(2)

    2008-02-02 16:01:16

    前面说过<%=%>来生成动态内容,其实ERb还有一种很重要的方式就是<% ruby code %>,来生成动态内容,他们两者的区别在于<%%>不会返回一个值,并用字符串的形式显示出来

     

    <%%>解释并运行里面的ruby code,例如:

     

    <% 3.times do %>
    大家好!
    <% end %>

     

    另外介绍一个有用的方法---“h”,了解html的朋友知道,html语言里面存在一些字符,通过简单的输入得到(这样的字符在html里面被叫做特殊字符),比如输入:,在html里面要使用"&copy;",输入空格,要使用"&nbsp;",现在有了h这个method,我们只需要输入我们想要的字符(特殊字符)它会完成转化,例如,最常用的:

    <%=h(" 2006  www.xxx.com") %>

     

    特殊字符表下载

     

    我们继续向下面看:

    前面说过View和Controller有密切的关系,所以把它们绑定成为一个component(action pack),那么下面就是有关于他们如何紧密接触的一个方面:

     

    定义hello:

    class SayController < ApplicationController
      def hello
        @time = Time.now
      end
    end

    hello.rhtml

    <html>

    <body>

    <%= @time %>

    </body>

    </html>

     

    结果显示出时间,我们知道对于一个instance variable,它的作用域仅仅限制在class内,外部是不能够应用的,在template里面可以调用@time是因为:controller将它的instance variable注入到了对应的template中了,强调2点,第一,某个controller的instance variable只能在对于的template中访问,注意对应;第二,注入的方向是从controller到template,也就是controller中的instance variable可以在template里面访问

  • Rails系列教程(9):Hello, Rails!(1)

    2008-02-02 16:00:05

    不论是什么语言,hello world都标志着一个开始,同样,我们来看看Rails的Hello, Rails。我们前面说过,requests被解析,这样就可以找到controller,并且调用controller里面的action method

     

    开始行动:

    我们建立好了demo app,同样也要建立controller,还记得我们前面用于启动WEBrick使用到的scrīpt吧,现在建立controller可以使用另外的一个scrīpt:generate

     

    1)建立SayController

    D:\demo>ruby scrīpt\generate controller Say
          exists  app/controllers/
          exists  app/helpers/
          create  app/views/say
          exists  test/functional/
          create  app/controllers/say_controller.rb
          create  test/functional/say_controller_test.rb
          create  app/helpers/say_helper.rb

     

    除了这个scrīpt建立一个controller,还可以通过destroy scrīpt来删除一个controller,比如:

    ruby scrīpt\destroy controller say

     

    这个时候我们最关注的是 say_controller.rb,它在app目录下面,controllers文件夹里面,controller用于处理进入的requests,每个request被发送到application,然后application回应这次的request,这是一个简单的模型(request and response model),现实的实现不会这么简单,我们看一个URL:

    什么是app和controller的分界线?我们在建立app的时候,就给与了一个名字,比如 rails demo,这个时候demo就是我们的app的名字,所以,在demo(包括demo)之前的一串,都是用于标识app的,紧紧的接在app后面的就是controller name,之后的就是action name

     

    添加action

    我们知道action是一个特殊的方法,一个controller里面不单单就是一个action,为了处理很多不同的requests,需要使用不同的action,添加一个hello action:

    class SayController < ApplicationController

      def hello                                    # 新建立的hello action

      end

    end

     

    大家看看下面的图解,就能很清楚的了解:

    每次request来的时候,都会生成一个controller的instance(WEBrick,Mongrel都会)

     

     

    总结一下URL对于app的映射:

    XxxController 对应 Xxx

    Xxx (action method) 对应 Xxx

    Xxx (app) 对应Xxx

     

    结果就是: http://app/aaa/bbb

    aaa对应 AaaController

    bbb对应 method bbb

     

    其实更加复杂的情况是建立这样的controller:建立EatApple,这就涉及到一个命名的问题,ruby约定的命名,除了类名其他的用"_"分开,这就使得我们建立起来的文件名(controller的文件名)为:eat_apple,但是Controller为:EatAppleController,我们使用这样的方式访问 http://localhost:3000/EatApple/hello 结果:

    Routing Error

     

    Recognition failed for "/EatApple/hello"
    

     

    使用 http://localhost:3000/eat_apple/hello 结果可以正确访问

    这也就是说明,实际上,URL对应的是controller的文件名字,而不是class name

     

    对于action method就没有什么疑惑了,因为不存在命名的问题

     

     

     

     

    现在看看我们的app,输入:http://localhost:3000/say/hello

     

    假如没有定义hello action,结果会显示:

    Unknown action

    No action responded to hello

     

     

     

    我们定义一下hello,结果为:

    Template is missing

    Missing template ./scrīpt/../config/../app/views/say/hello.rhtml

     

    因为我们为hello定义的仅仅是一个空方法,没有任何内容。在我们身成controller的时候,也在view下面生成了一个文件夹say,这里显示Template is missing是因为在say目录下没有找到hello.rhtml。默认情况下面,xyz action method 会自动寻找 view 下面的对应的controller下面的xyz.rhtml。所谓的rhtml文件其实就是类似jsp文件,这样的文件,里面包含html代码,我们写一个hello.rhtml:

    <html>

      <head>
     <title>Hello,Rails</title>
      </head>

      <body>

         <h1>Hello from Rails!</h1>

      </body>

    </html>

     

    结果如下:

     

    即使我们没有定义action,仍然可以访问有关页面,比如说,我在view\say 下面建立了一个 a.rthml,那么就可以通过 http://localhost:3000/say/a来访问,当然这个似乎是有点不太规范的做法

     

    ERb:我们前面说过通过templates可以动态改变内容,其中templates的一种产生动态内容的方法就是ERb

    ruby代码被放入 <%= ruby code %>中,这些代码将被解释并且运行,结果被转化成为字符串显示在<%= ruby code%>出现的地方,例如:

     

         <ul>
            <li>Addition: <%= 1+2 %></li>
            <li>Concatenation: <%= "cow" + "boy" %></li>
            <li>Time in one hour: <%=1.hour.from_now %></li>
         </ul>

     

    注意 1.hour.from_now 表示的是从现在开始的以后1小时后的时间,这里会显示 “Wed Aug 02 11:05:09 中国标准时间 2006 ”,这就牵涉到一个中文问题,有一种解决办法就是在controller下面的application中加入:

    class ApplicationController < ActionController::Base
      before_filter :set_charset                     #symbol object,只要下面defined method name和这个一样就可以了

      def set_charset                                   #和上面的symbol object一致
        @headers["Content-Type"] = "text/html;charset=gbk"
      end
    end

     

    这里是要注意的,设置charset为gbk使得API可以显示出中文

     

    ERb is a filter,所以有before_filter。所有的 filters 被放入一个数组中,这个array被叫做the before filter chain,这类的filters,将在action被调用之前运行,添加这样的filters 的方法就是 (1)使用before_filter method添加一个symbol object到数组,这个symbol就说filter的name (2)定义这个filter

     

    @headers["Content-Type"] = "charset=gbk",这里使用instance variable headers来设置Content-Type

     

    我们在添加code的时候,不需要重启服务器,在开发模式(development mode)中,服务器会运行最近更新的文件

  • Rails系列教程(8):Creating a New Application

    2008-02-02 15:58:31

    现在是一个开始,通过实际的操作让我们更加深入的了解Rails:

    Rails使我们仅仅需要“必要”的配置,而不是想其他的工具一样,要完成很多没有必要的配置,我们用rails产生一个完整的app结构,而不要我们自己定义,在适当的位置填充内容,就是程序员的工作了。注意我们前面说的用rails产生一个完整的app结构,其实是这样的,rails在被安装以后,我们就得到了一个命令行工具(command-line tool):rails,通过这个tool我们可以建立一个标准的rails app

     

    具体操作:

    使用Rails创建app,我们要建立一个名为demo的app

    >rails app

          create
          create  app/controllers
          create  app/helpers
          create  app/models
          create  app/views/layouts
          create  config/environments
          create  components
          create  db
          create  doc
          create  lib
          create  lib/tasks
          create  log
          create  public/images
          create  public/javascrīpts
          create  public/stylesheets
          create  scrīpt/performance
          create  scrīpt/process

          .

          .

          .

    demo的目录结构:

    [app]        [components]       [config]      [db]
    [doc]        [lib]                       [log]           [public]   

    [scrīpt]     [test]                     [tmp]         [vendor]

    Rakefile     README

     

    刚刚开始我们不用关注太多了目录

    1)public目录

    public目录里面的内容暴露在end users的面前(end users:使用我们app的用户),这个目录关键的文件是dispatcher:dispatch.cgi , dispatch.fcgi , dispatch.rb

    dispatchers:接受end users通过browers发送来的requests,并且指引requests进入我们的app

     

    2)scrīpt目录

    scrīpt目录里面有一个重要的scrīpt为server,我们这样启动server:

    >ruby scrīpt\server(我这里使用的是rails 1.1.4,到2006年7月29日的最新版本)

    这样我们就启动了web server,这个web server叫做WEBrick,其实我们会有更好的选择(见本blog《Mongrel---Faster Is Possible》),但是作为学习,WEBrick很不错

     

    注意我们这里启动的时候使用的是ruby scrīpt\server

    假如(windows下)这样启动WEBrick

    cd scrīpt

    ruby server

    结果是可以启动的,在browers里面输入http://localhost:3000/进入测试

    但是点击上面的 About your application's environment 运行就会出现错误:

     

    正确的运行应该是这样的(必须这样启动WEBrick--->ruby scrīpt\server):

     

    本来以上的内容都已经在以前的文章中强调过,但是,为了让一些没有读过之前文章的朋友不致于迷惑于此,再次说明一下,上图也显示了在winxp sp2下面笔者安装的各个信息,请在学习本文的时候,确定版本差异不大

     

     

    WEBrick运行之后显示如下信息:

    [2006-07-29 16:40:08] INFO  WEBrick 1.3.1
    [2006-07-29 16:40:08] INFO  ruby 1.8.4 (2006-04-14) [i386-mswin32]
    [2006-07-29 16:40:08] INFO  WEBrick::HTTPServer#start: pid=3552 port=3000

    注意到最后面的信息 port=3000,3000的端口号是默认的,所以我们可以通过 http://localhost:3000/来访问

    开始说了,在学习的时候使用WEBrick还是不错的,因为WEBrick提供了一个控制台,这样我们可以清楚的看见输出的信息,有些web server,比如Mongrel是在后台运行,我们要知道发生了什么事情,就是比较麻烦的事情,我们可以很容易的输出信息到控制台上面,以后就会看到,想要停止服务器,可以通过control+C

  • Rails系列教程(7):Rails and Databases

    2008-02-02 15:56:35

    Rails可以工作在DB2,MySql,Oracle,Postgres,SQL Server and SQLite databases.除了MySql之外的其他数据库,你必须安装database driver,它一个library,rails通过这个library可以连接和使用database,MySql database driver是纯ruby写的,但是大多数的driver都是用C语言写的,各个database的安装都相似,只是SQL Server有一些不同,如果有需要,请读者自行查阅资料,以后的内容都是基于MySql数据库的
  • Rails系列教程(6):Action Pack

    2008-02-02 15:55:10

    controller and view :
    controller and view 有密不可分的关系,controller为view提供data,controller接受由view产生的页面发出的events

    因为controller和view的密切关系,rails把他们绑定成一个component---Action Pack

     

    View Support
    动态内容的生成:使用controller里面的action method(action也用来控制requests的去向),templates也同样用于产生动态内容,这里主要说明一下templates产生动态内容的2种方法:

    1)ERb

    在view中直接嵌入ruby code,但是有些人认为这是违背了MVC的精髓(the spirit of MVC),因为这使得我们有可能在view中添加本来应该在controller和model中的逻辑(logic),不过维护MVC,是程序员的工作

     

    2)builder-style

    builder-style:使用ruby构建xml文档,生成的xml的结构将自动的遵循代码结构(the structure of the generated XML will auto-matically follow the structure of the code)

    以后以上的内容都会有具体的实现

     

     

     

    Controller:

    controller是application的逻辑中心,rails把问题呈现出来,把复杂的实现隐藏在后面,这样使得application容易开发和维护,做application所关注的不单是开发效率,还有一个重要方面就是维护

     

    controller的一些其他的重要的作用:

    1)它能将外部requests表示为内部的actions,能够很好的处理对人友善的URL

    2)管理caching

    3)管理help modules,它用于扩展view-templates的能力

    4)管理sessions

  • Rails系列教程(5):Active Records

    2008-02-02 15:54:00

    Active Records : 被应用于ORM layer


    ORM model : tables map to classes, rows to objects, and columns to object attributes
    还是要强调一下,a row maps a object, a table maps a class, a column maps a attribute

     

    Ex:

    require 'active_record'

    class Order < ActiveRecord::Base
    end

    # work on business logic

    order = Order.find(1)    # fetch the order with an id of 1
    order.discount = 0.5
    order.save

     

    很显然,这样的实现很直观,不需要考虑到内部如何连接数据库,如何使用SQL语句,一切都变得高效,快速(alige)

  • Rails系列教程(4):Object/Relational Mapping

    2008-02-02 15:52:12

    ORM:ORM libraries 是把 database tables 与 classes间产生一种mapping(映射),并不像ORM本身表示的Object /Relation Mapping,不是对象和关系的映射,在概念上来说,Relations 更像是 Classes

     

    具体来说说什么是mapping:

    假如database 有一个table---orders,那么就会有一个类Order与之对应,我们知道,database约定了table name使用复数,而类不需要,所以,这里才会有单复数的区别,这个不是当当为了满足程序员的习惯,同时是为了满足数据库设计人员的习惯(关于数据库命名《Database Principles Programming and Performance》一书中有提到)

     

    术语的对应关系:

    object --- row

    attributes用于set,get column

     

    Rails使得我们从table-level 操作转化到 class-level。

    这样对描述毕竟还是很难了解一些深度的东西,下面举例:

    order = Order.find(1)

    puts "Order #{order.customer_id}, amount=#{order.amount}"

     

    我们可以清楚看到前面说过的,order(object)对应的是一些行,这里Order这个class method返回一个order object,这个object可以使用attributes,来access一些columns,注意这里的object可以对应row,也可以对应rows,比如 ōrder = Order.find_al,但是rows不能通过一个object(对应row的)来保存,所以这里Class method返回的是一个collection,这个collection里面保存的是Order类型的object,为了更加详细的说明collection,这里有一个更加容易理解的例子:

     

    读取数据:

    Order.find(:all, :conditions => "name='dave'") do |order|

      puts order.amount

    end

    写入数据:

    Order.find(:all, :conditions => "name='dave'") do |order|

      order.discount = 0.5

      order.save                              #method save()

    end

     

    #the objects corresponding to individual rows in a table

     

    总结就是一句话:

    So an ORM layer maps tables to classes, rows to objects, and columns to attributes of those objects.

    注意,全部的对应关系,都是1对1,一个class对应一个table,一个row对应一个object,一个attribute对应一个column

  • Rails系列教程(3):Database-centric Programming

    2008-02-02 15:50:47

    执行SQL语句的做法通常有2种:1)在程序里面嵌入sql代码(embed)2)使用preprocessor

     

    1.ruby中使用嵌入sql语句的做法:

    def update_sales_tax

      update = @db.prepare("update orders set tax=? where id=?")

      @db.select_all("select id, amount from orders") do |id, amount|

        tax = calc_sales_tax(amount)

        update.execute(tax, id)

      end

    end

     

    其实这样的做法已经比java的做法简单很多了,更不用说是c语言对sql的操作,这种特性不是来自于rails,而是来源于ruby,当然作为层次型语言对平面型数据库的操作,本来就有很多不好的地方,其实,最好的解决应该是把数据库转化成层次型,这样更加直接,方便,能够更好的应对需求变更,但是现在的层次型数据库毕竟还没有兴起,只能看看以后的发展情况了

     

    但是这样的写法实际上是不好的,我们可以看见程序里面有sql,sql被涵盖在程序的可能出现的角落,结果application logic和database logic纠缠到了一起,这样的纠缠带来了错综复杂的关系,有可能造成了这样一种情况,修改了表里的一个属性项,需要修改很多程序的地方,而且这些地方不一定是明显的,这样的方式把数据暴露出来,就像我们使用继承的时候,使用class instance variables而不去使用方法是一样的,结果是恶劣的,当然这样的行为影响的是维护(maintain),同时我也相信,世界上没有系统一次就能开发完善以致于不需要任何维护。其实这也包含了一个复用和封装的问题,一段代码,如果以非methods的形式出现,而是通过copy来实现,那么这样的拷贝会带来很多的麻烦

     

    记住,封装(encapsulation)是解决重复代码的一个很有效的方法,encapulation解决的是:一处修改,处处更新

  • Ruby 基础教程

    2008-02-02 15:48:38

    Ruby 基础


    作者:Ralf Wirdemann, Thomas Baustert

    www.b-simple.de

    2006.2.14


    1.1 在线文档和书籍

        语言资料上,如果要找核心和标准API的文档可以去 www.ruby-lang.org,深步进阶我们推荐下面的书籍:

    Dave Thomas: Programming Ruby, Second Edition, Pragmatic Bookshelf, 2005
    Ruby书籍中的标准著作,极具推荐价值。
    Hal Fulton: The Ruby Way, Sams, 2001
    这部书不仅仅有Ruby语言的最新状况,还提供了一百个案例,很有意思。

    1.2 引言

        Ruby 是一个纯粹的面向对象的动态型语言。Ruby 程序不是被编译成二进制格式(如Java),而是直接由一个解释器来处理。这门语言在1995年由松本行弘(Matsumoto Yukihiro)发布,除了 Smalltalk、Python 等语言以外,Perl 对它的影响是首当其冲的。
        Ruby 里的一切都是对象,它没有原始类型(如Java)。除了面对对象,Ruby还提供了垃圾回收、异常、正则表达式,为迭代器和方法作参数的“代码块”,运行期的类扩展,线程及更多的东西。Ruby 语言易懂易用,原因在于它简单、语法干净。

    1.3 Ruby 程序

        Ruby 程序保存在以 .rb 结尾的文件里。程序内容包含类、模块、或者只是简单的Ruby代码。下面是地球人都知道的 Hello World 程序:

    # hello.rb                        
    puts "Hello World!"               

        如果这个代码是以 hello.rb 的文件名保存的,那么可以这样调用:

    > ruby hello.rb                   
    > Hello World!                    

        在Windows下允许您以文件关联的方式在IE中执行;在 Linux/Unix 下您可按照自已的操作系统情况使用 Shebang行:

    #!/usr/local/bin/ruby             
    puts "Hello World!"               
                                      
    #!/usr/bin/env ruby               
    puts "Hello World!"               

        随后直接执行这个程序:

    > chmod 744 hello.rb              
    > ./hello.rb                      

        Ruby 的语句可以以分号结尾,但不是必须。 Ruby 是一个面向行的语言,也就是说,一个语句不需要以分号结尾,解释器能够明白下一行是进一步的语句。下面前几个语句的输出相同,最后一个是错误的:

    puts "Hello World!";              
    puts "Hello World!"               
    puts "Hello " \                   
       "World!";                      
    puts "Hello" +                    
       "World!";                      
    puts "Hello"   # 语句至此结束     
       + "World!"; # 无法被解释的新语句

        多个语句可以用分号隔开写到一行里面,这不是很地道的写法,因为会影响阅读的流畅。

    # 可行,但不漂亮:                
    a = 42; b = 15; c = a + b        

    #这样比较好:                     
    a = 42                           
    b = 15                           
    c = a + b                        

        Ruby 以两个空格作为缩进的方式(不用Tab键),这是个推荐习惯并且应该尽可能地得到遵循:

    # 非常棒的缩进                   
    while line = file.readline       
      if !comment_line(line)         
        lines.add(line)              
      end                            
    end                              

    # oh~,oh~,您这是和外星人学的吧 
    while line = file.readline       
        if !comment_line(line)       
            lines.add(line)          
        end                          
    end                              

        Ruby 提供了全系列的标准类型,如数字、字符串、正则表达式、数组、哈希(Hash),等等。所有这些元素通过类和模块的方式以备使用,无需在程序(文件)中绑定,它们来自于核心库并在整个程序中自动生效。
        另外一些是以其它类和模块构成系列的标准库方式以备使用的,例如 Date、Logger、Test::Unit 等等。一旦要自行开发就必须在每个程序里通过关键字 require 来明确绑定。于是在 require 后加上或有或无结尾(.rb)的文件名。

    require "date"  # date.rb 用到 Date类              
    require "my_class" # my_class.rb 用到MyClass类     
    require "my_module" # my_module.rb 用到 MyModule模块

        对此处文件名的处理不是按绝对路径,Ruby 会在所有的标准目录下寻找这个文件,它们被包含在全局变量$: 里面,您可以在命令行用 ruby -e "puts $:" 或其它方式得到这些路径。一个装载了类和模块的程序里的所有名字都可以通过全局变量 $" 输出。

    1.4 注释

        在 Ruby 中注释行是以#号开始的,注释可出现在句首或句尾。

    # 下面这行是被注释掉的           
    # a = b - c                      
    a = b + c # 注释到行尾           

        一个注释块开始于 =begin 结束于 =end,这几个关键字必须在行首,不能有空格。

    =begin                           
      def my_method                  
        ...                          
      end                            
    =end                             

    1.5 数字

        Ruby支持整数和浮点数。Ruby 里面没有原始类型,都是数字对象。整数从负2的30次幂到正2的30次幂 (在64位机器上从负2的62次幂到正2的62次幂) 被定义成FixNum类型超出此范围的整数被定义成BigNum类型,类型的划归与转换是自动完成的,一个数字的长度最终由主存来判定。

    value = 42 # FixNum                               
    big_value = 123456789012345678901234567890 # BigNum

        数字可以用16进制、8进制或者2进制的数字系统来表示:

    # 42                             
    0x2A                             
    0052                             
    b101010                          

        有相应的数学运算符以供使用,数字的增减用运算符 += 与 -= 来实现,出自 C 和 Java 的 ++ 与 -- 在 Ruby 里是没有的。

    a = 2                            
    b = 3                            
    c = a + b    # 5                 
    c = a - b    # -1                
    c = a / b    # 0                 
    c = 2.0 / b  # 0.666666666666667 
    c = a * b    # 6                 
    c = a**b     # 2*2*2 = 8         
    a += 1       # a = 3             
    a -= 1       # a = 2             
    a++          # Ruby里非法        

        FixNum与BigNum 继承于基类 Integer,以下是可用的函数,是与块(见1.14节)结合的:

    1.upto(3) { [i] puts i }    # 1 2 3             
    3.downto(3) { [i] puts i }  # 3 2 1             
    0.step(10,2) { [i] puts i } # 0 2 4 6 8 10      
    3.times { puts *42* }       # 42 42 42          

        浮点数在 Ruby 里是用 Float 类来表示的。像其它语言一样,Ruby 里的浮点也有卷折误差。为了计算精确(如 合值),建议使用 Ruby 标准库里的 BigDecimal 类,相对于 Float,这个类描述了更为丰富的浮点数并且避开了卷折误差。

    1.6 字符串
     
        在 Ruby 里,字符串被放置在两个单引号或双引号之间。引号常会出现在另一个引号里面:
     
    str = "Hello"          # Hello
    str = "Hello 'Thomas'" # Hello 'Thomas'
    str = 'Hello'          # Hello
    str = 'Hello "Thomas"' # Hello "Thomas"
     
        字符串可以通过 %q 和 %Q 产生,这是为了避免当一个字符串内出现过多的引号或其它符号时会出现这样或那样的错误。%q 产生一个被包含在单引号中的字符串,%Q 产生一个被包含在双引号中的字符串,文本以分隔符为界来限定,分隔符可以是除字母与数字以外的所有符号。

    %q{a string}
    %q(a string)
    %Q$a string$
     
        %Q 可以替换 #{Ausdruck}这样的表达式,而%q不能:
     
    表格1.1 字符串中带双引号的逃脱符 \a 响铃
    \b 退格
    \e 逃脱
    \f 换页
    \n 换行
    \r 回车  \s 空格
    \t Tab
    \v 垂直表格跳位
    \nnn 八进制
    \xnn 十六进制 
    \cx Control-x  \C-x     Control-x
    \M-x     Meta-x
    \M-\C-x  Meta-Control-x
    \x       x
    #{code}  code 


    puts %q{result: #{42.0/3} EUR}     # result: #{42.0/3} EUR
    puts %Q{result: #{42.0/3} EUR}     # result: 14.0 EUR
     
        在花括号、圆括号、角括号的情况下字符串是括号括起来的部分,其它则是到那个符号再次出现为止。字符串也可多行显示,Ruby 在此并不清除起始空格符。
     
    s = %Q@ein String ber mehrere 
     Zeile mit "" und '' und durch
      einen Klammeraffen begrenzt@
    puts s
    =>
    ein String ber mehrere 
     Zeile mit "" und '' und durch
      einen Klammeraffen begrenzt
    puts s.inspect
    =>
    "ein String \374ber mehrere\n Zeile mit \"\" und '' ...
    ...und durch \n  einen Klammeraffen begrenzt"
     
         一个表达式的结果可以通过#{Ausdruck}插入到一个字符串中,然而这只有在双引号之间有效。
     
    "Ergebnis #{42*7/100} %" # Ergebnis       #{2.94} %
    "Die Anwort ist: #{answer(x)}"            # Die Anwort ist: 42
     
        如C与Java所惯用的,特殊符号由斜杠逃脱,表1.1列举了所有的特殊符号:
     
    "Ein Slash: \\"              # Ein Slash: \
    "Er rief: \"Ralf!\""         # Er rief: "Ralf!"
    'War\'s okey?'               # War\'s okey?
    "Neue\nZeile"                # Neue
                                 #Zeile
    'Neue\nZeile'                # Neue\nZeile
     
        两个字符串的内容可以用 == 方法来比较,与之相对照的是 equal? ,它用来判断是否是同一个字符串实例(参看1.15.5节):
     
    s1 = "Thomas"
    s2 = "Thomas"
    s3 = "Ralf"
    s1 == s2 # => true
    s1 == s3 # => false
    s1.equal? s1 => true
    s1.equal? s2 => false
    s1.equal? s3 => false
     
        字符串可以用+和<<这两个方法连接。使用 * 可实现多重累加。
     
    "Thomas" + "/Ralf"    # Thomas/Ralf
    s = "Thomas" 
    s << " und Ralf"      # Thomas und Ralf
    "Ralf " * 2           # Ralf Ralf
     
     
        字符串类提供了大量的方法,让你随心所欲,下面是几个例子;
     
    s = "Thomas und Ralf"
    s[3]                  # 109
    s[3].chr              # m
    s[7,3]                # und
    s[0..6]               # Thomas
     
    "Thomas und Ralf".delete("a")         # Thoms und Rlf
    "Thomas und Ralf".delete("aou")       # Thms nd Rlf

    "Thomas und Ralf".gsub("und", "oder") # Thomas oder Ralf
    "Thomas und Ralf".gsub(/[aou]/, "$")  # Th$m$s $nd R$lf
     
    "Thomas und Ralf".index('a')          # 4
    "Thomas und Ralf".index('a',5)        # 12
     
    "Thomas und Ralf".split               # ["Thomas", "und", "Ralf"]
     
        为字符串转换成整数和浮点数提供两种途径,安全一些的是使用核心方法 Integer 和 Float, 它们在出错的时候抛出异常; 另一种是 String 类的方法 to_i 和 to_f,限制较少 。


    "42".to_i            # => 42
    nil.to_i             # => 0
    "42x".to_i           # => 42
    Integer("42")        # => 42
    Integer(nil)         # => 0
    Integer("42x")       # => 参数错误

  • Rails系列教程(2):Models, Views, and Controllers

    2008-02-02 15:41:32

    一个应用(application)可以被拆分为3部分:

    1.models

      用于维护 the state of the application,通常我们可以理解 states 就是数据,这个概念和OO编程中的states相似,一种维护state的途径就是通过一定规则存取(access)数据通过数据库(database)。注意我们这里说的是维护,不是存取,因为,通常的数据处理过程并没有我们想象的那么简单,并不是取钱以后就减去相应金额,因为我们要保持余额大于0,这就产生了一条Rule,所以models应该是:

    models用于存取数据(从某种介质里存取,比如databases)
    models用于约束数据(通过某种规则约束,比如商业规则)

     

    2.views

      用于产生(generate)user interface,经常我们会把views和models结合起来,views的界面信息,部分基于models,注意,views是不处理数据的,只是呈现数据给用户,当然实际上,我们可能会用一些语言比如:javascrpit来约束数据,因此实际应用中,并不会这么死死的遵循MVC的规范。views和models并不是一一对应的关系,这对我们理解编写rails applications有重要的影响,比如一个views可能对应很多的models,就像一个网站的首页一样,会在很多tables里面调用信息,再如一个models可能对应很多的views,也就是说,一个models可能在一个页面显示,也可以在其他页面显示一样。这是灵活的

     

    3.controllers

     作为views和models的连接体,可以认为controllers是分治哲学里面的"治",可以认为controllers是一个乐队的指挥一样,协调MV工作

     

     

    MVC图解:

    *大家仔细看,会发现view和model间使用虚线,说明什么问题?说明view和model是不能直接通讯的,必须通过controller作为中间体

    MVC不仅仅是用于web的一个architecture,而是可以用于所有的applications,而且事实上MVC最早是用于GUI applications

     

    我们为什么要用MVC,即使不用MVC,特别在GUI applications,假如你仅仅做一些小型的applications,估计你根本不会去用,甚至很多人也不懂MVC,那为什么要用MVC?MVC可以使得程序更容易编写和维护,rails就使用MVC,使得程序非常容易编写。其实除了rails很多architectures比如:WebObjects, Struts, and JavaServer Faces都是用用MVC

     

     

     

    Rails architecture:

    外来的requests首先被发送到一个router,router首先解析requests,然后解决requests将被发送到什么地方的问题,这时候一个叫action的特殊method来控制requests的去向,action位于controllers,action着眼于requests的data,action可能和models互交,也可能调用其他的actions,或者影响views的表现,过程如图:

     

    为了说明问题,我们来看URL http://my.url/store/add_to_cart/123

    store : controller的名字
    add_to_cart : action的名字

    其他部分我们不用详细的追究它们的含义,而且过早的了解,也只能带来疑惑。这里强调一下URL,注意的是my.url后面第一个一定是controller name,而第二个一定是action name。

    因为URL的关系,router在接受requests的时候他会知道action在哪里,因为URL被规范了,所以上例中router很清楚的知道,request要被传递到StoreController这个class里面的add_to_cart这个action里,分析URL的过程就是我们前面说过的router工作之一,对requests的解析问题,解析完成,就需要把requests的相关内容传递到action里面,action在选择处理的方式,即前面提到的3种处理。注意到这里有些语句是含糊的,因为现在的知识不能让我们马上铺开全局进行讲解,这里只是描绘大概的轮廓,以便以后了解细节做好准备

  • Rails系列教程(1):Introduction

    2008-02-02 15:39:52

    1.Rails is a MVC architecture

      Rails在你身成 web app 的时候,已经帮你构造好了 MVC 骨架(skeleton)

     

    2.Rails的2条准则:

      1)DRY---don't repeat youself (尽力不要写重复的代码)

      2)convention over configuration (习惯优于配置)

     

    3.Rails包含很多有价值的东西:

      1)引入AJAX等外部支持

      2)提供完整的unit testing framework

      3)完全分离的开发,测试和发布

     

    4.Rails更加接近于问题域而不是计算机

     

    Dave喜欢Rails的10条理由:

    1. It brings agility to web development.

    2. I can create web pages with neat effects, just like the cool kids do.

    3. It lets me focus on creating the application, not feeding the frame-work.

    4. My applications stay maintainable as they grow.

    5. I get to say “Yes” to clients more often.

    6. Testing is built-in (and easy), so it gets used.

    7. Instant feedback: edit the code, hit Refresh, and the change is in my browser.

    8. Metaprogramming means I can program at a really high level.

    9. Code generators let me get started quickly.

    10. No XML!

  • ruby系列教材(26):More about methods(3)

    2008-02-02 15:32:28

    大家感兴趣的东西来了---Collecting Hash Arguments

     

    一切都来源于Hash,使用 Hash Object 作为 Arguments,例如:

    def test(a,b)

      #...

    end

     

    我们可以这样用:

    test("hi",{"no1"=>"good",

    "no2"=>"bad"})

     

    这里的括号让人看的很不舒服,ruby允许在调用method的时候,去掉hash object的括号:

    test("hi","no1"=>"good","no2"=>"bad")

    注意不要被表明的东西迷惑,这里并没有3个arguments,而是2个,所有的类似表达于 a=>b 都会被收集起来,作为一个hash object

     

    其实大家喜欢使用 symbol object 作为 key,这样,我们在Rails中常见的method就出现了:

    find(:all,:limit=>5,:order="id desc")

     

    为了说明问题,这里特别举例:

    def test(opts={})
      "#{opts[:name]}---#{opts[:des]}"
    end
    puts test(:name=>'kc',:des=>'I like ruby')
    puts test(:des=>'I like ruby',:name=>'kc')

    结果:

    kc---I like ruby
    kc---I like ruby

    这个例子给我们提供了一个清晰的映象,更加要注意的是,hash 中的 key-value 对交换顺序,结果也一样,这就说明了,当参数为hash object 时,我们可以不考虑顺序问题

  • ruby系列教材(26):More about methods(2)

    2008-02-02 15:31:27

    Methods and Blocks:

    我们前面谈到了blocks的调用方式,先检查method and arguments,然后开始运行method,当出现了yield statement的时候开始转到block,这就是说,假如有yield,就必须有block

     

    1.常见用法:

    def take_block(p1)

      if block_given?

        yield(p1)

      else

        p1

      end

    end

     

    take_block("block")

    take_block("block"){|s| p s}

     

    2.非常见用法:

    我们前面谈到,在parameter前面加上一个*,表示在此method中,生成一个同名(不含*)的Array Object,同样的在parameter前面加上一个&,表示在此method中,生成一个同名(不含&)的Proc Object,什么是Proc Object?我们这里介绍一下它的call method:

    a_proc = Proc.new {|a, *b| b.collect {|i| i*a }}      # new产生一个object,new后面接一个block
    a_proc.call(9, 1, 2, 3)   #=> [9, 18, 27]                 # 根据上句的block进行运算,返回值放入一个Array Object

    总的来说Proc class就是,使用block进行初始化,call method传递arguments到block中,返回值为block的返回值

     

    class TaxCalculator

      def initialize(name, &block)

        @name, @block = name, block

      end

      def get_tax(amount)

        "#@name on #{amount} = #{ @block.call(amount) }"

      end

    end

    tc = TaxCalculator.new("Sales tax") {|amt| amt * 0.075 }

    tc.get_tax(100)→"Sales tax on 100 = 7.5"

    tc.get_tax(250)→"Sales tax on 250 = 18.75"

     

     

    self 和 ::

    self 使用在methods里面,表示引用此method的object(或者说是receiver),前面的文章有提到过,并带有详细的例子

    ::的含义,这里我们只做简单的介绍,用法 ModuleName::ModuleName::ClassName 或者 ClassName::CONSTANTNAME。先来说说Module,简单的可以认为是class的集合(远远不止这些)。使用Test::Unit::TestCase 就是Test(模版名),Unit(模版名),TestCase(类名),经常在Rails中看见:class ApplicationController < ActionController::Base ,就是说ActionController module中的Base Class。除此之外,还有就是可以使用ClassName::CONSTANT,比如Math::PI。以后会设计到更加复杂的内容

     

     

    return的用途

    如果一个method有返回值,我们通常都这么做:

    def test

      "hi"

    end

    因为这样简洁方便,那是不是return就没有了用出了?其实return可以这样用:

    def test

      return "hi","hi2"       #必须使用return,否则报错,即不能省略return

    end

    这样一个method可以返回多个值,返回的结果会使用Array Object保存起来

     

     

    数组与arguments

    前面说过在定义的时候使用*args,可以提供一个可变argument lists,现在,在使用方法的时候,数组可以转换成argument lists,比如:

    def test(a,b,c)

      puts "#{a} #{b} #{c}"

    end

    arr = [1,2,3]

    test(*arr)              #星号不能少

    结果:

    1 2 3

     

    调用method的时候,在Array Object前面加上*,作为arguments,实际上是把Array Object中的elements作为arguments

  • ruby系列教材(26):More about methods(1)

    2008-02-02 15:30:25

    本部分的内容对学习Rails有很大的意义,Rails中最常见的使用methods的方式:

    redirect_to(:action => 'index')

    start_form_tag(:action => "save_order")

    这里symbol object 还有 => 符号到底是什么意思?以及::这个符号的含义,本篇将会涉及

     

    Method names

    1.使用def关键字定义一个method

    2.method name使用小写字母

    3.method name可以使用?结尾,返回值为一个TrueClass或者FalseClass类型,意义上表示疑问,例如:

    p [].empty?                # 意义:[]这个数组是空的吗?

    结果:

    true

     

    def is_zero?(num)       # 带参数
      num==0
    end
    p is_zero?(1)
    结果:
    false

     

    4.method name可以使用!结尾,表示这个method有一定的危险性,所谓的危险性就是method可以造成数据的丢失,!和?更多代表的是意义上的东西,而非语法上的东西

    a = "I am here"
    p a
    a.chop!
    p a
    结果:

    "I am here"
    "I am her"

     

    我们可以见到这样的情况,比如有些class有empty?这个method,用来判断是否非空,也有empty!这个method用来清空数据,这样的方式是让人很乐意见到的,表达起来也很舒服

     

    5.method name后面可以加=,注意这将有点不同于前面说过的加!,?这么简单,除了意义上层面之外,还有语法层面的东西

    回忆一下前面谈到的accessor methods(又叫attribute methods,简称attribute),accessor methods用于存取instance variables,对于reader attribute应该没有任何疑问,因为它就是一个普通的method,只是我们习惯上把它命名成为instance variable name而已,但是对于writer attribute,我们就应该注意了,它形如:

    class T

      def initialize(t)

        @text = t

      end

      def text=(t)

        @text = t

      end

    end

    使用的时候如下:

    t = T.new

    t.text = "hi"     #text和=间有空格

     

    也就说method name可以用=结尾,使用的时候=和前面的部分可以分开,这就是=于!,?在语法层面的差别(这是一个人性化的设计)。

    在意义层面上说,accessor name要和它对应的instance variable相同(不要@符号),表示对有关vairable的存取

     

    补充一下,我们同一的叫=,?,!等为suffix(后缀)

     

    Method parameters

    1.default values(默认值)

    一个method可以为其parameters设定默认值,比如:

    def test(args1="one",args2="two",args3="three")

      print args1," ",args2," ",args3," \n"

    end

    test

    test(1)

    test(1,2)

    test(1,2,3)

    结果:

    one two three
    1 two three
    1 2 three
    1 2 3

     

    有default values的method,被赋予default value的parameter可以不需要在调用的时候传递一个arguments进去,假如没有默认值,则必须传递arguments :

    def test(args1,args2)

      p args1," ",args2

    end

    test

    结果:

    in `test': wrong number of arguments (0 for 2) (ArgumentError)
        from -:6

     

    2.Variable-Length Arugment Lists(可变长度的参数列表)---method overloading(方法的重载)的ruby实现

    method的可以有一个特殊的parameters---以星号开始,比如:

    def test(*args)

      p args

    end

    test("one",1,3.14,(1..3))     # Arugment Lists

    test(1,2,3)                          # Arugment Lists

    结果:

    ["one", 1, 3.14, 1..3]

    [1, 2, 3]

     

    我们可以清楚的看见,args在这里是一个Array object

    在定义methods的时候使用一个特殊的parameter,它以*为开始,这个parameter实际上是一个数组,在Arugment Lists中,第一个arugment放在数组的0位置,以此类推。在前面的文章中,也提到过这样的定义方式,为此还讨论了ruby中多态(polymorphism),重载(overloading),覆盖(overriding)的问题,在《多态(polymorphism),重载(overloading),覆盖(overriding)和ruby》一文中提到,ruby是没有overloading的,但是ruby可以使用不同的参数列表,比如File.open等很多很多的method,在这里,就详细描述了ruby中的实现,相比下ruby的这种实现方式会更加具有优越性。举例说明一下:

    def add(*args)

      sum = 0

      args.each{|i| sum+=i}

      sum

    end

    p add(1,2,3)

    假如通过重载,那么需要有无数个定义,因为arguments是不确定的。即使在arguments固定的情况下使用ruby的做法也是有一定的优势,我们知道重载要做的事情就是:1.确定parameters,并进行定义 2.对每一个不同的parameters的组合进行具体实现。ruby的做法使得1步骤不用做了,因为一个带星号的parameter就可以表示任何组合,代码更加简洁

     

     

     

    术语补充:

    arguments 实际使用method的时候的,写在method后面的都叫arguments
    parameters 在method定义的时候,写在method后面的都叫parameters

    可能在前面的文章中,并没有太多区分这2个术语(有些资料的确不区分),可能带来一些问题,如果疑问,请联系我

  • ruby系列教材(25):Blocks for Transactions

    2008-02-02 15:28:37

    事务(transaction)决定了一段代码要么一起运行,要么一句也不运行

     

    class File

      def File.open_and_process(*args)

        f = File.open(*args)

        yield f

        f.close()

      end

    end

     

    File.open_and_process("testfile","r") do |f|

      while line = f.gets

        puts line

      end

    end

     

     

    ......未完

  • ruby系列教材(24):Implementing Iterators

    2008-02-02 15:26:49

    Block的内部调用过程:

     

    调用method three_times的时候,遇见yield,就立即调用Block,block运行完毕,马上返回yield的下一句

     

    method_name(parameter,parameter){block}

    这里,解释器遇见method_name(parameter,parameter)就进入method_name(parameter,parameter)的定义体,运行,当遇见block的时候,就执行{}中的内容,也就是解释器遇见method_name(parameter,parameter)时候并不关心后面的内容,也就是block的内容,只是在运行遇见yield statement的时候才转到block这里,所以在class里面定义以后,调用时候必须时候,否则将出现错误:

    def test

      yield

    end

    test

     

    结果:

    in `test': no block given (LocalJumpError)
        from -:4

     

    不过这里说了:ruby解释器,先了解method_name(parameter,parameter)就进入class definition,遇见yield就运行block,所以下面的代码也是合法的:

    def test

       p "run"    #没有yield,也可以运行

    end

    test{}

     

    结果:

    run

     

     

    Block可以接收一个来自yield处的变量,也可以返回一个值

    1)Block接收值

    值(value)来源于yield,例如
    def fib_up_to(max)

      i1,i2 = 1,1                                      # parallel assignment (i1 = 1 and i2 = 1)

      while i1 <= max

        yield i1                                         #i1讲被传递到block里面

        i1,i2 = i2,i1+i2

      end

    end

     

    fib_up_to(1000){|f| print f," "}        #i1的值赋值给f,表现就是i1的值传递到了block里面的f了,使用|variable|来接受值

     

    关于block的应用我们很久以前就说过了,假如yield有2个parameters,那么这里要用|para1,para2|这样的形式

     

    前面我们提过,不能从语法的角度来理解ruby,要从语意的角度理解ruby,因为ruby是一门更加贴近问题域的语言

    这里yield i1有一层语意就是,我要把 i1 这个值传递给一个block

     

    我们下面看一段程序:

    a = [1,2]
    b = 'cat'
    a.each{|b| c = b * a[1]}


    问题出现了,1)a,b在括号里面出现,会不会改变其值 2)c在括号外面可以用吗?

     

    结果是:

    a→[1, 2]

    b→2                        #值被括号内改变

    defined?(c)→nil        #c没有被定义,也就是c出了括号,就没有了

     

    事实上有2条规则:

    a. block外面的variables出现在block内部,内部将直接改变值

    b. block外面的variables没有出现在block内部,这时候的variables作用域仅仅在这个block内或者说是属于这个block

     

    这样我们得到了block和外部环境交互的能力,但是这样的方式也遭到了很多的质疑,也许会改今后的版本中进行一定的调整

     

    *注意,一般的情况,比如find,each,times这些迭代器都是从0开始到max结束

    find 适用于Array,yield带有1个parameter,迭代过程从0-array.length,返回值是element类型(or nil)
    each 适用于Array,yield带有1个parameter,迭代过程从0-array.length,返回值是这个array本身
    times 适用于Fixnum,yield带有1个parameter,迭代过程从0-(num-1),返回值是这个fixnum

    上面的总结不全面,注意,对于each,times我们根本不关注他们的返回类型

     

     

    2)Block返回值

    一个block可以返回一个值,可以认为一个yield(parameter,parameter)可以返回一个值,这个值是block中,最后一次赋值的表达式的值(同于methods)

     

    我们前面提到过find iterator,可能大家会觉得有些迷惑,它的实现如下:

    class Array

      def find

        for i in 0...self.length             #self表示引用它的object

          value = self[i]

          return value if yield(value)  #yield返回一个值,这里返回的是一个true or false

        end

        return nil

      end

    end

     

    *上面的self.length可以写成 size,表示引用它的对象的大小

     

    self 表示应用这个method的object,例如,在method里面有self,123.method_name 这个时候,self表示123这个object

     

    yield 有什么好处呢?yield实现了代码级的复用,我们一般来说,实现的是method级别的复用,也就是复用方法,而yield提供了这样的能力,使得我们重复出现的代码都消失了,这是十分神奇的

     

     

    Iterator:(一般来说只要是collection就有他的iterators)

    1)each

    遍历array中的所有element,对于array来说,可以这样用:

    [1,2,3,4,5].each{|i| p i}

     

    对于File class each iterator每次从file object里面每次读出一行:

    f =  File.open("testfile")

    f.each do |line|              #一次读出一行

      puts line

    end

    f.close

     

    2)collect

    和each一样进行遍历,但是collect将所有的block的返回值收集起来,建立一个array object返回,例如:

    num = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]
    num2 = num.collect do |i|
        if i%2==0
            i
        else
            []
        end
    end

    num2.each{|i| print i," "}

     

    结果:

     2  4  6  8  10  12  14 

     

    3)inject

    inject可以带parameter,inject的parameter和yield的parameter有一定的关系,yield有2个parameters

    object.inject(a){|p1,p2| p1+p2}

    这个表示p1初始化为a,p1以后的值为block的返回值,p2是object elements的值,一直遍历过去

    举例说明:

    print [1,2,4,9].inject(0){|sum,ele| sum+ele}    #结果:16

    print [1,2,4,9].inject(1){|sum,ele| sum*ele}     #结果:72

     

    inject也可以不带parameter,这个时候,yield第一个parameter的值为array object第一个element的值,yield第2个parameter的值是array object的第2个element的值,比如:

    print [1,2,4,9].inject{|sum,ele| sum*ele}     #结果:72    .............1)

    print [1,2,4,9].inject{|sum,ele| sum+ele}    #结果:16     .............2)

    1)中sum初始化的值是1,ele最初值是2

    2)中sum初始化的值是1,ele最初值是2

  • ruby系列教材(23):Blocks and Iterators

    2008-02-02 15:25:08

    在前面说过的SongList例子中,我们没有谈到with_title这个method的实现,这里要做一下描述:

     

    很直观的一种做法就是,盲目搜索,对所有情况进行匹配:

    class SongList

      def with_title(title)

        for i in 0...@songs.length                   #0开始,直到小于@songs.length,范围是[0,@songs.length)

          return @songs[i] if title == @songs[i].name

        end

        return nil

      end

    end

     

    *在for中,用...(3点),求数组长度用length,判断字符串是不是相等用==,这里和java里面不同的是,==不是用来判断2个class id是不是相当,for循环使用end结束

     

    我们可以很明显的感觉到上面的with_title使用的明显不是ruby的方式,不够简洁,ruby设计者已经考虑到了,array和iterator间的关系密切,所以为array设置了一个find这样的iterator(以前还介绍过Fixnum class里面的times iterator),下面是更加好的实现方式:

    class SongList

      def with_title(title)

        @songs.find{|song| song.name == title}

      end

    end

     

    find这个iterator,return element,如果没有要查找的element,就return nil,yield带有一个parameter,表示array里面的elements。

    不要从语法上学习ruby,这个是很重要的,因为ruby设计者希望ruby能够更加自然,更加接近人,所以,这里理解find,是这样:

    find{|element| condition}   #element会从0到最后一个element,condition包含了判断这个element是否合理,假如conditon成立,返回element,否则继续,直到element被遍历完成,返回nil

  • ruby系列教材(22):Implementing SongList

    2008-02-02 15:23:24

    举例来巩固一下前面学习的成果,让我们来实现播放器的播放列表(SongList):

     

    append(song)→ list

    为list添加一首歌,添加在首位置,并且返回这个list

     

    delete_first()→ song

    删除第一首歌,并返回这首歌

     

    delete_last()→ song

    删除最后一首歌,并返回这首歌

     

    [index]→ song

    工具index来返回一种index指定的song

     

    with_title(title)→ song

    通过song的title,反正这个指定的song

     

    我们可以看到,SongList可以在首位置添加一首歌,可以删除首位置的一首歌,可以删除末位的一首歌,由此我们应该是用dequeue这个数据结构来实现这个功能:

    dequeue:double ended queue

     

    这些本不是ruby的内容,但是我相信大家应该不会为此而吝惜时间,stacks,queues,dequeues都是有序排列成的,这个区别于sets的一个重要特点,hashes也是无序的,所谓的无序是items或者说elements的顺序

    1)stack:LIFO,后进先出,进入方式是压栈,从top压入,出栈弹出最上item

    2)queue:FIFI,先进先出,进入方式是从bottom进入,出栈弹出最上的item

    3)dequeue:前后(top,bottom)都可以进出item

     

     

    继续我们的implementing SongList之旅

    ruby里面提供了丰富的方法,在对于array这个class时,提供了一些方法,使得这个array变成一个stack,queue或者dequeue,这样的方式比java灵活,使用java如何实现?用interface,这就使得我们必须去关心实现。利用array我们可以实现很多功能

     

    initialize:

    class SongList

      def initialize

        @songs = Array.new

      end

    end

    这里为SongList建立一个用于保存song的array

     

     

    append:

    class SongList

      def append(song)

        @songs.push(song)     #push为array尾部添加一个element

        self                               #同于return self ,self 表示返回一个自己(这个method所在的class)的object,这里就是SongList的object

      end

    end

     

    这里希望大家不要去思考array class 的method到底是把array变成了什么样的数据结构,只要关系这个method对array做了什么事情,就可以了

     

    delete:

    class SongList

      def delete_first

        @songs.shift

      end

      def delete_last

        @songs.pop

      end

    end

     

     

    下面是特殊的方法:[]

    class SongList

      def [](index)            #定义[]方法,使用这样的规则

        @songs[index]

      end

    end

     

     

     

    对于array methods总结:

    methods parameters return
    push all array
    shift obj or nil
    pop obj or nil

    shift删除数组第一个element,然后返回这个element

    pop删除数组最后一个element,然后返回这个element

     

     

    最后说明一下:ClassName#method_name 这说明class有method_name 这个method,比如:

    Array#shift 这个说明Array这个class有shift这个method。在你参考library或者一些文章的时候,可能看到这样的表达,这里告诉大家一下

     

     

    我们实现到这里,不想再做复杂的实现过程,现在学习任何测试:

    Unit testing:ruby为我们提供了一个testing framework,在这个framework里面有2个比较重要的方法:

    assert_equal:带2个parameters,判断这2个parameters是不是相等,假如不相等,产生failures

    assert_nil:带1个parameter,判断这个parameter是不是nil,假如不等于nil,产生failures

     

     

    代码:

    require 'test/unit'
    class TestSongList < Test::Unit::TestCase
        def test_delete
            list = SongList.new
            s1 = Song.new('title1','artist1',1)
            s2 = Song.new('title2','artist2',2)
            s3 = Song.new('title3','artist3',3)
            s4 = Song.new('title4','artist4',4)
            list.append(s1).append(s2).append(s3).append(s4)
           
            assert_equal(s1,list[0])
            assert_equal(s3,list[2])
            assert_nil(list[9])
         
            assert_equal(s1,list.delete_first)
            assert_equal(s2,list.delete_first)

            assert_equal(s4,list.delete_last)
            assert_equal(s3,list.delete_last)
            assert_nil(list.delete_last)
        end
    end

     

     

    完整代码下载

     

    测试正确:

    -------------------------------------------------------

    Loaded suite D:/Ruby-1
    Started
    .
    Finished in 0.0 seconds.

    1 tests, 8 assertions, 0 failures, 0 errors
    -------------------------------------------------------

     

     

    出错:

    -------------------------------------------------------

    Loaded suite D:/Ruby-1
    Started
    F
    Finished in 0.03 seconds.

      1) Failure:
    test_delete(TestSongList) [D:/Ruby-1.rb:47]:
    <#<Song:0x2ba9fe4 @artist="artist3", @no=3, @title="title3">> expected but was
    <#<Song:0x2ba9fa8 @artist="artist4", @no=4, @title="title4">>.

    1 tests, 6 assertions, 1 failures, 0 errors

    -------------------------------------------------------

    这里给出信息得到在第7个assertion处出现了问题,注意不需要使用new产生TestSongList,直接运行就好了,因为test包含了initial housekeeping,它会通知ruby,要使用TestUnit framework,并且告诉framework我们已经写了测试代码

471/3123>

我的栏目

数据统计

  • 访问量: 22933
  • 日志数: 47
  • 建立时间: 2008-01-29
  • 更新时间: 2008-02-02

RSS订阅

Open Toolbar