從底層去認識 ruby 的load,require,gems,bundler,以及rails中的autoloading
在rails中,我一直對require和autoloading感到很疑惑,嚴重阻塞了我學習的進度,所以我覺得搞清楚這些概念是很有必要的, 在這裡翻譯一篇國外的博文,並寫下自己的一些理解。
load 和 require 到底做了些什麼?
第一次看到require語句的時候,立刻有一種似曾相識的感覺。對於熟悉的c語言,我看到了include的影子。從開發者的角度來說,這兩條指令完成了相似的工作:告訴編譯器我們想要引用其它地方的程式碼。但是,仔細思考和實驗過後,卻不難發覺,這兩條指令在實現層面上乃是千差萬別。
當我們include一個標頭檔案的時候發生了什麼?答案是編譯器找到對應的標頭檔案,並將其原地展開。通過對標頭檔案中程式碼的解析,編譯器知道了我們要引用的程式碼的宣告(Declaration)。所謂宣告,即是指編譯器知道將要引用的程式碼到底長成什麼樣,而並不知道程式碼具體是什麼(Definition)。而為了讓我們的程式碼真正地用上所引用的程式碼,我們需要在連結的時候,向linker指明引用庫的地址。而這個所謂的引用庫,才包含了我們欲引用程式碼的具體定義。
反觀require呢?當我們require某個外部程式碼的時候,我們實際上是告訴編譯器尋找對應的rb檔案。這個rb檔案中有什麼?答案是,引用程式碼的具體定義。這時候require和include有何區別的答案便呼之欲出了:由於Ruby沒有傳統意義上的連結過程,我們require實際上是載入程式碼的定義和定義;而對於C編譯器來說,include只不過是告訴編譯器程式碼長啥樣,這使得編譯器能夠生成"呼叫程式碼",而"實現程式碼"則是在連結階段予以提供的。
load
首先來看看rubu中load的用法:
puts("foo.rb loaded!")
$FOO = 2
我們開啟irb:
> load('/Users/zhang/foo.rb')
foo.rb loaded!
=> true
> $FOO
=> 2
load這個方法是是定義在Kernel模組裡面。當我們給load方法傳遞一個絕對路徑,這個方法會執行絕對路徑所對應的程式碼。load方法總是會返回true,除非這個路徑的檔案無法載入才會返回LoadError。除了區域性變數,全域性變數,常量,類都會載入進來:
# foo.rb
$FOO_GLOBAL_VARIABLE = 2
class FooClass; end
FOO_CONSTANT = 3
def foo_method; end
foo_local_variable = 4
在irb中:
> load('/Users/zhang/foo.rb')
=> true
> $FOO_GLOBAL_VARIABLE
=> 2
> FooClass
=> FooClass
> FOO_CONSTANT
=> 3
> foo_method
=> nil
> foo_local_variable
NameError: undefined local variable or method `foo_local_variable'
如果我們載入對於同一個路徑載入兩次,那麼在這個檔案裡面就會載入這段程式碼兩次。現在在foo.rb檔案中,我們定義了一個常量。當我們載入這個rb檔案的時候,因為定義了兩次,就會產生warning。
在irb中執行:
> load('/Users/zhang/foo.rb')
foo.rb loaded!
=> true
> load('/Users/zhang/foo.rb')
foo.rb loaded!
/Users/cstack/foo.rb:2: warning: already initialized constant FOO_CONSTANT
=> true
我們還可以使用相對路徑來使用這個load方法,只要這個檔案在同一個目錄下面:
> load('./foo.rb')
foo.rb loaded!
=> true
$LOAD_PATH
這是一個收集了全部絕對路徑的全域性的陣列,如果我們僅僅load了一個檔名,他就會遍歷整個陣列以及在每一個資料夾裡面去查詢檔案:
> $LOAD_PATH.push("/Users/zhang")
> load('foo.rb')
foo.rb loaded!
=> true
load 也可以在當前資料夾查詢:
> Dir.chdir("/Users/zhang")
=> 0
> load('foo.rb')
foo.rb loaded!
=> true
require
require和load是很像的,但是他們還是有一些不一樣的地方。在一個檔案中呼叫require兩次,所對應的程式碼只會執行一次。require返回true,如果這個路徑所對應的檔案還沒有被載入。
> $LOAD_PATH.push('/Users/zhang')
=> ["/Users/zhang"]
> require('foo.rb')
foo.rb loaded!
=> true
> require('foo.rb')
=> false
有時候我們search另一個檔案的時候,我們不一定需要包括這個這個檔案的字尾,比如說:
> $LOAD_PATH.push('/Users/zhang')
=> ["/Users/zhang"]
> require('foo')
foo.rb loaded!
=> true
在這段程式碼裡面,require不僅僅會找尋foo.rb的資源,還會search一些動態連結庫的資源,比如說foo.so、foo.o、foo.dll,這就是為什麼我們可以在rails中呼叫c程式碼的原因。
gems
一個gem實際上是一個有RubyGems代為管理的ruby包。比如說,json就是一個gem包,包含了一些解析和產生json的程式碼。讓我來看看這個gem包究竟儲存在我們電腦中的什麼地方:
~ gem which json
/Users/paul.zhang/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json.rb
那我們究竟應該怎麼載入這個gem包呢?如果我們知道了這個gem包的絕對路徑,我們可以使用load和require來載入:
> load('/Users/zhang/.rvm/rubies/ruby-2.4.1/lib/ruby/2.4.0/json.rb')
=> true
> JSON
=> JSON
還有一個點,在我們的Gemfile裡面,會看到一些:require => false的選項。一般來說,rails框架還是保證這個gem被安裝了,但是這個gem不會被require。我們需要用到時才require這個gem。
bundle exec 的作用
我們可以知道,當我們使用bundle install的時候,會生成一個Gemfile.lock的檔案。在我們執行一些檔案之前,時常會執行bundle exec這個指令,以保證我們require gem絕對路徑 的時候會載入我們寫在Gemfile.lock的gem。
相關文章
- ruby gems列表
- Ruby on Rails 生成指定版本的 Rails 專案AI
- rails on ruby,ruby on rails 之程式碼塊(二)AI
- 從NodeJS切換到Ruby on Rails - nikodunkNodeJSAI
- Ruby on Rails中的MVC架構是如何工作的AIMVC架構
- Ruby on rails專案中 引入BootstrapAIboot
- ruby on rails 小技巧AI
- Ruby on Rails Ping ++ 支付AI
- Rails並不是用Ruby編寫的AI
- 改變CRUD認知:Web3去中心化的底層邏輯Web中心化
- ruby on rails筆記和理解AI筆記
- [譯] 認識 rxjs 中的 BehaviorSubject、ReplaySubject 以及 AsyncSubjectJS
- Ruby on Rails Mountable vs. Full EngineAI
- 肖威洞察 | Ruby on Rails 的快速學習的體系框架;AI框架
- Vue中的底層原理Vue
- 強大的Rails/Ruby開發工具:JetBrains RubyMine 2023 for macAIMac
- 短影片的底層邏輯和認知
- 從三個層面認識SRAM儲存器
- 大語言模型底層架構丨帶你認識Transformer模型架構ORM
- 認識Java中String與StringBuffer以及StringBuilderJavaUI
- Unable to download data from https://gems.ruby-china.org/ - bad response Not Found 404HTTP
- 開發新手最容易犯的50個 Ruby on Rails 錯誤(1)AI
- 我對CDN以及CDN加速的認識
- 從OC角度思考OKR的底層邏輯OKR
- 初識LinkedList底層原理
- 從0使用Ruby on Rails打造企業級RESTful API專案實戰之我的雲音樂AIRESTAPI
- python進階(24)Python字典的底層原理以及字典效率Python
- 【雜談】從底層看鎖的實現2
- Redis 概念以及底層資料結構Redis資料結構
- Logstash中的ruby
- 認識JS中的ClassJS
- 如何從挨踢的食物鏈底層“往上爬”
- Netty的底層原理Netty
- Volatile的底層原理
- HashMap的底層原理HashMap
- 白話 Ruby 與 DSL 以及在 iOS 開發中的運用iOS
- 【Ruby on Rails全棧課程】2.7 塊(Block)和迭代器AI全棧BloC
- 從Rails到Clojure再到Java,最後回到RailsAIJava