# 4.2 字符串和方法
我們學習 Ruby 主要使用的工具是 Rails 控制臺,它是用來和 Rails 應用交互的命令行工具,在 [2.3.3 節](chapter2.html#a-user-has-many-microposts)介紹過。控制臺基于 Ruby 的交互程序(`irb`)開發,因此能使用 Ruby 語言的全部功能。([4.4.4 節](#a-controller-class)會介紹,控制臺還可以訪問 Rails 環境。)
如果使用云端 IDE,我建議使用一些 irb 配置參數。使用簡單的 `nano` 文本編輯器,把[代碼清單 4.8](#listing-irbrc) 中的內容寫入家目錄里的 `.irbrc` 文件:[[2](#fn-2)]
```
$ nano ~/.irbrc
```
[代碼清單 4.8](#listing-irbrc) 中的內容作用是簡化 irb 提示符,以及禁用一些煩人的自動縮進行為。
##### 代碼清單 4.8:添加一些 irb 配置
~/.irbrc
```
IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:AUTO_INDENT_MODE] = false
```
不管加沒加這些設置,控制器的啟動方法都是在命令行中執行下面的命令:
```
$ rails console
Loading development environment
>>
```
默認情況下,控制臺在開發環境中啟動,這是 Rails 定義的三個獨立環境之一(另外兩個是測試環境和生產環境)。這三個環境的區別對本章不重要,[7.1.1 節](chapter7.html#debug-and-rails-environments)會詳細介紹。
控制臺是學習的好工具,你可以盡情地探索它的用法。別擔心,你(幾乎)不會破壞任何東西。如果在控制臺中遇到了問題,可以按 Ctrl-C 鍵結束當前執行的操作,或者按 Ctrl-D 鍵直接退出。在閱讀本章后續內容的過程中,你會發現查閱 [Ruby API](http://ruby-doc.org/) 很有幫助。API 中有很多信息(或許太多了),例如,如果想進一步了解 Ruby 字符串,可以查看 `String` 類的文檔。
## 4.2.1 注釋
Ruby 中的注釋以井號 `#`(也叫“哈希符號”,或者更詩意一點,叫“散列字元”)開頭,一直到行尾結束。Ruby 會忽略注釋,但是注釋對人類讀者(往往也包括代碼的編寫者)很有用。在下面的代碼中
```
# 根據所在的頁面返回完整的標題
def full_title(page_title = '')
.
.
.
end
```
第一行就是注釋,說明其后方法的作用。
在控制臺中一般不用寫注釋,不過為了說明代碼的作用,我會按照下面的形式加上注釋,例如:
```
$ rails console
>> 17 + 42 # 整數加法運算
=> 59
```
閱讀的過程中,在控制臺中輸入或者復制粘貼命令時,如果愿意你可以不加注釋,反正控制臺會忽略注釋。
## 4.2.2 字符串
對 Web 應用來說,字符串或許是最重要的數據結構,因為網頁的內容就是從服務器發送給瀏覽器的字符串。我們先在控制臺中體驗一下字符串:
```
$ rails console
>> "" # 空字符串
=> ""
>> "foo" # 非空字符串
=> "foo"
```
這些是字符串字面量,使用雙引號(`"`)創建。控制臺回顯的是每一行的計算結果,本例中,字符串字面量的結果就是字符串本身。
我們還可以使用 `+` 號連接字符串:
```
>> "foo" + "bar" # 字符串連接
=> "foobar"
```
`"foo"` 連接 `"bar"` 得到的結果是字符串 `"foobar"`。[[3](#fn-3)]
另一種創建字符串的方式是通過特殊的句法 `#{}` 進行插值操作:[[4](#fn-4)]
```
>> 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` 方法還有一個副作用:`puts "foo"` 先把字符串打印到屏幕上,然后返回[空值字面量](http://www.answers.com/nil)——`nil` 在 Ruby 中是個特殊值,表示“什么都沒有”。(為了行文簡潔,后續內容會省略 `? nil`。)
從前面的例子可以看出,`puts` 方法會自動在輸出的字符串后面加入換行符 `\n`。功能類似的 `print` 方法則不會:
```
>> print "foo" # 打印字符串(和 puts 作用一樣,但沒添加換行符)
foo=> nil
>> print "foo\n" # 和 puts "foo" 一樣
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 \\."
```
最后,有一點要注意,單雙引號基本上可以互換使用,源碼中經常混用,沒有章法可循,對此我們只能默默接受——“歡迎進入 Ruby 世界”!
## 4.2.3 對象和消息傳送
在 Ruby 中,一切皆對象,包括字符串和 `nil` 都是。我們會在 [4.4.2 節](#class-inheritance)介紹對象技術層面上的意義,不過一般很難通過閱讀一本書就理解對象,你要多看一些例子才能建立對對象的感性認識。
對象的作用說起來很簡單:響應消息。例如,一個字符串對象可以響應 `length` 這個消息,返回字符串中包含的字符數量:
```
>> "foobar".length # 把 length 消息傳給字符串
=> 6
```
一般來說,傳給對象的消息是“方法”,是在這個對象上定義的函數。[[5](#fn-5)]字符串還可以響應 `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"
```
如果分支很多,可以使用 `elsif`(`else` + `if`):
```
>> if s.nil?
>> "The variable is nil"
>> elsif s.empty?
>> "The string is empty"
>> elsif s.include?("foo")
>> "The string includes 'foo'"
>> end
=> "The string includes 'foo'"
```
布爾值還可以使用 `&&`(和)、`||`(或)和 `!`(非)操作符結合在一起使用:
```
>> 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: undefined method `empty?' for nil:NilClass
>> 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 對象中它是唯一一個布爾值為“假”的。我們可以使用 `!!`(讀作“bang bang”)對對象做兩次取反操作,把對象轉換成布爾值:
```
>> !!nil
=> false
```
除此之外,其他所有 Ruby 對象都是“真”值,數字 0 也是:
```
>> !!0
=> true
```
## 4.2.4 定義方法
在控制臺中,我們可以像定義 `home` 動作([代碼清單 3.6](chapter3.html#listing-static-pages-controller))和 `full_title` 輔助方法([代碼清單 4.2](#listing-title-helper))一樣定義方法。(在控制臺中定義方法有點麻煩,我們一般會在文件中定義,這里只是為了演示。)例如,我們要定義一個名為 `string_message` 的方法,接受一個參數,返回值取決于參數是否為空:
```
>> def string_message(str = '')
>> if str.empty?
>> "It's an empty string!"
>> else
>> "The string is nonempty."
>> end
>> end
=> :string_message
>> puts string_message("foobar")
The string is nonempty.
>> puts string_message("")
It's an empty string!
>> puts string_message
It's an empty string!
```
如最后一個命令所示,可以完全不指定參數(這種情況可以省略括號)。因為 `def string_message(str = '')` 中提供了參數的默認值,即空字符串。所以,`str` 參數是可選的,如果不指定,就使用默認值。
注意,Ruby 方法不用顯式指定返回值,方法的返回值是最后一個語句的計算結果。上面這個函數的返回值是兩個字符串中的一個,具體是哪一個取決于 `str` 參數是否為空。在 Ruby 方法中也可以顯式指定返回值,下面這個方法和前面的等價:
```
>> def string_message(str = '')
>> return "It's an empty string!" if str.empty?
>> return "The string is nonempty."
>> end
```
(細心的讀者可能會發現,其實沒必要使用第二個 `return`,這一行是方法的最后一個表達式,不管有沒有 `return`,字符串 `"The string is nonempty."` 都會作為返回值返回。不過兩處都加上 `return` 看起來更好。)
還有一點很重要,方法并不關心參數的名字是什么。在前面定義的第一個方法中,可以把 `str` 換成任意有效的變量名,例如 `the_function_argument`,但是方法的作用不變:
```
>> def string_message(the_function_argument = '')
>> if the_function_argument.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.
```
## 4.2.5 回顧標題的輔助方法
下面我們來理解一下[代碼清單 4.2](#listing-title-helper) 中的 `full_title` 輔助方法,[[6](#fn-6)]在其中加上注解之后如[代碼清單 4.9](#listing-annotated-title-helper) 所示:
##### 代碼清單 4.9:注解 `full_title` 方法
app/helpers/application_helper.rb
```
module ApplicationHelper
# 根據所在的頁面返回完整的標題 # 在文檔中顯示的注釋
def full_title(page_title = '') # 定義方法,參數可選
base_title = "Ruby on Rails Tutorial Sample App" # 變量賦值
if page_title.empty? # 布爾測試
base_title # 隱式返回值
else
page_title + " | " + base_title # 字符串拼接
end
end
end
```
方法定義、變量賦值、布爾測試、流程控制和字符串拼接[[7](#fn-7)]——組合在一起定義了一個可以在網站布局中使用的輔助方法。這里還有一個知識點——`module ApplicationHelper`:模塊為我們提供了一種把相關方法組織在一起的方式,我們可以使用 `include` 把模塊插入其他的類中。編寫普通的 Ruby 程序時,你要自己定義一個模塊,然后再顯式將其引入類中,但是輔助方法所在的模塊會由 Rails 為我們引入,結果是,`full_title` 方法[自動](http://catb.org/jargon/html/A/automagically.html)可在所有視圖中使用。
- Ruby on Rails 教程
- 致中國讀者
- 序
- 致謝
- 作者譯者簡介
- 版權和代碼授權協議
- 第 1 章 從零開始,完成一次部署
- 1.1 簡介
- 1.2 搭建環境
- 1.3 第一個應用
- 1.4 使用 Git 做版本控制
- 1.5 部署
- 1.6 小結
- 1.7 練習
- 第 2 章 玩具應用
- 2.1 規劃應用
- 2.2 用戶資源
- 2.3 微博資源
- 2.4 小結
- 2.5 練習
- 第 3 章 基本靜態的頁面
- 3.1 創建演示應用
- 3.2 靜態頁面
- 3.3 開始測試
- 3.4 有點動態內容的頁面
- 3.5 小結
- 3.6 練習
- 3.7 高級測試技術
- 第 4 章 Rails 背后的 Ruby
- 4.1 導言
- 4.2 字符串和方法
- 4.3 其他數據類型
- 4.4 Ruby 類
- 4.5 小結
- 4.6 練習
- 第 5 章 完善布局
- 5.1 添加一些結構
- 5.2 Sass 和 Asset Pipeline
- 5.3 布局中的鏈接
- 5.4 用戶注冊:第一步
- 5.5 小結
- 5.6 練習
- 第 6 章 用戶模型
- 6.1 用戶模型
- 6.2 用戶數據驗證
- 6.3 添加安全密碼
- 6.4 小結
- 6.5 練習
- 第 7 章 注冊
- 7.1 顯示用戶的信息
- 7.2 注冊表單
- 7.3 注冊失敗
- 7.4 注冊成功
- 7.5 專業部署方案
- 7.6 小結
- 7.7 練習
- 第 8 章 登錄和退出
- 8.1 會話
- 8.2 登錄
- 8.3 退出
- 8.4 記住我
- 8.5 小結
- 8.6 練習
- 第 9 章 更新,顯示和刪除用戶
- 9.1 更新用戶
- 9.2 權限系統
- 9.3 列出所有用戶
- 9.4 刪除用戶
- 9.5 小結
- 9.6 練習
- 第 10 章 賬戶激活和密碼重設
- 10.1 賬戶激活
- 10.2 密碼重設
- 10.3 在生產環境中發送郵件
- 10.4 小結
- 10.5 練習
- 10.6 證明超時失效的比較算式
- 第 11 章 用戶的微博
- 11.1 微博模型
- 11.2 顯示微博
- 11.3 微博相關的操作
- 11.4 微博中的圖片
- 11.5 小結
- 11.6 練習
- 第 12 章 關注用戶
- 12.1 “關系”模型
- 12.2 關注用戶的網頁界面
- 12.3 動態流
- 12.4 小結
- 12.5 練習