iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 2
0
Modern Web

向 Rails 致敬!30天寫一個網頁框架,再拿來做一個 Todo List系列 第 2

[DAY 2] 復刻 Rails - 從建立第一個 gem 開始

為什麼要先建立一個 gem?

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 ,發佈到網路上給大家使用,該怎麼做呢?

在發佈事情有兩件事情必須注意

  1. 假如你要發佈到 rubygems 上,你必須先去申請一個帳號

  2. 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

試試看Ruby 的執行檔

相信大家都相當熟悉 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

嗯,成功了!

奇怪,為什麼有時候不能 build?

我們修改了 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/


上一篇
[DAY 1] 復刻 Rails - 向那些經典致敬
下一篇
[DAY 3] 復刻 Rails - Rails app or Rack app?
系列文
向 Rails 致敬!30天寫一個網頁框架,再拿來做一個 Todo List30

尚未有邦友留言

立即登入留言