日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

第四章 Rails 背后的 Ruby

發布時間:2024/5/17 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 第四章 Rails 背后的 Ruby 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第四章 Rails 背后的 Ruby

有了第三章中的例子做鋪墊,本章將為你介紹一些對 Rails 來說很重要的 Ruby 知識。Ruby 語言的知識點很多,不過對一個 Rails 開發者而言需要掌握的很少。我們采用的是有別于常規的 Ruby 學習過程,我們的目標是開發動態的 Web 應用程序,所以我建議你先學習 Rails,在這個過程中學習一些 Ruby 知識。如果要成為一個 Rails 專家,你就要更深入的掌握 Ruby 了。本書會為你在成為專家的路途上奠定一個堅實的基礎。如?1.1.1 節中說過的,讀完本書后我建議你閱讀一本專門針對 Ruby 的書,例如《Ruby 入門》、《The Well-Grounded Rubyist》或《Ruby 之道》。

本章介紹了很多內容,第一遍閱讀沒有掌握全部是可以理解的。在后續的章節我會經常提到本章的內容。

4.1 導言

從上一章我們可以看到,即使不懂任何背后用到的 Ruby 語言,我們也可以創建一個 Rails 應用程序骨架,也可以進行測試。不過我們依賴的是本教程中提供的測試代碼,得到錯誤信息,然后讓其通過。我們不能總是這樣做,所以這一章我們要暫別網站開發學習,正視我們的 Ruby 短肋。

上次接觸應用程序時,我們已經使用 Rails 布局去掉了幾乎是靜態的頁面中的代碼重復。(參見代碼 4.1)

代碼 4.1?示例程序的網站布局?
app/views/layouts/application.html.erb

<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>

讓我們把注意力集中在代碼 4.1 中的這一行:

<%= stylesheet_link_tag "application", :media => "all" %>

這行代碼使用 Rails 內置的方法?stylesheet_link_tag(更多內容請查看?Rails API 文檔)為所有的媒介類型引入了?application.css。對于經驗豐富的 Rails 開發者來說,這一行很簡單,但是這里卻至少包含了困惑著你的四個 Ruby 知識點:內置的 Rails 方法,不用括號的方法調用,Symbol 和 Hash。這幾點本章都會介紹。

除了提供很多內置的方法供我們在視圖中使用之外,Rails 還允許我們自行創建。自行創建的這些方法叫做幫助方法(helper)。要說明如何自行創建一個幫助方法,我們要來看看代碼 4.1 中標題那一行:

Ruby on Rails Tutorial Sample App | <%= yield(:title) %>

這行代碼依賴于每個視圖中定義的頁面標題(使用?provide),例如

<% provide(:title, 'Home') %> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p>

那么如果我們不提供標題會怎樣呢?我們的標題一般都包含一個公共部分,如果想更具體些就要加上一個變動的部分了。我們在布局中用了個小技巧,基本上已經實現了這樣的標題。如果我們刪除視圖中的?provide?方法調用,輸出的標題就沒有了變動的那部分:

Ruby on Rails Tutorial Sample App |

公共部分已經輸出了,而且后面還有一個豎杠?|。

為了解決這個標題問題,我們會自定義一個幫助方法,叫做?full_title。如果視圖中沒有定義標題,full_title?會返回標題的公共部分,即“Ruby on Rails Tutorial Sample App”;如果定義了,則會在公共部分后面加上一個豎杠,然后再接上該頁面的標題(如代碼 4.2)。1

代碼 4.2?定義?full_title?幫助方法?
app/helpers/application_helper.rb

module ApplicationHelper# Returns the full title on a per-page basis.def full_title(page_title)base_title = "Ruby on Rails Tutorial Sample App"if page_title.empty?base_titleelse"#{base_title} | #{page_title}"endend end

現在我們已經定義了一個幫助方法,我們可以用它來簡化布局,將

<title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title>

替換成

<title><%= full_title(yield(:title)) %></title>

如代碼 4.3 所示。

代碼 4.3?示例程序的網站布局?
app/views/layouts/application.html.erb

<!DOCTYPE html> <html> <head> <title><%= full_title(yield(:title)) %></title> <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>

為了讓這個幫助方法起作用,我們要在“首頁”視圖中將不必要的“Home”這個詞刪掉,讓標題只保留公共部分。首先我們要按照代碼 4.4 的內容更新現有的測試,增加對沒包含?'Home'?的標題測試。

代碼 4.4?更新“首頁”標題的測試?
spec/requests/static_pages_spec.rb

require 'spec_helper'describe "Static pages" dodescribe "Home page" doit "should have the h1 'Sample App'" dovisit '/static_pages/home'page.should have_selector('h1', :text => 'Sample App')endit "should have the base title" dovisit '/static_pages/home'page.should have_selector('title',:text => "Ruby on Rails Tutorial Sample App")endit "should not have a custom page title" dovisit '/static_pages/home'page.should_not have_selector('title', :text => '| Home')endend... end

試試看你能否猜到為什么我們添加了一個新測試而不是直接修改之前的測試。(提示:答案在?3.3.1 節中。)

運行測試,查看是否有一個測試失敗了:

$ bundle exec rspec spec/requests/static_pages_spec.rb

為了讓測試通過,我們要將“首頁”視圖中的?provide?那行刪除,如代碼 4.5 所示。

代碼 4.5?刪除標題定義后的“首頁”?
app/views/static_pages/home.html.erb

<h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p>

現在測試應該可以通過了:

$ bundle exec rspec spec/requests/static_pages_spec.rb

和引入應用程序樣式表那行代碼一樣,代碼 4.2 的內容對經驗豐富的 Rails 開發者來說看起來很簡單,但是充滿了很多會讓人困惑的 Ruby 知識:module,注釋,局部變量的賦值,布爾值,流程控制,字符串插值,還有返回值。這章也會介紹這些知識。

4.2 字符串和方法

學習 Ruby 我們主要使用的工具是 Rails 控制臺,它是用來和 Rails 應用程序交互的命令行,在?2.3.3 節中介紹過。這個控制臺是基于 Ruby 的交互程序(irb)開發的,因此也就能使用 Ruby 語言的全部功能。(在?4.4.4 節中會介紹,控制臺還可以進入 Rails 環境。)使用下面的方法在命令行中啟動控制臺:

$ rails console Loading development environment >>

默認情況下,控制臺是以開發環境啟用的,這是 Rails 定義的三個獨立的環境之一(其他兩個是測試環境和生產環境)。三個環境的區別在本章還不需要知道,我們會在?7.1.1 節中更詳細的介紹。

控制臺是個很好的學習工具,你不用有所畏懼盡情的使用吧,沒必要擔心,你(幾乎)不會破壞任何東西。如果你在控制器中遇到問題了可以使用 Ctrl-C 結束當前執行的命令,或者使用 Ctrl-D 直接退出控制臺。在閱讀本章后面的內容時,你會發現查閱?Ruby API?會很有用。API 包含很多信息,例如,如果你想查看關于 Ruby 字符串更多的內容,可以查看其中的?String類頁面。

4.2.1 注釋

Ruby 中的注釋以井號?#(也叫“Hask Mark”,或者更詩意的叫“散列字元”)開頭,一直到行尾結束。Ruby 會忽略注釋,但是注釋對代碼閱讀者(包括代碼的創作者)卻很有用。在下面的代碼中

# Returns the full title on a per-page basis.def full_title(page_title)...end

第一行就是注釋,說明了后面方法的作用。

一般無需在控制臺中寫注釋,不過為了說明代碼,我會按照下面的形式加上注釋,例如:

$ rails console >> 17 + 42 # Integer addition => 59

在本節的閱讀過程中,在控制臺中輸入或者復制粘貼命令時,如果愿意你可以不復制注釋,反正控制臺會忽略注釋。

4.2.2 字符串

字符串算是 Web 應用程序中最有用的數據結構了,因為網頁的內容就是從數據庫發送到瀏覽器的字符串。我們先在控制臺中體驗一下字符串,這次我們使用?rails c?啟動控制臺,這是?rails console?的簡寫形式:

$ rails c >> "" # 空字符串 => "" >> "foo" # 非空的字符串 => "foo"

上面的字符串是字面量(字面量字符串,literal string),通過雙引號(")創建。控制器回顯的是每一行的計算結果,本例中字符串字面量的結果就是字符串本身。

我們還可以使用?+?號連接字符串:

>> "foo" + "bar" # 字符串連接 => "foobar"

"foo"?連接?"bar"?的運行結果是字符串?"foobar"。2

另外一種創建字符串的方式是通過一個特殊的句法(#{})進行插值操作:3

>> first_name = "Michael" # 變量賦值 => "Michael" >> "#{first_name} Hartl" # 字符串插值 => "Michael Hartl"

我們先把“Michael”賦值給變量?first_name,然后將其插入到字符串?"#{first_name} Hartl"?中。我們可以將兩個字符串都賦值給變量:

>> first_name = "Michael" => "Michael" >> last_name = "Hartl" => "Hartl" >> first_name + " " + last_name # 字符串連接,中間加了空格 => "Michael Hartl" >> "#{first_name} #{last_name}" # 作用相同的插值 => "Michael Hartl"

注意,兩個表達式的結果是相同的,不過我傾向使用插值的方式。在兩個字符串中加入一個空格(" ")顯得很別扭。

打印字符串

打印字符串最常用的 Ruby 方法是?puts(讀作“put ess”,意思是“打印字符串”):

>> puts "foo" # 打印字符串 foo => nil

puts?方法還有一個副作用(side-effect):puts "foo"?首先會將字符串打印到屏幕上,然后再返回空值字面量:nil?是 Ruby 中的“什么都沒有”。(后續內容中為了行文簡潔我會省略?=> nil。)

puts?方法會自動在輸出的字符串后面加入換行符?\n,功能類似的?print?方法則不會:

>> print "foo" # 打印字符串(和 puts 類似,但沒有添加換行符) foo=> nil >> print "foo\n" # 和 puts "foo" 一樣 => nil

單引號字符串

目前介紹的例子都是使用雙引號創建的字符串,不過 Ruby 也支持用單引號創建字符串。大多數情況下這兩種字符串的效果是一樣的:

>> 'foo' # 單引號創建的字符串 => "foo" >> 'foo' + 'bar' => "foobar"

不過兩種方法還是有個很重要的區別:Ruby 不會對單引號字符串進行插值操作:

>> '#{foo} bar' # 單引號字符串不能進行插值操作 => "\#{foo} bar"

注意控制臺是如何使用雙引號返回結果的,需要使用反斜線轉義特殊字符,例如?#。

如果雙引號字符串可以做單引號所做的所有事,而且還能進行插值,那么單引號字符串存在的意義是什么呢?單引號字符串的用處在于它們真的就是字面值,只包含你輸入的字符。例如,反斜線在很多系統中都很特殊,就像換行符(\n)一樣。如果有一個變量需要包含一個反斜線,使用單引號就很簡單:

>> '\n' # 反斜線和 n 字面值 => "\\n"

和前例的?#?字符一樣,Ruby 要使用一個額外的反斜線來轉義反斜線,在雙引號字符串中,要表達一個反斜線就要使用兩個反斜線。對簡單的例子來說,這省不了多少事,不過如果有很多需要轉義的字符就顯得出它的作用了:

>> 'Newlines (\n) and tabs (\t) both use the backslash character \.' => "Newlines (\\n) and tabs (\\t) both use the backslash character \\."

4.2.3 對象及向其傳遞消息

Ruby 中一切皆對象,包括字符串和?nil?都是。我們會在?4.4.2 節介紹對象技術層面上的意義,不過一般很難通過閱讀一本書就理解對象,你要多看一些例子才能建立對對象的感性認識。

不過說出對象的作用就很簡單:它可以響應消息。例如,一個字符串對象可以響應?length?這個消息,它返回字符串包含的字符數量:

>> "foobar".length # 把 length 消息傳遞給字符串 => 6

這樣傳遞給對象的消息叫做方法,它是在對象中定義的函數。4?字符串還可以響應?empty??方法:

>> "foobar".empty? => false >> "".empty? => true

注意?empty??方法末尾的問號,這是 Ruby 的一個約定,說明方法的返回值是布爾值:true?或?false。布爾值在流程控制中特別有用:

>> s = "foobar" >> if s.empty? >> "The string is empty" >> else >> "The string is nonempty" >> end => "The string is nonempty"

布爾值還可以使用?&&(和)、||(或)和?!(非)操作符結合使用:

>> x = "foo" => "foo" >> y = "" => "" >> puts "Both strings are empty" if x.empty? && y.empty? => nil >> puts "One of the strings is empty" if x.empty? || y.empty? "One of the strings is empty" => nil >> puts "x is not empty" if !x.empty? "x is not empty" => nil

因為 Ruby 中的一切都是對象,那么?nil?也是對象,所以它也可以響應方法。舉個例子,to_s?方法基本上可以把任何對象轉換成字符串:

>> nil.to_s => ""

結果顯然是個空字符串,我們可以通過下面的方法串聯(chain)驗證這一點:

>> nil.empty? NoMethodError: You have a nil object when you didn\'t expect it! You might have expected an instance of Array. The error occurred while evaluating nil.empty? >> nil.to_s.empty? # 消息串聯 => true

我們看到,nil?對象本身無法響應?empty??方法,但是?nil.to_s?可以。

有一個特殊的方法可以測試對象是否為空,你應該能猜到這個方法:

>> "foo".nil? => false >> "".nil? => false >> nil.nil? => true

下面的代碼

puts "x is not empty" if !x.empty?

說明了關鍵詞?if?的另一種用法:你可以編寫一個當且只當?if?后面的表達式為真時才執行的語句。對應的,關鍵詞?unless也可以這么用:

>> string = "foobar" >> puts "The string '#{string}' is nonempty." unless string.empty? The string 'foobar' is nonempty. => nil

我們需要注意一下?nil?的特殊性,除了?false?本身之外,所有的 Ruby 對象中它是唯一一個布爾值為“假”的:

>> if nil >> true >> else >> false # nil 是假值 >> end => false

基本上所有其他的 Ruby 對象都是“真”的,包括 0:

>> if 0 >> true # 0(除了 nil 和 false 之外的一切對象)是真值 >> else >> false >> end => true

4.2.4 定義方法

在控制臺中,我們可以像定義?home?動作(代碼 3.6)和?full_title?幫助方法(代碼 4.2)一樣進行方法定義。(在控制臺中定義方法有點麻煩,我們一般會在文件中定義,不過用來演示還行。)例如,我們要定義一個名為?string_message?的方法,可以接受一個參數,返回值取決于參數是否為空:

>> def string_message(string) >> if string.empty? >> "It's an empty string!" >> else >> "The string is nonempty." >> end >> end => nil >> puts string_message("") It\'s an empty string! >> puts string_message("foobar") The string is nonempty.

注意 Ruby 方法會非顯式的返回值:返回最后一個語句的值。在上面的這個例子中,返回的值會根據參數是否為空而返回兩個字符串中的一個。Ruby 也支持顯式的指定返回值,下面的代碼和上面的效果一樣:

>> def string_message(string) >> return "It's an empty string!" if string.empty? >> return "The string is nonempty." >> end

細心的讀者可能會發現其實這里第二個?return?不是必須的,作為方法的最后一個表達式,不管有沒有?return,字符串?"The string is nonempty."?都會作為返回值。不過兩處都加上?return?看起來更好看。

4.2.5 回顧一下標題的幫助方法

下面我們來理解一下代碼 4.2 中的?full_title?幫助方法:5

module ApplicationHelper# 根據所在頁面返回完整的標題 # 在文檔中顯示的注釋def full_title(page_title) # 方法定義base_title = "Ruby on Rails Tutorial Sample App" # 變量賦值if page_title.empty? # 布爾測試base_title # 非顯式返回值else"#{base_title} | #{page_title}" # 字符串插值endend end

方法定義、變量賦值、布爾測試、流程控制和字符串插值——組合在一起定義了一個可以在網站布局中使用的幫助方法。還用到了?module ApplicationHelper:module 為我們提供了一種把相關方法組織在一起的方式,稍后我們可以使用?include把它插入其他的類中。編寫一般的 Ruby 程序時,你要自己定義一個 module 然后再顯式的將其引入類中,但是對于幫助方法所在的 module 就交由 Rails 來處理引入了,最終的結果是?full_title?方法自動的就可以在所有的視圖中使用了。

4.3 其他的數據類型

雖然 Web 程序一般都是處理字符串,但也需要其他的數據類型來生成字符串。本節我們就來介紹一些對開發 Rails 應用程序很重要的 Ruby 中的其他數據類型。

4.3.1 數組和 Range

數組就是一組順序特定的元素。本書尚且沒有用過數組,不過理解了數組就能很好的理解 Hash (4.3.3 節),也有助于理解 Rails 中的數據模型(例如?2.3.3 節中用到的?has_many?關聯,10.1.3 節會做詳細介紹)。

目前我們已經花了很多的時間理解字符串,從字符串過渡到數組可以從?split?方法開始:

>> "foo bar baz".split # 把字符串分割成有三個元素的數組 => ["foo", "bar", "baz"]

上述代碼的返回結果是一個有三個元素的數組。默認情況下,split?在空格處把字符串分割成數組,當然你幾乎可以在任何地方進行分割:

>> "fooxbarxbazx".split('x') => ["foo", "bar", "baz"]

和其他編程語言的習慣一樣,Ruby 中數組的索引(index)也是從零開始的,數組中第一個元素的索引是 0,第二個元素的索引是 1,依此類推:

>> a = [42, 8, 17] => [42, 8, 17] >> a[0] # Ruby 使用方括號獲取數組元素 => 42 >> a[1] => 8 >> a[2] => 17 >> a[-1] # 索引還可以是負數 => 17

我們看到,在 Ruby 中是使用方括號來獲取數組元素的。除了這種方法,Ruby 還為一些常用的元素獲取操作提供了別名(synonym):6

>> a # 只是為了看一下 a 的值是什么 => [42, 8, 17] >> a.first => 42 >> a.second => 8 >> a.last => 17 >> a.last == a[-1] # 用 == 進行對比 => true

最后一行介紹了相等比較操作符?==,Ruby 和其他語言一樣還提供了對應的?!=(不等)等其他的操作符:

>> x = a.length # 和字符串一樣,數組也可以響應 length 方法 => 3 >> x == 3 => true >> x == 1 => false >> x != 1 => true >> x >= 1 => true >> x < 1 => false

除了?length(上述代碼的第一行)之外,數組還可以響應一堆其他的方法:

>> a => [42, 8, 17] >> a.sort => [8, 17, 42] >> a.reverse => [17, 8, 42] >> a.shuffle => [17, 42, 8] >> a => [42, 8, 17]

注意,上面的方法都沒有修改?a?的值。如果你想修改數組的值要使用對應的“炸彈(bang)”方法(之所以這么叫是因為這里的感嘆號經常都讀作“bang”):

>> a => [42, 8, 17] >> a.sort! => [8, 17, 42] >> a => [8, 17, 42]

你還可以使用?push?方法向數組中添加元素,或者使用等價的?<<?操作符:

>> a.push(6) # 把 6 加到數組結尾 => [42, 8, 17, 6] >> a << 7 # 把 7 加到數組結尾 => [42, 8, 17, 6, 7] >> a << "foo" << "bar" # 串聯操作 => [42, 8, 17, 6, 7, "foo", "bar"]

最后一個例子說明你可以把添加操作串在一起操作;同時也說明,Ruby 不像很多其他的語言,數組可以包含不同類型的數據(本例中是數字和字符串混合)。

前面我們用?split?把字符串分割成字符串,我們還可以使用?join?方法進行相反的操作:

>> a => [42, 8, 17, 7, "foo", "bar"] >> a.join # 沒有連接符 => "428177foobar" >> a.join(', ') # 連接符是一個逗號和空格 => "42, 8, 17, 7, foo, bar"

和數組有點類似的是 Range,使用?to_a?方法把它轉換成數組或許更好理解:

>> 0..9 => 0..9 >> 0..9.to_a # 錯了,to_a 在 9 上調用了 NoMethodError: undefined method `to_a\' for 9:Fixnum >> (0..9).to_a # 調用 to_a 要用括號包住 Range => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

雖然?0..9?是一個合法的 Range,不過上面第二個表達式告訴我們調用方法時要加上括號。

Range 經常被用來獲取一組數組元素:

>> a = %w[foo bar baz quux] # %w 創建一個元素為字符串的數組 => ["foo", "bar", "baz", "quux"] >> a[0..2] => ["foo", "bar", "baz"]

Range 也可使用字母:

>> ('a'..'e').to_a => ["a", "b", "c", "d", "e"]

4.3.2 塊

數組和 Range 可以響應的方法中有很多都可以跟著一個塊(block),這是 Ruby 中最強大也是最難理解的功能:

>> (1..5).each { |i| puts 2 * i } 2 4 6 8 10 => 1..5

這個代碼在 Range?(1..5)?上調用了?each?方法,然后又把?{ |i| puts 2*i }?這個塊傳遞給?each?方法。|i|?兩邊的豎杠在 Ruby 句法中是用來定義塊變量的。只有這個方法才知道如何處理后面跟著的塊。本例中,Range 的?each?方法會處理后面的塊,塊中有一個本地變量?i,each?會將 Range 中的各個值傳進塊中然后執行相應的操作。

花括號是一種定義塊的方法,還有另一種方法可用:

>> (1..5).each do |i| ?> puts 2 * i >> end 2 4 6 8 10 => 1..5

塊可以多于一行,也經常是多于一行的。本書中我們會遵照一個常用的約定,當塊只有一行簡單的代碼時使用花括號形式;當塊是一行很長的代碼,或者多行時使用?do..end?形式:

>> (1..5).each do |number| ?> puts 2 * number >> puts '--' >> end 2 -- 4 -- 6 -- 8 -- 10 -- => 1..5

上面的代碼用?number?代替了?i,我想告訴你的是任何變量名都可以使用。

除非你已經有了一些編程知識,否則對塊的理解是沒有捷徑的。你要做的是多看,看得多了最后你就會習慣它的用法了。7幸好人類擅長于從實例中歸納出一般性。下面是一些例子,其中幾個用到了?map?方法:

>> 3.times { puts "Betelgeuse!" } # 3.times 后跟的塊沒有變量 "Betelgeuse!" "Betelgeuse!" "Betelgeuse!" => 3 >> (1..5).map { |i| i**2 } # ** 表示冪 => [1, 4, 9, 16, 25] >> %w[a b c] # 再說一下,%w 可以創建元素為字符串的數組 => ["a", "b", "c"] >> %w[a b c].map { |char| char.upcase } => ["A", "B", "C"] >> %w[A B C].map { |char| char.downcase } => ["a", "b", "c"]

上面的代碼說明,map?方法返回的是在數組或 Range 的每個元素上執行塊中代碼后的結果。

現在我們就可以來理解一下我在?1.4.4 節中用來生成隨機二級域名的那行 Ruby 代碼了:

('a'..'z').to_a.shuffle[0..7].join

我們一步一步分解一下:

>> ('a'..'z').to_a # 字母表數組 => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] >> ('a'..'z').to_a.shuffle # 打亂數組 => ["c", "g", "l", "k", "h", "z", "s", "i", "n", "d", "y", "u", "t", "j", "q", "b", "r", "o", "f", "e", "w", "v", "m", "a", "x", "p"] >> ('a'..'z').to_a.shuffle[0..7] # 取出前面的 8 個元素 => ["f", "w", "i", "a", "h", "p", "c", "x"] >> ('a'..'z').to_a.shuffle[0..7].join # 將取出的元素合并成字符串 => "mznpybuj"

4.3.3 Hash 和 Symbol

Hash 本質上就是數組的一個特例:你可以認為 Hash 基本上就是數組,只不過它的索引不局限于使用數字。(實際上在一些語言中,特別是 Perl,因為這個原因就把 Hash 叫做關聯數組(associative array)。)Hash 的索引(或者叫“鍵”)幾乎可以是任何對象。例如,我們可以使用字符串當鍵:

>> user = {} # {} 是一個空 Hash => {} >> user["first_name"] = "Michael" # 鍵為 "first_name",值為 "Michael" => "Michael" >> user["last_name"] = "Hartl" # 鍵為 "last_name",值為 "Hartl" => "Hartl" >> user["first_name"] # 獲取元素的方式類似數組 => "Michael" >> user # Hash 的字面量形式 => {"last_name"=>"Hartl", "first_name"=>"Michael"}

Hash 通過一對花括號中包含一些鍵值對的形式表示,只有一對花括號而沒有鍵值對({})就是一個空 Hash。需要注意,Hash 中的花括號和塊中的花括號不是一個概念。(是的,這可能會讓你迷惑。)不過,Hash 雖然和數組類似,但卻有一個很重要的區別:Hash 的元素沒有特定的順序。8?如果順序很重要的話就要使用數組了。

通過方括號的形式每次定義一個元素的方式不太敏捷,使用?=>?分隔的鍵值對這種字面量的形式定義 Hash 要簡潔得多,我們稱后一種方式為“hashrocket”:

>> user = { "first_name" => "Michael", "last_name" => "Hartl" } => {"last_name"=>"Hartl", "first_name"=>"Michael"}

在上面的代碼中我用到了一個 Ruby 句法約定,在左花括號后面和右花括號前面加入了一個空格,控制臺會忽略這些空格。(不要問我為什么這些空格是約定俗成的,或許是某個 Ruby 編程大牛喜歡這種形式,然后約定就產生了。)

目前為止我們的鍵用的都是字符串,但在 Rails 中用 Symbol 當鍵卻很常見。Symbol 看起來像字符串,只不過沒有包含在一對引號中,而是在前面加一個冒號。例如,:name?就是一個 Symbol。你可以把 Symbol 看成沒有約束的字符串:9

>> "name".split('') => ["n", "a", "m", "e"] >> :name.split('') NoMethodError: undefined method `split' for :name:Symbol >> "foobar".reverse => "raboof" >> :foobar.reverse NoMethodError: undefined method `reverse' for :foobar:Symbol

Symbol 是 Ruby 特有的一個數據類型,其他語言很少用到,初看起來感覺很奇怪,不過 Rails 經常用到它,所以你很快就會習慣的。

用 Symbol 當鍵,我們可以按照如下的方式定義一個?user?Hash:

>> user = { :name => "Michael Hartl", :email => "michael@example.com" } => {:name=>"Michael Hartl", :email=>"michael@example.com"} >> user[:name] # 獲取 :name 對應的值 => "Michael Hartl" >> user[:password] # 獲取一個未定義的鍵對應的值 => nil

從上面的例子我們可以看出,Hash 中沒有定義的鍵對應的值是?nil。

因為 Symbol 當鍵的情況太普遍了,Ruby 1.9 干脆就為這種情況定義了一個新的句法:

>> h1 = { :name => "Michael Hartl", :email => "michael@example.com" } => {:name=>"Michael Hartl", :email=>"michael@example.com"} >> h2 = { name: "Michael Hartl", email: "michael@example.com" } => {:name=>"Michael Hartl", :email=>"michael@example.com"} >> h1 == h2 => true

第二個命令把 hashrocket 形式的鍵值對變成了鍵后跟著一個冒號然后再跟著一個值的形式:

{ name: "Michael Hartl", email: "michael@example.com" }

這種結構更好的沿襲了其他語言(例如 JavaScript)中 Hash 的表現方式,在 Rails 社區中也越來越受歡迎。這兩種方式現在都在使用,所以你要能識別它們。本書后續內容中大多數的 Hash 都會使用新的形式,在 Ruby 1.8.7 或之前的版本中是不可以使用的。如果你使用的是較早前的版本,你可以更新到 Ruby 1.9(推薦),或者使用舊的形式。

Hash 元素的值可以是任何對象,甚至是一個 Hash,如代碼 4.6 所示。

代碼 4.6?Hash 嵌套

>> params = {} # 定義一個名為 params(parameters 的簡稱)的 Hash => {} >> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" } => {:name=>"Michael Hartl", :email=>"mhartl@example.com"} >> params => {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}} >> params[:user][:email] => "mhartl@example.com"

這種 Hash 中有 Hash 的形式(或稱為 Hash 嵌套)在 Rails 中大量的使用,我們從?7.3 節開始會接觸到。

與數組和 Range 一樣,Hash 也可以響應?each?方法。例如,一個名為?flash?的 Hash,它的鍵是兩個條件判斷,:success?和?:error:

>> flash = { success: "It worked!", error: "It failed." } => {:success=>"It worked!", :error=>"It failed."} >> flash.each do |key, value| ?> puts "Key #{key.inspect} has value #{value.inspect}" >> end Key :success has value "It worked!" Key :error has value "It failed."

注意,數組的?each?方法后面的塊只有一個變量,而 Hash 的?each?后面的塊接受兩個變量,key?和?value。所以 Hash 的?each?方法每次遍歷都會以一個鍵值對為基本單位進行。

上面的示例中用到了很有用的?inspect?方法,返回被調用對象的字符串字面量表現形式:

>> puts (1..5).to_a # 把數組作為字符串輸出 1 2 3 4 5 >> puts (1..5).to_a.inspect # 輸出一個數組字面量形式 [1, 2, 3, 4, 5] >> puts :name, :name.inspect name :name >> puts "It worked!", "It worked!".inspect It worked! "It worked!"

順便說一下,因為使用?inspect?輸出對象的方式經常使用,為此還有一個專門的快捷方式,p?方法:

>> p :name # 等價于 puts :name.inspect :name

4.3.4 重溫引入 CSS 的代碼

現在我們要重新認識一下代碼 4.1 中在布局中引入 CSS 的代碼:

<%= stylesheet_link_tag "application", :media => "all" %>

我們現在基本上可以理解這行代碼了。在?4.1 節中簡單的提到過,Rails 定義了一個特殊的函數用來引入樣式表,下面的代碼

stylesheet_link_tag "application", :media => "all"

就是對這個函數的調用。不過還有兩個奇怪的地方。第一,括號哪兒去了?因為在 Ruby 中,括號是可以省略的,下面的兩行代碼是等價的:

# 函數調用時括號是可以省略的 stylesheet_link_tag("application", :media => "all") stylesheet_link_tag "application", :media => "all"

第二,:media?部分顯然是一個 Hash,但是怎么沒用花括號?因為在調用函數時,如果 Hash 是最后一個參數,它的花括號是可以省略的。下面的兩行代碼是等價的:

# Hash 是最后一個參數時花括號可以省略 stylesheet_link_tag "application", { :media => "all" } stylesheet_link_tag "application", :media => "all"

所以我們就看到了這樣一行代碼

stylesheet_link_tag "application", :media => "all"

它調用了?stylesheet_link_tag?函數,傳進兩個參數:一個是字符串,指明樣式表的路徑;另一個是 Hash,指明媒介類型。因為使用的是?<%= %>,函數的執行結果會通過 ERb 插入模板中,如果你在瀏覽器中查看網頁的源代碼就會看到引入樣式表所用的 HTML(參見代碼 4.7)。(你可能會在 CSS 的文件名后看到額外的字符,例如??body=1。這是 Rails 加入的,可以確保 CSS 修改后瀏覽器會重新加載它。)

代碼 4.7?引入 CSS 的代碼生成的 HTML

<link href="/assets/application.css" media="all" rel="stylesheet" type="text/css" />

如果你打開?http://localhost:3000/assets/application.css?查看 CSS 的話,會發現是空的(除了一些注釋)。在第五章中我們會看介紹如何添加樣式。

4.4 Ruby 類

我們之前已經說過 Ruby 中的一切都是對象,本節我們就會自己定義一些對象。Ruby 和其他面向對象(object-oriented)編程語言一樣,使用類來組織方法。然后實例化(instantiate)類創建對象。如果你剛接觸面向對象編程,這些都似天書一般,那么讓我們來看一些實際的例子吧。

4.4.1 構造器

我們看過很多例子使用類初始化對象,不過還沒有正式的初始化過。例如,我們使用一個雙引號初始化一個字符串,雙引號是字符串的字面構造器(literal constructor):

>> s = "foobar" # 使用雙引號的字面構造器 => "foobar" >> s.class => String

我們看到字符串可以響應?class?方法,返回的結果是字符串所屬的類。

除了使用字面構造器之外,我們還可以使用等價的具名構造器(named constructor),即在類名上調用?new?方法:10

>> s = String.new("foobar") # 字符串的具名構造器 => "foobar" >> s.class => String >> s == "foobar" => true

上面的代碼和字面構造器是等價的,只是更能表現我們的意圖。

數組和字符串類似:

>> a = Array.new([1, 3, 2]) => [1, 3, 2]

不過 Hash 就有些不同了。數組的構造器?Array.new?可接受一個可選的參數指明數組的初始值,Hash.new?可接受一個參數指明元素的默認值,就是當鍵不存在時返回的值:

>> h = Hash.new => {} >> h[:foo] # 試圖獲取不存在的鍵 :foo 對應的值 => nil >> h = Hash.new(0) # 設置不存在的鍵返回 0 而不是 nil => {} >> h[:foo] => 0

在類上調用的方法,如本例的?new,我們稱之為類方法(class method)。在類上調用?new?得到的結果是這個類的一個對象,也叫做這個類的實例(instance)。在實例上調用的方法,例如?length,叫做實例方法(instance method)。

4.4.2 類的繼承

學習類時,理清類的繼承關系會很有用,我們可以使用?superclass?方法:

>> s = String.new("foobar") => "foobar" >> s.class # 查找 s 所屬的類 => String >> s.class.superclass # 查找 String 的父類 => Object >> s.class.superclass.superclass # Ruby 1.9 使用 BasicObject 作為基類 => BasicObject >> s.class.superclass.superclass.superclass => nil

這個繼承關系如圖 4.1 所示。我們可以看到,String?的父類是?Object,Object?的父類是?BasicObject,但是?BasicObject就沒有父類了。這樣的關系對每個 Ruby 對象都是適用的:只要在類的繼承關系上往上多走幾層就會發現 Ruby 中的每個類最終都是繼承自?BasicObject,而其本身沒有父類。這就是“Ruby 中一切皆對象”技術層面的意義。

圖 4.1:String?類的繼承關系

要更深入的理解類,最好的方法就是自己動手編寫。我們來創建一個名為?Word?的類,包含一個名為?palindrome??方法,如果單詞順讀和反讀時都一樣則返回?true:

>> class Word >> def palindrome?(string) >> string == string.reverse >> end >> end => nil

我們可以按照下面的方式使用:

>> w = Word.new # 創建一個 Word 對象 => #<Word:0x22d0b20> >> w.palindrome?("foobar") => false >> w.palindrome?("level") => true

如果你覺得這個例子有點大題小做,很好,我們的目的達到了。定義一個新類,可是只創建一個可以接受一個字符串參數的方法,這么做很古怪。既然單詞是字符串,讓?Word?繼承?String?不就行了,如代碼 4.8 所示。(你要退出控制臺然后再在控制臺中輸入這寫代碼,這樣才能把之前的?Word?定義清除掉。)

代碼 4.8?在控制臺中定義?Word?類

>> class Word < String # Word 繼承自 String >> # 如果字符串和自己反轉后相等則返回 true >> def palindrome? >> self == self.reverse # self 代表這個字符串本身 >> end >> end => nil

上面代碼中的?Word < String?在 Ruby 中表示繼承(3.1.2 節中簡單介紹過),這樣除了剛定義的?palindrome??方法之外,Word?還擁有所有字符串擁有的方法:

>> s = Word.new("level") # 創建一個新的 Word,初始值為 level => "level" >> s.palindrome? # Word 實例可以響應 palindrome? 方法 => true >> s.length # Word 實例還繼承了字符串所有的常規方法 => 5

Word?繼承自?String,我們可以在控制臺中查看類的繼承關系:

>> s.class => Word >> s.class.superclass => String >> s.class.superclass.superclass => Object

繼承關系如圖 4.2 所示。

圖 4.2:代碼 4.8 中定義的?Word?類(非內置類)的繼承關系

在代碼 4.8 中,注意,要檢查單詞和單詞的反轉是否相同,要在?Word?類中引用這個單詞,在 Ruby 中我們使用?self?進行引用:在?Word?類中,self?代表的就是對象本身。所以我們可以使用

self == self.reverse

來檢查單詞是否是一個回文。11

4.4.3 修改內置的類

雖然繼承是個很強大的功能,不過在回文判斷的例子中,如果能把?palindrome??加入?String?類就更好了,這樣(除了其他對象外)我們就可以在字符串字面量上調用?palindrome??方法了。現在我們還不能直接調用:

>> "level".palindrome? NoMethodError: undefined method `palindrome?\' for "level":String

有點令人驚訝的是,Ruby 允許你這么做,Ruby 中的類可以被打開進行修改,允許像我們自己這樣的普通人添加一些方法:12

>> class String >> # 如果字符串和自己反轉后相等則返回 true >> def palindrome? >> self == self.reverse >> end >> end => nil >> "deified".palindrome? => true

(我不知道哪一個更牛:Ruby 允許向內置的類中添加方法,或“deified(神化,奉為神明)”是個回文。)

可以修改內置的類是個很強大的功能,不過功能強大意味著責任也大,如果沒有一個很好的理由,向內置的類中添加方法被認為是不好的習慣。Rails 自然有很好的理由,例如,在 Web 應用程序中我們經常要避免變量是空白(blank)的,像用戶名之類的就不應該是空格或空白,所以 Rails 為 Ruby 添加了一個?blank??方法。因為 Rails 控制臺會自動加載 Rails 添加的擴展功能,我們可以看一下示例(在?irb?就不可以):

>> "".blank? => true >> " ".empty? => false >> " ".blank? => true >> nil.blank? => true

我們可以看到,一個包含空格的字符串不是空的(empty),卻是空白的(blank)。還要注意,nil?也是空白的。因為?nil不是字符串,所以上面的代碼說明了 Rails 其實是把?blank??添加到?String?的基類?Object?上的。我們會在?8.2.1 節中介紹一些 Rails 擴展 Ruby 類的例子。)

4.4.4 控制器類

討論類和繼承時你可能覺得似曾相識,不錯,我們之前介紹過,在使用 StaticPages 控制器時(代碼 3.15):

class StaticPagesController < ApplicationControllerdef homeenddef helpenddef aboutend end

你現在可以理解,至少有點能理解,這些代碼的意思了:StaticPagesController?是一個類,繼承自?ApplicationController,StaticPagesController?類中有三個方法?home、help?和?about。因為 Rails 控制臺會加載本地的 Rails 環境,所以我們可以在控制臺中創建一個控制器來查看一下它的繼承關系:13

>> controller = StaticPagesController.new => #<StaticPagesController:0x22855d0> >> controller.class => StaticPagesController >> controller.class.superclass => ApplicationController >> controller.class.superclass.superclass => ActionController::Base >> controller.class.superclass.superclass.superclass => ActionController::Metal >> controller.class.superclass.superclass.superclass.superclass => AbstractController::Base >> controller.class.superclass.superclass.superclass.superclass.superclass => Object

這個繼承關系如圖 4.3 所示。

圖 4.3:StaticPages 控制器的繼承關系

我們還可以在控制臺中調用控制器的動作,動作其實就是方法:

>> controller.home => nil

home?動作的返回值為?nil,因為它是空的。

注意,動作沒有返回值,或至少沒返回真正需要的值。如我們在第三章看到的,home?動作的目的是渲染網頁,而不是返回一個值。不過,我記得沒有在任何地方調用過?StaticPagesController.new,這是怎么回事?

原因在于,Rails 是用 Ruby 編寫的,但 Rails 不是 Ruby。有些 Rails 類就像普通的 Ruby 類一樣,不過也有些則得益于 Rails 的強大功能。Rails 是單獨的一門學問,應該區別于 Ruby 進行學習和理解。這就是為什么,如果你的興趣是開發 Web 應用程序,我建議你先學 Rails 再學 Ruby,然后再回到 Rails 上的原因。

4.4.5 用戶類

我們通過創建一個完整的類來結束對 Ruby 的介紹,一個?User?類,提前實現第六章的 User 模型。

到目前為止,我們都是在控制臺中定義類的,這樣很快捷,但也有點不爽。現在我們要在應用程序的根目錄創建一個名為?example_user.rb?的文件,寫入代碼 4.9 中的內容。

代碼 4.9?User?類的代碼?
example_user.rb

class Userattr_accessor :name, :emaildef initialize(attributes = {})@name = attributes[:name]@email = attributes[:email]enddef formatted_email"#{@name} <#{@email}>"end end

上面的代碼有很多要說明的,我們一步步來。先看下面這行:

attr_accessor :name, :email

它為用戶的名字和 Email 地址創建了屬性訪問器(attribute accessors)。也就是定義了“獲取(getter)”和“設定(setter)”方法,用來取回和賦值?@name?和?@email?實例變量,我們在?2.2.2 節中介紹過實例變量。在 Rails 中,實例變量的意義在于,它們自動的在視圖中可用。而通常實例變量的作用是用來在 Ruby 類中不同的方法之間傳遞變量值。(稍后會更詳細的介紹這點。)實例變量總是以?@?符號開頭,如果未定義則其值為?nil。

第一個方法,initialize,在 Ruby 中有特殊意義:當我們執行?User.new?時會調用該方法。這個?initialize?方法可以接受一個參數,attributes:

def initialize(attributes = {})@name = attributes[:name]@email = attributes[:email]end

attributes?變量的初始值是一個空的 Hash,所以我們可以定義一個沒有名字或沒有 Email 地址的用戶(回想一下?4.3.3 節,如果鍵不存在則返回?nil,所以如果沒定義?:name?鍵,則?attributes[:name]?會返回?nil,attributes[:email]?也是一樣)。

最后,定義了一個名為?formatted_email?的方法,它使用被賦了值的?@name?和?@email?變量進行插值,組成一個格式良好的用戶 Email 地址(4.2.2 節):

def formatted_email"#{@name} <#{@email}>"end

因為?@name?和?@email?都是實例變量(如?@?符號指明),所以它們在?formatted_email?方法中自動可見。

讓我們打開控制臺,加載(require)這個文件,實際操作一下這個用戶類:

>> require './example_user' # 這就是加載文件的方式 => ["User"] >> example = User.new => #<User:0x224ceec @email=nil, @name=nil> >> example.name # 返回 nil,因為 attributes[:name] 是 nil => nil >> example.name = "Example User" # 賦值一個非 nil 的名字 => "Example User" >> example.email = "user@example.com" # 賦值一個非 nil 的 Email 地址 => "user@example.com" >> example.formatted_email => "Example User <user@example.com>"

上面代碼中的點號?.?在 Unix 中是指“當前目錄”,./example_user?告訴 Ruby 在當前目錄中尋找這個文件。接下來的代碼創建了一個空的用戶,然后通過直接賦值給相應的屬性來提供他的名字和 Email 地址(因為有了代碼 4.9 中?attr_accessor?那行才能進行賦值操作)。我們輸入

example.name = "Example User"

Ruby 會將?@name?變量的值設為?"Example User"(email?屬性類似),然后可以在?formatted_email?中使用。

如?4.3.4 節中介紹的,如果最后一個參數是 Hash,我們就可以省略花括號,我們可以把一個預先定義好的 Hash 傳遞給?initialize?方法來創建另一用戶:

>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com") => #<User:0x225167c @email="mhartl@example.com", @name="Michael Hartl"> >> user.formatted_email => "Michael Hartl <mhartl@example.com>"

從第七章開始,我們會使用 Hash 初始化對象,這種技術叫做“mass assignment”,在 Rails 中很常用。

4.5 小節

現在結束對 Ruby 語言的介紹。在第五章我們會好好的利用這些知識來開發示例程序。

我們不會使用?4.4.5 節中創建的?example_user.rb?文件,所以我建議把它刪除:

$ rm example_user.rb

然后把其他的改動提交到代碼倉庫中:

$ git add . $ git commit -m "Add a full_title helper"

4.6 練習

  • 將下面代碼 4.10 中的問號換成合適的方法,結合split、shuffle?和?join?實現一個函數,把一個給定字符串中的字符順序打亂。
  • 以下面代碼 4.11 為藍本,將?shuffle?方法添加到?String?類中。
  • 創建三個 Hash,分別名為?person1、person2?和?person3,將名和姓對應到?:first?和?:last?鍵上。再創建一個名為?params?的 Hash,使?params[:father]?對應?person1,params[:mother]?對應?person2,params[:child]?對應?person3。驗證一下?params[:father][:name]?的值是否正確。
  • 找一個在線的 Ruby API 文檔,閱讀?Hash?的?merge?方法的使用方法。
  • 跟著?Ruby Koans14?學習 Ruby 入門知識。
  • 代碼 4.10?打亂字符串函數的骨架

    >> def string_shuffle(s) >> s.split('').?.? >> end => nil >> string_shuffle("foobar")

    代碼 4.11?添加到?String?類的?shuffle?方法骨架

    >> class String >> def shuffle >> self.split('').?.? >> end >> end => nil >> "foobar".shuffle ? 第三章 基本靜態的頁面第五章 完善布局 ?
  • 如果幫助函數是針對某個特定控制器的,你應該把它放進該控制器相應的幫助文件中。例如,為 StaticPages 控制器創建的幫助函數一般放在?app/helper/static_pages_helper.rb?中。在這個例子中,我們會把?full_title?這個幫助函數用在網站內所有的網頁中,針對這種情況 Rails 提供了一個特別的文件:app/helper/application_helper.rb。
  • 關于“foo”和“bar”,以及不太相關的“foobar”和“FUBAR”的起源,請查看?Jargon File 中介紹“foo”的文章。
  • 熟悉 Perl 或 PHP 的編程人員可以把這個功能與自動插值美元符號開頭的變量相對應,例如?"foo $bar"。
  • 很抱歉本章在函數和方法之間隨意的來回使用。在 Ruby 中這二者是同一個概念:所有的方法都是函數,所有的函數也都是方法,因為一切皆對象。
  • 其實這里還有一個地方我們還不能理解,那就是 Rails 是怎么把這些聯系在一起的:把 URI 映射到動作上,full_title?幫助函數可以在視圖中使用,等。這是個很有意思的話題,我建議你以后好好的了解一下,不過使用 Rails 并不需要完全了解 Rails 的運作機理。(若想更深入的了解 Rails,我推薦閱讀 Obie Fernandez 的《Rails 3 之道》。)
  • 下面代碼中使用的?second?方法不是 Ruby 定義的,而是 Rails 添加的。在這里可以使用這個方法是因為 Rails 控制臺會自動加載 Rails 對 Ruby 的功能擴展。
  • 塊是閉包(closure),知道這一點對資深編程人員可能會有點幫助。閉包是一種匿名函數,其中附帶了一些數據。
  • 在 Ruby 1.9 中,其實會按照元素輸入時的順序保存 Hash,不過依賴順序顯然是不明智的。
  • 沒有了約束的好處是,Symbol 很容易進行比較,字符串要按照字母一個一個的比較,而 Symbol 只需進行一次操作。這就使得 Symbol 成為 Hash 鍵的最佳選擇。
  • 返回的結果可能由于 Ruby 版本的不同而有所不同。這個例子假設你使用的是 Ruby 1.9.3。
  • 關于 Ruby 類和?self?關鍵字,請閱讀?RailsTips?上的《Class and Instance Variables in Ruby》一文。
  • 了解 JavaScript 的人可能知道這個功能與使用內置的類原型對象擴充類的方式類似。(感謝讀者?Erik Eldridge?指出這一點。)
  • 你沒必要知道繼承關系中的每個類。我也不知道它們都是干什么的,而我從 2005 年就開始使用 Ruby on Rails 進行開發了。這可能意味著了以下兩個問題中的一個,第一,我是個廢柴,第二,你不需要知道所有的內在知識也能成為熟練的 Rails 開發者。我們當然都希望是第二點。
  • http://rubykoans.com/


  • http://yongyuan.name/pcvwithpython//chapter4.html

    總結

    以上是生活随笔為你收集整理的第四章 Rails 背后的 Ruby的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。