2014/12/01

Homestead 2.1

Laravel 真的很坑爹,不久前才寫了 Homestead 的安裝方式,Homestead 就出了 2.0,不過其實也沒有差異到非常大啦,讓我們來 step by step 吧。

一樣,先加入 laravel 的 box,現在的版本是 0.2.1。

vagrant box add laravel/homestead

下載完之後,要進行這次最大的改變,使用 composer 安裝啟動 homestead 的相關套件指令,因此我們輸入 composer global require "laravel/homestead=~2.0",安裝完畢以後,我們就可以在任意處使用 homestead 相關指令了。

基本指令
destroy   Destroy the Homestead machine
edit      Edit the Homestead.yaml file
halt      Halt the Homestead machine
help      Displays help for a command
init      Create a stub Homestead.yaml file
list      Lists commands
resume    Resume the suspended Homestead mach
run       Run commands through the Homestead
ssh       Login to the Homestead machine via
status    Get the status of the Homestead mac
suspend   Suspend the Homestead machine
up        Start the Homestead machine
update    Update the Homestead machine image

基本上指令就是沿用 Vagrant 的常用指令,只不過讓你省去切換到 homestead 資料夾後執行指令這個動作,改用類似 homestead up 替代,接下來我們先執行 homestead init,執行之後在 ~/.homestead 目錄下會產生 Homestead.yaml 檔案,該檔案就是 homestead 的設定檔,也就是 1.0 的 Vagrantfile,Windows 的部分存在於 c:/Users/Your_Account/.homestead,打開檔案後其實跟之前的內容是一模一樣的,就不再贅述了

---
ip: "192.168.1.12"
memory: 1024
cpus: 1

authorize: C:/Users/ACCOUNT/.ssh/id_rsa.pub

keys:
    - C:/Users/ACCOUNT/.ssh/id_rsa

folders:
    - map: D:/www/laravel
      to: /home/vagrant/www/laravel

sites:
    - map: homestead.app
      to: /home/vagrant/www/laravel/public

databases:
    - homestead

variables:
    - key: APP_ENV
      value: local

這是我這次的 config,儲存後執行 homestead up,如果遇到權限問題,請執行 ssh-keygen -t rsa -C "you@homestead",啟動完畢後,你可以使用 homestead ssh 進入 server,或者用第三方軟體 ssh ip,預設帳密都是 vagrant,要使用 root 的話密碼也是 vagrant,登入以後,可以切換到 /home/vagrant/www 下,可以看到 Laravel 已經裝好了,1.0 的話要自己在執行 create project 的動作,2.0 之後會幫你自己裝好,切到自己本機 D:/www/laravel 目錄,發現檔案是同步的,安裝部分成功了。

nginx 的部分 homestead 也自動會對應完成,所以我們現在要修改 Windows 下的 C:\Windows\System32\drivers\etc\hosts,Linux 則是在 /etc/hosts/,將對應的 domain 加入 192.168.1.12 homestead.app,開啟我們的 browser,輸入 http://homestead.app,當你看到美麗的 Laravel logo 之後,美好的事情正要開始 :)

2014/11/26

MySQL Scripter

MySQL Script

最近常常需要在沒有 phpMyAdmin 的情形下進行 MySQL 的操作,索性把一些常用的指令寫成了一個 tool,就叫做 MySQL Scripter 吧。

2014/11/11

Laravel With Vagrant

一般 programmer 開發程式都是自己安裝自己的開發環境,不管在 Windows 或者是 Linux 都有所謂的懶人包,譬如說早期的 AppServXAMPP 以及 WampServer,Linux 的部分也有,但我通常都是用套件管理裝的所以就不舉例了,如果大家都是 Windows 並且強迫安裝同一套就還好,但如果 A 同事是 Windows 愛用者,B 同事用 Ubuntu,C 同事用 Mac,這樣就有點麻煩了,虛擬機器這個 solution 就可以確保所有的人開發環境都一樣,目前最佳解大概就是 Vagrant + VirtualBox 了。

Vagrant 可以說是 VirtualBox 的管理套件,以往我們要使用 VirtualBox,就是開一個環境,然後 download 某個 OS 的 ISO 檔案,掛載虛擬光碟,然後再進 VirtualBox 慢慢安裝,Vagrant 可以讓你把已經設定好的環境打包成一個 box,有點類似 ghost,所以你可以找人家包好的環境,例如說今天需要一個「真正」的 IE 6 的測試環境,你可以去 http://www.vagrantbox.es/ 或者是 https://vagrantcloud.com/ 搜尋 Windows XP,看到有人打包了 indows XP with IE6,我們就可以輕輕鬆鬆部屬這個環境,只要以下的指令即可。

vagrant add ie6_test http://aka.ms/vagrant-xp-ie6
vagrant ie6_test
vagrant up

甚至你可以自己把自己建置好的環境打包給別人使用,今天來示範一下怎麼快速建置一個 Laravel 環境,Laravel 有製作自己的 Vagrant Box 叫 Homestead,我來一步步操作怎麼在 Windows 建立官方的範例。

下載 laravel/homestead

vagrant box add laravel/homestead

當然你要在你的電腦先裝好 Vagrant 以及 VirtualBox,當他跑完安裝以後,可以執行 vagrant box list,如果有看到 laravel/homestead (virtualbox, 0.2.0) 代表你的 box 已經進來了,接下來我在 D:/ 建立一個叫 vm 的資料夾,進入 D:/vm,執行 git clone https://github.com/laravel/homestead.git Homestead,成功後看到 D:/vm/Homestead 資料夾,進去之後打開 Homestead.yaml 來修改,一般來說我們要 init 一個新的 box 都是修改 Vagrantfile 這個 config,而 Laravel 這個 yaml 就是他們獨立出來的客製化 config,預設如下:

---
ip: "192.168.10.10"
memory: 2048
cpus: 1

authorize: ~/.ssh/id_rsa.pub

keys:
    - ~/.ssh/id_rsa

folders:
    - map: ~/Code
      to: /home/vagrant/Code

sites:
    - map: homestead.app
      to: /home/vagrant/Code/Laravel/public

variables:
    - key: APP_ENV
      value: loca

ip 就是該虛擬機器的 ip,memory 就是你要給這台機器占用多少記憶體,authorize 以及 keys 是 Laravel 驗證 ssh 使用,folders 是指 folder sync,也就是說,假設我們在 D:/ 建立了一個資料夾,將他跟 vm 裡面的開發資料夾做 sync 的話,我們就不用整天都要 ssh 進去該 vm 來改程式,只要改我們 D:/ 的資料夾以後 vm 裡面的開發資料夾就會同步 sync 了,sites 就是將 domain name 指定到開發資料夾,如果你沒有要修改 hosts 的話,這個東西就沒有作用了,variables 就不用管,所以修改過後的內容如下:

---
ip: "192.168.10.14"
memory: 1024
cpus: 1

authorize: C:/Users/YOUR_USER_NAME/.ssh/id_rsa.pub

keys:
    - C:/Users/YOUR_USER_NAME/.ssh/id_rsa

folders:
    - map: 'D:/vm/laravel'
      to: /home/vagrant/www

sites:
    - map: laravel.app
      to: /home/vagrant/www/laravel/public

variables:
    - key: APP_ENV
      value: local

如果你的驗證有問題的話,請執行 ssh-keygen -t rsa -C "you@homestead",如果順利起來的話,執行 vagrant ssh 就可以直接進入 server,如果你想用其他的 ssh 軟體,只要指定 ssh 127.0.0.1 2222 即可,帳號密碼都是 vagrant,我們進入剛指定的資料夾 /home/vagrant/www,開始建立我們的 Laravel 專案,執行 composer create-project laravel/laravel --prefer-dist,composer 會開始幫我們載入檔案,我們可以切換到 D:/vm/laravel 資料夾,應該多了一個 laravel 資料夾並且正在同步下載檔案,安裝完畢後在 browser 跑 192.168.10.14,如果看到 Laravel 歡迎畫面,表示打完收工。

Homestead 幫你打造了下列的環境:

  • Ubuntu 14.04
  • PHP 5.6
  • HHVM
  • Nginx
  • MySQL
  • Postgres
  • Node (With Bower, Grunt, and Gulp)
  • Redis
  • Memcached
  • Beanstalkd
  • Laravel Envoy
  • Fabric + HipChat Extension

實用的 vagrant 指令

vagrant box list # 列出現在所有的 box
vagrant box remove xxx # 移除某個 box
vagrant destroy # 將某個目錄下的 vm 銷毀
vagrant status # 查看目前 vm 的狀況
vagrant halt # 關閉目前的 vm
vagrant reload # 重啟 vm

2014/11/07

Transaction With Eloquent

今天研究 Database Transaction 的時候發現了一招可以 Eloquent 跟 Transaction 混用。

<?

$user = new User;
$user->name = 'Chan';

$member = new Member;
$member->age = 33;

DB::transaction(function() use ($user, $member) {
    try {
        $user->save();
        $member->save();
    } catch (Exception $e) {
        dd($e->getMessage());
    }
});

Abstract Interface Traits 差異

PHP 對於 class 的使用方式很多,在這邊做一下筆記整理。

Basic

<?php

class Main
{
    public function foo()
    {
        echo 'foo';
    }
}

class Sub extends Main
{
    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->bar(); // bar
$sub->foo(); // foo

這是最基本的 class 應用,Sub extends 了 Main,因此可以在 Sub 拿到 protected 以及 public 的變數以及使用 function。

Interface

<?php

interface Main
{
    public function foo();
}

class Sub implements Main
{
    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->bar(); // bar

interface 是規範 class 一定要有對應的 function 宣告,所以上面的例子退吐出下列的錯誤訊息。

Fatal error: Class Sub contains 1 abstract method and must therefore be declared abstract or implement the remaining methods

我們只要宣告一個同等名稱的 function 即可。

<?php

interface Main
{
    public function foo();
}

class Sub implements Main
{
    public function foo()
    {
    }

    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->bar(); // bar

interface 本身是不可以下功能的,所以通常設計來當作一種使用規範。

abstract

<?php

abstract class Main
{
    abstract public function foobar();

    public function foo() {
        echo 'foo';
    }
}

class Sub extends Main
{
    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->foo(); // foo
$sub->bar(); // bar

abstract 等於 basic 用法跟 interface 用法的集合,所以上述的內容會吐出錯誤訊息。

Fatal error: Class Sub contains 1 abstract method and must therefore be declared abstract or implement the remaining methods

此時我們要在 Sub 加上對應的 function name。

<?php

abstract class Main
{
    abstract public function foobar();

    public function foo() {
        echo 'foo';
    }
}

class Sub extends Main
{
    public function foobar()
    {
    }

    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->foo(); // foo
$sub->bar(); // bar

traits

<?php

trait Main
{
    public function foo() {
        echo 'foo';
    }
}

class Sub
{
    use Main;

    public function bar()
    {
        echo 'bar';
    }
}

$sub = new Sub;
$sub->foo(); // foo
$sub->bar(); // bar

trait 是一個有趣的東西,他可以透過 use 的方式直接調用 class,彈性無比的大,但確切的使用時機目前我還不清楚。

大亂鬥

<?php

abstract class Cool
{
    abstract public function foo3();

    public function foo2()
    {
        echo 'foo2';
    }
}

interface Happy
{
    public function foo4();
}

interface Smile
{
    public function foo5();
}

trait Main
{
    public function foo() {
        echo 'foo';
    }
}

class Sub extends Cool implements Happy, Smile
{
    use Main;

    public function bar()
    {
        echo 'bar';
    }

    public function foo3()
    {
    }

    public function foo4()
    {
    }

    public function foo5()
    {
    }
}

$sub = new Sub;
$sub->foo(); // foo
$sub->bar(); // bar
$sub->foo2(); // foo2

2014/11/06

MongoDB 初探

NoSQL 紅一陣子了,比較有名的不外乎是 Redis 或者是 MongoDB,Redis 一般來說比較會運用在快取上(類似 memcached),MongoDB 比較會使用在 persistence database solution,來示範一下這兩天試用 Mongo 的心得。

安裝

http://www.mongodb.org/downloads

MongoDB 對各家 OS 支援度很高,今天測試的環境是 Windows,所以我們就下載 Windows 的版本,我是下載了 zip 版本,下載後將壓縮檔案內 bin 資料夾下的檔案全部複製到 c:\mongodb

PHP 預設是沒有支援 MongoDB 的,但安裝方式很簡單,首先我們去下載 dll 檔案,下載最新的版本 php_mongo-1.5.7.zip,解壓縮後選擇符合你 server 版本的 dll,將他複製到 PHP 的 ext 資料夾,並且更名為 php_mongo.dll,修改 php.ini,加入 extension=php_mongo.dll,重啟 Apache,接著看一下 phpinfo,如果你看到以下的畫面,表示你已經成功了。

啟用

基本的 MongoDB 啟用很簡單,首先我在 c:\mongodb 下多建了一個 db 資料夾,接著啟用他。

mongod --dbpath="c:\mongodb\db"

如果沒有任何的錯誤訊息,恭喜你成功啟用了

使用

http://us3.php.net/manual/en/book.mongo.php

PHP 官方有非常詳細的使用文件,我們來看幾個比較常用的例子。

Connection

$m = new MongoClient();
$db = $m->selectDB('db');

若預設值如 ip 以及 port 沒改變的話,這是最基本的連線方式,當然還有其他的連線方式,甚至可以做到 replica。

$m = new MongoClient();
$db = $m->selectDB('db');
$users = $db->users;
echo $users->count(); // 0

MongoDB 的資料是一種 collection 的概念要存取相當簡單,上面的例子指定了 users 這個 connection,但我們沒有任何的資料,所以 count 的結果當然是 0,接下來我們塞一些資料進去。

$m = new MongoClient();
$db = $m->selectDB('db');
$users = $db->users;
$data = array(
    'name' => 'Chan',
    'age'  => 34
);

$users->insert($data);
echo $users->count(); // 1

上面的例子我們使用了 insert 功能塞進了一筆資料,count 的結果就變成 1 了,如果 refresh 頁面的話數字就會變成 2,若要多重存取的話可以使用另一個 batchInsert function。

$m = new MongoClient();
$db = $m->selectDB('db');
$users = $db->users;
$users->drop();
$data = array(
    array(
        'name' => 'Chan',
        'age'  => 34
    ),
    array(
        'name' => 'Phoebe',
        'age'  => 35
    )
);

$users->batchInsert($data);
echo $users->count(); // 2

上面的範例為了避免資料重複的 insert,所以我多加了 $users->drop() 的 function,此時 count 的結果就為 2 了,接下來我們來抓取資料。

$m = new MongoClient();
$db = $m->selectDB('db');
$users = $db->users;
$users->drop();
$data = array(
    array(
        'name' => 'Chan',
        'age'  => 34
    ),
    array(
        'name' => 'Phoebe',
        'age'  => 35
    )
);

$users->batchInsert($data);

foreach ($users->find() as $id => $item) {
    echo 'id: ' . $id;
    var_dump($item);
}

我們會得到

id: 545af6263ad521b819000031
array (size=3)
  '_id' => 
    object(MongoId)[9]
      public '$id' => string '545af6263ad521b819000031' (length=24)
  'name' => string 'Chan' (length=4)
  'age' => int 34
id: 545af6263ad521b819000032
array (size=3)
  '_id' => 
    object(MongoId)[10]
      public '$id' => string '545af6263ad521b819000032' (length=24)
  'name' => string 'Phoebe' (length=6)
  'age' => int 35

以上就是最基本的存跟取了,其他的條件式 PHP 官網有詳細的範例,SQL to Mongo Mapping Chart

SQL Statement Mongo Query Language Statement
CREATE TABLE USERS (a Number, b Number) Implicit or use MongoDB::createCollection().
INSERT INTO USERS VALUES(1,1) $db->users->insert(array("a" => 1, "b" => 1));
SELECT a,b FROM users $db->users->find(array(), array("a" => 1, "b" => 1));
SELECT * FROM users WHERE age=33 $db->users->find(array("age" => 33));
SELECT a,b FROM users WHERE age=33 $db->users->find(array("age" => 33), array("a" => 1, "b" => 1));
SELECT a,b FROM users WHERE age=33 ORDER BY name $db->users->find(array("age" => 33), array("a" => 1, "b" => 1))->sort(array("name" => 1));
SELECT * FROM users WHERE age>33 $db->users->find(array("age" => array('$gt' => 33)));
SELECT * FROM users WHERE age<33 $db->users->find(array("age" => array('$lt' => 33)));
SELECT * FROM users WHERE name LIKE "%Joe%" $db->users->find(array("name" => new MongoRegex("/Joe/")));
SELECT * FROM users WHERE name LIKE "Joe%" $db->users->find(array("name" => new MongoRegex("/^Joe/")));
SELECT * FROM users WHERE age>33 AND age<=40 $db->users->find(array("age" => array('$gt' => 33, '$lte' => 40)));
SELECT * FROM users ORDER BY name DESC $db->users->find()->sort(array("name" => -1));
CREATE INDEX myindexname ON users(name) $db->users->ensureIndex(array("name" => 1));
CREATE INDEX myindexname ON users(name,ts DESC) $db->users->ensureIndex(array("name" => 1, "ts" => -1));
SELECT * FROM users WHERE a=1 and b='q' $db->users->find(array("a" => 1, "b" => "q"));
SELECT * FROM users LIMIT 20, 10 $db->users->find()->limit(10)->skip(20);
SELECT * FROM users WHERE a=1 or b=2 $db->users->find(array('$or' => array(array("a" => 1), array("b" => 2))));
SELECT * FROM users LIMIT 1 $db->users->find()->limit(1);
EXPLAIN SELECT * FROM users WHERE z=3 $db->users->find(array("z" => 3))->explain()
SELECT DISTINCT last_name FROM users $db->command(array("distinct" => "users", "key" => "last_name"));
SELECT COUNT(*y) FROM users $db->users->count();
SELECT COUNT(*y) FROM users where AGE > 30 $db->users->find(array("age" => array('$gt' => 30)))->count();
SELECT COUNT(AGE) from users $db->users->find(array("age" => array('$exists' => true)))->count();
UPDATE users SET a=1 WHERE b='q' $db->users->update(array("b" => "q"), array('$set' => array("a" => 1)));
UPDATE users SET a=a+2 WHERE b='q' $db->users->update(array("b" => "q"), array('$inc' => array("a" => 2)));
DELETE FROM users WHERE z="abc" $db->users->remove(array("z" => "abc"));

管理工具

Robomongo

2014/10/31

我的婚禮攝影

結婚也快半年了,一直想找時間分享關於婚禮攝影這件事情,但忙碌愉快的婚姻生活讓我拖稿許久,先聲明一下,這是我自己長期下來的感受,或許每個人有每個人的看法,大家還是可以選擇自己舒適的方式進行,結婚一生一次,在這個重要的時刻,還是要尊重聽取雙方的意見,快快樂樂地進行婚禮才對。

相機發達的現代,人手一台單眼的朋友應該不在少數,所以常常會看到托自己朋友幫忙拍攝婚禮的情況,我自己之前就常常被找,所以感受是最深的,來說說我的經驗談。

我想好好的參加

基本上,被找去幫朋友拍攝結婚過程當然開心,不過這也代表了你準備裡裡外外東奔西跑,全神貫注在攝影上面,其實這樣很累,也沒有辦法好好享受朋友重要的人生過程,時常得觀察現場狀況作出臨場反應,畢竟你不是拿這個當職業,很少人能夠每個場地拍超過三次,也就是說,可能每一次的拍攝對你來說都是「挑戰」,而這個挑戰,是你好哥們或好姊妹人生最重要的時刻,壓力可想而知。

我不能出包

我想很少有業餘的朋友身上會背兩台相機的,我自己就沒有,所以每一次的「挑戰」基本上都有一定的風險存在,相機要故障可以掛點的零件太多了,如果沒有萬全的準備,每一次的幫忙根本都是個賭注,求神拜佛我的機器不要出事,不然我該怎麼交代,專業的攝影師每次出機會準備兩台甚至三台的相機以備不時之需,這是一般人不可能做的事情。

我的東西真的行嗎

拍照這件事感覺好像都一樣,不就按下快門然後記錄當下嗎,其實不然,各種題材都有其精專的地方,你商攝拍的好人像不一定,人像拍的好風景不一定,風景拍得好的食物不一定,婚禮攝影尤其困難,因為你要面對的是人,人變化的排列組合根本是無窮大,有沒有經驗差很多,沒有拍出當下人與人的自然感情跟現場的氣氛,控光、構圖、顏色那些就不重要了,攝影眼是很難培養的,而通常被找的朋友可能只屬於男方或女方的朋友,假設是男方的朋友可能跟男方熟,所以一些親朋好友還可以做些微引導,女方那邊在不熟的狀態下你可能從頭到尾只會聽到「看這邊」,「一、二、三」,「在一張」,這三句話。

我的工具足以面對這一切嗎

攝影環境的光線其實很難掌控,尤其是遇到那種有各種投射燈的餐廳,控光、色偏都可能是一大麻煩,雖然單眼人人有,但你確保自己有足夠的設備(閃光燈、鏡頭)來應付這一切嗎?

基於以上幾點我長年下來的經驗,我自己的婚禮決定好好找一個專業的婚禮攝影,因為我想要我的好兄弟好朋友好好的 enjoy that moment,而且我希望他們全部都出現在照片裡面,被你找去當攝影師的好朋友,好兄弟,往往都消失在你的照片中,這點非常可惜,而且對我來說,我認為婚禮的照片可能是結婚過程第二重要的事情,僅次於婚紗照片。

我跟老婆網路上找了很久很久,一比再比,老婆基本上比較尊重我想法,而我個人偏好日式風格,照片輕鬆有韻味那種感覺,於是我們找上了自然捲

自然捲位於板橋,是幾個朋友自己出來創業的自助婚紗公司,少了大公司的束縛跟規範,他們可以盡情做自己想做的事情,跟我的主攝影師 Evan 聊天當中,我發現他視幫客人拍攝這件事情為一種玩樂的過程,一種開心的態度,當你真正喜歡自己的工作時,你是不會看輕他的,當你把新人當作同樂的朋友時,你是不會辜負他的,我們來看圖說故事吧。

專業的攝影師是會創造畫面的

專業的攝影師是很會利用所有空間的

專業攝影師會在對的時間做對的事情

專業攝影師不會錯過重要的時刻

所以如果可以的話,讓這一生中少數幾次你可能會願意花錢在照相,並且可以男的帥女的美的機會上,聘請個專業攝影師吧,我推薦自然捲,你會滿意的 :)

自然捲-攝影軽美学
22064 臺北市新北市板橋區實踐路93巷3號
週二 - 週日: 13:00 - 21:00
02 8961 2090 (採預約制)

2014/10/20

Xdebug Setting

軟體網址

WinCacheGrind

php.ini

[xdebug]
xdebug.remote_enable = off
xdebug.auto_trace = off
xdebug.auto_profile = off
xdebug.profiler_enable = off
xdebug.profiler_enable_trigger = on
xdebug.profiler_output_name = cachegrind.out.%u.%H_%R
xdebug.profiler_output_dir = "c:/wamp/xdebug_log"

參數帶上 XDEBUG_PROFILE 就可以產生檔案。

Request Many Ajax By Deferred Tip

jQuery Deferred 可以讓 user 達到同時使用許多 Ajax request 時,等待所有 request 完成後再執行功能的目的,大部分的範例都是這樣:

$.when($.ajax('xxx.php'), $.ajax('xxx.php'))
 .done(function() {
  cosnole.log('all done');
 });

程式會等到兩個 Ajax 都執行完畢後才觸發 console log,假設今天我們遇到的情況符合以下條件:

  1. 超過兩個以上的 ajax request
  2. 有一定的邏輯

我們不會希望有十個 request 就在裡面塞十次 code,可以利用一種寫法來達到這個目的

deferred.js

$(function() {
    load()
        .done(function() {
            console.log('all done');
        })

    function load() {
        var results = [];

        for (i = 1; i <= 3; i++) {
            result = $.get('ajax.php').done(function(i) {
                console.log(i + ' is done');
            });

            results.push(result);
        }

        return $.when.apply($, results);
    }
})

ajax.php

<?

sleep(2);
echo rand(1, 999);

js 的範例是指呼叫了 ajax.php 三次,當 array 完成時,回傳 $.when.apply 的結果,使用這個 function 會將所有完成的行為視為 deferred 物件傳回,所以 done 就會有所反應,當然也可以寫成這樣:

$(function() {
    var results = [];

    for (i = 1; i < 3; i++) {
        result = $.get('ajax.php').done(function(i) {
            console.log(i + ' is done');
        });

        results.push(result);
    }

    $.when.apply($, results).done(function() {
        console.log('all done');
    });
})

寫成 function 彈性比較大一點,得到的結果如下

923 is done
144 is done
all done 

2014/10/02

Git Export By Commit Range

git archive 指令可以將檔案匯出一份乾淨的程式碼,不會有任何的 repo 紀錄,我最常用的壓縮成 zip。

git archive --format zip --ouput "file.zip" master

如果想要及時解出一個乾淨的資料夾,可以使用 tar 的功能。

git archive master | tar -x -C /folderName

如果我們有更新檔案想要傳給客戶,但是每次都把整個資料夾 zip 過去對方要覆蓋的話也很麻煩,有一個方法可以指定範圍來壓縮或者是 tar 乾淨的目錄,假設今天要壓縮的範圍是 。

# 首先使用 git log 指令來查出你要更動的範圍
git log --name-only

# 找到了這次變動需要匯出的檔案,範圍是 78428a 到 c8cd230 指令為
git archive --format zip --ouput "file.zip" 78428a $(git diff --name-only 78428a^..c8cd230)

這樣就可以匯出這個範圍 commit 的檔案了。

2014/08/03

紅燒獅子頭

食材
  • 豬絞肉 300g
  • 開陽 20g
  • 香菇三朵
  • 大白菜
調味料
  • 米酒
  • 柴魚片
  • 胡椒
  • 醬油
  • 蠔油
  • 八角
作法
  • 絞肉、一半的開陽末、香菇末、薑末、全蛋、蔥末、糖、胡椒粉、醬油加上少許太白粉攪拌
  • 油滾大火後關火,將食材捏成球狀放入炸
  • 另一半的開陽、蒜頭、八角爆香
  • 倒入高湯、大白菜、柴魚片以及獅子頭煨煮(獅子頭可先用筷子插洞方便入味)
  • 約煮 20 分鐘等大白菜煮爛後即可擺盤
  • 將鍋中湯汁勾薄芡淋上獅子頭

蔭鼓鮮蚵


食材
  • 鮮蚵
  • 豆鼓
  • 蒜苗一支
  • 蔥一支
  • 板豆腐一塊
  • 辣椒一支
調味料
  • 醬油
  • 蠔油
  • 高湯
  • 胡椒
  • 香油
  • 米酒
  • 蒜頭
作法
  • 燒一鍋滾水,放入鮮蚵後立刻熄火,等稍微縮了一點以後撈取備用
  • 豆鼓稍微清洗泡水備用
  • 蔥切小段、跟蒜末、豆鼓、薑末一起爆香
  • 倒入高湯
  • 倒入豆腐
  • 放一點點糖、醬油、蠔油
  • 水滾後下鮮蚵
  • 勾芡後淋上香油即可承盤

2014/07/21

生炒花枝

食材
  • 花枝
  • 紅蘿蔔
調味料
  • 蒜末
  • 薑末
  • 九層塔
  • 蔥一支
  • 辣椒一支
  • 米酒
  • 醬油
  • 胡椒
作法
  • 花枝切片過水川燙備用
  • 薑、蒜頭、辣椒,一半的蔥爆香
  • 倒入紅蘿蔔跟筍片
  • 加入水、醬油、胡椒、糖燒紅蘿蔔跟筍片
  • 紅蘿蔔跟筍片快熟時倒入花枝,太白粉水勾芡
  • 稍微悶一下下放入剩下的蔥跟九層塔
  • 稍微攪拌後鍋邊米酒起鍋

糖醋里肌

食材
  • 里肌肉
  • 鳳梨罐頭兩片
  • 半片青椒
  • 半根紅蘿蔔
  • 洋蔥 1/6
  • 番茄 1/2
調味料
  • 蔥一條
  • 薑末
  • 蒜末
  • 醬油膏一大匙
  • 番茄醬五大匙
  • 醬油
作法
  • 里肌肉切厚片
  • 使用太白粉、胡椒、水、米酒、醬油、蛋黃抓肉醃漬
  • 下油鍋炸酥約七分熟
  • 薑末、蒜末、蔥段爆香
  • 倒入里肌肉後,放入番茄、洋蔥、鳳梨
  • 加入水、醬油膏、番茄醬,少許糖,少許鹽
  • 收汁的差不多最後再加青椒拌勻後起鍋

鹹酥蝦

食材
  • 蝦子
調味料
  • 辣椒
  • 胡椒
  • 米酒
作法
  • 蝦子先用、米酒、鹽醃漬
  • 高溫下油鍋酥炸約 20 秒
  • 蒜末、薑末、蔥末、辣椒末爆香
  • 蝦子下去加胡椒跟鹽炒勻,鍋邊米酒起鍋

麻婆豆腐

食材
  • 豬肉末
  • 豆腐一盒
  • 蔥一支
  • 薑少許
  • 蒜頭一顆
調味料
  • 辣豆瓣醬兩匙
  • 米酒
  • 香油
作法
  • 蔥薑蒜切末爆香
  • 放入絞肉炒熟
  • 放入豆腐以及辣豆瓣醬,加水煮
  • 收汁差不多後加入少許胡椒,太白粉水勾芡
  • 滴入少量香油起鍋

2014/07/20

客家小炒

食材
  • 五花肉
  • 乾魷魚
  • 五香豆乾約三片
  • 芹菜兩支
  • 蒜苗兩支
  • 蔥一支
  • 辣椒一支
調味料
  • 冰糖
  • 蠔油兩大匙
  • 米酒
  • 烏醋
  • 胡椒
作法
  • 五花肉切條,先下鍋與冰糖拌炒,因為豬肉本身會再出油,所以油不用放太多
  • 肉約六分熟後,將處理過切條的魷魚與豆乾下去再炒一會
  • 加少許水,蠔油,將蔥、與辣椒下來一起炒
  • 水快收乾時加入蒜苗以及芹菜
  • 些許番炒後,加入鍋邊醋以及米酒

2014/03/21

jQuery Video Creator Plugin

這個 plugin 可以初始擁有 YouTube 或 Vimeo 的超連結點選之後將自己或指定的目標轉換成影片。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Video</title>
    <script src="http://code.jquery.com/jquery-1.11.0.min.js"></script>
    <script src="js/jquery.video_creator.js"></script>
    <script>
    $(function() {
        $('a').video_creator({
            width: 200,
            height: 100,
            target: '#video'
        });
    });
    </script>
</head>
<body>
<a href="https://www.youtube.com/watch?v=p6oaqY6flJg">Youtube</a>
<a href="http://vimeo.com/89495751">Vimeo</a>
<div id="video"></div>
</body>
</html>
(function() {
    $.fn.video_creator = function(options) {
        var params = {
            width: 640,
            height: 360,
            target: ''
        }

        var set = $.fn.extend(params, options);

        return this.each(function() {
            var url = $(this).prop('href'),
                id = '',
                matches = '',
                result = '';

            // Youtube
            if (-1 !== url.toLowerCase().indexOf('youtube')) {
                matches = url.match(/[\\?\\&]v=([^\\?\\&]+)/);
                id = matches[1];
                result = '<iframe width="' + set.width + '" height="' + set.height + '" src="//www.youtube.com/embed/' + id + '" frameborder="0" allowfullscreen></iframe>';
            }

            // Vimeo
            if (-1 !== url.toLowerCase().indexOf('vimeo')) {
                matches = url.match(/\/\/(www\.)?vimeo.com\/(\d+)($|\/)/);
                id = matches[2];
                result = '<iframe src="http://player.vimeo.com/video/' + id + '?title=0&amp;byline=0&amp;portrait=0&amp;badge=0&amp;color=ffffff" width="' + set.width + '" height="' + set.height + '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
            }

            $(this).on('click', function() {
                if ('' === set.target) {
                    $(this).replaceWith(result);
                } else {
                    $(set.target).html(result);
                }

                return false;
            });
        });
    }
}(jQuery));

2014/03/06

Show Directory

我們都知道要秀出目前目錄下的檔案或目錄內容是用 ls,但如何快速精確的只秀出目錄呢

ls -lF | grep '/'

我個人覺得這個指令常常用到,所以就把他加入 .bashrc

alias ld='ls -lF | grep "/"' # 使用 ld 當作指令名稱

然後記得 source .bashrc,大功告成

2014/02/21

Vim Macro

在使用 vim 的時候常常會不小心讓狀態列出縣 recording 的字樣,按 q 可以消除,今天不小心看到某篇文章才發現這是 vim 的 macro 功能,而且十分強大,vim 真的是一個可以活到老學到老的工具,來示範幾個範例。

.sample {
    border: 1px; color: red; padding: 4px; margin: 10px;
}

以這個 css class 來講,假設我們要讓每個 ; 後斷行,有非常非常多的方式,marco 的步驟如下

  1. 先將游標移到 border 字樣
  2. 在 visual mode 按下 qa(qa 是指將過程錄到 a 這個暫存,a-z, 0-9 都可以使用)
  3. 按下 f;,此時游標會跑到 ; 上面
  4. 按下 a,游標會跑到 ; 後面並且變成 insert mode
  5. 按下 enter,完成了第一個斷行
  6. 按下 esc 變成 visual mode,在按一次 q 完成錄製
.sample {
    border: 1px;
    color: red; padding: 4px; margin: 10px;
}

現在內容應該會變成這個樣子

將滑鼠移到 color 那行按下 @a(@ 就是執行 macro,後面接要使用的紀錄),斷行自動產生了,如果要到每一行按 @a 也太弱了,可以在前面接數字指定 marco 次數,以這個 case 來說 3@a 就可以完成所有的斷行。

另外示範一個 increment 的範例,在 vim 中使用 ctrl + a 可以針對某個數字增量,ctrl + x 可以減量,但在 windows 裡面 ctrl + a 會被定義為全選,關掉的方式很簡單,打開 .vimrc 檔,在 behave mswin 下加上 :nunmap <C-a>,這樣便會取消全選功能。

a = 1

以這個例子當範例,可以選擇取代法,也可以用 macro

  1. 在 visual model 將游標移到 a = 1 那行
  2. 按下 qa
  3. 使用 yy 複製,按下 p 貼上
  4. 移到新的行數後按 ctrl + a,會發現數字變成 2 了
  5. 按下 q 完成錄製

按下 @a 可以發現新的行數會變成 a = 3,搭配數字便可以做出你需要的數量了。

2014/01/21

偵測內容改變警告

Facebook 有一個貼心的小功能,如果你有留下訊息,但在還沒有儲存的情況下意外的離開頁面時,Facebook 會給予警告,在討論區如果打了一堆字卻不小心按到回上頁,或者是不小心按到某個連結整頁直接跳走,而瀏覽器又沒有 cache 住你剛輸入的內容,只能用欲哭無淚來形容,怎麼實作這個功能呢。

HTML

Javascript
var isChanged = false;

$('select, input, textarea').on('change', function() {
        isChanged = true;
});

$(window).on('beforeunload', function() {
    if (true === isChanged) {
        return '您已經修改過內容,確定要離開嗎?';
    }
});

簡單來說,預設一個變數是叫 isChange 為 false,偵測該標單的 input、select、textarea 等物件內容改變時將 isChage 更改為 true,然後在 window 的 beforeunload 事件上判斷如果 isChange 為 true 時予以警告。

demo page