require、require_relative是什麼意思?差在哪?


雖然Ruby本身已經有非常多的功能,但還是常常在Ruby檔案中看到require 'something',例如要使用JSON.parse就必須先require 'json',如果不require就無法使用這些功能。到底這些在檔案最開頭的玩意兒代表什麼意思呢?如果不require又會怎麼樣?

require最常見的意義

Ruby內建有許多library,在執行.rb檔案時就已經包含在程式內,包括常用的FileDate等等三不五時就會出現的class,這些因為大家常用,就直接在開始執行.rb程式時包含在Ruby內。但如果載入Ruby時就把所有libraries都放進去,啟動時間會太久,因此Ruby將許多比較沒那麼常用到的class抽出,需另外require。很常見的例子是require 'digest',是一個專門產生亂數、轉碼的工具:

Digest::SHA256.hexdigest("HelloWorld")
# => NameError: uninitialized constant Digest

require 'digest'
Digest::SHA256.hexdigest("HelloWorld")
#=> "872e4e50ce9990d8b041330c47c9ddd11bec6b503ae9386a99da8584e9bb12c4"

由於平常不一定會使用到digest的功能,因此要使用必須要require,以節省資源。

使用require的理由

require之所以會用來載入ruby的libraries,是基於兩個原因:

1. require的預設載入路徑會讀取各式ruby gems及libraries,接著才是載入檔案

官方文件中可以看到,require會預先搜尋$ LOAD_PATH,這個東西是定義在Ruby的設定當中,在任何環境下輸入$ LOAD_PATH就會看到目前的設定內容:

$ LOAD_PATH
#=> ["/Library/Ruby/Site/2.0.0", 
#    "/Library/Ruby/Site/2.0.0/x86_64-darwin13", 
#    "/Library/Ruby/Site/2.0.0/universal-darwin13", 
#    "/Library/Ruby/Site", 
#    ...(省略)]

以Mac OSX 10.9內建的Ruby為例,會從最上方開始搜尋到最下方,可以看出都是針對系統library進行搜尋。

2. require在同一個環境下只會載入一次,如果出現第二次require就不會載入。

若在同一個環境下require兩次相同的內容,則不會重複載入,節省資源。

require 'digest'
#=> true
require 'digest'
#=> false 

使用require讀取檔案

只要在require後方加上絕對路徑,require也可以用於讀取檔案,例如:

require File.expand_path("../another_file", __FILE__)
# 或
require "#{Rails.root}/app/models/another_file"

看似有點麻煩,但必須如此。因為如果只寫檔案名稱例如require 'another_file'他並不會搜尋我目前的資料夾,也不會搜尋該檔案所處的資料夾。一定要用檔案的絕對路徑,才可以用require的方式來讀取其他檔案。

另外一種作法是加上"./"代表是相對於你目前執行檔案的資料夾。例如require './file'也可以載入目前資料夾內的檔案。但這種作法有一個壞處,就是跟你執行檔案的地點會有關係,例如:

# ~/folder/file_1.rb
puts "success"

# ~/file_2.rb
require './folder/file_a'

看似完美的對比,假如我在使用者根目錄資料夾~/底下執行$ ruby file_2.rb會皆大歡喜的看到"success"訊息。但如果我移動到folder資料夾內,從內部執行$ ruby ../file_2.rb就會發生無法require的慘劇,因為在file2.rb當中是根據我目前的位置來進行require,會試圖在我所處的"folder"資料夾內搜尋"file1.rb"這個檔案,當然結果就是找不到。

讀取檔案別忘了require_relative

假如我們非常確定要require檔案的相對關係,那可以直接用乾淨漂亮的require_relative來處理。就以剛剛的例子來說,可以寫成:

require_relative '../file_1.rb'

基本上require_relative並不會搜尋library,也完全不建議這樣做,寫起來很醜。在這樣方便的方法之下,我個人都是用require來讀取library和gem,而用require_relative來讀取其他檔案,比較不容易搞混。

延伸閱讀

Stackoverflow上對於require的討論

官方文件對於require的說明

$ LOAD_PATH解釋

對於各種load及require方式的分析

Read more