Skip to content

Instantly share code, notes, and snippets.

@motephyr
Created July 12, 2015 06:22
Show Gist options
  • Save motephyr/0662b8ff09896b5dde3d to your computer and use it in GitHub Desktop.
Save motephyr/0662b8ff09896b5dde3d to your computer and use it in GitHub Desktop.
RubyGem 如何運作的?
大部分的時候,RubyGem都可以直接使用。
但是Ruby有個大問題是:如果出錯的話,你很難找到為什麼出錯。
通常在使用Gem的時候你不會遇到什麼問題,如果運氣不好碰到的話,Google也沒什麼用
如果你不知道Gem到底是怎麼和程式一起運作的話,你就會花很多時間在debug上。
Gem的運作有點像在變魔術,不過花點時間研究,它其實很好懂的。
gem install做了什麼事?
一個Ruby gem只是一些打包起來的code,加上一些額外的data。
你可以用gem unpack來看裡面的code長什麼樣子:
~/Source/playground jweiss$ gem unpack resque_unit
Fetching: resque_unit-0.4.8.gem (100%)
Unpacked gem: '/Users/jweiss/Source/playground/resque_unit-0.4.8'
~/Source/playground jweiss$ cd resque_unit-0.4.8
~/Source/playground/resque_unit-0.4.8 jweiss$ find .
.
./lib
./lib/resque_unit
./lib/resque_unit/assertions.rb
./lib/resque_unit/errors.rb
./lib/resque_unit/helpers.rb
./lib/resque_unit/plugin.rb
./lib/resque_unit/resque.rb
./lib/resque_unit/scheduler.rb
./lib/resque_unit/scheduler_assertions.rb
./lib/resque_unit.rb
./lib/resque_unit_scheduler.rb
./README.md
./test
./test/resque_test.rb
./test/resque_unit_scheduler_test.rb
./test/resque_unit_test.rb
./test/sample_jobs.rb
./test/test_helper.rb
~/Source/playground/resque_unit-0.4.8 jweiss$
gem install最簡單的行為是,它抓到gem之後把這些檔案放到你的系統上一個特別的目錄下
你可以執行gem environment來看它把gem裝在哪裡:
~ jweiss$ gem environment
RubyGems Environment:
- RUBYGEMS VERSION: 2.2.2
- RUBY VERSION: 2.1.2 (2014-05-08 patchlevel 95) [x86_64-darwin14.0]
- INSTALLATION DIRECTORY: /usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0
...
~ jweiss$ ls /usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0
bin bundler doc gems
build_info cache extensions specifications
所有你安裝的gem都在gems的目錄下。
這個路徑每個系統都不一樣,而它也依照你如何安裝Ruby(rvm,homebrew,rbenv)有所不同。
所以如果你想知道gems的code在哪裡,gem environment這個指令很有幫助。
gem的code是如何被載入進來的?
為了讓你使用gem裡面的code, RubyGems覆寫了Ruby的require方法. (實際的code在core_ext/kernel_require.rb)
它的注釋很清楚:
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
#
# When you call <tt>require 'x'</tt>, this is what happens:
# * If the file can be loaded from the existing Ruby loadpath, it
# is.
# * Otherwise, installed gems are searched for a file that matches.
# If it's found in gem 'y', that gem is activated (added to the
# loadpath).
#
例如你要載入active_support,rubyGem會試著用Ruby的require方法,然後你會得到這個error:
LoadError: cannot load such file -- active_support
from (irb):17:in `require'
from (irb):17
from /usr/local/bin/irb:11:in `<main>'
RubyGems看到這個錯誤訊息,發現它需要去找某個gem的active_support.rb。
然後它去掃描所有的gem的metadata,看看哪個gem有active_support.rb
irb(main):001:0> spec = Gem::Specification.find_by_path('active_support')
=> #<Gem::Specification:0x3fe366874324 activesupport-4.2.0.beta1>
然後它啟動這個gem,把這個gem的code加到了ruby的load path裡
(就是你可以載入程式的列表裡)
irb(main):002:0> $LOAD_PATH
=> ["/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0/x86_64-darwin14.0"]
irb(main):003:0> spec.activate
=> true
irb(main):004:0> $LOAD_PATH
=> ["/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/i18n-0.7.0.beta1/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/thread_safe-0.3.4/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0.beta1/lib", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/site_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin14.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/vendor_ruby", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0", "/usr/local/Cellar/ruby/2.1.2/lib/ruby/2.1.0/x86_64-darwin14.0"]
既然 active_support 已经被加到了load path中,你就可以像往常一样 require gem 中的檔案了。你甚至可以使用原始的 require 方法,也就是被 RubyGems 所覆寫的那個方法:
irb(main):005:0> gem_original_require 'active_support'
=> true
小知識,但它很有用。
RubyGems看起來有點複雜,但它最基本的情況只是幫你管理Ruby的load path。
這不是指它很簡單,我沒有講到RubyGems如何管理Gems之間的版本衝突,
gem的binary(像rails和rake),C的extension等其他內容
但即使是了解RubyGems的基礎也很有幫助,透過這些code和在irb上的測試,
你就知道怎樣去讀 gems裡面的原始碼。
你知道了你的 gems 在哪裡,所以能確定 RubyGems 也能找到它們。
你明白了 gems 是如何被載入進來的,你就可以去找出一些看來很難的載入程式時發生的問題了。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment