慕凡(@ryudoawaru)'s blog

目前還沒想到

Install Ruby / Rails / Passenger / PostgreSQL Stack in RHEL and Variant OS (後篇)

| Comments

接續前篇,本篇內容包含:

  • Nginx 設定檔
  • 設定 systemd 啟動檔
  • Logrotate & Iptables 設定

Nginx 設定檔

由於編譯 Nginx 時把 prefix 設定在 /usr/local/nginx 下,所以設定檔就在 /usr/local/nginx/conf 下,先編輯 /usr/local/nginx/conf/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
user  nobody; #這邊的 user 和 Rails App 的 user 是不一樣的設定,但是要考慮到 Asset File 的存取,也就是這個 user 必需要可以唯讀 Rails App 的 public 目錄
worker_processes  4;
error_log  logs/error.log  info;
pid       /var/run/nginx.pid; #設定 pid file 的位置,必需和後面 systemd 設定檔一致
worker_rlimit_nofile 40960;
events {
    worker_connections  10240;
}#worker_rlimit_nofile 和 worker_connections:為了突破系統開啟檔案上限的設定,必需一起設定,一般來說 worker_rlimit_nofile 為 worker_connections x worker_processes

http {
  include       mime.types;
  default_type  application/octet-stream;
  access_log logs/access.log;
  sendfile        on;
  keepalive_timeout  65;
  gzip  on;
  send_timeout 600;
  gzip_min_length  1000;
  gzip_buffers     4 8k;
  gzip_types       text/plain application/x-javascript text/css application/xml text/javascript;
  client_max_body_size 100M; #關係到上傳檔案的上限
  passenger_root /usr/local/ruby23/lib/ruby/gems/2.3.0/gems/passenger-5.1.1; #Passenger Gem 的根目錄,會因為使用的 Ruby 版本與 Passenger 版本變化
  passenger_max_pool_size 16; # 最大同時可以存在的 Rails App 行程數,請依照系統的記憶體大小計算,一般應該是 "(總記憶體 x 0.75) / 單一 Rails App 使用記憶體",例如 4GB 的機器,單一 Rails App 記憶體約 150 就是 ``` 4096 * 0.75 / 150  = 20 ```
  server{
    server_name _;
    location /{
      return 404;
    }
  }# 對應不屬於任何 Virtual Host 的請求,在這裡返回 404 Not Found 的頁面。
  include valid-vhosts/*.conf; #在其它檔案中設定 Virtual Host
}

以下是 Virtual Host 的設定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server{
  server_name 5xruby.tw; #Virtual Host 的主機名稱
  root /home/5xruby/5xruby.tw/current/public; #務必設定為 App 的 Public 目錄
  passenger_enabled on; #開啟 Passenger
  rails_env production; # 設定 Rails App Stage
  passenger_ruby /usr/local/ruby23/bin/ruby; #如果這個 App 和 Passenger 使用的 Ruby 版本不同的話就需要指定
  access_log logs/5xruby-access.log;
  error_log logs/5xruby-err.log;
  location ~ ^/assets/ {
    # 讓瀏覽器快取靜態 Asset Pipeline 檔案的設定
    expires 1y;
    add_header Cache-Control public;
    add_header ETag "";
    break;
  }
}

設定 systemd 啟動檔

  • 編輯 /lib/systemd/system/nginx.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Unit]
Description=The NGINX HTTP and reverse proxy server
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStartPre=  /usr/local/nginx/sbin/nginx -t
ExecStart=  /usr/local/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
  • 以 root 身份輸入 systemctl enable nginx 設定開機自動啟動
  • 輸入 /usr/local/nginx/sbin/nginx -t 測試設定檔正確與否
  • systemctl start nginx 啟動,可以再透過 systemctl status nginx 觀察啟動情形

Logrotate 設定

如果在前面的 Virtual Host 設定中有設定 access_log 或 error_log 的話,就必需要設定 Logrotate 來定期輪替 Nginx log,輪替的設定如下。

1
2
3
4
5
6
7
8
9
10
11
/usr/local/nginx/logs/*.log {
    missingok
    size=20M #單檔超過 20M 為條件
    compress #壓縮舊檔案
    rotate 10
    notifempty
    sharedscripts
    postrotate
    test ! -f /var/run/nginx.pid || kill -s USR1 `cat /var/run/nginx.pid` #讓 Nginx 重新開啟 Log 檔
    endscript
}

對 Rails 產生的 Log 也可以比照辦理

Iptables 設定

現在的 RHEL 系 OS 通常都預設使用 Firewalld 做為系統防火牆,在此先改回筆者比較熟悉的 Iptables

  • yum remove firewalld
  • yum install iptables-services
  • 編輯 /etc/sysconfig/iptables
1
2
3
4
5
6
7
8
9
10
11
12
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -s 127.0.0.1 -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport ssh -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport http -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport https -j ACCEPT
COMMIT
  • systemctl start iptables

到此為止就完成了基本 Rails Stack 的設定。

Install Ruby / Rails / Passenger / PostgreSQL Stack in RHEL and Variant OS (前篇)

| Comments

在敝公司五倍紅寶石每期的RUBY ON RAILS 從零開始課程中,常常會遇到學員詢問如何在 LinodeDigital Ocean 這類 VPS 平台中安裝 Ruby / Rails Stack 的相關問題,以下的文章將簡介在目前最新版的 RHEL 系 OS 上安裝 Rails Stack 的步驟。

本篇文章的 Rails Stack 包含:

  1. 系統上可以跑多重版本的 Ruby
  2. Rails 5
  3. PostgreSQL 9.6
  4. 使用 Passenger 為 Application Server,Nginx 為 Web Frontend Server

本篇文章適用的 Linux Distribution:

  1. RedHat Enterprise 7
  2. CentOS 7
  3. Fedora 24+
  4. 其它 RHEL 衍生的,以 systemd 為啟動系統的 Linux Distribution 們 (理論上)

從乾淨的 OS 開始,步驟大致如下:

  • 安裝必要套件
  • 安裝 PGDG Repo & PostgreSQL
  • 編譯 Ruby
  • 安裝與編譯 Passenger / Nginx Extension
  • 編譯 Nginx
  • 啟動設定
  • Logrotate 設定

注意:以下所有步驟皆需以 root 身份或 sudo 執行

安裝必要套件

1
yum groupinstall "Development Tools"

如果是 Fedora 的話,通常還會有 “C Development Tools and Libraries” 和 “D Development Tools and Libraries” 兩種

1
yum install v8-devel v8 screen libmcrypt-devel wget libxslt-devel libxml2-devel gdbm-devel libffi-devel zlib-devel openssl-devel libyaml-devel readline-devel curl-devel  pcre-devel git memcached-devel valgrind-devel ImageMagick-devel ImageMagick npm geoip-devel pcre-devel gd-devel openssl-devel

套件說明:

主要是針對編譯 Ruby 所需,以及其它常見的 RubyGem 如 RMagick 或是 Asset Pipeline 編譯 JS 所需要的套件等。

安裝 PGDG Repo / PostgreSQL

什麼是 PGDG

PGDG (PostgreSQL Global Development Group) 是 PostgreSQL (以下簡稱 PgSQL) 的開發單位的簡稱,PGDG Repo 是 PgSQL 的官方為各大 Linux Distribution 提供的套件庫。

安裝步驟:

這邊找到對應的 PgSQL 版本與 Distribution 的對應 RPM Url 後,在 Console 下:

以 Fedora 25 + PgSQL 9.6 為例:

1
rpm -Uvh https://download.postgresql.org/pub/repos/yum/9.6/fedora/fedora-25-x86_64/pgdg-fedora96-9.6-3.noarch.rpm

然後安裝 PgSQL Server 與開發套件

1
yum install postgresql96 postgresql96-devel postgresql96-server postgresql96-libs postgresql96-contrib

請注意如果你安裝的不是 9.6 的話,請將上面的 96 改成對應的版號即可

編譯 Ruby

這邊不使用 rvm 或 rbenv 等 Ruby 版本管理套件,而是採用將不同的 Ruby 版本安裝到不同路徑的方式來解決可能在 Server 上同時運行多版本 Ruby 的需求。

路徑設定原則:

例如 2.3 就安裝到 /usr/local/ruby23,2.4就在 /usr/local/ruby24 以此類推

流程:

  • 下載 CRuby source code
1
cd /usr/src && wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.3.tar.bz2
  • 解壓縮
1
tar -jxvf ruby-2.3.3.tar.bz2
  • configure
1
cd ruby-2.3.3 && ./configure --prefix=/usr/local/ruby23
  • make
1
make && make install

其它版本可以比照辦理。

安裝 Passenger 與 Nginx Module

  • 在執行任何 Ruby 指令前請記得設定系統路徑到要使用 Ruby 版本的 bin 目錄,例如:
1
export PATH=$PATH:/usr/local/ruby23/bin:

或是把以上內容加到 ~/.bash_profile 的尾端,讓每次登入時自動執行。

  • 安裝 bundler & passenger Gem
1
gem install passenger bundler

此時要注意安裝的 Passenger 版號,進入 Passenger 目錄編譯 Passenger Nginx Module,以 Ruby 2.3 和 Passenger 5.1.1 為例:

1
2
cd /usr/local/ruby23/lib/ruby/gems/2.3.0/gems/passenger-5.1.1/
rake nginx
  • 下載 Nginx 源碼並使用編譯好的 Passenger Module 編譯 (以 1.10.1 版為例)
1
2
3
4
5
6
7
cd /usr/src && wget http://nginx.org/download/nginx-1.10.1.tar.gz

tar -zxvf nginx-1.10.1.tar.gz && cd nginx-1.10.1

./configure --with-http_v2_module --with-http_ssl_module --prefix=/usr/local/nginx --with-http_image_filter_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --add-module="/usr/local/ruby23/lib/ruby/gems/2.3.0/gems/passenger-5.1.1/src/nginx_module"

make && make install #這裡會將 Nginx 安裝到 /usr/local/nginx 下

至此為止 Nginx 已設定完成,後續操作請看下篇

RubyKaigi 2016

| Comments

今年的 RubyKaigi 2016 是我從 2013 以來,連續第四年參加的 RubyKaigi,前面三次除了去年以外都有留下一些紀錄(2013, 2014

RubyKaigi 是神馬?

RubyKaigi 是日本最大的 Ruby 語言年度研討會,Kaigi 就是日文漢字的「会議」,在日本除了全國性的這個 RubyKaigi 外,也有很多地區性(Regional)的 RubyKaigi,例如去年底我受邀參加的大江戶 RubyKaigi 等。

今年的行程

和去年一樣,今年的 RubyKaigi 行除了參加 conf 外,同時也是敝公司的年度員工旅遊,所以提前在 conf 前一週的 9/3 出發到大阪,9/6 移師到京都,然後在 Kaigi 結束後的隔天 9/11 回台。

團員的構成是包含我在內的 12 名五倍紅寶石正職員工以及 2 位實習生(是的,你沒看錯,在本公司實習就是有這種福利)以及公司的講師和合作夥伴共 4 位一共 18 人。

行程大概是長這樣:

  • 9/3 出發,晚上逛街
  • 9/4 宅宅日本橋調查兵團組 & USJ 組
  • 9/5 京女大交流 & 神戶.rb 交流
  • 9/6 移師京都 & 嵐山遊
  • 9/7 二条城 & 川崎重工參訪 & pre-Party
  • 9/8 DAY1 & Official Party
  • 9/9 DAY2 & 一次會 & #RubyKaraoke
  • 9/10 DAY3 & After Party
  • 9/11 歸國

個別行程的內容由於已經有同事寫出遊記,在此就不特別贊述,可以參考下面的連結:

會場

會場「国立京都国際会館」應該是今年最大的亮點,很可能也是未來永遠無法超越的經典了,不愧是簽訂過「京都議定書」的地方,除了國際級的內裝和座位數(超過 1700)外,最厲害的就是會場旁的庭院,會有種讓人誤以為是在山中而不是都會區的錯覺。

庭院區的後面還有一個很大的池子,目測大概和花蓮的鯉魚潭差不多的程度,以後有機會一定還要再來看看。

議程

和往年一樣陣容堅強的議程,特色是沒有 Rails 功能直接相關的議程,然後 mruby 相關的議程很多,我個人還蠻期待 haconiwa 這個 LXC 實作的,除了和往年一樣提供日翻英口譯外,今年甚至有幾位日本講者像 @_ko1 和前面提到的 haconiwa 是直接以英文發表,對於一個在日本舉辦的會議來說算是相當不簡單的事情,不過最後總召(日本這邊叫 Chief Organizer)松田大神也在閉幕式時提到因為要節省口譯費用所以鼓勵日本講者們多多以英文發表。

@youchan 的「Isomorphic web programming in Ruby」主要是繼去年的「Writing web application in Ruby」也就是用 Opal = Ruby 語法寫 JS 和 React 後,再加上另一個新的 Gem 去做 Isomorphic,聽起來很炫,不過個人目前還無法突破用 Ruby 語法寫 JS 這個關卡就是。

第二天的 Keynote 講者 Justin Searls 之前就聽 Juanito 推薦過他好多次,除了內容外,演講技巧和 Slide 也都非常棒,可惜今年來不及邀請他在 RubyConf Taiwan 發表了。

這次和前幾年一樣也都有台灣人的議程上榜,其中也有我們家蒼時,和 iCHEF 的 John,今年會場的台灣會眾加本公司大概有 30 上下,應該是除了日本美國以外人數排第三的國家。

週邊活動

和過往一樣,RubyKaigi 這一週從會前一天(週三)到週六,每天晚上都有無限制的酒食 Party,不過和過往不同的地方是–大部份的 Party 都要自費,主要是因為今年超低的票價(Early Bird:10,000 Yen,去年同樣票種是 25,000)無法包山包海之故,唯三免費的是第二天由 Misoca 和 Agile Works 主辦的兩場並行 Party 和我最期待的 #RubyKaraoke(感謝贊助商 Drecom),這次因為沒有終電拘束器(旅館在 KTV 場地附近),最後唱到了 3:30,結束後還一起去吃拉麵,好不開心。

最後的 After Party 還特地請我們家 Nina 妹妹上去發表 LT 推一下即將在 12/2~3 舉辦的 RubyConf Taiwan 2016!

總結

從 2013 以來每年都參加 RubyKaigi,每年都有亮點,但對我自己來說最大的變化是每年的同行陣容都不一樣:

  • 2013:自己當 LT 講者和社群的朋友同行
  • 2014:和同事、朋友以及一群被 Heroku 與日本社群朋友贊助的 Rails Girls 們同行
  • 2015:變成公司旅遊行程,團員 6 名
  • 2016:團員變成去年的 3 倍(今年不是五倍嗎?)

每年的陣容也反應了人生階段的演變,目前的遺憾是還沒有機會作為正式議程的講者上台,希望未來還有機會。

用 Letsencrypt-cli Rubygem 在 Nginx 上實裝 Let’s Encrypt 免費 SSL 服務

| Comments

Let’s Encrypt 是一個(目前)免費的 SSL 服務,它和其它 SSL 服務商的不同,除了免費之外,就是它需要透過一些方式來驗證你對網站的所有權來申請與更新憑證,而且一次憑證的有效期限(目前)最大是 90 天,也就是說你要持續定期的更新憑證才能使用。

Let’s Encrypt 運作原理

官網所示,比較白話一點的流程大概是:

  1. 用指令向 Let’s Encrypt 要求產生驗證 host example.com 的檔案
  2. LE 給你驗證檔案
  3. 你將檔案放在 http://example.com/.well-known/acme-challenge 下
  4. LE 會發出 HTTP Request 驗證該檔案是否存在前述 URL 下
  5. 如果有,則發放證書

今天要講的是用 Nginx 加上Let’s Encrypt Ruby Cli Rubygem的驗證方法

為何要使用 ruby-letsencrypt-cli 而非 LE 官方版本 CLI

  • 不需要 root 權限即可執行
  • 函式庫相依性較簡單
  • 功能相對單純

安裝 Let’s Encrypt Ruby Cli

安裝方式就是單純的 gem install letsencrypt-cli 即可。

事前準備

  • 安裝好 Gem
  • 預備好以下目錄:
    • key-directory:放 key 的地方
    • webroot-path:在前述的 step 3 給 LE 做 acme-challenge 的檔案放置目錄
    • 放置 account_key 的目錄,通常會放在你的 home
    • 在你要生 SSL 證書的網站的 Virtual Host 的設定內放置以下設定並重啟 nginx,讓 LE 可以用 http://example.com/.well-known/acme-challenge 的網址認證 :
1
2
3
4
5
6
7
server{
  ...
  location '/.well-known/acme-challenge/' {
    default_type "text/plain";
    alias /var/www/acme-challenge/;
  }
}
  • 註冊 account,執行:letsencrypt-cli register xxx.ooo.com ,產生帳號密鑰 account_key.pem
  • 讓 LE 認證你的網站,執行:letsencrypt-cli authorize --webroot-path /var/www/acme-challenge example.com
  • 生成 key:cd key-directory && letsencrypt-cli cert example.com,會生成:cert.pem chain.pem fullchain.pem key.pem 等四個檔案
  • 設定 Nginx 在 Virtual Host 使用剛才產生的檔案:
1
2
3
4
5
server{
   ...
   ssl_certificate key-directory/fullchain.pem;
   ssl_certificate_key key-directory/key.pem;
}
  • 重啟 Nginx,完成

更新 Key

執行

1
letsencrypt-cli manage --days-valid 30 -a :account_key.pem的位置: --webroot-path /var/www/acme-challenge --key-directory /etc/letsencrypt/live :你要更新的 hosts 以空白分隔:

建議把它加到 crontab 中定時執行

問題整理:

  • 執行 letsencrypt-cli 出現 Acme::Client::Error::Malformed 錯誤:請降級你的 json-jwt gem 到 1.5.2
  • 出現 Account Key Note Found:命令列加上 -a :account_key.pem的位置:
  • openssl 版本太舊:openssl 版本建議在 1.0.2 以上,特別是如果要跑 http/2 的話

將 5xruby.tw 升級到 Rails 5.0.0.beta2

| Comments

最近將敝公司 5xruby.tw 的官網的 Rails 版本升級到 5.0.0.beta2,具體的升級 commit 主要在 5d7587d,以下是一些步驟說明:

  1. 確認 Ruby 版本在 2.2.1 以上,最好是 2.3
  2. 在 Gemfile 設定 rails 的版本為 5.0.0.beta2
  3. 同時,確認所有安裝的 Rubygem 是否需要升級,將版號設定好
  4. 執行 bundle update
  5. 先執行 rails update 確認是否每個檔案都需要更改,先讓每個檔案都被覆寫到,再回頭看 diff 進行必要修改

需要配合升級的 RubyGem

  1. kaminari: 請用 Github 上的 Master branch
  2. simple_form: 請使用最新版 3.2.1
  3. acts-as-taggable-on: 一定要用 Master branch,即使版號看起來一樣
  4. rack-mini-profiler: Master branch
  5. Rails 相關的 Asset Rubygems
  6. Capistrano 升級後配合修改 Capfile 中的版號

這次升級比預料中平穩,沒有花很多時間就完成了。

以上

在 OSX 下升級 Homebrew 安裝的 PostgreSQL(dump 版)

| Comments

前言

繼上一篇的 在 Mac / Homebrew 下升級 PostgreSQL 從 9.3 到 9.4 之後,最近由於更換筆電 / 不想用 TimeMachine 加上剛好 PostgreSQL 9.5 發佈的關係,必需重新用 Homebrew 安裝 PostgreSQL 9.5,本來使用了前篇所介紹的方式來移轉舊版 PostgreSQL 9.4 的資料檔,但是很神奇地發現在所有 Rails App 的 DB 中的 schema_migration 表的資料通通遺失了,所以就必需要使用另一種方式來升級。

升級的方式

一般來說有以下兩種

  • 從資料檔案 (raw data file) 轉移

    不像 MySQL,PostgreSQL 每個版本的 raw data file 都是不相容的(註:MySQL 也要看 Storage Engine),所以當升級新版的時候是不能直接套用的,PostgreSQL 也提供了 pg_upgrade 指令來幫助升級,這也是前一篇的主題。

  • 使用 SQL 檔案轉移

    使用 pg_dump 或 pg_dumpall 等工具將舊 DB Dump 成 SQL 指令的格式,再輸入到新 DB 上即可>

流程

  1. 更改資料檔目錄

    Homebrew 預設的資料檔目錄在 /usr/local/var/postgres 請把它移到另一個目錄去

  2. 自舊版 PostgreSQL Dump SQL 檔案

    如果還沒有升級新版前,就直接執行

    pg_dumpall -c -f 目的檔案名

    即可,選項 -c 是在 dump 檔中附加移除既存 DB 的指令,可視個人需求決定要不要加。

    要是已經升級新版的話,由於 PostgreSQL 套件的發佈者都很貼心的把所有檔案照版本排列,所以可以用以下的指令開啟一個暫時的 PostgreSQL Daemon

    /usr/local/Cellar/postgresql/9.4.1/bin/postmaster -D /usr/local/var/postgres94/ -k /tmp/pg94/ -p 8822

    選項中 -D 是指定資料檔目錄,-k 是 socket file 的目錄,-p 是 port,要注意這兩者都不能和執行中的新版本重疊,這樣就順利開啟了服務,然後再執行 pg_dumpall -c -p 8822 -h /tmp/pg94 就可以取得 dump SQL 檔了

  3. 自 dump SQL 檔還原到新版資料庫中

    在上一步的 dump 指令中就可以用 pipe 一步完成了:

    pg_dumpall -c -p 8822 -h /tmp/pg94 | psql postgres

以上

Table Inheritance in PostgreSQL

| Comments

前言

Single Table Inheritance(以下簡稱 STI) 是我們在 Rails 常用的一個功能,一般來說 STI 在 Rails 的實現方式如下:

  1. 一張資料表
  2. 多個 ActiveRecord Class 使用這張表
  3. 預設用 type 這個欄位來界定這筆紀錄所屬的 class

這樣做的好處是子類別可以用父類別的欄位,主要的缺點是在可能會浪費到不需要的欄位;Rails 之所以這樣設計,主要是因為大部份的 RDBMS 系統除了 PostgreSQL 和 Oracle 以外幾乎都沒有實作 Table Inheritance。

PostgreSQL 的 Table Inheritance

PostgreSQL 的 Table Inheritance 和 STI 最大的差別在於多表繼承,在官網上就有相當詳細的介紹,基本上就是在資料表之間實作出繼承的關係,B 表繼承 A 表的話,則:

  • B 表會有所有 A 表的欄位資訊
  • 即使日後 A 表欄位有所變動,也會即時反應到 B 表上
  • B 表所建立的資料,在查尋 A 表時會全部出現,反之則無

接下來我們就來介紹如何在 Rails 中用 PostgreSQL 的 Table Inheritance 來實作 ActiveRecord 的物件繼承。

期望的 Schema

實作

建立父類別 User

1
rails g model User
1
2
3
4
5
6
7
8
9
10
11
12
13
class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string :username
      t.string :password
      t.string :type
      t.timestamps
    end
  end
end

class User < ApplicationRecord
end

建立 sub-class Staff

1
rails g model Staff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CreateStaffs < ActiveRecord::Migration[5.0]
  def up
    execute <<-SQL
    CREATE TABLE staffs(level integer,CONSTRAINT "PK" PRIMARY KEY (id))
    INHERITS(users)
    SQL
  end

  def down
    drop_table :staffs
  end
end
class Staff < User
  self.table_name = 'staffs'
end

和原本 STI 的行為不同,由於 ActiveRecord Migration 並沒有內建相關功能,所以需要自行撰寫 SQL 式來建立 Staffs 表,在建立時需實際指定繼承自 users 表,並且將額外附加的欄位等屬性設定進去即可。

在 Model 的程式方面,必需指定子類別的資料表,否則 Rails 會按照 STI 預設行為去使用父類別的資料表(users)。

在這個範例中,在 staffs 表建立的資料都會出現在 users 表上,反之則不會。

1
2
3
4
5
6
7
8
9
10
11
12
13
User.create(username: 'ryudo', password:'12345')
Staff.create(username:'Jodeci',password:'12345')
User.pluck(:username)
   (0.4ms)  SELECT "users"."username" FROM "users"
 => ["ryudo", "Jodeci"]
Staff.pluck(:username)
   (0.4ms)  SELECT "staffs"."username" FROM "staffs" WHERE "staffs"."type" IN ('Staff')
 => ["Jodeci"]
User.count #=> 2
Staff.count #=> 1
User.last
  User Load (0.6ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT $1  [["LIMIT", 1]]
 => #<Staff id: 2, username: "Jodeci", password: "12345", type: "Staff", created_at: "2015-12-31 07:12:16", updated_at: "2015-12-31 07:12:16">

Table Inheritance 會繼承的東西:

  • 欄位資訊:包括預設值或是 NOT NULL,也就是在 SQL 建構式中看到欄位敘述的內容。
  • 父表上的欄位變動:也就是之後在父表上新增刪除修改欄位的變動,會確實的反應到子表上。

Table Inheritance 不會繼承的東西:

  • 欄位資訊以外的全部:包括 Primary Key、Constraint 或是 Index 等。

主鍵重複值問題

這應該是最大的困擾之一,就是繼承的子表的主鍵是可以和父表的值重複的,例如以下的程式碼是可以正確執行的:

測試title
1
2
User.create(id:1, username: 'ryudo', password:'12345') #=> 建立 id 為 1 的 User
Staff.create(id:1,username:'Jodeci',password:'12345') #=> 建立 id 為 1 的 Staff

這樣在 users 表會真的有兩筆 id 為 1 的紀錄,而且目前無法用資料庫的方式去迴避這個問題,不過由於兩個 id 之間是共用同一個 Sequence,在不指定 id 建立資料的狀況下,id 的值是不會重複的。

inheritance_column 存廢問題:

在上面的範例中,我們可以看到原本 STI 的 inheritance_column,也就是 type 這個欄位的存在,原本 STI 的設計是:子類別的資料將會自動在 type 上加上類別名稱,查詢 Staff 時也會預設查詢 type 為 Staff 的資料。

這個設計是因為在資料同屬一張表的前提下,必需用一個欄位去區分類別的關係,而在 Table Inheritance 下,由於每個類別有獨立的資料表,在查詢子類別時已經不需要用 type 這個條件了;但是如果在父類別的資料表查詢時,還是需要用 inheritance_column 去區分出這筆資料所屬的類別,否則所有父類別資料表的資料在 ActiveRecord 中會被視為父類別的資料。

多表繼承

其實是有這項功能的,但是目前還沒想到需要使用的場合所以這次就不討論了。

是否要使用這個功能?

好處

  • 比起 ActiveRecord 提供的 STI 更趨近於物件化
  • 由於資料表是物理上的切分,等於是一種 Partitioning,在資料量大的時候可以提升子表的效能
  • 同上,減少欄位或 Index 等資源不必要的浪費

壞處

  • Rails 預設不支援相關的 Migration 操作,需手動撰寫 SQL
  • 同上,在 Model 的設定上必需客製一些設定

以上!

大江戶 RubyKaigi 05, Asakusa.rb 以及 Ruby World Conference

| Comments

這篇 Blog 是我和五倍紅寶石的同事趙子皓於 11 月初一同參加了大江戶 RubyKaigi’05 以及 Ruby World Conference 2015 並分別發表的心得記事。

逆邀講

回到今年六月,在準備 RubyConf Taiwan 2015 時,想要邀請連續兩屆來台的 Ruby VM 作者笹田耕一來台發表,但由於他已預定在之後的 RubyConf Portugal 發表所以未能成行,在這信件一來一往中,突然被問到是否可以 11 月的大江戶 RubyKaigi 中發表,驚訝和榮幸之餘想說是一個難得的機會就答應了。

Ruby World Conference–投稿與贊助

和 RubyKaigi 或是 RubyConf 系列的技術研討會不同,這個在 Ruby City — 松江市舉辦的研討會是以「Ruby 的應用和生態」為主,由於敝社原本就是以促進 Ruby 受台灣的程式生態圈愛用為目標,所以就和同事嘗試著投稿看看,沒想到兩個人都順利入選。

除了投稿之外,由於在不論在社群或是公司的方面都受到了日本 Ruby 社群很多照顧,今年本著回饋的心情也贊助了這個活動,也成為了該活動史上第一個海外贊助商。

行程

由於大江戶和 RWC 剛好是在前後週,所以我們在大江戶的前一天(11/7)出發,然後在 RWC 結束後一天(11/14)回台。

大江戶 RubyKaigi

大江戶 RubyKaigi 是日本眾多地區性 RubyKaigi 中,歷史最悠久,規模最大的一個,主辦團隊是 Asakusa.rb,這次的場地在東京的中央區舉辦。

今年,也是第五屆的場地是在一個綜合會議中心,除了 7F 的會場外,在 13F 還提供了一個很大的休息區,休息區除了給大家交流之外還提供了會場畫面的 LIVE 直播。

門票方面,活動門票是 2,000, 不含午餐,After Party 是 5,000 日元,繼承 Asakusa.rb 對外國會眾友善的傳統,外國人和講者全免。

這個活動和一般認知的技術研討會不同的地方,就是它有許多被稱為「生活發表會」的議程;今年的議程除了兩場 Keynote 外,又分為「Ruby Committers’ Lightning Talks」以及「Ninja Talks」兩種,前者顧名思議就是 Ruby Committer 專屬的 LT 時間,後者則是每場 15 分鐘的短講,會眾約 200 多人。

由於我自己的議程是在下午,所以在之前的議程就只有聽到一部份,以下就我自己有聽到的部份做個簡介:

  1. @usa:あいおーのはなし(中譯:有關 IO 的話題)

    講者是 CRuby Windows 版的主要維護者,這篇主要就是在討論 CRuby IO Class 內部實作方式的演進,然後表示他要在 Windows 上 Porting m17n IO 的功能。

  2. 松田明:「The Kaigi Must Go On

    講者是今年五月才在 ModernWeb 登台的大神松田明,內容是關於他接任 RubyKaigi 總召的經過,其中提到了原本去年 9 月 RubyKaigi 2014 之前就已經訂好了今年 Kaigi 的場地與時間,但是因為原本的總召角谷大大生了一場病所以中斷了籌備的事情,本來已經打算停辦了,後來由於他實在太常被國內外朋友們詢問是否還會有下一屆的事情,所以最後就自己出來接任總召了,身為 RubyConf.TW 的主辦人真是感到心有戚戚焉。

  3. 中田伸悅:「Best Commits of the year 2015 (仮)

    講者是被稱為 “Patch Monster” 的,也是包括 Matz 在內的三位被 Heroku 全職聘用的 Ruby Committer 之一,簡短的內容提到了他在今年最滿意的幾個 Commit。

  4. 小栁真太:「SQL 脳から見た Ruby(中譯:從 SQL 腦看到的 Ruby)

    講者和我一樣是 PostgreSQL 的同好,裡面講到如何把 SQL 查詢用 Ruby 語法去實作的方式,後面還提到了 PostgreSQL 的專有語法 with 等在 ActiveRecord 的用法,令人腦洞大開,with 相關介紹可以參照阿土伯大大在 Ruby Tuesday 24 的分享。

  5. 西嶋悠貴:「地獄のニューヨーク(紐約)」

    講者是新進的 Ruby Committer,也是在國外工作的日本 Rubyist,裡面提到他在紐約的生活,例如怎麼租房子比較省錢,或是交通問題之類,其中最爆笑的一段就是提到當他在住宅遇到搶匪,逃到樓下對面的 PUB 後,酒保竟然跟他說「總之先來一杯再說,我請客」,遺憾的是似乎沒有公開 Slide。

  6. Kei Sawada:「詳解Burn

    Burn 是一個可以讓你用 DSL 去寫出任天堂 ROM 的Ruby Gem,去年在 RubyKaigi 就有聽到這個令人印象深刻的議程,這次還加上 telnet mode 的功能,後面還提到了一些 g0v。

After Party 和二次與三次會

這次的 Party 是在知名的 Recruit 公司的員工餐廳舉辦,Party 除了啤酒喝到飽之外,料理陣容也非常豪華,同時還有講者繼續白天的話題,現場又跟大家討論了起來。

Party 結束時大概是 9:30 左右,這時很神奇地就會有一群人自動的聚集在一起等待二次會的來臨,二次會在附近的小 Pub 裡續攤,神奇的是在那個場合中,依然看到 Patch Monster 大大在改 CRuby Source Code XD。

由於二次會時偶然提到很想唱歌,所以就變成在附近的 KTV 進行人生第一次的三次會了 XD,最後在快一點時趕上了終電(最後一班電車)回到旅館。

Asakusa.rb

Asakusa.rb 是一個在東京定期舉辦的 Ruby 社群聚會,除了定期聚會以外,也主辦包括前面提到的大江戶,以及每年 RubyKaigi 的 pre-Party,一直以來都很想參加,趁著這次的機會終於可以一嘗宿願。

這次的活動在位於秋葉原附近的永和 System Management 公司舉辦,活動本身沒有主題演講等,比較像是我們經常在動漫畫裡看到的「部活」,也就是社團活動,大概在晚上七點開始會陸續的有人加入,然後有的人會聚在一起討論或是做自己的事。

大概到了 20:30 ,松田大大就開始請大家自我介紹,大概到 9:30 前部活階段就結束了,接下來就移師到附近的居酒屋舉行二次會,這時松田大大突然對我說;「真正的 Asakusa.rb 現在才開始」,然後就是喝酒聊天了,由於小弟的日文不太輪轉,和大家聊天時松田大大還會很貼心的請大家對我「慢慢地講話」,二次會在大概 12:00 左右結束。

Ruby World Conference

和我們熟知的,針對開發者為主的 RubyConf 系列或是 RubyKaigi 不同,在 Ruby 之父的家鄉島根縣松江市舉辦的 Ruby World Conference 專注在「Ruby 的應用」而非技術。

接著週三就前往羽田機場搭上往出云機場的飛機,在下午四點左右到達了旅館,當天晚上有限定講者和相關人士參加的 pre-Party,是在一間很傳統的日式餐廳舉辦,現場請每位講者上來自我介紹,在場和這次大會的 Keynote Spaker 的 Linda Liukas 相見歡,同時也認識了 RG 松江的主辦團隊,由於其中兩位都是媽媽,話題很快就轉移到育兒經上面 XD。

Pre-Party 結束後,主辦單位很貼心的招待大家搭計程車回到旅館,雖然有二三次會但是因為要準備演講的關係只好 Pass

隔天就開始了 RWC 的正式議程,開場時先請所有的講者到舞台上合照,接著就是一些地方政治人物像是島根縣長等的致詞,和我們對一般政治人物致詞很「搞威」的印象不同,每位的致詞時間都不超過五分鐘,接下來就是 Matz 的主題演講,這次的演講主題是「Second System Syndrome」,內容和不久前 RubyConf Taiwan 2015 的發表沒有很大的差異。

在單軌兩天的議程中,大會提供了「雙向」的即時口譯服務,接下來的議程由於都在準備講稿和練習(我是第二天倒數第二個講者),幾乎都沒有參加到。

會場是在當地最大的會議中心,除了三樓的演講廳之外,在一樓有一個比演講廳還大的攤位交流區,這裡同時也是會眾午餐以及晚上 Official Party 的場地,攤位的外觀和陳列方式感覺比較像台灣每年五六月間舉辦的 Computex Taipei 這樣的商業展覽,贊助商多半也是在展示自己的技術解決方案之類,沒有看到徵才。

除了交流區的攤位之外,還有一樓的地方旅遊介紹以及 Ruby 拉麵攤位,第二天也帶了一組 Ruby 拉麵回家。

三樓會場外還有一個茶席區,穿著和服的妹妹會用傳統日本茶道的方式磨出抹茶和點心然後再端上來給你。

第一天晚上的 Official Party 當然也是酒類無限暢飲,不過由於一直在交流遞名片聊天的關係所以幾乎沒吃到多少東西(淚~),這個 Party 除了吃吃喝喝外,還有奇妙的島根吉祥物 しまねっこ 出沒。

另一個讓我印象很深的地方,就是在會場有寄放衣服與背包的地方,超貼心。

Party 結束後,當然也是有各種二次三次會,不過一樣由於隔天有演講的關係,只能在電腦前看著已經完成發表的同事邊唱 KTV 邊發推😂。

第二天全部議程結束後,參加了一場需要事先報名的當地特產啤酒燒肉會,接下來一樣有各種續攤,三次會是和一群日本各地的 RG 主辦人一起交流 RG 主辦的經驗等等,拜同行的日文通子皓的幫忙,可以更加輕鬆的交流了,在整個活動期間認識了許多不同城市的 RG 主辦人或教練,真的很開心。

最後一天的早上,在搭車前往機場之前短暫的參觀了 RG 松江,這邊的規模雖然不大,不過會場的佈置很用心,和台灣不一樣的是,一組的配置是 2 位教練配四位學員,等於是台灣兩組的配置,現場還有機器人…以及 Matz 的立牌。

在活動開場後,有 Matz 和 Linda 的致詞,然後我也突然被拉上去致詞了 XD,再度獲得了另一個人生的成就。

最後就在各種趕飛機(出雲 -> 羽田 -> 松山)中結束了整個行程。

個人成就紀錄

在這次八天的日本行之中,完成了各種成就,包括:

  • 在海外用英文發表技術 & 非技術議題
  • 一週內兩次英文發表
  • 在英文發表前用日文自介
  • 和日本人唱 KTV
  • 二次會 & 三次會
  • 趕終電

感想

地方的熱情

最讓我印象深刻的,就是在日本這個國家,即使是所謂的「田舍」,也就是鄉下地方的人或是政府單位都會很努力的用實際的方式(而不是炒地皮之類)來振興地方的經濟與發展,像 RWC 這種幾乎由地方政府一手主辦的活動就是一個很顯著的例子,為了吸引外國人參加,甚至不惜重本不計成效(畢竟全場只有 15 來個外國人)的提供雙向口譯服務;同時也認識了許多地方的 IT 公司,像是 Matz 以前所屬的 NaCl 等等,很努力的把工作機會帶回自己出身的地方,這點和台灣是很不一樣的;另外松江的消費水平,像是食宿或交通費用(主要是計程車)幾乎和東京沒有什麼差別。

這次也認識了很多地方 RG 活動,像是神戶、松江甚至是以前沒聽過的塩尻等等的主辦人,他們很熱心的將 RG 活動帶到自己的家鄉,同時很多 RG 的教練,例如敝公司的好捧友五十嵐邦明等人,也會從東京自費(或是公司贊助)過去支援;縱觀 RG 的世界各地活動列表可以發現,日本的都市數量是全世界所有國家最多的,活動的頻率也是最頻繁的,可以感受到他們社群之間互相支援的熱情。

多樣的 Ruby

一直以來 Ruby 在台灣的認知就是 = Rails,甚至還有很多人把 Rails 誤認為一門語言,雖然我也是從 Rails 入門 Ruby,但是在熟練到一定程度之後就開始著眼於 Rails 或 Web 以外的 Ruby 應用;在這次日本行中又再次見識到 Ruby 的多樣性,以及日本人把 Ruby 的應用延伸到各個層面上的努力,像是這次我就認識了用 Ruby 控制都市瓦斯的系統,或是用 mruby 控製太陽能發電系統的公司,而且在大學也有相關的研究,我想除了 Ruby 是日本發明的語言之外,也包括了日本人對基礎研究的動力,這點和台灣是非常不同的。

社群的熱情

有人認為,區別一個國家是否現代化的方式,就是在於其中的人民有沒有一個「對國家主體的認同和想像」而非對個別人物的效忠;我想這點也適用在 Ruby 或是任何的 Open Source 社群上,在日本的 Ruby 社群可以感受到大家對於 Ruby 社群這個主體的認同,各個地區的社群間也會互相支援,例如在我參加 RWC 之前,一直有「這就是很 local 的活動」的刻板印象,不過實際參加之後才發現真的有很多來自東京的 Rubyist 們或公司在會場出沒甚至贊助,包括前面提到的 RG 互相支援,都可以體現出日本的 Ruby 社群有一個共同的認同主體,進而在這個主體之下互相扶持,共同發展的熱情。

最後

整體來說,我認為這次的日本行可說是收獲滿滿,最大的遺憾是因為行程排的太緊加上八天兩場發表的關係,沒有好好的在松江當地觀光;希望明年可以作為普通的會眾參加這些活動。

RubyConf Taiwan 2015

| Comments

今年是第五屆的 RubyConf Taiwan,也是第二次以總召的身份主辦這項活動。

Matz Driven Development

去年九月和龍哥帶著一群 Rails Girl 們去參加 RubyKaigi 2014 ,在會前的某個特別 Party 上遇到 Matz,意外的被問到 2015 的 RubyConf Taiwan 何時舉辦,當時因為老婆的預產期在今年 2 月,所以就當場回覆可能無法在上半年舉辦,最後就和 Matz 敲定了大概會在今年的 9 月,所以我們是 Matz Driven Development 無誤。

用公司推動 Conf

剛好在去年的 RubyConf Taiwan 的前夕,考慮到未來的社群發展,和龍哥等人成立了 五倍紅寶石 這間公司來推廣 Ruby,之後經過將近一年的努力經營之後,公司也漸漸開始有能力支援 Sitcon / Rails Girls Taipei / Weekly 和 Ruby Tuesday 等等,在這幾個月,可以說幾乎動用了整間公司的人力在舉辦這個活動。

以往在準備活動時,往往是以草創的心態去做,很多事情的聯繫或處理往往做的不到位或是有失誤的地方,今年要特別感謝敝公司的管理員 Sabrina ,帶領幾位同仁將大部份(對我來說)瑣碎的事務以最洗練的方式處理好,讓我幾乎不需要煩惱講者 / 贊助商 / 預算以外的事情,而且能適時的給我許多建議,讓我發現許多事情其實還可以做的比想像的更好。

Matz is awseome, so we are awesome

  • 補助外國講者住宿

    來自 11 個不同國家的外國講者們,除了 Keynote Speaker 以外,全部都是自費來台,為了體貼遠道而來的外國講者們,今年特地補助外國講者免費住宿中研院的客房服務。

ホテルついたー。快適〜。

A photo posted by igaiga (@igaiga555) on

  • 使用 Icecast 做口譯接收解決方案

    去年第一次嘗試在 Conf 做中翻英的即時口譯服務,當時因為場地因素,就找了專業的翻譯社架口譯間以及租借 Receiver;今年因為回到中研院,由於會場具備口譯間以及有 CPRTeam 支援網路的情況下,就決定使用之前 HITCON 使用的 Icecast 做口譯語音廣播的解決方案,讓外國會眾們用自己的手機接收口譯語音,得到了許多正面回應。

  • Pre-Conf Party

    由於經費與人力考量,以往 Conf 前通常沒有正式的活動,今年在 Conf 前兩天在敝礦坑舉辦 Pre-Conf Party,增加一個交流的機會。

場內

  • 茶 / 咖啡贊助商

    去年首次在活動中引進茶贊助商就有相當不錯的回應,後來也引發各大 Conf 爭相採用,今年剛好因為龍哥的關係認識了 iOS 大神張景隆夫妻經營的蘋果貓咖啡,另外剛好實習生的馨儀家裡又是經營茶莊的,就一次湊齊了兩種飲料攤位;連 Tenderlove 大神都表示他覺得這裡的咖啡比他住的西雅圖(星巴客發源地)的還要厲害,聽說連來自比利時的 @lrz 大神都帶了幾包回去。

  • 在會場交誼廳舉辦 Party

    從 2012 起,我們就有在第一天晚上舉辦 Official Party 的傳統,前兩屆都在同一間 Lounge Bar 舉辦,雖然不用自己準備,但是需要一筆包場費用,也因此 Party 和 Conf 都要分開購票;今年直接使用會場四樓的交誼廳做為 Party 會場,從外面叫外燴,再配合 Wish8 和 金色三麥贊助的啤酒,除了讓所有人都可以參加 Party 外,沒有座位的設定也讓大家比較容易主動交流。

  • RFID by Codeme.cc

    以往贊助商擺攤,都需要自行收集會眾的資料,對於贊助商和會眾多有所不便之處,今年剛好透過龍哥認識了 PyCon 協辦單位之一的 GliaCloud,他們研發了一套 Raspberry Pi + RFID 打卡的解決方案,雙方討論之後就決定引進這個服務給贊助商使用,也感謝 Andy 和 Django Girl Taipei 的主辦人 Michelle 在場佈與兩天活動的全力支援,誰說不同語言 / 社群之間就不能通力合作呢?

團隊

  • 議程:ihower
  • 設計:Rice / 康橋
  • 顧問:龍哥
  • 統籌:Sabrina
  • 場地協調:Rock(OSSF)
  • 攝影:徐聖淵
  • 執行:Tina / 馨儀 / Nina
  • 網站:Nina / 西瓜 / Tina / 蒼時
  • 口譯:Sarah / 宜靜
  • 司儀:Lillian / 郁蓁
  • 場務:Tina / 馨儀 / Nina / 雯晴 / 嘉佑 / Christine / okay / 椽家 / 孟穎 / 宇辰 / 芳妤 / 毓柔 / 佾庭 / 詩晴
  • 會場網路:Mouse / Pennyyken(CPR Team)
  • RFID:Andy / Michelle(Gliacloud)
  • 外交大使:@juanitofatas

結語

感謝贊助商 / 協力夥伴 / Staff / 講者以及許多朋友的幫忙才能促成今年的活動順利舉辦,謝謝大家!

解決用 Mac 登入 Linux 主機時出現的 Locale 警告訊息

| Comments

前陣子用 Mac 登入遠端的 Linux 主機時常常遇到一個有點討厭的訊息:

1
-bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8)

這個狀況是從幾個月前突然發生的,Google 之後發現兩種解法:

  1. 修改主機 locale 設定

    編輯:/etc/environment 或是 .bash_xxx 等檔案並輸入: LANG=en_US.utf-8 LC_ALL=en_US.utf-8

  2. 修改 iterm 設定

將裡面的 “Set locale variables automatically” 取消勾選即可。

相關連結

  • https://sskaje.me/2014/01/lc-ctype-issue/#.VOwSjULdVRE