由於過去這幾年每次 deploy 到 Heroku 都吃了大虧,所以我決定趁這次一次把話講清楚,把所有會遇到的雷都一次記起來 = =
第一步驟:永遠記得先開 git 才開 heroku
第一步當然是先開 rails app 啦,這個沒爭議齁
rails new my_project
重點來啦!再跑 heroku create
之前一定千萬記得要先跑完 git
的相關指令,尤其第一次跑別忘了 git init
:
git init
git add .
git commit -m "init"
等到上面四個都跑完了,才可以開你的 heroku ,也就是
heroku create my-project
如果你先創 Heroku 才跑 git
,那麼等到跑上面那一行的時候就會GG。 萬一這件事情真的發生了,其實也沒關係,就是要打開 Heroku 的官方網頁,登進去把你之前創的那個 app 刪掉,重新來過。
my-project
是我在這邊取的 app 名稱啦,請自行換成你的名字;為了區別,所以 rails 的 app 名稱我取叫 my_project
但是 Heroku 上面的 app 名字我取叫做 my-project
。
每次要deploy Rails 專案到 heroku 都會遇到一樣惱人的問題: 要用 PostgreSQL,每次光想到這點就很崩潰。
為了可以兼顧 heroku 但是又可以簡化開發程序,所以我在自己的電腦上還是用 sqlite3
XD,所以我的 Gemfile
是這樣寫的:
gem 'pg', :group => :production
gem 'sqlite3', :group => [:development, :test]
但是如果這樣改完你就直接去跑 bundle install
一定會有問題,他會跟你抱怨
An error occurred while installing pg (0.17.0), and Bundler cannot continue.
Make sure that `gem install pg -v '0.17.0'` succeeds before bundling.
想單然爾你實際上去跑 gem install pq -v=0.17.0
一定也是失敗收場,不會成功了。值得一提的是因為我是用 RVM 所以 gem install
前面並不需要加 sudo
喔。
就像 mySQL 一樣,其實要安裝 pq
需要電腦上先有 PostgreSQL 的相關 lib ,但是我們又要怎麼裝起來呢?經過多次嘗試之後,我覺得毫無疑問最無痛的方法就是透過 brew
去裝,關於 brew 的安裝方式這邊就不解釋了。
你唯一需要做的就是
brew install PostgreSQL
接著當你再次執行
gem install pq
你會發現你成功了。超爽der。
然後你就可以跑 bundle install
惹。
我真的是視送app上heroku為畏途...... 儘管開發 rails 再怎麼愉快,每次光想到要送app上heroku就先軟一半。後來我逐漸掌握到一些訣竅:
- 學會搞定 pq
- 永遠用最新版的 rails
基本上掌握這兩點就八九不離十了。
這一次我還用了 xdite 的團隊開發的 gem boostrappers
,想不到卻因此遭受到前所未有的鬼打牆局面。一開始是push
完到最後會抱怨 precompile 失敗,解決之道是app/assets/javascripts/README
把它改成README.txt
,然後先在自己的電腦上先跑
rake assets:precompile
嗯,總之先在自己電腦上跑 precombile 就對了, somehow it works。至於為什麼我直覺就想到要把README
加上附檔名這是因為 rails 4.0 剛出不久就在 xdite 大大的文章裡面看到這件事情了XD。
另外一件事情鬼打牆比較久,push
成功之後,還是打不開網站。查看 heroku logs
之後好不容易找到這一段:
2013-11-18T19:28:42.063321+00:00 heroku[web.1]: Starting process with command `bin/rails server -p 4734 -e $RAILS_ENV`
2013-11-18T19:28:49.787036+00:00 app[web.1]: from /app/vendor/bundle/ruby/2.0.0/gems/settingslogic-2.0.9/lib/settingslogic.rb:102:in `initialize'
2013-11-18T19:28:49.787036+00:00 app[web.1]: /app/vendor/ruby-2.0.0/lib/ruby/2.0.0/open-uri.rb:36:in `initialize': No such file or directory - /app/config/config.yml (Errno::ENOENT)
咦,奇~~~~~~~怪了,明明config/config.yml
就好好地蹲在那邊啊,為什麼會找不到咧?弄到半夜三點實在找不出來,只好去怒睡,結果隔天一早醒來就想到了,原來在根目錄下面有一個 .gitignore
...... 好樣地,最下面居然有寫
# Ignore all logfiles and tempfiles.
/log/*.log
/tmp
*.DS_Store
*.swp
.env
public/uploads
vendor/bundler_gems
config/database.yml
config/config.yml
真的是只能大罵靠北啊啊啊啊啊,好吧,那就把 config/config.yml
那行先 comment 掉吧,然後就push成功惹。
啊啊啊啊,再寫下去都可以寫一個 幹譙 heroku 專欄惹。
總之昨天最新遇到的困境是可以跑 heroku run rake db:migrate
,但是不能跑heroku run rake db:reset
或者heroku run rake db:drop
,這根本超級崩潰啊啊啊啊,教練我想刪檔啊QQ
查看 heroku logs
之後,發現做這兩個動作,PostgreSQL會回傳 permission denied,哪招!
後來我自己找到的解法是
heroku pg:reset DATABASE
果然是解鈴仍需繫鈴人,用 pg
的指令就解決了。
另外做個筆記,如果用 heroku 的話, database.yml
不需要寫 username
,然後密碼留白就好,例如
production:
adapter: postgresql
encoding: unicode
database: myapp_production
pool: 5
password:
這招是之前在電機系當網管助教的時候跟學長學的,因為電機系的學生與教授帳號都是基於工作站系統,但是我們寫 web 網頁的主機並沒有直接的管道跟工作站主機溝通,所以其中一個驗證登入身份的方法式利用 telnet
連接 POP3 主機,確認是否可以登入。
這次會實際用到是為了清大校長模擬選舉網站,我們作為一個非官方的網站,但是要能驗證投票者是否為清大學生或者校友,因此最佳的實作方法就是請投票者提供學校計中的信箱帳號和密碼,我們嘗試登入 POP3 主機,便可以快速證明該使用者是否為信箱擁有者。
Ruby 對 telnet 的支援
在寫入 Ruby on rails 之前我先在自己電腦上寫 Ruby 沙盤推演,先確定了 POP3 主機可以用 telnet
溝通而且走 port 110 [1] ,並且很快找到了兩個參考資料,一篇是中文的實作[2] 一篇是 Net::Telnet
的 doc
,看了這兩篇之後才發現 Ruby 的 telnet 支援遠比我想像中還要強大,因此不到半小時就把流程用 Ruby 寫出來了。
最近因為自己經營的粉絲團要辦個抽獎活動,於是就面臨怎麼有效率地把符合資格者的名單列出來的問題。在我的例子裡,符合資格的條件是在有效的時間內,對某篇文章留言。
我想很多商家面對這種事情,一定是花錢請個工讀生或者所謂的「小編」自己把所有的名字複製貼上下來。這樣做不是不好,但是缺點有三
- 曠日費時,留言者動輒上百人
- 容易有人工上遺漏
- 你不確定 facebook 是否有把所有的留言都列出來
而我們身為碼農,怎麼可以讓自己做這種 dirty 的事情呢?用程式來解是一定要的。我第一個想到的方法是抓原始碼,看有沒有結構化的規律,用 regex 來抓就好,因為 Rubular 可以在網頁上解決,就不用 動刀動槍 的了。
但是當你把那一頁存下來之後,你會發現, WTF 根本沒有幾個字是明碼啊。
所以,我放棄了。接著我試著 Google 關鍵字,發現很多人教大家用 FGL 來抓資料。因為我知道 open graph 可以做到我想要做的事情,但是懶得學;但是我會 SQL ,所以看到可以用看起來跟 SQL 長得根本一樣的 FQL 來做,當然是高興的不得了。
但是,人生最厲害的就是這個 but ! FQL 已經停用了。 GG。只好學用 open graph 啦。
使用 open-graph 取得名單
要取得某個粉絲頁(page)特定文章(post)的按讚或者留言者名單,首先你得先知道兩個資訊:
- 該 page 的 Facebook Page ID
- 該 post 的 Post ID
首先粉絲頁的 ID 可以在「關於」頁面的最下面找到。
接著 post id 可以用許多方法找到。
這時候我們就用打開 Facebook 的 API explorer 啦!第一次使用必須要先按一下 Get Access Token,隨意勾選一些項目後,就可以授權讀取資料。
接著我們先試著將剛才得到的 Page ID 代入 open graph 的路徑之下:
此時回傳資料會列出這個粉絲頁的一些諸元。接著我們就要試著探索這個粉絲頁下面的貼文。首先可以試試看
1494556507449732/posts
那麼就會列出前幾篇貼文,還有詳細的推文資料等等。其中我們感興趣的是post id,可以在這裡找到:
另外一個可以找到 post id 的地方當然就是貼文的網址啦!
如果是從網址抓來的話,那你要餵給 open graph 的形式就會變成是
1494556507449732_1541869892718393
總之就是用一個底線隔開啦!
接著事情就簡單了,如果你要查的是該貼文按讚的名單,就是
1494556507449732_1541869892718393/likes
如果要查留言的名單,就是
1494556507449732_1541869892718393/comments
最後,如果回傳資料不是全部的話,可以加上 ?limit=100
這樣就可以指定回傳的數量了。
總之就是如果要抓回該篇貼文的按讚名單,我們就是去詢問這個 open graph
http://graph.facbook.com/1494556507449732_1541869892718393/likes
取出留言者
有了這個 json 檔案之後,最理想的方法當然就是直接用支援 json 的語言去開它。但是如果剛好手邊沒有任何程式語言的平台可以用的時候(例如一台野生的 Windows 電腦...),用網頁工具也是一個好主意。我自己第一個想到的是我很常拿來練習 regex 的網頁工具: Rubular。
方法一: 用 regex
首先把 json 裡面的資料全部貼到網頁上,接著我第一個想到的策略是寫一個「中文或者英文」的條件去抓「名字」,後來又補上了.
和-
也可以。
/"name": "([\s\u4e00-\u9fa5]+|[\w\s\-\.]+)"/
看起來成效不錯,但是居然有漏網之魚!原來有的按讚者是用日文、阿拉伯文...當名字。沒辦法,只好用更骯髒的方式了:
/{\s+"id": "\d+",\s+"name": "([\w\W]{2,30})"\s+}/
嗯,這是連初學者都寫得出來的格式,就只是限制字數而已,但是最重要的是它可行!
方法二: 用 Ruby 讀 json
當然如果手上有 Ruby 或者 Python 當然還是直接對付 json 最快啦。
不囉唆,我的解法如下
# encoding=utf-8
require 'open-uri'
require 'json'
url = 'http://graph.facebook.com/pageid_postid/likes?limit=200'
json = open(url) # download the json file
data_hash = JSON.parse(json.read) # parse the json into hash
data_hash["data"].each do |datum|
puts datum["name"]
end
因為按讚者是包在 data
之中的巢狀結構,所以我寫了一個 each do 去把它們逐一印出來。
如果要印的是「留言者」名單,除了 url 要把 likes
改成 comments
之外,程式的部分請把迴圈部分改為
data_hash["data"].each do |datum|
puts datum["from"]["name"]
end
References
上禮拜辦了一個活動,完全用 facebook 的活動 (event)功能邀請賓客;活動前一天我想要印出賓客清單,才發現 facebook 居然沒有把賓客名單匯出成 .csv 的功能..... 我記得以前有啊?
這種時候就只好動用 facebook 強大的 opengraph API了。opengraph 的 EDGE 除了可以是一個人,一個fan page之外,也可以是一個活動。
取得 event 相關資訊前,要在申請 access token 前額外要求要 event 的權限:
話不多說,來看 code:
# encoding=utf-8
require 'open-uri'
require 'json'
event_id = '1609895432567499'
token = STDIN.read
url = 'https://graph.facebook.com/v2.3/' \
+ event_id + '/attending/?limit=250'
url = url + '&access_token=' + token
json = open(url)
data_hash = JSON.parse(json.read)
data_hash["data"].each do |datum|
puts datum["name"]
end
執行
ruby list_attendees.rb < token > guests.csv
即可
其中 < token
是利用 standard input 將我放在另一個檔案的 token 吃進來; > guests.csv
就是把列印出來的內容存到csv檔裡面。
同場加映
繼上一篇印出 facebook 活動賓客名單,同場加映一下,這次我們把 EDGE 由活動換成社團,印出全部社團成員,並且隨機排列,給他們一個亂數編號。
原先我的想法是按照賓客的長度創照一個連續自然數陣列,再利用 ruby 內建的 shuffle
洗牌,但是這樣名單印出來就是亂的。例如:
nums = 1..16
nums = nums.to_a.shuffle
後來想到一個更簡單的做法,就是直接把成員名單的順序打亂就好。實作如下:
# encoding=utf-8
require 'open-uri'
require 'json'
group_id = '67226335296474'
token = STDIN.read
url = 'https://graph.facebook.com/v2.3/' \
+ group_id + '/members/?fields=name&limit=250'
url = url + '&access_token=' + token
json = open(url)
data_hash = JSON.parse(json.read)
nums = 1..data_hash["data"].size
data = data_hash["data"].shuffle
for t in nums
datum = data[t-1]
puts t.to_s + "\t" + datum["name"]
end
執行
ruby get_members.rb < token
其中 token
放有你的 access token。
如此就會印出洗牌過的成員名單,並加上一個照順序印出的編號了。