2013年9月27日 星期五

PostgreSQL 新增使用者及設定「唯讀」權限

新增user

以下是新增使用者之指令及相關選項:

Usage:
  createuser [OPTION]... [ROLENAME]

Options:
  -c, --connection-limit=N  connection limit for role (default: no limit)
  -d, --createdb            role can create new databases
  -D, --no-createdb         role cannot create databases
  -e, --echo                show the commands being sent to the server
  -E, --encrypted           encrypt stored password
  -i, --inherit             role inherits privileges of roles it is a
                            member of (default)
  -I, --no-inherit          role does not inherit privileges
  -l, --login               role can login (default)
  -L, --no-login            role cannot login
  -N, --unencrypted         do not encrypt stored password
  -P, --pwprompt            assign a password to new role
  -r, --createrole          role can create new roles
  -R, --no-createrole       role cannot create roles
  -s, --superuser           role will be superuser
  -S, --no-superuser        role will not be superuser
  --help                    show this help, then exit
  --version                 output version information, then exit

Connection options:
  -h, --host=HOSTNAME       database server host or socket directory
  -p, --port=PORT           database server port
  -U, --username=USERNAME   user name to connect as (not the one to create)
  -w, --no-password         never prompt for password
  -W, --password            force password prompt

If one of -d, -D, -r, -R, -s, -S, and ROLENAME is not specified, you will
be prompted interactively.

其中比較重要的是 -P 這個選項,在建立使用者的同時為他設定密碼。如最後一行(紅字)提到的,「創建DB」(d)、「創建user」(r)、「是否為最高權限」(s)。


為使用者設定唯讀權限


在postgres中,並沒有所謂的「唯獨權限」,所以我們必須透過修改使用者的預設參數來達到這個目的:

alter role digoal set default_transaction_read_only=true;



開放 Tables 和 Sequences 權限給使用者


特定 Table:
GRANT SELECT ON table_name TO role_name;

所有 Tables:
GRANT SELECT ON ALL TABLES IN SCHEMA schema_name(通常是public) TO role_name;

所有 Sequences:
GRANT SELECT ON ALL SEQUENCES IN SCHEMA schema_name TO role_name;

Reference

德哥@Digoal的博格:http://blog.163.com/digoal@126/blog/static/1638770402011111274336235/

2013年9月21日 星期六

Ajax跨域請求 - jQuery.getJSON

從事前端開發的工程師應該或多或少都知道ajax無法做跨域請求,除了JSONP這種形式以外:
$.getJSON( 
    "http://localhost:3030/parse?callback=resHandler",
    { q: queryText }
);

但是這樣的用法是不會觸發success callback的,因為從server side回傳的資料會被當做script直接執行,假設從server side回傳的訊息為:
{
    name: "kevin",
    age: 18
}

如此一來在收到這個訊息後會直接執行這段程式碼,但是因為這段訊息並不遵守javascript的文法,因此會fail掉。聰明的程序猿一定知道,我們必須在server side回傳的資料上加點料,讓我們要的資料能適合的函式處理。
從第一段程式碼中看到,我們在請求的url上加了一個callback參數,通知server side 我們要用什麼函式來對資料做處理,假如這段函式叫resHandler好了,我們期待的運算是這樣:
resHandler(data);

data為server應該要回傳的資料。所以在server side我們就應該要這樣處理:
var data = '{msg: "success!"}' //主機回傳資料
var callbackFunction = req.query.callback;

res.send(callbackFunction+"("+")");

這時client端接收到這段資訊後會直接執行他:
resHandler({msg: "success!"})

假如resHandeler為 function(data){alert(data.msg);} ,user便會看到視窗跳出"success"的提示。

2013年9月8日 星期日

還22k一個清白吧,請先檢視自己的價值及定位

聽到太多人罵22k,到底22k是怎麼來的?


「大專畢業生企業實習方案」,俗稱22k方案

當初政府在推行這個政策的心態是好的,教育部補助26,190元讓企業聘請「實習生」,扣除勞健保後,實習生實領22k,這也是22k的由來。
這個政策的推出使得了三萬八千人能夠投入職場,而即便這個方案結束了,當年的38,000人中也有六成五的留職率。

但如果沒有22k方案,那些人能這麼順利找到工作嗎?

老實說當年的政策讓受惠者能得到「寶貴的第一份工作」,有了經驗後總比白紙一張好找工作。所以我覺得這個政策本質上是利大於敝的。


22k只是個數字罷了

當然有人說,從此以後22k成了企業的標準薪資。這可能是當初政策所造成的定錨效應沒錯,所以很多人看到這個數字就「高潮」了。但是問題真的不在這個數字,而是供需法則。當大家通通爭著讀大學、研究所,甚至博士班,但相應的職缺又沒有增加。所以大家只好開始往待遇相對較差的職位找工作,然後就有「你看隔壁的那個小孩唸到碩士班結果薪水還不到四萬,你大學畢業後一定要念個研究所不然一定活不下去」這種說法。

看到同儕讀研究所,於是也跟著讀研究所,發現碩士供過於求於是又讀博士,然後跟弟弟妹妹說至少要讀個研究所不然找不到工作.....於是就進入了惡性循環,大家永遠在跟風、讀書、嚇自己。


改變心態吧,教育不是為了讓你找個好工作,而是希望你適性發展

在台灣,高等教育是分一般、技職、跟師範體系的,為的就是因材施教。但大家往往對技職體系不屑一顧,搞到最後技職體系也沒守住貞操,紛紛改稱「科技大學」,放棄了最重要的技職訓練。搞到最後大家讀出來都一樣,科大出來的不會修水電不會車床不會修馬達,也不想進生產現場學習,人人都想坐辦公室。
但退一百步來說,其實這樣也ok,因為這是你的選擇,喜歡讀書就讀吧~但是從什麼時候開始你有了「讀好書就可以拿高薪」的錯覺?現在的文憑很貴,但對職場來說一點都不值錢,為什麼?因為「好學校」是教你怎麼讀書,而不是怎麼「和職場接軌」
其實當初政府希望教育部去推動這個22k專案時,很多學校是覺得很莫名其妙的,「我們的目的是要『作育英才』,這乾我屁事?」
所以這也是為什麼我覺得大家要從心態開始改變,認真考慮自己為了什麼讀書。大家都想大學畢業後坐辦公室,才符合自己所受的「高等教育」,每月看到薪水條上的數字後就大嘆學歷不值錢。但是偏偏自己的工作可替代性太高,你不做還有一堆人等著做,「唉,還是待著吧」。
相較之下,很多藍領的薪水不知道比22k高多少了,更別提三到五年的「老手」,薪水可能隨便都是22k的3~4倍。到你家修馬桶、冷氣的可能比你的薪水還要高。

嫌薪水低?承認吧,你只是不想把手弄髒而已。


假設你是老闆,你會.....?

案例一

如果我今天要雇一個應屆畢業生當工程師,一樣的錢我寧願雇學士,而不是碩士甚至博士生。Why?很簡單,因為學校絕對沒教我需要的技能,反正我還是要從頭訓練,學士、碩士、博士對我來說有差嗎?

真的沒差嗎?ㄟ.....讓我想想......

有!
一,大學畢業的比碩士少操兩年、比博士少操2+n年,別講熱情,光講體力就比你不知道好多少。
二,一樣的薪資,「學士感激、碩士無感、博士嫌少」。

所以你說我幹嘛不請個學士就好?


案例二

如果你當了老闆,今天要找一個行政助理,門檻超低。現場有10位畢業生搶著應徵,學歷都差不多,私立xx大學xx管理系之類的,個性或積極度也看不出有什麼不同,而期望待遇從2萬到4萬都有,你會願意花多少錢?


結論勒?

  1. 問題不會全都在政府,如果你自覺被虧待,也請好好檢討一下自己
  2. 如果你真的覺得賺錢最重要,那別急著念研究所,多賺兩年的薪水再加上年資絕對比得上一個碩士學位
  3. 想賺錢切忌眼高手低
  4. 如果你真的覺得辦公室就是你的歸屬,而且念了一堆書也拿不出什麼可以說嘴的專長,薪水的部份就別太要求了吧

2013年8月17日 星期六

Rails排程工作套件 - whenever

專案中或多或少會需要排程的功能,不免俗的,Rails強大的生態圈中當然也出現了幾套好用的排程gem,其中最多人使用的就是「whenever」。

安裝

gem 'whenever', :require => false

其中":require => false"的變數是為了不讓這個gem被專案require,因為其實他本身並不會被專案所呼叫,那也就沒有必要被載入專案,佔用記憶體。

原理:
linux中本身就有排程的功能(參見鳥哥的linux私房菜),whenever只是以更親切的方式來提供開發者使用,但實際上還是由os來執行cronjob。


初始化

在專案底主目錄下:
wheneverize .
這樣會產生config/schedule.rb,我們需要在裡面設定隔多久執行特定的job。


設定cronjob

上一個步驟中產生的schedule.rb中其實已經有一些範例可以做參考,基本上格式就是:

every 1.day, :at => '4:30 am' do
  runner "MyModel.task_to_run_at_four_thirty_in_the_morning"
end

以上述程式碼來說,要執行的method就寫在my_model.rb中,這樣應該是足夠明瞭了。


最後一步

但可不是這樣就搞定了,因為對os來說,crontab中仍然沒有任何排程工作。可以用以下指令來檢視:
crontab -l
所以我們要先執行whenever的一段指令讓我們寫在schedule.rb的排程工作實際加到crontab中才行:
whenever --update-crontab store
這時我們再執行 crontab -l 就可以看到剛剛加入的行程了。


2013年7月28日 星期日

如何在Rails中重置所有user的session

前陣子因為遇到devise更改了他的session配置,使得專案中一個功能出了點問題(改版前登入使用者的session配置與改版後登入的使用者不同),所以最近申請的user會引發一個bug。為了讓所有的使用者統一session格式,我必須強制清除所有user的session,讓他們登出才行。

重設secret_token

要做到這樣的目標很簡單,只需要將環境中的secret_token更新在重啟即可,方法如下:

  1. 在terminal中,專案資料夾下輸入 rake secret,會拿到一串亂碼
  2. 將上一步驟中得到的亂碼替換掉「config/initializers/secret_token.rb」中的MyApp::Application.config.secret_token
  3. 重啟伺服器

這樣就完成了

2013年6月27日 星期四

利用formData來達成ajax上傳檔案

最近手邊有個案子用到canvas來做圖片的合成,並且要將輸出圖片存到facebook相簿中,一般來說這種要將圖片合成的功能都在server-side完成,這次用canvas除了為了省時以外,也想盡量減輕server-side的負擔。就這個專案的需求來說,操作canvas不是難事,但是以canvas.toDataURL輸出的dataURL要怎麼以file的形式post到fb呢?


首先,要把 dataURL轉成blob

下面這四點是我在stackoverflow上查到的四個步驟:
  1. Take a bytearray or any other data
  2. Convert it to base64
  3. Convert it to blob
  4. Send it as a normal file with Ajax/xhr/FormData using your favorite method
下面這段程式碼就是上述的1~3個步驟

function (dataURI) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs
    var byteString = atob(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    var blob = new Blob([ab]);
    return blob;
}

不要問我上面的code怎麼來的,我抄來的!(挺)
接下來我曾經好傻好天真的認為可以把blob塞進form的欄位中,然後把它submit出去,但最後發現是不可以的...


用Ajax將檔案post出去吧


查了一下發現最方便的方式是使用formData物件,只要只用他的append方法就能把blob或string append上去:
var fd = new FormData();
fd.append("source", blob);
接下來只要將他post出去就好囉:
var xhr = new XMLHttpRequest();
xhr.open("POST", url);
xhr.send(fd);

2013年6月26日 星期三

Rails的db:seed小技巧

在開發Rails專案時,在db的sync上,常常會遇到下面兩個情況:

1. 開發環境與正式環境的初始資料
2. 多人開發時的local DB同步問題

第二個問題其實只要開一個遠端的伺服器來共用就解決了,但是在使用sqlite或是沒有網路的情況下,這麼做實在很麻煩,而且有時候我們也不希望別人的資料弄髒資料庫。這時候我們可以使用seed.rb來初始化資料庫中的資料,我們來看看怎麼做:
# seed.rb

areas = [
  {name:"台北"},
  {name:"高雄"}
]
Area.create areas
然後下rake db:seed這個指令就行了


進階需求


但是需要初始化的資料往往都是系統的固定選項(如縣市等不應被隨意增加、減少的資料),這時常常會有需要指定id的需求,這時我們就要稍微修改一下作法:
areas = [
  {id:1, name:"台北"},
  {id:2, name:"高雄"}
]
areas.each do |area|
  s = Area.new area
  s.id = area[:id]
  s.save
end

如果是確定此資料表中只會出現seed中所寫入的資料的話,我們可以加上delete_all來先清空資料表再做插入資料的動作:
Area.delete_all

使用csv匯入資料


在只有少量資料需要被處理時,直接將資料寫在seed.rb十分方便,但是當資料量很大時,我們希望能以報表的形式事先整理好再做匯入。而csv是一個格式簡單,也能被Excel, Numbers等報表軟體支援的檔案格式,因此我選擇它作為外部資料匯入的媒介。

但是在Excel中比較難去設定成我們想要的utf-8編碼,比較建議使用Apple的Numbers軟體,可以直接輸出成utf-8的csv檔。

來一段簡單的程式碼大家應該就能看懂了:
# seed.rb

require "csv"
SysSchool.delete_all
CSV.foreach("#{Rails.root}/db/school.csv") do |row|
 school = {
  id:row[0],
  name:row[1],
  sys_area_id:row[2]
 }
 s = SysSchool.new school
 s.id = school[:id]
 s.save
end

這段程式碼也是要貼在seed.rb中的,這樣一來在執行"rake db:seed"時,就會先刪除資料表中的資料,並將db/school.csv裡的資料一列一列插進資料表中。