Rails 生態圈,擁有 gem 來做套件管理,如果你曾經看過 Rails 原始碼,會發現他本身也是用了很多其他套件,再加上 Rails 本身的程式碼所組合起來的 framework
所以我們想要建立一個 Rails,這裡說的建立不是指 rails new app
,而是從 0 開始打造一個 rails 框架,其實就是建立一個新的 gem(沒錯,Rails本身也是個 gem),這裡我們會用 Bundler,除了處理套件之間的版本相依性,他本身也可以用來產生一個新的 gem,另外還有一個很重要的優點,就是你可以透過打包成 gem,將你的框架分享給全世界的開發者使用
首先確認版本
$ bundle -v
# Bundler version 2.1.4
寫這篇文章當下用的是 2.1.4
,如果你跟我用的版本不同,有些設定可能會些許不同,但做法大同小異
如果需要更新請執行
$ gem update bundler
如果還沒有安裝可以執行
$ gem install bundler
接下來我們就開始建立一個新的 gem
$ bundle gem hola_el_mundo
建立完後會發現 Bundler 幫你建立了許多檔案和目錄
15 files changed, 255 insertions(+)
create mode 100644 .gitignore
create mode 100644 .rspec
create mode 100644 .travis.yml
create mode 100644 CODE_OF_CONDUCT.md
create mode 100644 Gemfile
create mode 100644 LICENSE.txt
create mode 100644 README.md
create mode 100644 Rakefile
create mode 100755 bin/console
create mode 100755 bin/setup
create mode 100644 hola_el_mundo.gemspec
create mode 100644 lib/hola_el_mundo.rb
create mode 100644 lib/hola_el_mundo/version.rb
create mode 100644 spec/hola_el_mundo_spec.rb
create mode 100644 spec/spec_helper.rb
其中在 lib/
資料夾,裡面有個 hola_el_mundo.rb
的 Ruby 檔案 和 hola_el_mundo/
的資料夾,大部分的程式碼都會放在這裡,檔案和資料夾會根據建立的名稱所命名,另外打開 hola_el_mundo.rb
這個檔案,會發現預設已經寫好一個 module 取名叫 HolaElMundo
,這樣的做法可以減少與其他套件命名衝突
# hola_el_mundo/lib/hola_el_mundo.rb
module HolaElMundo
class Error < StandardError; end
# Your code goes here...
end
另一個重要的檔案叫做 hola_el_mundo.gemspec
,這個 gemspec 裡面包含了這個套件的基本介紹與設定,也包含了版本(version)編號,這些內容會顯示在rubygem這個網站
了解完基本設定和架構以後,我們就來開始撰寫第一隻程式吧
# lib/hola_el_mundo.rb
require "hola_el_mundo/version"
module HolaElMundo
def self.quien_es
puts 'Soy apa'
end
end
不免俗的先寫段簡單的程式碼向大家打聲招呼,這裡我取名一個方法叫 quien_es
(西語: 是誰?),然後在裡面印出 'Soy apa'
(西語: 我是阿帕),接著我們準備來將 gem 打包
$ gem build hola_el_mundo.gemspec
執行完後會發現出現錯誤
WARNING: See http://guides.rubygems.org/specification-reference/ for help
ERROR: While executing gem ... (Gem::InvalidSpecificationException)
metadata['homepage_uri'] has invalid link: "TODO: Put your gem's website or public repo URL here."
嗯,看錯誤訊息似乎是有一些資料沒有填寫完全,如果你在 gemspec 有 TODO
的資料沒有填寫完,就不會讓你打包檔案,竟然這樣就花點時間把 gemspec 修正一下吧,因為畢竟是 DEMO 用,只要是填寫 url 相關的我都填上 github 連結,其他文字部分可以用空字串代替
接著再打包一次試試看
$ gem build hola_el_mundo.gemspec
Successfully built RubyGem
Name: hola_el_mundo
Version: 0.1.0
File: hola_el_mundo-0.1.0.gem
成功了!
打包完後,會發現目錄底下多了一個 hola_el_mundo-0.1.0.gem
,我們就直接來安裝看看吧
$ gem install ./hola_el_mundo-0.1.0.gem
順利的話會出現安裝成功的畫面
Successfully installed hola_el_mundo-0.1.0
Parsing documentation for hola_el_mundo-0.1.0
Installing ri documentation for hola_el_mundo-0.1.0
Done installing documentation for hola_el_mundo after 0 seconds
1 gem installed
就這樣,你的第一隻 gem 就安裝在本機了,咦..就這樣就好了嗎?我們使用看看,使用方式就跟平常開發時,使用其他的套件一樣
首先我們打開 irb
$ irb
然後 require 剛剛寫好的 gem
2.6.6 :002 > require 'hola_el_mundo'
=> true
接著執行剛剛定義的 class method
2.6.6 :003 > HolaElMundo.quien_es
Soy apa
=> nil
成功了!
經過一陣子的努力以後,我們迫不及待的想要把這個可以改變世界的 gem ,發佈到網路上給大家使用,該怎麼做呢?
在發佈事情有兩件事情必須注意
假如你要發佈到 rubygems 上,你必須先去申請一個帳號
在 gemspec
檔案裡面有一個 allowed_push_host
的 metadata,他可以指定你想發佈到那個 host server 上,這個設定讓你可以只允許發佈到私人伺服器上,但如果想要公開發佈到 rubygems,可以這樣填寫
# hola_el_mundo.gemspec
spec.metadata["allowed_push_host"] = https://rubygems.org'
然後執行
$ gem push hola_el_mundo-0.1.0.gem
接著會詢問你在 rubygems 上的帳號密碼
Enter your https://rubygems.org/ credentials.
Don't have an account yet? Create one at https://rubygems.org/sign_up
Email: rx836@hotmail.com
Password:
Signed in.
Pushing gem to https://rubygems.org/...
Successfully registered gem: hola_el_mundo (0.1.0)
成功發佈後,可以到自己的帳號底下看,就會看到 hola_el_mundo 這個 gem了
我們可以檢查一下伺服器上是不是有這個 gem
$ gem list -r hola_el_mundo
也可以下指令從 rubygems 安裝
$ gem install hola_el_mundo
相信大家都相當熟悉 rails new app_name
這個指令,可以讓 Rails 快速的幫你建立一個專案,但你是不是曾經好奇過,這些指令怎麼來的?答案就在我們剛剛所建立的 gem,裡面有一個 bin/
這個資料夾,現在我們就來做嘗試自己做一個執行檔
首先在 bin/
資料夾底下建立一個名為 hola 檔案
$ touch bin/hola
接著改變檔案權限
$ chmod a+x bin/hola
然後在 bin/hola
檔案裡面加上下面這些程式
# hola_el_mundo/bin/hola
#!/usr/bin/env ruby
require 'hola_el_mundo'
puts HolaElMundo.quien_es(ARGV[0])
其中 ARGV[0]
代表接收從 command line 傳來的第一個引數(Command-line Argument)
接著我們將原本的程式碼修改為
# lib/hola_el_mundo.rb
require "hola_el_mundo/version"
module HolaElMundo
def self.quien_es(name)
puts "Soy #{name}"
end
end
然後回到終端機測試看看
$ ./bin/hola apa
# Soy apa
嗯,成功了!
我們修改了 gem,當然也會想修改 version.rb
裡面的版號,畢竟我們做了更新,想要發佈新的程式碼,那就來升級版號吧
# lib/hola_el_mundo/version.rb
module HolaElMundo
VERSION = "0.1.1"
end
然後砍掉原本的 hola_el_mundo-0.1.0.gem
,這時候就會發現,如果刪掉後立刻執行 gem build hola_el_mundo.gemspec
的話,可能會出現下面錯誤
ERROR: While executing gem ... (Gem::InvalidSpecificationException)
["hola_el_mundo-0.1.0.gem"] are not files
為什麼會出現這樣的錯誤?這是因為每次在 build 的時候,會透過 gemspec 檔案來呼叫 git,檢查這個 gem 到底包含了那些檔案(下面程式碼所示)
# hola_el_mundo.gemspec
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
所以這時候只要執行
$ git add .
將刪除的狀態加回 git 裡面,就可以 rebuild 新的 gem
$ gem build hola_el_mundo.gemspec
如果要測試新版本的套件,記得把之前剛剛安裝的 hola_el_mundo-0.1.0.gem
刪掉,不然你會發現程式怎麼改,都還是舊的
透過練習建立套件,讓我們了解到那些好用的 gem 到底怎麼來的,明天我們將要利用今天所學習的知識,來開始打造屬於自己的框架
參考資料:
https://guides.rubygems.org/make-your-own-gem/
https://bundler.io/guides/creating_gem.html
https://guides.rubygems.org/patterns/