2014/12/1

Google 模式 - Eric Schmidt & Jonathan Rosenberg

這本書是 Eric Schmidt 與 Jonathan Rosenberg 所撰寫,內容是說明 Google 的企業文化,Larry Page 在推薦序中指出,Google 人就該有自主思考的力量,每個人都該大膽思考,探尋可能,解決問題,尋找答案。

Google 的文化也不是在公司開門的第一天就建立起來了,我們該有開放的態度,慢慢地去調整與建立一個屬於我們自己的企業文化。接下來,我只把一些我想再討論的議題 highlight 出來,並嘗試進行一些討論,其中會夾雜著我的個人意見。

持續賺錢的方法

大家都知道 Google 最賺錢的業務就是 Adwords,也因為這個能夠持續獲利的金雞母,讓 Google 可以貫徹自己的企業理念,可以不斷賠錢去實驗一大堆高風險的新專案。

但在這樣思考的同時,我們也該想到,如果 Google 天生沒有擁抱創新的原生泥漿,也可能不會誕生一個 Adwords。有點雞生蛋或是蛋生雞的問題。

但很明確的是,Google 在 2002 年以前都還是用最重要的 100個專案的表單來進行資源分配與管理,也就是說,在公司發展的初期,受限於當時的時空背景,Google 還是得用傳統的方式,集中發展的資源在最高決策者認定最有發展潛力的專案上。

一直等到穩定獲利的時空下,才來談 70/20/10 的資源分配法則,這都是為了讓公司規模在快速膨脹下,還能夠保有企業內部創業的原生文化才提出的口號與決定,20% 的資源放在有初步成果的新產品,10% 投入在全新的專案。

10%的資源投資很適當,還有一個道理:創造力熱愛資源受限(creativity loves constraints)。真正有效的想法,反而在受限資源的條件下,讓人可以快速地切入重點。

別聽河馬的意見

河馬也就是公司裡面最高薪資的人的意見,但是決策品質基本上跟薪資高低無關,只要是一個有說服力的理論,就能勝出。

Adword 發展初期遇到了「河馬」布林以及蘇利哈.拉瑪斯兩個點子的對抗,而爭辯的最後,布林的方式被放棄了,原因是布林了解討論中提出的資料,如果換成不了解的河馬,就可能會以職位強壓進行他自己提出的策略。

決策的討論要以資料為基礎進行討論,收集資料成了最重要的前提,如果是大家都沒有做過的事情,沒有解決過的議題,很明顯地就會落入不容易找到佐證資料的狀況。

如果是全新的想法,就可能要做 prototype 來實驗,例如 Google Books 的想法,一開始是用簡單的掃描機制去計算,掃描每一本書所需要耗費的時間,以此推論整個圖書館需要處理多久。

適應改變的能力

人才招募是最重要的事務,找對了人,就能在環境中得到相乘的效果,至於什麼樣的人才,對公司來說是最佳的人才,不同的公司可能有不同的答案,有些公司需要即時的人力,他考慮的只要有基本的程式技能就好了,不需要員工想太多,反正案子進來了,配合客戶把客戶照顧好,就沒有問題了。

在前一次進行面試時,應試者問了我一個問題,你認為我欠缺了什麼樣的能力,應該補足什麼樣的技能?我想了一下,給他的答案是,要擁有「適應改變的能力」。

我給他的理由是,在發展產品的過程中,有很多功能是在公司內部發想,然後進行開發,接下來推送到客戶端,根據回應進行產品功能修正。

什麼樣的功能對產品來說是好的?如果是 Google,可能就是用 20% 的時間先去開發一個 prototype,接下來在內部推動,並取得其他人的回饋意見,最終決策者會判定這樣的修正是不是可以被接受。

20% 的時間事實上是 120%

在沒有看過這本書以前,我們已經聽過 Google 人可以使用 20% 的時間自己進行專案不需要老闆的同意,但事實上這 20% 的 Free Time,還是有一些遊戲規則在背後的。

首先是 20% 的時間不代表每一星期都可以使用星期五作為 Free Time,而是在不影響現有工作的前提下,才能進行自己的專案,止於這 20% 的時間,並不一定就是在公司上班時間的 20%,而是要包含自己的時間。

換句話說,公司鼓勵內部創業,但自己的事情要先做好,而且內部創業,有想法之後,必須自己找到認同你的想法的人加入你的專案,取得別人的認同是首要條件。

至於時間,有些人是利用一個暑假,或是週六週日的時間,沒有人規定可以放掉手邊的工作,直接進行自己的專案。也就是說,20% 的 Free Time,在發展初期可能是 120%,一直要等到有初步成果開花結果下,公司才認定可以分配資源繼續進行下去。

使用 email 的方法

在處理對應客戶的問題的習慣下,我常常會做以下的事情,接收到問題,嘗試去解決並尋找答案,在一天內進行回應,我非常認同,每一個人做事都該盡可能地在很短的時間內,進行回應,就算是告訴客戶,你已經在了解中,但還不知道怎麼解決,這也是一種良性的回應。經常清除 email 收件匣這個建議,就跟快速回應是一樣的,因為要快速回應,我們就不應該常在收件匣裡面看到有還沒有處理過的 email。

讓要求事項很容易後續追蹤這個建議,讓我想到,gmail 的基本功能,其實就是圍繞著這個想法,收到郵件時,首先是過濾器,可輕鬆地設定標籤,決定優先順序,並排除掉 promo 或是垃圾信。接下來是閱讀與處理,如果閱讀後,認定需要追蹤處理,則可以歸類到 todo 的項目中。至於 Draft 也像是一種 todo,還沒有寫完的回信,都是必須要盡快處理的事項。

被挑戰的準備

正如同前面提到的適應改變的能力,我常常在等待我的意見被挑戰與否定,原因是一方面我不認為自己永遠是對的,如果只因為常常主導意見的角色,而壓抑了其他人的聲音,這並不是個健康的互動方式。

我承認有些時候,會因為時程的壓力,我必須進行強勢的時間安排與規格的主導,但在這樣的會議過程中,如果有不一樣的聲音,而且又有絕佳的理由與論點,這是非常有幫助的。如果我可以在還沒有實現的一開始,就馬上調整方法與作法,相對來說就節省了很多成本的浪費。

所以我必須告訴自己說,要有被挑戰的準備,而相對來說,也要做好準備挑戰其他人。

擁抱改變

我認定自己在專案管理上,一直以來常常在進行動態調控,因為我並沒有辦法在產品發展的初期,就知道會遇到什麼困難,因此我必須不斷地根據開發的狀況,遇到的問題與解決的狀況,動態調控人力的分配與資源,甚至有時候還需要更深入去了解問題,並在沒有在該專案上寫過任何一行 code的狀況下,嘗試去想像 programmer 可能會怎麼去寫這一段的程式邏輯,而因為這樣的邏輯缺陷,而造成某些問題。

我認為自己該「擁抱改變」,甚至該擁抱不間斷地改變,這改變並非一夕之間,但確實每天都在發生。企業生命體也該如此,每天持續地做出更適當的決定,在能持續生存的前提下,持續鍛鍊出更健康的公司體質。

如果你的出發點是個人的利益得失,那每天就得帶著鋼盔與龜殼做事,期待不會被子彈掃射,如果出發點是公司的成敗,那麼就該擇善固執,認為是對的事情,就得去做,並嘗試證明你的意見是對的。

2014/11/19 「關於把Google模式用在台灣,我想說的是...」
(轉)從書中Google的實際故事了解google獨樹一幟的管理哲學
《Google模式》:Eric Schmidt 教你 Google 人怎麼使用電子郵件
《Google 模式》:在網路時代找工作如同衝浪,讓技術洞察者為你指引明燈
Google人才招募九大守則,不想解雇員工,一開始就別錄取他
Google模式(讀後心得)
偷學《Google 模式》!學會 70/20/10 法則,建立說 Yes 的企業文化
《Google模式》談人才 -- 招募是最重要的事務!

2014/11/24

再用 list comprehension 解魔方陣

小朋友的學校又給了一個特殊的魔方陣題目,erlang 的 list comprehension 解魔方陣真的很快,把每一種可能發生的狀況慢慢列出來,就可以得出結果了。

題目:
五芒星共十個節點,填入 1 ~ 10 的數字,外側有五個小三角形,每一個小三角形的三個數字和,如果是 13,請列出十個節點的填法。再把總和改為14,15,16,17,18,19,20,分別找出可能的填寫方法。



如果用筆慢慢計算,必須要這樣想,五個三角形總和都是 13,13*5=45,因為 1~10 十個數字的總和是 55,但中間的 A,B,C,D,E 各被多加了一次,所以 A+B+C+D+E+55=45,因此 A+B+C+D+E = -10,此題無解。

至於 14,就是 A+B+C+D+E+55=14*5,所以 A+B+C+D+E=15,再慢慢列舉 A,B,C,D,E 進而找出 F,G,H,I,J。

用 erlang 可以這樣寫 matrix.erl

-module(matrix).
-export([resolve/1]).

resolve(SUM) ->
    statistics(runtime),
    statistics(wall_clock),
    K = lists:seq(1,10),
    L=[{A,B,C,D,E,F,G,H,I,J}||
     A <- K,
     B <- K--[A],
     F <- K--[A,B],
     A+B+F == SUM,

     C <- K--[A,B,F],
     G <- K--[A,B,F,C],
     B+C+G == SUM,

     D <- K--[A,B,F,C,G],
     H <- K--[A,B,F,C,G,D],
     C+D+H == SUM,

     E <- K--[A,B,F,C,G,D,H],
     I <- K--[A,B,F,C,G,D,H,E],
     D+E+I == SUM,

     J <- K--[A,B,F,C,G,D,H,E,I],
     A+E+J == SUM
    ],
    {_, Time1} = statistics(runtime),
    {_, Time2} = statistics(wall_clock),
    io:format("runtime=~p wall_clock=~p ~n", [Time1, Time2]),
    L.

編譯後,再到 erl 執行。

1> matrix:resolve(13).
runtime=0 wall_clock=0
[]
2> matrix:resolve(14).
runtime=15 wall_clock=15
[{1,3,5,2,4,10,6,7,8,9},
 {1,4,2,5,3,9,8,7,6,10},
 {2,4,1,3,5,8,9,10,6,7},
 {2,5,3,1,4,7,6,10,9,8},
 {3,1,4,2,5,10,9,8,7,6},
 {3,5,2,4,1,6,7,8,9,10},
 {4,1,3,5,2,9,10,6,7,8},
 {4,2,5,3,1,8,7,6,10,9},
 {5,2,4,1,3,7,8,9,10,6},
 {5,3,1,4,2,6,10,9,8,7}]

我們把一個答案填到五芒星中。



目前的缺點是 A,B,C,D,E 重複的 set,並沒有排除掉相同的答案,還沒想到要怎麼處理。

結果蠻奇怪的,13, 15, 18, 20 沒有解,14,16,17,19 有解,沒有什麼特別的規則。

2014/11/17

kamailio installation step by step

先前已經測試過 opensips,雖然可以使用,但還是遇到一些問題,例如網頁使用者界面的套件一直沒辦法運作地很順利,我們試著改成版本更新速度比較快的 kamailio,接下來就是安裝的過程。

準備工作

安裝 kamailio 之前,必須把 CentOS 基本的套件裝好,通常我們會把開發者工具、kernel 的開發套件都裝上去,還會裝上 EPEL、rpmforge 這兩個 package repository。

因為 kamailio 的 dialplan 需要,所以必須要安裝 pcre,因為 rtpengine 的需要,所以要安裝 xmlrpc-c-devel iptables-devel。

yum -y install pcre pcre-devel libpcap libpcap-devel libunistring libunistring-devel xmlrpc-c-devel iptables-devel

安裝 kamailio

kamailio 才剛在 2014/10/16 發布 kamailio 4.2.0 版,我們可以到 kamailio download page 下載 kamailio-4.2.0_src.tar.gz。

把原始程式碼放在 /usr/local/src 資料夾中。

cd /usr/local/src
tar zxvf kamailio-4.2.0_src.tar.gz

cd kamailio-4.2.0
make cfg

修改 modules.lst 一行資料

vi modules.lst

include_modules= db_mysql websocket tls dialplan

編譯並安裝 kamailio

make all
make install

現在安裝完成的 kamailio

設定檔在 /usr/local/etc/kamailio
執行擋在 /usr/local/sbin

這裡面有4個執行檔

kamailio - Kamailio SIP server
kamdbctl - script to create and manage the Databases
kamctl - script to manage and control Kamailio SIP server
sercmd - CLI - command line tool to interface with Kamailio SIP server

modules在 /usr/local/lib64/kamailio/modules/
文件在 /usr/local/share/doc/kamailio/
man page 在 /usr/local/share/man/man5/ 以及 /usr/local/share/man/man8/

產生 mysql database

編輯 /usr/local/etc/kamailio/kamctlrc

vi /usr/local/etc/kamailio/kamctlrc
把這一行設定的註解移掉
DBENGINE=MYSQL

修改 DB 預設的密碼

DBRWPW="dbpassword"
DBROPW="dbpassword"

執行

/usr/local/sbin/kamdbctl create

結果會產生兩個 mysql users,預設密碼的部份剛剛有改過了,應該會變成 dbpassword。

kamailio 預設密碼 kamailiorw
    有 'kamailio' database 完整權限
kamailioro 預設密碼 kamailioro
    有 'kamailio' database read-only 權限

雖然 已經改過 /usr/local/etc/kamailio/kamctlrc 的密碼,需要再一次覆寫資料庫的密碼。

mysql -u root -p
use mysql;
UPDATE user SET Password=PASSWORD("dbpassword") WHERE User='kamailio';
UPDATE user SET Password=PASSWORD("dbpassword") WHERE User='kamailioro';
flush privileges;

製作啟動服務的 script

先把 kamailio 核心的設定檔改好。

cp /usr/local/src/kamailio-4.2.0/pkg/kamailio/centos/6/kamailio.init /etc/init.d/kamailio
mkdir -p /etc/kamailio
cp /usr/local/etc/kamailio/kamailio.cfg /etc/kamailio/

修改 DB 的設定

vi /etc/kamailio/kamailio.cfg

在檔案前面增加三行
#!define WITH_MYSQL
#!define WITH_AUTH
#!define WITH_USRLOCDB
修改 DBURL 密碼
#!ifndef DBURL
#!define DBURL "mysql://kamailio:max168kit@localhost/kamailio"
#!endif

以 kamailio 的 init script sample 把啟動服務的 script 做好。

cp /usr/local/src/kamailio-4.2.0/pkg/kamailio/centos/6/kamailio.sysconfig /etc/sysconfig/kamailio

chmod 755 /etc/init.d/kamailio

修改 script 內容

vi /etc/init.d/kamailio
修改這2行
KAM=/usr/local/sbin/kamailio
RUN_KAMAILIO=yes


# 最後面增加 -f $KAMCFG 
OPTIONS="-P $PID_FILE -m $SHM_MEMORY -M $PKG_MEMORY -u $USER -g $GROUP $EXTRA_OPTIONS -f $KAMCFG "

修改執行 kamailio 的 user 權限

mkdir -p /var/run/kamailio
adduser --system --shell "/sbin/nologin" --home /var/run/kamailio kamailio
chown kamailio:kamailio /var/run/kamailio

設定 SIP Domain 變數, 有二種方式

1.
export SIP_DOMAIN=192.168.1.24
2.
vi /root/.kamctlrc
SIP_DOMAIN=192.168.1.24

執行 script 時有一些錯誤。

which: no greadlink in (/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/)

修改第 19 行可以解決這個問題

vi /usr/local/sbin/kamctl

which greadlink > /dev/null 2>&1

使用獨立的log檔案

檢查 kamailio.cfg 設定

vi /etc/kamailio/kamailio.cfg
debug=3 #此值控制日誌輸出的詳細程度,3為普通,4為詳細(會產生很多日誌)。
log_stderror=no #設置為no表示將日誌輸出到文件,否則輸出到控制台(應該是以前台方式啟動opensips服務時才有用)。
log_facility=LOG_LOCAL0 #應該是用於在syslog服務的配置文件裡區分opensips產生的日誌(見下面"使用獨立的log文件")。
fork=yes #設置為yes表示在後台啟動opensips服務,設置為no表示在前台啟動。

kamailio 使用syslog服務,在沒有作任何設定的狀況下,log 會進入/var/log/message這個檔案,如果希望使用獨立的log檔案,可以這樣設定

touch /var/log/kamailio.log
vi /etc/rsyslog.conf
增加一行
local0.* /var/log/kamailio.log

/etc/init.d/rsyslog restart

一併把 logrotate 設定好

vi /etc/logrotate.d/kamailio.logrotate
/var/log/kamailio.log {
   missingok
   rotate 5
   daily
   create 0640 root root
}

安裝 網頁界面 siremis

把 siremis 準備好

cd /usr/local/src
tar zxvf siremis-4.1.0.tgz
mv siremis-4.1.0 /var/www/html/siremis

產生 apache conf file

cd /var/www/html/siremis
make apache-conf

依照內容建議,編寫 apache httpd config for siremis

vi /etc/httpd/conf.d/siremis.conf
Alias /siremis "/var/www/html/siremis/siremis"
<Directory "/var/www/html/siremis/siremis">
    Options Indexes FollowSymLinks MultiViews
    AllowOverride All
    Order allow,deny
    Allow from all
    <FilesMatch "\.xml$">
        Order deny,allow
        Deny from all
    </FilesMatch>
    <FilesMatch "\.inc$">
        Order deny,allow
        Deny from all
    </FilesMatch>
</Directory>

修改資料夾權限,重新啟動 httpd

make prepare
chown -R apache:apache /var/www/html/siremis

service httpd restart

建立 siremis DB

mysqladmin create siremis -p

mysql -uroot -p
GRANT ALL PRIVILEGES ON siremis.* TO siremis@localhost IDENTIFIED BY 'dbpassword';

連上 siremis 網頁,一開始會是一個設定的 wizard,我們必須把資料庫的密碼,改成剛剛修改後的 dbpassword。

http://localhost/siremis/

在產生 DB 的畫面最下面,把這三個項目打勾

Import Default Data 打勾
Update sip database 打勾
Replace DB Config 打勾

最後只要看到 Installation Completed 的畫面,就完成安裝程序了。

2014/11/10

在 CentOS 6 安裝 redmine 專案管理工具

原本公司內是使用 bugzilla 做專案的 task/issue tracking 工具,但是 bugzilla 卻沒有 gantt chart 的功能。我們需要的是一個可以取代 bugzilla 的專案管理工具,這要分兩個部份來看,programmer 必須要能使用 Eclipse Mylyn 直接連結、查看、處理跟自己相關的 task/issue,專案管理者要能看到 gantt chart,再加上一些其他的管理工具,survey 後就選擇跟 trac 相近的 redmine。

雖然 redmine 在安裝上並不像 php 專案那麼簡單,但因為內建提供了 gantt chart,網頁使用者界面又比較簡潔,因此就試著安裝 redmine,接下來進行試用,最後就是把 bugzilla 退役。

準備工作

安裝 redmine 之前,必須把 CentOS 基本的套件裝好,通常我們會把開發者工具、kernel 的開發套件都裝上去,還會裝上 EPEL、rpmforge 這兩個 package repository。

redmine 的官方網頁目前提供了兩個版本 2.5.3 以及 2.6.0,我先把兩個版本的原始程式碼取回來。

安裝 redmine

首先把 Ruby on Rails 相關的套件裝好。

yum -y install ruby ruby-devel ImageMagick ImageMagick-devel rubygem-rake

wget http://download.opensuse.org/repositories/home:csbuild:centosextra/CentOS_CentOS-6/home:csbuild:centosextra.repo
mv home:csbuild:centosextra.repo /etc/yum.repos.d/

yum -y install rubygem-bundler

在 MariaDB(或是 MySQL)建立 redmine DB 跟使用者資料。

mysql --user=root --password=dbpassword
create database redmine character set utf8;
create user 'redmine'@'localhost' identified by 'dbpassword';
grant all privileges on redmine.* to 'redmine'@'localhost';
quit;

解壓縮 redmine 原始程式碼,一開始我們是先試著安裝 2.6.0 版,但因為到後面一直遇到 email 的設定問題,嘗試多個解決方案的過程中,又使用 2.5.3 版重新安裝了一次,基本上 2.6.0 以及 2.5.3 的安裝過程沒有什麼很大的差異。

tar zxvf redmine-2.6.0.tar.gz
mv redmine-2.6.0 /var/www/redmine

cd /var/www/redmine/config
cp database.yml.example database.yml

設定資料庫使用者名稱、密碼。

vi database.yml

把
production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: root
  password: ""
  encoding: utf8

改為

production:
  adapter: mysql2
  database: redmine
  host: localhost
  username: redmine
  password: "dbpassword"
  encoding: utf8

調整 Gemfile 的相依性套件,增加 mongrel, dispatcher

vi /var/www/redmine/Gemfile

在
gem "rbpdf", "~> 1.18.1"
這一行的後面增加兩行

gem 'mongrel', '>= 1.2.0.pre2'
gem 'dispatcher'

安裝 ROR 相關套件,在鍵入 bundle install 這一個步驟的指令之後,console 的畫面會停住,看起來很像是當掉的狀態,而且會停著大約10~20分鐘,搜尋解法後,大家回應只說這可能是 https://rubygems.org/ 的網路問題,所以這個步驟就只能等,沒有別的解決方法。

cd /var/www/redmine
bundle install --without development test

安裝 redmine 資料庫

rake generate_secret_token
RAILS_ENV=production rake db:migrate
# 載入預設資料
RAILS_ENV=production rake redmine:load_default_data

# 輸入 zh-TW

如果先裝了 2.6.0 再重裝 2.5.3,在上面這個步驟,就會遇到 rake 升級後的 版本錯誤,這時候就改用下面這些指令

bundle exec rake generate_secret_token
RAILS_ENV=production bundle exec rake db:migrate
RAILS_ENV=production bundle exec rake redmine:load_default_data

調整 redmine 資料夾

mkdir -p tmp tmp/pdf public/plugin_assets
chown -R apache:apache files log tmp public/plugin_assets
chown -R apache:apache /var/www/redmine
chmod -R 755 files log tmp public/plugin_assets

基本上這樣就裝好了,可以直接用 webrick 啟動 redmine。

ruby script/rails server webrick -e production

redmine 網頁會在

http://localhost:3000/

跟 Apache 整合

因為要用Apache當作我的Web Server,所以要透過 Phusion passenger 做和 Ruby on Rail的處理。

首先要安裝Phusion passenger

yum -y install make zlib-devel ruby-devel rubygems ruby-libs apr-devel apr-util-devel httpd-devel mod_dav_svn subversion subversion-ruby automake autoconf curl-devel darcs hg bzr

cd /var/www/redmine/
gem install passenger
passenger-install-apache2-module

畫面看起來很奇怪時, 就鍵入 ! ,因為預設就選擇了 ruby python, 所以就直接 enter。

在/etc/httpd/conf.d/加上redmine.conf,並編輯redmine.conf加入以下的設定。

vi /etc/httpd/conf.d/redmine.conf

LoadModule passenger_module /usr/lib/ruby/gems/1.8/gems/passenger-4.0.53/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
    PassengerRoot /usr/lib/ruby/gems/1.8/gems/passenger-4.0.53
    PassengerDefaultRuby /usr/bin/ruby
</IfModule>

RailsBaseURI /redmine
<Directory /var/www/redmine/public>
    # This relaxes Apache security settings.
    AllowOverride all
    Options -MultiViews
</Directory>

在DocumentRoot的路徑下加上redmine的symbolic link

ln -s /var/www/redmine/public /var/www/html/redmine

這個設定過程很重要,因為 redmine 官方只提供了以 virtual host 的方式跟 apache 整合,但我們希望用 http://localhost/redmine/ 的網址方式,連結 redmine,努力兩天後,得到上面的解決方案。

設定 email

首先取得設定檔

cd /var/www/redmine/config
cp configuration.yml.example configuration.yml

因為我們是使用 gmail 帳號,同時也使用了 google apps 代管公司的 email,在設定 email 的時候,會遇到很多狀況,基本上再努力兩天,得到下面的解決方案。

修改 Gemfile.lock 的 mail 版本號碼, 由 2.5.4 改為 2.5.3

vi /var/www/redmine/Gemfile.lock

修改第 7 行
    mail (~> 2.5.3)

修改第 50 行
    mail (2.5.3)

修改後要執行

bundle install --without development test

如果是使用 gmail 帳號,要把 POP3 的功能打開,然後填寫設定

vi /var/www/redmine/config/configuration.yml
    delivery_method: :smtp
    smtp_settings:
       enable_starttls_auto: true
       address: "smtp.gmail.com"
       port: 587
       domain: "smtp.gmail.com"
       authentication: :login
       user_name: "maxkit@gmail.com"
       password: "youremailpassword"

如果是使用 google apps 代管 email 帳號,要把 POP3 的功能打開,然後填寫設定

vi /var/www/redmine/config/configuration.yml
  email_delivery:
    delivery_method: :smtp
    smtp_settings:
      enable_starttls_auto: true
      address: "smtp.gmail.com"
      port: 587
      domain: "maxkit.com.tw"
      authentication: :login
      user_name: "maxkit@maxkit.com.tw"
      password: "youremailpassword"

讓 eclipse mylyn 可以使用 redmine

Redmine官網的HowToMylyn裡提到Redmine的V2.x以後需要使用Redmine-Mylyn Connector。但是這個只是服務端的plugin。客戶端的Eclipse plugin有好幾個git repo的clone。目前最活躍的是這兩個。

服務端的Redmine plugin的版本庫地址: https://github.com/danmunn/redmine_mylyn_connector

客戶端的Eclipse plugin的版本庫地址: https://github.com/ljader/redmine-mylyn-plugin

安裝過程如下

cd /var/www/redmine/plugins
git clone git://github.com/danmunn/redmine_mylyn_connector.git
cd ..
bundle install --without development test

Eclipse 的部份則是先下載 plugin: net.sf.redmine_mylyn.p2repository-0.4.0-SNAPSHOT.zip

接下來在 Eclipse -> Help -> Add New Software -> Add -> Archive ,選取 net.sf.redmine_mylyn.p2repository-0.4.0-SNAPSHOT.zip 後就可以安裝 plugin,剩下的部份,就跟一般設定 mylyn repository 一樣了。

結語

雖然 redmine 的界面簡潔,但要安裝 remine 會遇到不少困難,而這些問題,在官方網站卻找不到明確的幫助,反而得要自己慢慢地 google 搜尋,然後測試每一個人說的到底對或錯,才能解決這些奇怪的問題。

2014/11/3

scala: 不在迴圈中使用變數

通常在撰寫迴圈時,最直覺的寫法,就是帶入變數,隨時在迴圈中檢查變數的值,也可以利用變數控制,是否要跳出迴圈,但 functional style programming 的重點,除了希望 programmer 能用更易讀的方式撰寫程式,同時希望程式的效能,可以得到顯著的提昇,因此消滅不必要的迴圈與變數,成了 functional programming 的另一項重要任務,而 java programmer 要學習的,是忘記那些 OO Design Patterns,讓程式碼更精簡。

Java 的定位是商用語言,以往的歷史也證實,在 Server Side 的運算環境中,採用 Java J2EE solution 是個很好的選擇,但也因為是 Server Side 的語言,java programmer 比較不在意使用了多少變數,消耗了多少記憶體,著眼點通常放在要把功能做好,模組切割要合理,系統要穩定,只要選個好一些的 Server 多一些記體體就可以運作了。

Function Programming 讓我重新回到撰寫 C 語言程式碼的心情,C 語言追求卓越的速度,在意自己使用了多少記憶體,有時候甚至還要動用 assembly,相容於 Java 的 Scala 是個雙面人,我們該沿用 Java 帶來的物件導向分析習慣,將系統模組化,接下來在實作時,思考如何撰寫高效率的 FP 程式。

if expression

  var filename = "default.txt"
  if (!args.isEmpty)
    filename = args(0)

如果換個寫法,就可以不需要使用 var,程式也比較短,使用 val 也比較接近 functional style,要使用 variable 之前,要先想一下是不是可以 改寫成 expression,另外要盡可能使用 val,可讓程式更容易 refactor。

  val filename =
    if (!args.isEmpty) args(0)
    else "default.txt"

while loops

unit value 寫成 (),() 的存在,就是跟 java void 不同的地方,因為 greet() 會回傳 Unit, 所以 greet() 就會等於 ()。

  scala> def greet() { println("hi") }
  greet: ()Unit

  scala> greet() == ()
  hi
  res0: Boolean = true

以 java 的習慣,可能會這樣寫

  var line = ""
  while ((line = readLine()) != "")
    println("Read: "+ line)

但因為 line = readLine() 的結果是 () Unit,而 Unit 一定不會等於 "",所以就會永遠是 true

  var line = ""
  do {
    line = readLine()
    println("Read: "+ line)
  } while (line != "")

使用 recursion 取代 while

如果要計算 g.c.d 可以這樣寫

  def gcdLoop(x: Long, y: Long): Long = {
    var a = x
    var b = y
    while (a != 0) {
      val temp = a
      a = b % a
      b = temp
    }
    b
  }

改用 functional style: recursion 撰寫 gcd, 同時可以省去很多不需要的vars。

  def gcd(x: Long, y: Long): Long =
    if (y == 0) x else gcd(y, x % y)

generator

  val filesHere = (new java.io.File(".")).listFiles

  for (file <- filesHere)
    println(file)

file <- filesHere 的語法稱為 generator

如果要對一堆檔案重複進行某個運算,也許我們會很直覺地這樣寫

  for (i <- 0 to filesHere.length - 1)
    println(filesHere(i))

這種寫法的缺點是需要產生一個變數 i,因此這種寫法,在 scala 是不常見的,我們必須修改成,直接對 file collection 做 iteration,而 iteration 可能有下面四種問題:filtering、nested iteration、mid-stream variable binding、producing a new collection。

filtering

取得檔名是 .scala 結尾的 檔案

  val filesHere = (new java.io.File(".")).listFiles
  for (file <- filesHere if file.getName.endsWith(".scala"))
    println(file)

也可以使用兩個以上的 filter

  for (
    file <- filesHere
    if file.isFile
    if file.getName.endsWith(".scala")
  ) println(file)

nested iteration

在 for 裡面,也可以同時使用多個 generator

  def fileLines(file: java.io.File) = 
    scala.io.Source.fromFile(file).getLines().toList

  def grep(pattern: String) =
    for (
      file <- filesHere
      if file.getName.endsWith(".scala");
      line <- fileLines(file)
      if line.trim.matches(pattern) 
    ) println(file +": "+ line.trim)

  grep(".*gcd.*")

mid-stream variable binding

上面的程式碼,重複執行了 line.trim,如果想要只運算一次,就要產生一個變數 trimmed = line.trim,trimmed 用了兩次, 一次在 if, 一次在 println。

  def grep(pattern: String) =
    for {
      file <- filesHere
      if file.getName.endsWith(".scala")
      line <- fileLines(file)
      trimmed = line.trim
      if trimmed.matches(pattern)  
    } println(file +": "+ trimmed)

  grep(".*gcd.*")

producing a new collection

用 yield, 可以把過濾後的結果 array 紀錄起來,結果會得到 Array[File], 因為 filesHere 是 array, file 是 File。yield 必須放在 for 的外面,語法為 for clauses yield body。

  def scalaFiles =
    for {
      file <- filesHere
      if file.getName.endsWith(".scala")
    } yield file

try - catch - finally

在 finally 中關閉 file, 確保檔案一定有被關閉,scala 可以在 finally 裡面 return value, 但會把結果覆蓋掉。

  val file = new FileReader("input.txt")
  try {
    // Use the file
  } finally {
    file.close()
  }

match expression

類似 java 的 switch,預設是 _ ,每個 case 裡面不需要寫 break,不只可以用 int, enum, 也可以用 string,可直接將 match 結果,儲存到變數中。

  val firstArg = if (!args.isEmpty) args(0) else ""

  val friend =
    firstArg match {
      case "salt" => "pepper"
      case "chips" => "salsa"
      case "eggs" => "bacon"
      case _ => "huh?"
    }

  println(friend)

no break and continue

因為 scala 沒有 break 跟 continue,最簡單的方式,就是把 continue 換成 if,把 break 換成 boolean 變數。

  var i = 0
  var foundIt = false

  while (i < args.length && !foundIt) {
    if (!args(i).startsWith("-")) {
      if (args(i).endsWith(".scala"))
        foundIt = true
    }
    i = i + 1
  }

更好的寫法是 recursive 呼叫 seachFrom

  def searchFrom(i: Int): Int =
    if (i >= args.length) -1
    else if (args(i).startsWith("-")) searchFrom(i + 1) 
    else if (args(i).endsWith(".scala")) i
    else searchFrom(i + 1)

  val i = searchFrom(0)

Variable scope

scala 的 scoping rule 跟 java 大部分都一樣
只有一點不同,scala 可在 nested scopes 中定義同樣的 variable name。

  val a = 1;
  {
    val a = 2 // Compiles just fine
    println(a)
  }
  println(a)

但是在 scala interpreter 中,看起來雖然像是同一個 scope,但實際上是不同的,因此在 interpreter 中,變數可重複定義。

  scala> val a = 1
  a: Int = 1

  scala> val a = 2
  a: Int = 2

  scala> println(a)
  2

Reference

Programming In Scala by Martin Odersky, Lex Spoon, and Bill Venners